PDA

View Full Version : Adafruit PCD8544 LCD library



SteveC
10-26-2012, 06:07 AM
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

SteveC
10-26-2012, 06:22 AM
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

JBeale
10-26-2012, 06:29 AM
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

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!

JBeale
10-26-2012, 06:56 AM
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.

#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
}

Paul
10-26-2012, 08:47 AM
Does this library work if you remove the #include <util/delay.h> and change "_delay_ms(500)" to "delay(500)" ?

JBeale
10-27-2012, 02:24 AM
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 )


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'

SteveC
10-27-2012, 02:58 AM
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 )


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

JBeale
10-27-2012, 07:12 AM
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.

pixelk
10-27-2012, 07:15 AM
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.

JBeale
10-27-2012, 08:47 AM
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.

8

pixelk
10-27-2012, 11:24 AM
Just in case there is my version of the Adafruit_ST7735 (https://www.adafruit.com/products/358) lib for the from adafruit, in case it's of any help to anybody :

11

nevetsokyeron
10-30-2012, 03:42 AM
I am seeing this same linker error trying to use the Adafruit GFX library.

Any idea whats happening here yet?

JBeale
10-30-2012, 03:55 AM
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).

cmason
10-30-2012, 06:50 AM
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 (https://github.com/adafruit/Adafruit-GFX-Library) and PCD8544 (https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library.git) libraries, copied the files into the teensy3 directory from Paul's Kickstarter page (also cloned in my mercurial repo here (https://bitbucket.org/cmason/teensy3)). I made a few code changes:

In Makefile add
-DARDUINO=100 to OPTIONS.

In the Adafruit_GFX class in Adafruit_GFX.h, add a line:


virtual ~Adafruit_GFX() {} // force inclusion of vtable.

and change:

virtual void drawPixel(int16_t x, int16_t y, uint16_t color);
to:

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:



#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 (http://www.surplusgizmos.com/Noritake-Itron-CU24025ECPB-W1J-VFD-Display-Module-2x24-charectors-HD44780_p_2103.html). ($25 at surplus gizmos!)

Hope this helps,

-c

JBeale
10-30-2012, 07:52 AM
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.)

nevetsokyeron
10-30-2012, 08:05 AM
cmason... thanks! With your change to force include the vtable, I got my version of the Adafruit_GFX library to compile and draw text!

djsz
10-30-2012, 05:02 PM
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:


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;
}
}

JBeale
10-30-2012, 05:53 PM
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 */

Paul
11-03-2012, 01:39 PM
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.

djsz
11-04-2012, 09:26 PM
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!