Search code examples
assemblypicmicrochiplcd

What is wrong with my assembly code for displaying data onto LCD using 'check busy' flag


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

image 1

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).

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

Solution

  • 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.