Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] Controlling an LCD via a port expander

Status
Not open for further replies.
That's very kind of you!

Hopefully if the C18 code is simple enough for me to understand I can send the commands the mirkroC way!

Well, let us make sure the SPI port expander channel setup by MikroC is functioning correctly. Do you have a voltmeter? If so, send an interlaced byte "10101010" down to the LCD and holding LCD pins in that pattern and then measure the data pins with respect to GND.

This will ensure you have successfully piped the byte to the LCD. You can also then send "01010101" to verify the control of the SPI port expander channel.

BigDog
 

I tried this exact thing earlier to verify if the port expander library was working and the LCD pins had the correct pattern on them. So i guess the problem is soley with the SPI_LCD8 library.
 

Well at least you've narrowed it down.

Try creating an initialization sequence for the LCD following this diagram. This is the exact sequence used by Microchip in their assembly demo code.



The following is the actual ASM routine from Microchip's code, you can definitely see the specified commands in the diagram above being sent to the LCD:

Code:
        call	Delay
	movlw	b'00110000'
	movwf	temp_wr
	call	InitWrite

	call	Delay
	movlw	b'00110000'
	movwf	temp_wr
	call	InitWrite

	call	Delay
	movlw	b'00110000'
	movwf	temp_wr
	call	InitWrite

;Function Set
	call	Delay
	movlw	b'00111100' ;0011NFxx
	movwf	temp_wr
	call	InitWrite



	call	Delay
	movlw	b'00001100' ;Display off
	movwf	temp_wr
	call	InitWrite


	call	Delay
	movlw	b'00000001' ;Display Clear
	movwf	temp_wr
	call	InitWrite


	call	Delay
	movlw	b'00000110' ;Entry mode
	movwf	temp_wr
	call	InitWrite

Additional information can be found at the following link:

LCD Initialization Issues

This should allow you to code a proper initialization routine for the LCD.

BigDog
 

Thank you for finding all this! Unfortunately I don't really understand the ASM routine, I cannot tell which port of the expander is being written too. I'm pretty out of my depth here...
 

Thank you for finding all this! Unfortunately I don't really understand the ASM routine, I cannot tell which port of the expander is being written too. I'm pretty out of my depth here...

You simply write the bytes as specified in the diagram and ASM to the same port you were writing the test patterns.

I thought you indicated you sent test patterns to the LCD and tested them with the voltmeter?

BigDog
 

The expander has a port A which has the E and RS signals and port B which has the 8 data bits. It's unclear to me which port is being written to in the ASM routine. For the test patterns I simply wrote to port B and checked if the pins on the LCD were the same.
 

You'll need to write the LCD commands to Port B while ensuring the E and RS on Port A lines are set correctly.

Now that you know this would it help if I furnished a PIC C program initializing and displaying a simple message on a LCD?
 
Ok,

Let me find a sample C program that displays a simple string of characters. I can help you adapt it to using the SPI port expander channel.

I rarely use MikroC largely due to this reason, without the source you cannot always adapt it to real world hardware.

What MikroC library function were you using to send the test pattern byte to Port B? Give me an example?

BigDog
 

The following code will write to the expanders port B:

Code:
// Port Expander module connections
sbit  SPExpanderRST at RF6_bit;
sbit  SPExpanderCS  at RA2_bit;
sbit  SPExpanderRST_Direction at TRISF6_bit;
sbit  SPExpanderCS_Direction  at TRISA2_bit;
// End Port Expander module connections


void main(){

CMCON = 0x07; // turn off comparators
ADCON1 = 0x3f;
SPI1_Init();


//Note that Port Expander pins A2 A1 A0 are connected to GND so Port Expander Hardware Address is 0.

Expander_Init(0);
Expander_Set_DirectionPortA(0,0x00);  // Set Expander's PORTA to be output
Expander_Set_DirectionPortB(0,0x00);   // Set Expander's PORTB to be output

 
    Expander_Write_PortB(0, 0x55);        // Write to expander's PORTB
   
    while(1);

}
 

Here is a Microchip C18 translation from the original ASM demo file:


main.c
Code:
//*****************************************************************
// PICDEM Explorer 18 Demo for LCD display
// Filename : main.c
//
// By Rockie Roper
// Converted from the ASM that come with the demo board
//
// Created on 2009-06-05
//
// Compiler is the C18 from MPLAB
// 
//*****************************************************************
#include <p18F8722.h>

