Nokia 51100 LCD and STM32F4 Discovery - Page 1
Forum section: electronics / general

author: roli
author_rank: Regular Contributor
timestamp: January 04, 2014, 03:35:21 pm
content: Hi!I've been trying to get one of these cheap LCDs to work with my STM32F4 Discovery for the past two days and I can't seem to make any progress. No matter what I do, I can't seem to get anything out of the LCD.I've been trying to use the hardware SPI2 controller to drive the thing (I'm using SPI1 for another other device).I've connected the LCD like this:- RST=PB8- CE=PB11- DC=PB12- DIN=PB15- SCLK=PB13- VCC=3V- GND=GNDAnd here is the code - sorry if it is messy as heck, but I've been writing and rewriting the same code in hopes of getting anything out of this LCD. I've also taken out the code that doesn't have anything to do with the LCD.Code:#define PCD8544_NOP 0#define PCD8544_FUNCTION_SET (1<<5)#define PCD8544_POWER_DOWN (1<<2)#define PCD8544_HORIZONTAL_ADDRESSING 0#define PCD8544_VERTICAL_ADDRESSING (1<<1)#define PCD8544_EXTENDED_INSTRUCTION (1<<0)#define PCD8544_DISPLAY_CONTROL (1<<3)#define PCD8544_DISPLAY_BLANK 0x0#define PCD8544_DISPLAY_NORMAL (1<<2)#define PCD8544_DISPLAY_ALL_ON (1<<0)#define PCD8544_DISPLAY_INVERTED (1<<2|1<<0)#define PCD8544_SET_Y_ADDRESS 0x40#define PCD8544_SET_X_ADDRESS 0x80#define PCD8544_SET_TEMP (1<<2)#define PCD8544_TEMPCO_0 0b00#define PCD8544_TEMPCO_1 0b01#define PCD8544_TEMPCO_2 0b10#define PCD8544_TEMPCO_3 0b11#define PCD8544_SET_BIAS (1<<4)#define PCD8544_SET_VOP (1<<7)#define PCD8544_MAX_BANKS 6#define PCD8544_MAX_COLS 84void SPI_and_GPIO_Init2(){GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;/* Enable the SPI periph */RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);/* Enable SCK, MOSI and MISO GPIO clocks */RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB , ENABLE);GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2); //not usedGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;/* SPI pins configuration */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15|GPIO_Pin_14;GPIO_Init(GPIOB, &GPIO_InitStructure);/* SPI configuration -------------------------------------------------------*/SPI_I2S_DeInit(SPI2);SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_Init(SPI2, &SPI_InitStructure);/* Enable SPI2 */SPI_Cmd(SPI2, ENABLE);GPIO_InitTypeDef GPIO_InitStructure2;GPIO_InitStructure2.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11|GPIO_Pin_12;GPIO_InitStructure2.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure2.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure2.GPIO_Speed = GPIO_Speed_2MHz;GPIO_InitStructure2.GPIO_PuPd = GPIO_PuPd_DOWN;GPIO_Init(GPIOB, &GPIO_InitStructure2);//LCD resetGPIO_ResetBits(GPIOB, GPIO_Pin_11);GPIO_ResetBits(GPIOB, GPIO_Pin_8);delay(50); //wait 50msGPIO_SetBits(GPIOB, GPIO_Pin_8);/* Deselect : Chip Select high */GPIO_SetBits(GPIOB, GPIO_Pin_11);}void SPI2_Pisanje(uint8_t data, uint8_t command){//command parameter is there for future useGPIO_ResetBits(GPIOB, GPIO_Pin_11);while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET){}SPI_I2S_SendData(SPI2, data);//while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)!=SET){}GPIO_SetBits(GPIOB, GPIO_Pin_11);}int main(void) {init_essentials_time(); //custom code that initializes the delay functionSPI_and_GPIO_Init2();GPIO_ResetBits(GPIOB, GPIO_Pin_12);SPI2_Pisanje(PCD8544_FUNCTION_SET | PCD8544_EXTENDED_INSTRUCTION, 0);SPI2_Pisanje(PCD8544_SET_BIAS | 0x2, 0);SPI2_Pisanje(PCD8544_SET_VOP | 80, 0);SPI2_Pisanje(PCD8544_FUNCTION_SET, 0);SPI2_Pisanje(PCD8544_DISPLAY_CONTROL | PCD8544_DISPLAY_ALL_ON, 0);while(1){}}This should in theory initialize the LCD and turn all pixels on. But it doesn't seem to do anything. Any ideas on what could be wrong?

author: andyturk
author_rank: Frequent Contributor
timestamp: January 04, 2014, 04:11:24 pm
content: Quote from: roli on January 04, 2014, 03:35:21 pmNope, none.Your best bet is to use an oscilloscope or logic analyzer to see if your signals are making it to the display lines. If not, check your GPIO configuration. If data is getting out, make sure the clock frequency is within the bounds that the display can handle and that the basic signaling is there (e.g., chip select, D/C, etc.). If all that works but the display doesn't then you're probably not sending the right commands. Check your bit order against the data sheet and then the commands themselves.

author: dannyf
author_rank: Super Contributor
timestamp: January 04, 2014, 04:13:15 pm
content: 1. be careful with timing: check out the datasheet for the various timing requriments (particularly reset time and minimum width). Make sure that you have the baud rate right for the hardware spi, or have sufficient delays in between pin changes. Having a logic analyzer here would be quite helpful.2. I would do it in two steps: implement your 5110 code over software spi. Once it works, you know that your lcd code is good. After that, swap out the software spi and use hardware spi.Again, in either case, having a logic analyzer is helpful.

author: roli
author_rank: Regular Contributor
timestamp: January 04, 2014, 05:17:29 pm
content: The display is apparently capable of up to 4Mbit/s speeds (from 0MHz up to 4MHz clock). If my thinking is correct then the SPI_BaudRatePrescaler_128 should be more than slow enough.RES pulse should be at least 100ns long (with no upper limit) - it is 50ms here (I've tried less and more too). It should take maximum of 30ms from power up to the reset pulse (it takes about 2ms here). And all pulses should be stable for 100ns (I sadly can't test that).I think the LCD commands are ok - I've copied them from a library I found online for STM32F10. And I've copied them from the datasheet long before I found that library.I've also tried another LCD module - doesn't work either. So it is probably not hardware related (at least not the LCD hardware).I don't have an oscilloscope or a logic analyzer. So I can't test the signals. I'll try the software SPI but I don't have particularly high hopes.

author: dannyf
author_rank: Super Contributor
timestamp: January 04, 2014, 07:26:34 pm
content: QuoteYou have a choice: you can continue to 'think' that things 'should' work; Or you go make sure that things do work (by reading the datasheet and compare it to your code).Which one do you think is going to help you?

author: dannyf
author_rank: Super Contributor
timestamp: January 04, 2014, 07:37:35 pm
content: I would put the following in the same category:QuoteDebugging is as much about doubting your code as doubting your approach / thought process: you will need to also be prepared for the possibility that your "should" / "seem" above is wrong.Again, other than reading the datasheet, no other alternatives.

author: AndreasF
author_rank: Frequent Contributor
timestamp: January 04, 2014, 08:21:23 pm
content: The only relevant difference I see to the code I use with my Discovery board and this display, is that I wait to set the CE bit to high until the SPI transfer is complete (this is currently commented out in your code). Here's my initialization routine:Code:void LCD_init() {GPIO_InitTypeDef PinInit;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 | RCC_AHB1Periph_GPIOB, ENABLE);//RST, CE & DC pinsPinInit.GPIO_Pin = (GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_14);PinInit.GPIO_Mode = GPIO_Mode_OUT;PinInit.GPIO_OType = GPIO_OType_PP;PinInit.GPIO_PuPd = GPIO_PuPd_DOWN;PinInit.GPIO_Speed = GPIO_Speed_25MHz;GPIO_Init(GPIOB, &PinInit);GPIO_ResetBits(GPIOB, GPIO_Pin_14 | GPIO_Pin_12); //RST, DCGPIO_SetBits(GPIOB, GPIO_Pin_11 );//CE// CLK, MOSIPinInit.GPIO_Pin = (GPIO_Pin_13 | GPIO_Pin_15);PinInit.GPIO_Mode = GPIO_Mode_AF;GPIO_Init(GPIOB, &PinInit);GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//...removed some stuff here related to DMA and NVICSPI_InitTypeDef spi_is;spi_is.SPI_Mode = SPI_Mode_Master;spi_is.SPI_Direction = SPI_Direction_1Line_Tx;spi_is.SPI_DataSize = SPI_DataSize_8b;spi_is.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;spi_is.SPI_CPOL = SPI_CPOL_High;spi_is.SPI_CPHA = SPI_CPHA_2Edge;spi_is.SPI_FirstBit = SPI_FirstBit_MSB;spi_is.SPI_NSS = SPI_NSS_Soft;SPI_Init(SPI2, &spi_is);SPI_SSOutputCmd(SPI2, DISABLE);SPI_Cmd(SPI2, ENABLE);uint32_t delay = 10000;while (delay>0) delay--;//RST_PinGPIO_SetBits(GPIOB, GPIO_Pin_14);delay = 5000;while (delay>0) delay--;GPIO_ResetBits(GPIOB, GPIO_Pin_11 | GPIO_Pin_12); //CE, DCdelay = 1000;while (delay>0) delay--;while (SPI_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET) {};SPI_I2S_SendData(SPI2, 0x21); //Enter extended function setwhile (SPI_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET) {};SPI_I2S_SendData(SPI2, 0x13); //set BIASwhile (SPI_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET) {};SPI_I2S_SendData(SPI2, 0x80 | 0x30); //set VOP (contrast)while (SPI_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET) {};SPI_I2S_SendData(SPI2, 0x22); //standard function setwhile (SPI_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET) {};SPI_I2S_SendData(SPI2, 0x0c); //display control: normalwhile (SPI_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET) {};SPI_I2S_SendData(SPI2, 0x40); //set ywhile (SPI_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET) {};SPI_I2S_SendData(SPI2, 0x80); //set xwhile (SPI_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET) {};GPIO_SetBits(GPIOB, GPIO_Pin_12);//more stuff related to NVIC and DMA}Notice the very crude delays. I didn't play around with those much, but I doubt the exact timing is that critical here.

author: Skimask
author_rank: Super Contributor
timestamp: January 04, 2014, 08:27:51 pm
content: Quote from: dannyf on January 04, 2014, 07:37:35 pmSure there is...there's always alternatives.Go back to the Arduino and script-kiddie the whole thing...

author: roli
author_rank: Regular Contributor
timestamp: January 04, 2014, 08:58:44 pm
content: I am reading the datasheet. Those "should" are from the datasheet. When I said that the reset pulse should be at least 100ns long I meant this: datasheet specifies that the res pulse should be at least 100ns long. From my understanding it is currently 50ms long. But sadly I can't check if it really is.Those commands there are taken from the datasheet so I am pretty sure (unless I've missed something) that they initialize the LCD and turn all pixels on. Of course that doesn't work. So logic leads me to believe that something is either wrong with GPIO initialization or the actual SPI transfer/initialization.@AndreasF: I'll take a look at your code and check if I am doing something stupid.

