Adafruit PCD8544 LCD library

SteveC

Member
I'm trying to build the example for the Adadfruit PDC8544 LDC display: http://ladyada.net/products/nokia5110/#testing. First the compiler could not find util\delay.h and util\delay_basic.h. Then when copied and placed them directly in the example directory it complained about some inline assembly in delay_basic.h. Sure enough delay_basic.h contains some assembly delay loops that are probably AVR assembly.

_delay_loop_1(uint8_t __count)
{
__asm__ volatile (
"1: dec %0" "\n\t"
"brne 1b"
: "=r" (__count)
: "0" (__count)
);
}

I couldn't find and ARM version of this include file. Any ideas would be appreciated. Should I expect this to be part of the Arduino ARM port? I guess given some time I could figure out the ARM assembly for this decrement/branch delay loop.

Thanks,
SteveC
 
Also I found the PROGMEM attribute in the pcdtest example under the library.
static unsigned char PROGMEM logo16_glcd_bmp[] =​
I changed it to:
const unsigned char logo16_glcd_bmp[] =​
and the compiler accepted it. I have to read up on the static qualifier.

SteveC
 
Hmm, I wonder why they need sub-microsecond timing resolution to drive a LCD display? If you did a straight translation of that code to ARM assembly for Teensy 3, you would also need three different versions depending on the clock speed setting (24,48,96 MHz).

Wouldn't a simple C program like
Code:
void simple_delay(int value) { 
volatile int i;
  for(i=0;i<value;) {
    i++;
  } 
}
also do this? Not sure if the 'VOLATILE' keyword prevents the loop from being optimized away to nothing.

By the way, I also have a few of those displays and I would be interested to see this library working!
 
Last edited:
short time delays in C

I just tried a test on my T3, and my timings are shown below. So you can have a minimum delay around 0.5 usec and about 0.2 usec granularity with this function (at 48 MHz clock), without using any assembly. Note that the delay at 96 MHz is almost but not quite half as long, for some reason.
Code:
#define LED 13

void setup() {                
  pinMode(LED, OUTPUT);     
  digitalWrite(LED, HIGH);   // turn the LED on
  delay(2000);                 // wait for a while so user can see LED signal
  digitalWrite(LED, LOW);    // turn the LED off 
}

void loop() {
  shortPulse(10);  // @ 48 MHz: 1 = 0.54 usec, 5 = 1.29 usec, 10 = 2.2 usec
                        // @ 96 MHz: 10 = 1.25 usec
  delay(1000);               // wait for a second
}

void shortPulse(int value) { 
volatile int i;
  digitalWriteFast(LED, HIGH);  // turn on LED
  for(i=0;i<value;i++) { }      // a brief pause...
  digitalWriteFast(LED, LOW);    // turn the LED off 
}
 
Last edited:
Does this library work if you remove the #include <util/delay.h> and change "_delay_ms(500)" to "delay(500)" ?
 
As far as I can tell, the only reason util/delay.h was included was the strange use of _delay_ms(500) instead of the standard Arduino call, delay(500).
Unfortunately it still fails to compile, this time with a C++ error that I don't understand. (line 28 in Adafruit_GFX.h is a class definition of Adafruit_GFX : public )

Code:
Adafruit_PCD8544\Adafruit_PCD8544.cpp.o: In function `Adafruit_GFX':
C:\Program Files\arduino101T\libraries\Adafruit_GFX/Adafruit_GFX.h:28: undefined reference to `vtable for Adafruit_GFX'
 
As far as I can tell, the only reason util/delay.h was included was the strange use of _delay_ms(500) instead of the standard Arduino call, delay(500).
Unfortunately it still fails to compile, this time with a C++ error that I don't understand. (line 28 in Adafruit_GFX.h is a class definition of Adafruit_GFX : public )

Code:
Adafruit_PCD8544\Adafruit_PCD8544.cpp.o: In function `Adafruit_GFX':
C:\Program Files\arduino101T\libraries\Adafruit_GFX/Adafruit_GFX.h:28: undefined reference to `vtable for Adafruit_GFX'

I have been trying to figure out the same linker error. I suspect this kind of error occurs because the ARM compiler is more strict about some coding issues than the AVR compiler. I haven't given up yet.