#pragma config  OSC = HSPLL
#pragma config 	FCMEN = OFF
#pragma config 	IESO = OFF
#pragma config 	PWRT = OFF           
#pragma config 	BOREN = OFF
#pragma config 	WDT = OFF 
#pragma config 	MCLRE = ON  
#pragma config 	LVP = OFF  
#pragma config 	XINST = OFF 

#pragma udata UDATA_ACS
unsigned char LCDText[16*2+1];

#pragma code

void main(void)
{
	int			tmpcnt = 0;
	OSCTUNEbits.PLLEN = 1;

  // Initialize the LCD display
	LCDInit();	
	
	ADCON1 = 0b00001110;
	TXSTA = 0b10100100;
	SPBRG = 0xff;
	RCSTA = 0b10010000;
	
	TRISBbits.TRISB0 = 1;
	TRISAbits.TRISA5 = 1;

  while(1)
  	{
	  	// Write the cammand to start on line 1
			LCDLine_1();  
			// Write the data one char at a time.
			d_write('H');
			d_write('e');
			d_write('l');
			d_write('l');
			d_write('0');
			// Write the cammand to start on line 2
			LCDLine_2();
			// Write the data to line 2 one char at a time
			// You can put this in a loop and read from a table
			d_write('P');
			d_write('I');
			d_write('C');
			d_write('1');
			d_write('8');
			delay_1s();
			tmpcnt++;
			switch(tmpcnt)
				{
					case 10:
						d_write(' ');
						d_write('1');
						d_write('0');
						break;
					case 20:
						d_write(' ');
						d_write('2');
						d_write('0');
						break;
					case 30:
						d_write(' ');
						d_write('3');
						d_write('0');
						break;
					case 40:
						d_write(' ');
						d_write('4');
						d_write('0');
						break;
				}
		}
}

picLCD.c
Code:
//*****************************************************************
// PICDEM HPC Explorer 18 LCD function
// Filename : pic18lcd.c
//
// By Rockie Roper
// Converted from the ASM that come with the demo board
//
// Created on 2009-06-05
// 
//*****************************************************************
#ifndef __PIC18LCD_C
#define __PIC18LCD_C

#include <p18F8722.h>

#define	LCD_CS						(LATAbits.LATA2)		//LCD chip select
#define	LCD_CS_TRIS				(TRISAbits.TRISA2)	//LCD chip select
#define	LCD_RST						(LATFbits.LATF6)		//LCD chip select
#define	LCD_RST_TRIS			(TRISFbits.TRISF6)	//LCD chip select

#define LCD_TXSTA_TRMT		(TXSTAbits.TRMT)
#define LCD_SPI_IF				(PIR1bits.SSPIF)
#define LCD_SCK_TRIS			(TRISCbits.TRISC3)
#define LCD_SDO_TRIS			(TRISCbits.TRISC5)
#define LCD_SSPBUF				(SSPBUF)
#define LCD_SPICON1				(SSP1CON1)
#define LCD_SPICON1bits		(SSP1CON1bits)
#define LCD_SPICON2				(SSP1CON2)
#define LCD_SPISTAT				(SSP1STAT)
#define LCD_SPISTATbits		(SSP1STATbits)

extern void Delay(void);
extern void SDelay(void);

#pragma code

