This is my first time working with LCDs and I am trying to get my code (see below) written for a PIC18f458 to display the ASCII characters 'N' and 'O' on a LCD; using a "check busy" flag instead of a delay, to work.
When I debug the code within the MPLAB X IDE, it seems to perform as expected. But, when I test it out using Proteus, it only displays the 'O' character (see image 1).
REGA EQU 0x07 ; set aside registers for delay
REGB EQU 0x08 ; subroutines
REGC EQU 0x09
LCD_DATA EQU PORTD ; LCD data pins RD0-RD7
LCD_CTRL EQU PORTC ; LCD control pins
RS EQU RC0 ; RS pin of LCD
RW EQU RC1 ; R/W pin of LCD
EN EQU RC2 ; Enable pin of LCD
ORG 0000H ; burn into ROM starting at 0
CLRF TRISD ; make PORT D an output
CLRF TRISC ; make PORTC an output
BCF LCD_CTRL,EN ; enable idle low
CALL LDELAY ; wait for initialisation
MOVLW 0x38 ; init LCD 2 lines, 5x7 char
CALL COMMAND ; issue command
CALL LDELAY ; initialisation hold
MOVLW 0x0E ; LCD on, cursor on
CALL COMMAND ; issue command
CALL READY ; is LCD ready?
MOVLW 0x01 ; clear LCD command
CALL COMMAND ; issue command
CALL READY ; is LCD ready?
MOVLW 0x06 ; shift cursor right
CALL COMMAND ; issue command
CALL READY ; is LCD ready?
MOVLW 0x86 ; cursor: line 1, pos. 6
CALL COMMAND ; issue command
CALL READY ; is LCD ready?
MOVLW A'N' ; display letter 'N'
CALL DATA_DISPLAY
CALL READY ; is LCD ready?
MOVLW A'O' ; display letter 'O'
CALL DATA_DISPLAY
CALL READY ; is LCD ready?
HERE BRA HERE
;------------------------------------------------------
COMMAND MOVWF LCD_DATA ; issue command code
BCF LCD_CTRL,RS ; RS = 0 for command
BCF LCD_CTRL,RW ; R/W = 0 for write
BSF LCD_CTRL,EN ; E = 1 for high pulse
CALL SDELAY ; make a wide En pulse
BCF LCD_CTRL,EN ; E = 0 for H-to-L pulse
RETURN
;-----------------------------------------------------
DATA_DISPLAY
MOVWF LCD_DATA ; copy WREG to LCD DATA pin
BSF LCD_CTRL,RS ; RS = 1 for data
BCF LCD_CTRL,RW ; R/W = 0 for write
BSF LCD_CTRL,EN ; E = 1 for high pulse
CALL SDELAY ; make a wide En pulse
BCF LCD_CTRL,EN ; E = 0 for H-to-L pulse
RETURN
;-----------------------------------------------------
; check busy flag
READY SETF TRISD ; make PORTD input port for LCD data
BCF LCD_CTRL,RS ; RS = 0 access command reg
BSF LCD_CTRL,RW ; R/W = 1 read command flag
; read command reg and check busy flag
BACK BSF LCD_CTRL,EN ; E = 0 for L-to-H pulse
CALL SDELAY ; make a wide En pulse
BCF LCD_CTRL,EN ; E = 1 for L-to-H pulse
BTFSC LCD_DATA,7 ; stay until busy flag = 0
BRA BACK
CLRF TRISD ; make PORTD output port for LCD data
RETURN
; 1/4 SECOND DELAY
LDELAY
MOVLW D'200' ; load WREG with literal value 200
MOVWF REGA ; copy to delay regA
L1 MOVLW D'250' ; load WREG with literal value 250
MOVWF REGB ; copy to delay regB
L2 NOP ; waste soem time
NOP
DECF REGB, F ; decrement value in regB
BNZ L2 ; repeat until it equals to zero
DECF REGA, F ; decrement value in regA
BNZ L1 ; repeat until it equals to zero
RETURN
; Short DELAY
SDELAY
MOVLW D'2' ; load WREG with literal value 2
MOVWF REGC ; copy to delay regE
S1 NOP
DECF REGC, F ; decrement value in regE
BNZ S1 ; repeat until it equals to zero
RETURN
END
When I change the infinite loop at the end of the code to instead loop around loading and displaying the characters, I can see that both characters are being displayed in the loop (see image 2).
So, my question is, what is wrong with my code (below) that is causing it to only display a single character, i.e., the last character?
Please note that when I use a delay (in the assembly code) intead of a busy flag, the code works as intended. Likewise, the C version (see below) using a "check busy" flag also works as intended. So, I am left scratching my head as to what could possibly be the issue. Therefore, any insight that anyone can provide will be very much appreciated.
#define ldata PORTD // PORTD = LCD data pins
#define rs PORTCbits.RC0 // rs = PORTC.0
#define rw PORTCbits.RC1 // rw = PORTC.1
#define en PORTCbits.RC2 // en = PORTC.2
#define busy PORTDbits.RD7 // busy = PORTD.7
void lcdcmd(unsigned char value);
void lcddata(unsigned char value);
void lcdready(void);
void MSDelay(unsigned int itime);
void main()
{
TRISD = 0; // both ports B and D as output
TRISB = 0;
en = 0; // enable idle low
MSDelay(250); // long delay
lcdcmd(0x38); // init. LCD 2 lines, 5x7 matrix
MSDelay(250); // long delay
lcdcmd(0x0E); // display on, cursor on
lcdready(); // check the LCD busy flag
lcdcmd(0x01); // clear LCD
lcdready(); // check the LCD busy flag
lcdcmd(0x06); // shift cursor right
lcdready(); // check the LCD busy flag
lcdcmd(0x86); // line 1, position 6
lcdready(); // check the LCD busy flag
lcddata('N'); // display letter 'N'
lcdready(); // check the LCD busy flag
lcddata('O'); // display letter 'O'
}
void lcdcmd(unsigned char value)
{
ldata = value; // put the value on the pins
rs = 0;
rw = 0;
en = 1; // strobe the enable pin
MSDelay(1);
en = 0;
}
void lcddata(unsigned char value)
{
ldata = value; // put the value on the pins
rs = 1;
rw = 0;
en = 1; // strobe the enable pin
MSDelay(1);
en = 0;
}
void lcdready()
{
TRISD = 0xFF; // make PORTD an input
rs = 0;
rw = 1;
do // wait here for busy flag
{
en = 1; // strobe the enable pin
MSDelay(1);
en = 0;
}while(busy==1);
TRISD = 0;
}
void MSDelay(unsigned int itime)
{
unsigned int i, j;
for(i=0;i<itime;i++)
for(j=0;j<135;j++);
}
The busy flag for the HD44780 8-bit parallel interface is valid only while the ENABLE is at a HIGH level.
In practice when using a real LCD character module the state of the LCD outputs will remain at their last driven state for a microsecond or so after the ENABLE is set to a LOW level. This does not happen in simulation so the data will be invalid at the time the ENABLE is set to a LOW level.
You need to read and comprehend the information in the LCD module data sheet. Pay attention to the timing diagrams for the LCD read cycles.