SteveC
 
older version does compile

Update: The older version of the PCD8544 LCD library from 2010 (before it was split into two separate parts with Adafruit_GFX) compiles OK on Teensy 3 beta 6, after changing two instances of '_delay_ms' to 'delay'. The included example works OK with my Teensy 3, although I had to change the contrast on my particular display for better legibility: nokia.setContrast(60);

It is very small (9k zip file) so I enclose the older, modified library here.
 

Attachments

  • PCD8544_2010.zip
    8.7 KB · Views: 1,315
Last edited:
I had the same problem with the Adafruit 1.8LCD until I switched to an older version, nonAdafruit_GFX dependent, version of the support lib.

I found that some of the offending part was int the initialisation of the main library, which called a constructor in the Adafruit_GFX, but I can be wrong on that one.

What I'm sure about is that with the current (Adafruit_GFX dependent) version I got the same linker error as you, with a tottaly different bit of gear. When I switched to the older (stand-alone) version of the main lib, it ran nicely.

I hope this helps.


*** EDIT ***

JBeale, you were faster than me on the post :), I think we're getting to the same conclusion here.
 
Example of T3 driving LCD display

Yep, still don't know what they did with the newer lib- too fancy for me :). Anyway, just to demonstrate it works, here's my project for this evening, a display to show the reading of my 24-bit ADC. The sketch is also attached in zip.

T3-LCD_Display.JPG
 

Attachments

  • T3_LTC2440.zip
    2.7 KB · Views: 1,103
I have to confess I gave up on this for now, since the older library for my LCD display works OK (before they split it into the GFX part).
 
Fixes to adafruit LCD library for Teensy3

Code:
Adafruit_PCD8544\Adafruit_PCD8544.cpp.o: In function `Adafruit_GFX':
C:\Program Files\arduino101T\libraries\Adafruit_GFX/Adafruit_GFX.h:28: undefined reference to `vtable for Adafruit_GFX'

Yeah, this is a common C++ error: the class needs a virtual destructor in some file to force the compiler to add a virtual table for the class (vtable).

I don't have any of these displays, so I can't test this.. However, I cloned the Adafruit_GFX and PCD8544 libraries, copied the files into the teensy3 directory from Paul's Kickstarter page (also cloned in my mercurial repo here). I made a few code changes:

In Makefile add
Code:
-DARDUINO=100
to OPTIONS.

In the Adafruit_GFX class in Adafruit_GFX.h, add a line:
Code:
  virtual ~Adafruit_GFX() {} // force inclusion of vtable.
and change:
Code:
virtual void drawPixel(int16_t x, int16_t y, uint16_t color);
to:
Code:
virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;

(This latter is an addition of = 0, making this base class method "pure virtual" because it's actually implemented in the derived class. This avoids the error: undefined reference to `Adafruit_GFX::drawPixel(short, short, unsigned short)'.)

In Adafruit_PCD8544.cpp:
Comment out #include <util/delay.h>
change _delay_ms to delay.

I can't seem to attach a patch here, but I've checked my changes into the above mercurial repo.

I then created a main file:

Code:
#include "Adafruit_PCD8544.h"

int main(void)
{
    // TODO change these to your pins:
    // pin 7 - Serial clock out (SCLK)
    // pin 6 - Serial data out (DIN)
    // pin 5 - Data/Command select (D/C)
    // pin 4 - LCD chip select (CS)
    // pin 3 - LCD reset (RST)

    Adafruit_PCD8544 display(7, 6, 5, 4, 3);
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0,0);
    display.println("Hello, world!");
    display.setTextColor(WHITE, BLACK); // 'inverted' text
    display.println(3.141592);
    display.setTextSize(2);
    display.setTextColor(BLACK);
    display.print("0x"); display.println(0xDEADBEEF, HEX);
    display.display();

}


With this, the library compiles and links. Again, I can't test that it works, however. Would be curious to know if it does. I'd really like to play around with one of these VFDs. ($25 at surplus gizmos!)

Hope this helps,

-c
 
Last edited:
Thanks cmason for the library fixes for Teensy 3, I did them here and I confirm that it compiles and works as expected on my Nokia 5110 display.