//*****************************************************************
// LCD busy delay
//*****************************************************************
void LCDBusy(void)
{
	SDelay();
	SDelay();
}
//*****************************************************************
// Write to MCP923S17 Port A
//*****************************************************************
void WritePortA(char b)
{
	LCD_CS = 0;
	
	LCD_SSPBUF = 0x40;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_SSPBUF = 0x12;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_SSPBUF = b;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_CS = 1;
}
//*****************************************************************
// Write to MCP923S17 Port B
//*****************************************************************
void WritePortB(char b)
{
	LCD_CS = 0;
	
	LCD_SSPBUF = 0x40;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_SSPBUF = 0x13;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_SSPBUF = b;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_CS = 1;
}
//*****************************************************************
// Write the data to the display
//*****************************************************************
void d_write(char b)
{
	WritePortA(0x80);
	LCDBusy();
	WritePortB(b);
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0xC0);
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0x00);
	TXREG = b;								//carriage return
	while(!LCD_TXSTA_TRMT);		//wait for data TX
	LCD_TXSTA_TRMT = 0;
}
//*****************************************************************
// Send a instruction to the display
//*****************************************************************
void i_write(char b)
{
	WritePortA(0x00);
	LCDBusy();
	WritePortB(b);
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0x40);
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0x00);
}
//*****************************************************************
// Write to line 1 of the display
//*****************************************************************
void LCDLine_1(void)
{
	i_write(0x80);
}
//*****************************************************************
// Write to line 1 of the display
//*****************************************************************
void LCDLine_2(void)
{
	i_write(0xC0);
}
//*****************************************************************
// To clear the display
//*****************************************************************
void LCDClear(void)
{
	i_write(0x01);
}
//******************************************************************
// Function to write to the PORT
//******************************************************************
void InitWrite(char b)
{
	WritePortA(0);
	WritePortB(b);
	Nop();
	Nop();
	Nop();
	WritePortA(0x40);
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0);
}
//*****************************************************************
// Initialize MCP923S17 Port A
//*****************************************************************
void InitPortA_SPI(char b)
{
	LCD_CS = 0;
	LCD_SSPBUF = 0x40;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_SSPBUF = 0x00;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_SSPBUF = b;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_CS = 1;
}
//*****************************************************************
// Initialize MCP923S17 Port B
//*****************************************************************
void InitPortB_SPI(char b)
{
	LCD_CS = 0;
	LCD_SSPBUF = 0x40;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_SSPBUF = 0x01;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_SSPBUF = b;
	while(!LCD_SPI_IF);
	LCD_SPI_IF = 0;
	
	LCD_CS = 1;
}
//*****************************************************************
// Initialize MCP923S17 SPI
//*****************************************************************
void InitSPI(void)
{
	LCD_SCK_TRIS = 0;
	LCD_SDO_TRIS = 0;
	
	LCD_SPICON1 = 0x22;
	LCD_SPISTATbits.CKE = 1;
	//LCD_SPISTATbits.SMP = 0;
	LCD_SPI_IF = 0;
}
//******************************************************************
// LCD Initialization function
//******************************************************************
void LCDInit(void)
{
	LCD_CS_TRIS = 0;
	LCD_CS = 1;
	Delay();
	Delay();
	Delay();
	
	LCD_RST_TRIS = 0;
	LCD_RST = 0;
	Delay();
	LCD_RST = 1;
	
	InitSPI();
	InitPortA_SPI(0);
	InitPortB_SPI(0);
	
	WritePortA(0);
	
	Delay();
	InitWrite(0x3C);				//0011NFxx
	
	Delay();
	InitWrite(0x0C);				//Display Off
	
	Delay();
	InitWrite(0x01);				//Display Clear
	
	Delay();
	InitWrite(0x06);				//Entry mode
}

I have attached the project which will require downloading and installing Microchip's MPLAB and C18 Compiler if you wish to compile and test the code directly.

BigDog
 

Attachments

  • HPC18.zip
    3.8 KB · Views: 46
Wow this is really really useful, many thanks!
I'm going to work on editing this to run on mikroC.
 

Ok,

So don't panic! You may look at the code and take a deep gasp for air, but it's actually quite straight forward.

The program contains code to setup the SPI port expander, which you have already done using MikroC library functions.

There is an ASM file containing a delay routine, you don't need it, you can use MikroC's delay routines.

Most of the routines you will need to convert are in the pic18LCD.c file.

So what do you think? Up for it?

BigDog
 
I will give this a shot right now!

---------- Post added at 23:28 ---------- Previous post was at 23:10 ----------

Are the routines InitSPI, InitPortB_SPI and InitPortA_SPI actually required when using mikroC? I'd say the Expander_Init() and SPI1_Init() covers this.
 

Correct.

Once you have configured the SPI port expander using the MikroC library functions, all you will need to do is pipe the instruction byte to the LCD through PORTB and toggle the RS and E lines correctly through PORTA with the appropriate delays between these tasks.

You would do this using the following routines, you posted earlier:

Expander_Set_DirectionPortB(0,0x00); // Set Expander's PORTB to be output
Expander_Write_PortB(0, 0x55); // Write to expander's PORTB

Try coding the LCD initialization routine and post it. I'll take a look and keep you going in the right direction.

BigDog
 
My initialisation routine is as follows:

Code:
//******************************************************************
// LCD Initialization function
//******************************************************************
void LCDInit(void)
{
	LCD_CS_TRIS = 0;
	LCD_CS = 1;
	Delay_ms(10);
	Delay_ms(10);
	Delay_ms(10);

	LCD_RST_TRIS = 0;
	LCD_RST = 0;
	Delay_ms(10);
	LCD_RST = 1;

	SPI1_Init();
	Expander_Init();
	Expander_Set_DirectionPortA(0,0x00);  // Set Expander's PORTA to be output
        Expander_Set_DirectionPortB(0,0x00);   // Set Expander's PORTB to be output

	WritePortA(0);

	Delay_ms(10);
	InitWrite(0x3C);				//0011NFxx

        Delay_ms(10);
	InitWrite(0x0C);				//Display Off

        Delay_ms(10);
	InitWrite(0x01);				//Display Clear

	Delay_ms(10);
	InitWrite(0x06);				//Entry mode
}

