Hi, I have been looking at a few different resources both on here and elsewhere on the internet about 4-bit LCD interfacing. I am using the SPI with the microcontroller stated in the title. I have managed to get the SPI working properly and the correct bits show up on the output of the 74HC595 serial in, parallel out shift register.
I can not for the life of me get the LCD to initialize or do anything.
when I plug the board in to do multilink debugging with codewarrior, the first of the two 8-digit lines fills all dark and the second line is clear.
the initialize logic and data sending techniques I have come up with is loosely based off of this site.
Anyway here is my code. If anyone has any ideas please let me know
#include <hidef.h> /* common defines and macros */#include "derivative.h" /* derivative-specific definitions */void SPI_Initialize(void);void SPI_Send(unsigned char data);void LCD_Initialize(void);void LCD_SendCommand(unsigned char data);void LCD_SendCharacter(unsigned char data);void Delay(unsigned int time); void Delay3us(unsigned int time);void main(void) { SPI_Initialize(); LCD_Initialize(); while(1) { LCD_SendCharacter(0x4F); }}void LCD_Initialize(void){ // wait 20ms for LCD to turn on Delay(21); SPI_Send(0x83); SPI_Send(0x03); Delay(10); SPI_Send(0x83); SPI_Send(0x03); Delay(1); SPI_Send(0x83); SPI_Send(0x03); Delay(1); SPI_Send(0x82); SPI_Send(0x02); Delay(1); // Write 08 hex (don't shift display, hide cursor) LCD_SendCommand(0x08); // Write 01 hex (clear and home display) LCD_SendCommand(0x01); // Write 06 hex (move cursor right) LCD_SendCommand(0x06); // Write 0C hex (turn on display) LCD_SendCommand(0x0F);}void LCD_SendCharacter(unsigned char data){ // break up data for transfer volatile unsigned char low = data&0x0F; volatile unsigned char high = (data&0xF0)>>4; // set enable low SPI_Send(high); // set enable bit to 1 SPI_Send(high|0xC0); // set enable low SPI_Send(high); // Delay Delay3us(200); // send low byte SPI_Send(low); // set enable bit to 1 SPI_Send(low|0xC0); // clear enable bit SPI_Send(low); // delay 5ms Delay(1);}void LCD_SendCommand(unsigned char data){ // break up data for transfer volatile unsigned char low = data&0x0F; volatile unsigned char high = (data&0xF0)>>4; // send high byte of data SPI_Send(high); // set enable bit to 1 SPI_Send(high|0x80); // set enable low SPI_Send(high); // delay 5ms Delay(5); // clear enable bit SPI_Send(low); // set enable bit to 1 SPI_Send(low|0x80); // clear enable bit SPI_Send(low);}void SPI_Initialize(void) { char clear; // route SPI to port M MODRR_MODRR4 = 1; DDRM = 0b00111000; // baud rate = 520.83 kHz SPI0BR = 0x23; // SPI control register 2 SPI0CR2 = 0x10; // enable SPI and set to master mode SPI0CR1 = 0x52; // clear flag clear = SPI0SR; clear = SPI0DR; }void SPI_Send(unsigned char data){ char clear; // write to data register SPI0DR = data; // wait until ready while (SPI0SR_SPIF == 0) { // wait for flag } // reset flag clear = SPI0SR; clear = SPI0DR;}void Delay(unsigned int time) { unsigned int i; unsigned int j; for (i = 0; i < time; i++) { for (j = 0; j < 328; j++) { } }}void Delay3us(unsigned int time){ int j; for (j = 0; j < time; j++) { }}
Hello Jesus, and welcome to the forum.
Firstly, each SPI transfer takes about 15 us to complete, so there is no need for additional delay between the nybbles. However, a delay is required after the two nybbles have been transferred. Your code has a delay after a display character sequence is sent, but there is no delay following a control byte sequence. The required delay is usually a minimum of 40 us, however note that the specific commands to clear the display and home the cursor require a much longer delay, a minimum of 1.64 milliseconds.
Magic Jesus wrote:I can not for the life of me get the LCD to initialize or do anything.
when I plug the board in to do multilink debugging with codewarrior, the first of the two 8-digit lines fills all dark and the second line is clear.
I notice that you have not sent a command sequence to initialise lthe display for two lines - the default is for single line. You will also need to check that you have an appropriate contrast setting.
The display controller also requires a minimum 40ns setup time between RS changing state, and the E signal going high. For sending a display character, your code is also dropping the RS state between nybbles. In case this is problematic, you might alter this function to set RS high for the first SPI send. On the next SPI send, send the data nybble with both RS and E high. On the third SPI send for each nybble, drop E to low, leaving the data nybble and RS state intact.
void LCD_SendCharacter( unsigned char data)
{
// Split data into two nybbles
unsigned char low = data & 0x0F;
unsigned char high = (data & 0xF0) >> 4;
// Send high nybble
SPI_Send( high | 0x40); // RS = 1, E = 0
SPI_Send( high | 0xC0); // RS = 1, E = 1
SPI_Send( high | 0x40); // RS = 1, E = 0
// Send low nybble
SPI_Send( low | 0x40); // RS = 1, E = 0
SPI_Send( low | 0xC0); // RS = 1, E = 1
SPI_Send( low | 0x40); // RS = 1, E = 0
Delay( 1); // delay 1ms
}
Regards,
Mac
Thanks for the reply! Now I have more questions ha.
Does it do any harm to delay longer than the required time?
I am mostly confused about the initialization. Most sources say you must first tell the LCD to use 4-bit data transfer and do other odd initialization techniques. Must I do these? or can I just start sending commands? so far nothing I have sent to the LCD will make it do anything.
Unfortunately I have not had time to get into the lab and try your suggestions (End of term work load) but I am mostly worried about initialization and sending commands just so something will happen on the LCD.
Thanks for the Info! it will definitely help me.
Hello,
The time delays are minimum delays - it does no harm to delay by a greater period.
Your initialisation code looks OK, apart from you not having enabled dual line operation, as already mentioned. The attached Application Note AN1745 details the sequences for both 8-bit and 4-bit initialisation.
It is possible that the command to clear the display and home the cursor did not work because of insufficient delay following the command.
I had previously assumed that you initialised the SPI module so that the SS signal was automatically output, and this was used to strobe the serial data into the output latches of the shift register. This would seem correct since you mentioned that you had observed the expected shift register output states.
Regards,
Mac
Thanks again! I have implemented all of your suggestions and looked at the pdf.
but still....nothing
It must have something to do with the initialization because it still just displays a single filled in line, the clear display command does not seem to work at all. Does the enable line need to be high during initialization?
anyway here is my current code. Hopefully it is just some glaring mistake.
#include <hidef.h> /* common defines and macros */#include "derivative.h" /* derivative-specific definitions */void SPI_Initialize(void);void SPI_Send(unsigned char data);void LCD_Initialize(void);void LCD_SendCommand(unsigned char data);void LCD_SendCharacter(unsigned char data);void Delay(unsigned int time); void Delay3us(unsigned int time);void main(void) { SPI_Initialize(); LCD_Initialize(); while(1) { LCD_SendCharacter(0x4F); }}void LCD_Initialize(void){ // wait 20ms for LCD to turn on Delay(21); //SPI_Send(0x83); SPI_Send(0x03); Delay(10); //SPI_Send(0x83); SPI_Send(0x03); Delay(1); //SPI_Send(0x83); SPI_Send(0x03); //Delay(1); //SPI_Send(0x82); SPI_Send(0x02); SPI_Send(0x02); // 2 lines, 5x7 font SPI_Send(0x08); LCD_SendCommand(0x08); // Write 01 hex (clear and home display) LCD_SendCommand(0x01); // Write 06 hex (move cursor right) LCD_SendCommand(0x06); // Write 0C hex (turn on display) LCD_SendCommand(0x0F);}void LCD_SendCharacter( unsigned char data){ // Split data into two nybbles unsigned char low = data & 0x0F; unsigned char high = (data & 0xF0) >> 4; // Send high nybble SPI_Send( high | 0x40); // RS = 1, E = 0 SPI_Send( high | 0xC0); // RS = 1, E = 1 SPI_Send( high | 0x40); // RS = 1, E = 0 // Send low nybble SPI_Send( low | 0x40); // RS = 1, E = 0 SPI_Send( low | 0xC0); // RS = 1, E = 1 SPI_Send( low | 0x40); // RS = 1, E = 0 Delay(2); // delay 1ms}void LCD_SendCommand(unsigned char data){ // break up data for transfer volatile unsigned char low = data&0x0F; volatile unsigned char high = (data&0xF0)>>4; // send high byte of data SPI_Send(high); // set enable bit to 1 SPI_Send(high|0x80); // set enable low SPI_Send(high); // delay 5ms //Delay(1); // clear enable bit SPI_Send(low); // set enable bit to 1 SPI_Send(low|0x80); // clear enable bit SPI_Send(low); // Delay 2ms in between commands Delay(2);}void SPI_Initialize(void) { char clear; // route SPI to port M MODRR_MODRR4 = 1; DDRM = 0b00111000; // baud rate = 520.83 kHz SPI0BR = 0x23; // SPI control register 2 SPI0CR2 = 0x10; // enable SPI and set to master mode SPI0CR1 = 0x52; // clear flag clear = SPI0SR; clear = SPI0DR; }void SPI_Send(unsigned char data){ char clear; // write to data register SPI0DR = data; // wait until ready while (SPI0SR_SPIF == 0) { // wait for flag } // reset flag clear = SPI0SR; clear = SPI0DR;}void Delay(unsigned int time) { unsigned int i; unsigned int j; for (i = 0; i < time; i++) { for (j = 0; j < 328; j++) { } }}void Delay3us(unsigned int time){ int j; for (j = 0; j < time; j++) { }}
Hello,
Firstly, check the hardware connections between the display and the 'HC595 device. Your code assumes that RS connects to pin 6 of the shift register, and the E signal to pin 7.
The E signal needs to be pulsed high, then low, for each nybble to be entered to the display on the negative edge of the pulse. I notice that you have now commented out the code that raises the E signal, so that no initialisation data is actually received by the display.
Additionally, the setup of the display for two lines should use the LCD_SendCommand() function for the normal send of a pair of nybbles.
void LCD_Initialize( void)
{
Delay(21); // wait 20ms
SPI_Send( 0); // Ensure RS = 0
SPI_Send( 0x83); // E = 1
SPI_Send( 0x03); // E = 0
Delay(10);
SPI_Send( 0x83); // E = 1
SPI_Send( 0x03); // E = 0
Delay(1);
SPI_Send( 0x83); // E = 1
SPI_Send( 0x03); // E = 0
Delay(1);
SPI_Send( 0x82); // E = 1
SPI_Send( 0x02); // E = 0
Delay(1);
LCD_SendCommand(0x28); // Dual line & 4-bit interface
LCD_SendCommand(0x08); // Display off
LCD_SendCommand(0x01); // Clear display, home cursor
Delay(2) // Additional delay
LCD_SendCommand(0x06); // Shift off
LCD_SendCommand(0x0F); // Display on, blinking cursor
}
void LCD_SendCommand( unsigned char data)
{
// Split data into two nybbles
unsigned char low = data & 0x0F;
unsigned char high = (data & 0xF0) >> 4;
SPI_Send( 0); // Initialise RS = 0
// Send high nybble
SPI_Send( high | 0x80); // RS = 0, E = 1
SPI_Send( high); // RS = 0, E = 0
// Send low nybble
SPI_Send( low | 0x80); // RS = 0, E = 1
SPI_Send( low); // RS = 0, E = 0
Delay(1); // delay 1ms
}
void LCD_SendCharacter( unsigned char data)
{
// Split data into two nybbles
unsigned char low = data & 0x0F;
unsigned char high = (data & 0xF0) >> 4;
SPI_Send( 0x40); // Initialise RS = 1
// Send high nybble
SPI_Send( high | 0xC0); // RS = 1, E = 1
SPI_Send( high | 0x40); // RS = 1, E = 0
// Send low nybble
SPI_Send( low | 0xC0); // RS = 1, E = 1
SPI_Send( low | 0x40); // RS = 1, E = 0
Delay(1); // delay 1ms
}
Regards,
Mac
HALLEJULAH!! IT WORKS!!
Thank you so much! The world needs more people like you.
I will post a cleaned up version of my code in case anyone comes looking in the future.
Here is the finished code with string support.
/* LCD Driver code -PBMCUSLK project board with MC9S12DT256 -Assumes the LCD has two, eight digit lines. Each digit has 5x7 pixels Written by Magic Jesus and bigmac from http://forums.freescale.com/ March 24th 2012 */ #include <hidef.h> /* common defines and macros */ #include "derivative.h" /* derivative-specific definitions */ #include "string.h" // strings void SPI_Initialize(void); void SPI_Send(unsigned char data); void LCD_Initialize(void); void LCD_SendCommand(unsigned char data); void LCD_SendCharacter(unsigned char data); void Delay(unsigned int time); void LCD_SendString(int line, char *sptr); void LCD_CursorPos(int line, int position); void main(void) { char message[9] = "Testing!"; SPI_Initialize(); LCD_Initialize(); LCD_SendString(1, message); LCD_SendString(2, message); while(1) { } } void LCD_Initialize(void) { // wait 20ms for LCD to turn on Delay(21); SPI_Send(0x00); SPI_Send(0x83); SPI_Send(0x03); Delay(10); SPI_Send(0x83); SPI_Send(0x03); Delay(1); SPI_Send(0x83); SPI_Send(0x03); Delay(1); SPI_Send(0x82); SPI_Send(0x02); Delay(1); LCD_SendCommand(0x28); LCD_SendCommand(0x08); // clear and home display LCD_SendCommand(0x01); Delay(2); // increment cursor (move cursor right) LCD_SendCommand(0x06); // turn on display, cursor and blink on LCD_SendCommand(0x0F); } void LCD_SendCharacter(unsigned char data) { // break up data for transfer unsigned char low = data & 0x0F; unsigned char high = (data & 0xF0) >> 4; // Set RS high SPI_Send(0x40); // Send high nybble SPI_Send(high | 0xC0); // RS = 1, E = 1 SPI_Send(high | 0x40); // RS = 1, E = 0 // Send low nybble SPI_Send(low | 0xC0); // RS = 1, E = 1 SPI_Send(low | 0x40); // RS = 1, E = 0 // delay in between character sends Delay(1); } void LCD_SendCommand(unsigned char data) { // break up data for transfer volatile unsigned char low = data & 0x0F; volatile unsigned char high = (data & 0xF0) >> 4; // set RS low SPI_Send(0x00); // send high nybble SPI_Send(high | 0x80); // RS = 1, E = 1 SPI_Send(high); // RS = 1, E = 1 // send low nybble SPI_Send(low | 0x80); // RS = 1, E = 1 SPI_Send(low); // RS = 1, E = 1 // delay 2ms in between commands Delay(2); } void LCD_SendString(int line, char *sptr) { int i; char *ptr; int length; // set cursor to correct line LCD_CursorPos(line, 0); // get length of string length = strlen(sptr); // prevent strings of length longer than the display width if (length > 8) { length = 8; } // send characters for (i = 0 ; i < length; i ++) { ptr = sptr + i; LCD_SendCharacter(*ptr); } } void LCD_CursorPos(int line, int position) { if (line == 1) { LCD_SendCommand(0x80 + position); } else { LCD_SendCommand(0xC0 + position); } } void SPI_Initialize(void) { char clear; // route SPI to port M MODRR_MODRR4 = 1; DDRM = 0b00111000; // baud rate = 520.83 kHz SPI0BR = 0x23; // SPI control register 2 SPI0CR2 = 0x10; // enable SPI and set to master mode SPI0CR1 = 0x52; // clear flag clear = SPI0SR; clear = SPI0DR; } void SPI_Send(unsigned char data) { char clear; // write to data register SPI0DR = data; // wait until ready while (SPI0SR_SPIF == 0) { } // reset flag clear = SPI0SR; clear = SPI0DR; } void Delay(unsigned int time) { // function delays "time" amount of milliseconds unsigned int i; unsigned int j; for (i = 0; i < time; i++) { for (j = 0; j < 328; j++) { } } }