EDIT... and I see the syntax of the library has changed a little bit too. Just for my own reference, attached is my ADC display application using the new library ("clear" became "clearDisplay" etc.)
 

Attachments

  • LCD_LTC2440.zip
    2.9 KB · Views: 704
Last edited:
SSD1306 compatibilty change?

I have this working with an Adafruit 128x32 OLED SSD1306 display via SPI but had to make one more change in addition to the ones outlined in this thread. But I have no idea what this means (outside of being related to I2C). Can anyone help understand what this means and what effects it might have?

I had to comment out all references to TWBR in this function in Adafruit_SSD1306.cpp:

Code:
void Adafruit_SSD1306::display(void) {
  ssd1306_command(SSD1306_SETLOWCOLUMN | 0x0);  // low col = 0
  ssd1306_command(SSD1306_SETHIGHCOLUMN | 0x0);  // hi col = 0
  ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0

  if (sid != -1)
  {
    // SPI
    *csport |= cspinmask;
    *dcport |= dcpinmask;
    *csport &= ~cspinmask;

    for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
      fastSPIwrite(buffer[i]);
      //ssd1306_data(buffer[i]);
    }
    // i wonder why we have to do this (check datasheet)
    if (SSD1306_LCDHEIGHT == 32) {
      for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
        //ssd1306_data(0);
        fastSPIwrite(0);
      }
    }
    *csport |= cspinmask;
  }
  else
  {
    // save I2C bitrate
    //uint8_t twbrbackup = TWBR;
    //TWBR = 12; // upgrade to 400KHz!

    //Serial.println(TWBR, DEC);
    //Serial.println(TWSR & 0x3, DEC);

    // I2C
    for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
      // send a bunch of data in one xmission
      Wire.beginTransmission(_i2caddr);
      Wire.write(0x40);
      for (uint8_t x=0; x<16; x++) {
	Wire.write(buffer[i]);
	i++;
      }
      i--;
      Wire.endTransmission();
    }
    // i wonder why we have to do this (check datasheet)
    if (SSD1306_LCDHEIGHT == 32) {
      for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
	// send a bunch of data in one xmission
	Wire.beginTransmission(_i2caddr);
	Wire.write(0x40);
	for (uint8_t x=0; x<16; x++) {
	  Wire.write((uint8_t)0x00);
	  i++;
	}
	i--;
	Wire.endTransmission();
      }
    }
    //TWBR = twbrbackup;
  }
}
 
TWBR is the Two Wire Interface Bit Rate Register, which controls the I2C clock rate (for example 100 kHz, 400 kHz or other speed).
I think Atmel calls their I2C implementation "Two Wire Interface" because calling it I2C would require trademark royalties to Philips.
At any rate, Teensy 3 is not an Atmel part, so maybe this TWBR register in the normal Arduino CPU hardware is not currently translated during compile into an equivalent register setting on the ARM chip.

from p. 4, http://www.atmel.com/Images/doc2564.pdf
Application Note AVR315: Using the TWI module as I2C master
The Bite Rate Generator unit controls the period of SCL when operating in a Master
mode. The SCL period is controlled by settings in the TWI Bit Rate Register (TWBR)
and the Prescaler bits in the TWI Status Register (TWSR).

And from the Arduino Wire library:
https://github.com/arduino/Arduino/blob/master/libraries/Wire/utility/twi.c

/* twi bit rate formula from atmega128 manual pg 204
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
note: TWBR should be 10 or higher for master mode
It is 72 for a 16mhz Wiring board with 100kHz TWI */
 
Last edited:
cmason, awesome work on fixing Adafruit_GFX. :)

Have you submitted a pull request? It'd be really nice to get this merged back to their code base.

On TWBR, I'm considering adding it to avr_emulation.h. It's the one AVR TWI register that's easy to emulate.
 
A quick follow up: I swapped my SPI display out for the equivalent Adafruit breakout in I2C and it works, even with the commented out TWBR lines. However, it is extraordinarily slow--I write to serial once per loop and using software SPI feels at least 4x as fast as I2C. Perhaps since the TWBR lines are referencing hardware registers things will speed up once they're emulated. But just wanted to report what I'm seeing in case that means anything.

Thanks to all who got this working!
 
Back
Top