I was unsure how long to delay for. In regards to the whole program I'm unsure about the d_write routine, particularly this part:

Code:
	TXREG = b;		        //carriage return
	while(!LCD_TXSTA_TRMT);		//wait for data TX
	LCD_TXSTA_TRMT = 0;


---------- Post added at 00:31 ---------- Previous post was at 00:29 ----------

Here is the whole thing together, the #defines and d_write routine need attention otherwise the rest seems to make sense:

Code:
// Port Expander module connections
sbit  SPExpanderRST at RF6_bit;
sbit  SPExpanderCS  at RA2_bit;
sbit  SPExpanderRST_Direction at TRISF6_bit;
sbit  SPExpanderCS_Direction  at TRISA2_bit;
// End Port Expander module connections

sbit  LCD_RST at RF6_bit;
sbit  LCD_CS  at RA2_bit;
sbit  LCD_RST_TRIS at TRISF6_bit;
sbit  LCD_CS_TRIS  at TRISA2_bit;

sbit  LCD_SCK_TRIS at TRISC3_bit;
sbit  LCD_SDO_TRIS at TRISC5_bit;

#define LCD_TXSTA_TRMT		(TXSTAbits.TRMT)
#define LCD_SPI_IF				(PIR1bits.SSPIF)

#define LCD_SSPBUF				(SSPBUF)
#define LCD_SPICON1				(SSP1CON1)
#define LCD_SPICON1bits		(SSP1CON1bits)
#define LCD_SPICON2				(SSP1CON2)
#define LCD_SPISTAT				(SSP1STAT)


//extern void Delay(void);
//extern void SDelay(void);

//*****************************************************************
// Write to MCP923S17 Port A
//*****************************************************************
void WritePortA(char b)
{
	Expander_Write_PortA(0, b);
}
//*****************************************************************
// Write to MCP923S17 Port B
//*****************************************************************
void WritePortB(char b)
{
	Expander_Write_PortB(0, b);
}
//*****************************************************************
// Write the data to the display
//*****************************************************************
void d_write(char b)
{
	WritePortA(0x80);
	LCDBusy();
	WritePortB(b);
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0xC0);
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0x00);
	TXREG = b;		        //carriage return
	while(!LCD_TXSTA_TRMT);		//wait for data TX
	LCD_TXSTA_TRMT = 0;
}
//*****************************************************************
// Send a instruction to the display
//*****************************************************************
void i_write(char b)
{
	WritePortA(0x00);
	Delay_ms(10);
	WritePortB(b);
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0x40);
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0x00);
}
//*****************************************************************
// Write to line 1 of the display
//*****************************************************************
void LCDLine_1(void)
{
	i_write(0x80);
}
//*****************************************************************
// Write to line 1 of the display
//*****************************************************************
void LCDLine_2(void)
{
	i_write(0xC0);
}
//*****************************************************************
// To clear the display
//*****************************************************************
void LCDClear(void)
{
	i_write(0x01);
}
//******************************************************************
// Function to write to the PORT
//******************************************************************
void InitWrite(char b)
{
	WritePortA(0);
	WritePortB(b);
	Nop();
	Nop();
	Nop();
	WritePortA(0x40);
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	Nop();
	WritePortA(0);
}

//******************************************************************
// LCD Initialization function
//******************************************************************
void LCDInit(void)
{
	LCD_CS_TRIS = 0;
	LCD_CS = 1;
	Delay_ms(10);
	Delay_ms(10);
	Delay_ms(10);

	LCD_RST_TRIS = 0;
	LCD_RST = 0;
	Delay_ms(10);
	LCD_RST = 1;

	SPI1_Init();
	Expander_Init();
	Expander_Set_DirectionPortA(0,0x00);  // Set Expander's PORTA to be output
        Expander_Set_DirectionPortB(0,0x00);   // Set Expander's PORTB to be output

	WritePortA(0);

	Delay_ms(10);
	InitWrite(0x3C);				//0011NFxx

        Delay_ms(10);
	InitWrite(0x0C);				//Display Off

        Delay_ms(10);
	InitWrite(0x01);				//Display Clear

	Delay_ms(10);
	InitWrite(0x06);				//Entry mode
}
 

Ok so I changed a few parts and tried it out this morning and the code was sucessful!

Heres the final code for anyone that is interested:

Code:
// Port Expander module connections
sbit  SPExpanderRST at RF6_bit;
sbit  SPExpanderCS  at RA2_bit;
sbit  SPExpanderRST_Direction at TRISF6_bit;
sbit  SPExpanderCS_Direction  at TRISA2_bit;
// End Port Expander module connections

sbit  LCD_RST at RF6_bit;
sbit  LCD_CS  at RA2_bit;
sbit  LCD_RST_TRIS at TRISF6_bit;
sbit  LCD_CS_TRIS  at TRISA2_bit;

sbit  LCD_SCK_TRIS at TRISC3_bit;
sbit  LCD_SDO_TRIS at TRISC5_bit;

//extern void Delay(void);
//extern void SDelay(void);

//*****************************************************************
// Write to MCP923S17 Port A
//*****************************************************************
void WritePortA(char b)
{
        Expander_Write_PortA(0, b);
}
//*****************************************************************
// Write to MCP923S17 Port B
//*****************************************************************
void WritePortB(char b)
{
        Expander_Write_PortB(0, b);
}
//*****************************************************************
// Write the data to the display
//*****************************************************************
void d_write(char b)
{
        WritePortA(0x80);
        Delay_ms(10);
        WritePortB(b);
        Nop();
        Nop();
        Nop();
        Nop();
        WritePortA(0xC0);
        Nop();
        Nop();
        Nop();
        Nop();
        Nop();
        Nop();
        WritePortA(0x00);
        //TXREG = b;                        //carriage return
        //while(!LCD_TXSTA_TRMT);                //wait for data TX
        //LCD_TXSTA_TRMT = 0;
}
//*****************************************************************
// Send a instruction to the display
//*****************************************************************
void i_write(char b)
{
        WritePortA(0x00);
        Delay_ms(10);
        WritePortB(b);
        Nop();
        Nop();
        Nop();
        Nop();
        WritePortA(0x40);
        Nop();
        Nop();
        Nop();
        Nop();
        Nop();
        Nop();
        WritePortA(0x00);
}
//*****************************************************************
// Write to line 1 of the display
//*****************************************************************
void LCDLine_1(void)
{
        i_write(0x80);
}
//*****************************************************************
// Write to line 1 of the display
//*****************************************************************
void LCDLine_2(void)
{
        i_write(0xC0);
}
//*****************************************************************
// To clear the display
//*****************************************************************
void LCDClear(void)
{
        i_write(0x01);
}
//******************************************************************
// Function to write to the PORT
//******************************************************************
void InitWrite(char b)
{
        WritePortA(0);
        WritePortB(b);
        Nop();
        Nop();
        Nop();
        WritePortA(0x40);
        Nop();
        Nop();
        Nop();
        Nop();
        Nop();
        Nop();
        Nop();
        Nop();
        Nop();
        WritePortA(0);
}

//******************************************************************
// LCD Initialization function
//******************************************************************
void LCDInit(void)
{
        LCD_CS_TRIS = 0;
        LCD_CS = 1;
        Delay_ms(10);
        Delay_ms(10);
        Delay_ms(10);

        LCD_RST_TRIS = 0;
        LCD_RST = 0;
        Delay_ms(10);
        LCD_RST = 1;

        SPI1_Init();
        Expander_Init(0);
        Expander_Set_DirectionPortA(0,0x00);  // Set Expander's PORTA to be output
        Expander_Set_DirectionPortB(0,0x00);   // Set Expander's PORTB to be output

        WritePortA(0);

        Delay_ms(10);
        InitWrite(0x3C);                                //0011NFxx

        Delay_ms(10);
        InitWrite(0x0C);                                //Display Off

        Delay_ms(10);
        InitWrite(0x01);                                //Display Clear

        Delay_ms(10);
        InitWrite(0x06);                                //Entry mode
}

void main(void)
{
          // Initialize the LCD display.
        LCDInit();

        // Switch ti LCD line 1 ( sends a command to the LCD to tell it ).
        LCDLine_1();

        // Write the data one char at a time.
        d_write('T');

        d_write('H');

        d_write('A');

        d_write('N');

        d_write('K');
        
        d_write('S');
        
        LCDLine_2();
        
        d_write('B');
        
        d_write('I');
        d_write('G');
        d_write(' ');
        d_write('D');
        d_write('O');
        d_write('G');

        while(1);

}

Here is it writing to the display:

thanksbigdog.jpg

Thank you for all the help guys!
 

Fantastic,

The next step you should take is write your own routines for the MCP23S17 SPI port expander and then modify your LCD routines to use them. If written correctly your own routines will provide portability. And in the future, if you need to make modifications, you have source code to do so.

BigDog
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top