author: mrflibble
author_rank: Super Contributor
timestamp: January 05, 2014, 12:56:08 am
content: Or things are not exactly going according to the datasheet, because the datasheet is not exactly the same thing as the module you are using. Been there done that. For example if there's a difference in pull-up compared to what you expect, you might get different voltages, and while you are toggling the IO pin just fine this happy fact doesn't quite make it to the LCD modules. Or even worse, depending on timings sometimes it works and sometimes it doesn't. Put a scope on it and see what the signals look like, at the very least to validate your assumptions.

author: roli
author_rank: Regular Contributor
timestamp: January 05, 2014, 01:58:03 am
content: Well I've figured it out. Thanks to the help from AndreasF.His code worked so I slowly started inserting my own parts into that function until it stopped working. My problem was in the function that was writing the data to the SPI. I was deselecting the LCD too fast after writing the data. I've added a 1ms delay and it seems to be working.

author: andyturk
author_rank: Frequent Contributor
timestamp: January 05, 2014, 03:28:27 am
content: FWIW, here's the C++ driver code I use for the same display:Code:namespace akt {namespace views {PDC8544::PDC8544(IO &io) :Canvas(Size(W, H)),io(io){}void PDC8544::reset() {// clear the frame bufferfor (unsigned bank=0; bank < H/8; ++bank) {for (unsigned col=0; col < W; ++col) {banks = 0;}}static const uint8_t sequence[] = {VIEW_IO_UNSELECT,VIEW_IO_RESET(10),VIEW_IO_SLEEP(10),VIEW_IO_COMMANDS,VIEW_IO_SELECT,0x20 + 0x01, // set display active, horizontal addressing, H = 10x80 + 80, // set Vop = 800x01 + 0x04, // set bias system = 40x20, // set display active, horizontal addressing, H = 00x09, // display all pixels onVIEW_IO_UNSELECT,VIEW_IO_END};io.interpret(sequence);}void PDC8544::set_pixel(Point p, pixel value) {assert(p.x >= 0 && p.x < size.w);assert(p.y >= 0 && p.y < size.h);uint8_t &byte = banks[p.y/8][p.x];uint8_t mask = 1 << p.y%8;if (value) byte |= mask; else byte &= ~mask;}pixel PDC8544::get_pixel(Point p) const {const uint8_t &byte = banks[p.y/8][p.x];uint8_t mask = 1 << p.y%8;return (byte & mask) != 0;}void PDC8544::flush(const Rect &r) {static const uint8_t sequence0[] = {VIEW_IO_COMMANDS,VIEW_IO_SELECT,0x20, // power on, horizontal addressing, H=00x0c, // normal display configurationVIEW_IO_END};io.interpret(sequence0);for (int y=0; y < H/8; ++y) {const uint8_t sequence1[] {VIEW_IO_COMMANDS,(uint8_t) (0x40 + y), // set y address0x80, // set x address=0VIEW_IO_DATA(0),VIEW_IO_END};io.interpret(sequence1);io.send(banks, sizeof(banks));}static const uint8_t sequence2[] = {VIEW_IO_UNSELECT,VIEW_IO_END};io.interpret(sequence2);}};