Using the LCD on the PBMCUSLK project board with SPI (MC9S12DT256)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Using the LCD on the PBMCUSLK project board with SPI (MC9S12DT256)

1,750 Views
MagicJesus
Contributor I

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. :smileysad:

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 :smileyvery-happy:

 

#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++)  {  }}
Labels (1)
0 Kudos
Reply
7 Replies

1,058 Views
bigmac
Specialist III

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

 

0 Kudos
Reply

1,058 Views
MagicJesus
Contributor I

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.

0 Kudos
Reply

1,058 Views
bigmac
Specialist III

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

 

0 Kudos
Reply

1,058 Views
MagicJesus
Contributor I

Thanks again! I have implemented all of your suggestions and looked at the pdf.

 

but still....nothing :smileysad:

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++)  {  }}

 

0 Kudos
Reply

1,058 Views
bigmac
Specialist III

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

 

0 Kudos
Reply

1,058 Views
MagicJesus
Contributor I

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.

0 Kudos
Reply

1,058 Views
MagicJesus
Contributor I

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++)
  {
  }
 }
}

 

0 Kudos
Reply