1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice
Ian Rogers

Basic 8051 tutorial 4

LCD in ASM and C

  1. Ian Rogers
    The tutorial for the LCD screen will use the basic 1602 type from Hitachi!! This has been cloned to death... Everyone has a copy of this chip set down to mimicking its every function..

    I use “Displaytech” and “Fordata” mainly because of the cost... I'm certainly not advertising them.. Use whatever you have... But they have served me well over the past few years...

    To use these devices you have to set them up before you can use them.... The chip set can use a variety of sizes, the main ones being 16 character by 2 line, 20 character by 2 line, 16 character by 4 line and 20 character by 4 line.

    The basic commands for the display set up are shown here..

    lcd commands.png

    This is the start of the digital communication age... devices talking to other devices we can control various devices by addressing, commands and data... As we move on through the next few tutorials we will see more and more “control” over the way we read and write to sensors and displays..


    I have connected an LCD to our circuit.. The data lines D0~D7 are connected to P1 of our micro and the three control lines RS, RW and E are connected to P3 of our micro..
    LCD.png
    The display has 16 pins D0~D7, RS, RW, E , Vo, Vcc and Vee, we also have the Cathode and Anode for the back light.. The screen I use uses an LED back light that needs 20mA at 4.2 volts..

    This needs a current limiting resistor of 0.8v /20mA ( Ohms law ) = 40 ohm resistor... This will give optimal lighting, however! I try to give the LED its best life so I decrease the current to 10mA to prolong its life... As the daylight reflection isn't too bad and the display is still readable in the dark and the light, so I use an 80 ohm resistor to limit the back light current.. ( nearest value = 82R )

    The display also needs a contrast!! The contrast varies from manufacturer to manufacturer... The small 5mm digit sized displays run with a contrast of 5 volts... Bigger displays need more contrast voltage... This can be up to 17 volts. This voltage is almost always referenced from the Vcc rail down... So the display I use has a maximum of -5 volts.... I use a small negative voltage generator... Namely the ICL7660, a small 8 pin IC that can produce the negative of the voltage that is supplied.. In our case +5 volts will produce -5 volts to generate the negative voltage the LCD contrast needs.

    For your convenience just use the single rail type... then the contrast will fall between 5 volts and ground...

    I have written the ASM and C code to write to the LCD screen and display messages and user input information..

    Code 13
    Code (asm):


        org    0        ; Reset vector
        sjmp    Start

        org    30H        ; Code starts here
    Start:
        acall     LcdInit        ; Initialise screen
    While:  
        acall    LcdGotoline1
        mov    DPTR,#mess1    ; Get message 1
        acall    LcdPrint
        acall    LcdGotoline2
        mov    DPTR,#mess2    ; Get message 2
        acall    LcdPrint
        mov    A,#0CDH        ; Line 2 position 13
        acall    LcdCmd
        jb     P0.0, SW2    ; Is switch one pressed
        mov    A,#31H        ; print a '1'
        acall    LcdData
    SW2:
        jb    P0.1,NS        ; Is switch two pressed
        mov    A,#32H        ; print a '2'
        acall    LcdData
    NS:  
        sjmp    While        ; Back to loop

    ; Screen Init..
    ;---------------
    LcdInit:
        clr    P3.6
        clr    P3.7
        acall    Delay15        ; Settle time
        mov    P1,#33H        ; Init 1
        setb    P3.5
        nop
        clr    P3.5        ; Init 2
        acall    Delay5
        setb    P3.5
        nop
        clr    P3.5
        acall    Delay1
        mov    A,#38H        ; Function set 38
        acall    LcdCmd
        mov    A,#0CH        ; Display on
        acall    LcdCmd
        mov    A,#06H        ; cursor off
        acall    LcdCmd
        mov    A,#01H        ; Clear and home..
        acall    LcdCmd
        ret

    ; Screen goto
    ;---------------
    LcdGotoline1:
        mov    A,#080H        ; Home 0,0
        acall    LcdCmd
        ret

    LcdGotoline2:
        mov    A,#0C0H        ; Home 0,1
        acall    LcdCmd
        ret

    ; Screen Print
    ;---------------
    LcdPrint:
        clr    A
    Lp:  
        push    Acc        ; save index
        movc    A,@A+DPTR    ; Get character
        jz    Fin        ; NULL terminator
        acall    LcdData        ; place on screen
        pop    Acc
        inc    A        ; Increase index
        sjmp    Lp        ; Next character
    Fin:  
        pop    Acc
        ret            ; Done

    ; Screen Busy
    ;---------------
    LcdBusy:
        mov    P1,#0FFH    ; Port as input
        clr    P3.7        ; RS = 1
        setb    P3.6        ; RW = 0
        setb    P3.5        ; E on
        nop
        mov    A,P1        ; Busy flag
        clr    P3.5        ; E off
        clr    P3.6        ; RW = off
        ret

    ; Screen data
    ;---------------
    LcdData:
        push     Acc        ; save data
    Dbusy:  
        acall    LcdBusy        ; See if screen is ready
        jb    Acc.7,DBusy    ; Loop until it is
        pop    Acc        ; get data
        setb    P3.7        ; RS = 1
        clr    P3.6        ; RW = 0
        mov    P1,A        ; Data to screen
        setb    P3.5        ; E on
        nop
        clr    P3.5        ; E off
        clr    P3.7        ; RS = off
        ret

    ; Screen command
    ;---------------
    LcdCmd:
        push     Acc        ; save data
    Cbusy:
        acall    LcdBusy        ; See if screen is ready
        jb    Acc.7,CBusy    ; Loop until it is
        pop    Acc        ; get data
        clr    P3.7        ; RS = 0
        clr    P3.6        ; RW = 0
        mov    P1,A        ; Command to screen
        setb    P3.5        ; E on
        nop
        clr    P3.5        ; E off
        ret

    delay15:
        mov    R2,#1BH        ; Aproximately 15mS
        sjmp    delay
    Delay5:
        mov    R2,#0AH        ; aproximately 5mS
        sjmp    delay
    Delay1:
        mov    R2,#2H        ; Aproximately 1mS
    delay:
        mov    R1,#0        ; 2 clock cycles (loading)    = 2
    d1:
        djnz    R1,d1        ; 2 * 256 clock cycles *180    = 92160
        djnz    R2,d1        ; 2 * 180 clock cycles     = 380
        ret            ; 2 clock cycles (return)    = 2
                    ; * 1.0815 (11.0952 osc)    = 100.07ms

    Mess1:    db    "  PRESS A KEY   ",0
    Mess2:    db    "YOU PRESSED:    ",0

        END

     
    From the code above you will see that all the commands are made by manipulating the data and control lines ( buses ). By writing several low level “device drivers” then several High level routines, we have proper control over the device connected to port 1

    LcdPrint can be called to print an entire NULL terminated string to the display.. Almost automating the process.

    The C code version starts to show the big differences when programming.... We can use strings as simply as typing them out.. If you examine the differences in the code, you start to understand the ease without the mundane programming.... I do not want to discourage ASM coding as its the nuts and bolts that all compilers are built on....

    I program comfortably in both environments so I can get to grips with both... I have written oodles of line of code in ASM and in C.. You can include ASM into C code to make the timing aspect of ASM possible in C...

    Here's my representation of the code in C.... Representation because the code WILL start to differ greatly..

    Code 14
    Code (c):

    #include<8051.h>        // definition file

    #define LCDPORT P1

    __sbit __at (0xB7) RS;    // Register select pin
    __sbit __at (0xB6) RW;    // Read write pin
    __sbit __at (0xB5) E;    // Enable pin


    void delayUs(int x)
        {                // As I don't need uS delays:----
        x/=19;            // Explanation:  This takes 380uS
        while(x--);        // This takes 11uS 1000 / 19 = 52.
        }                // 52 * 11uS + 380uS + 4uS = 963uS
     
    void delayMs(int x)
        {
        while(x--)
            delayUs(1000);    // 13uS + 963uS = 976uS (close enough )
        }
     

    unsigned char LcdBusy(void)
        {
        char ERR = 0;
        LCDPORT = 0xFF;            // Port as input
        RS = 0;                    // Set for command
        RW = 1;                    // Set to read
        E = 1;                    // Clock on
        ERR = LCDPORT & 0x80;    // Bit 7 is busy flag
        E = 0;                    // Clock off
        RW = 0;                    // Set to write
        if(ERR)return 1;        // Return 1 or 0
        return 0;
        }
     
    void LcdCmd( unsigned char C)
        {
        while(LcdBusy());        // Wait until not busy
        RS = 0;                    // Set to command
        RW = 0;                    // Set to write
        LCDPORT = C;            // Write command
        E = 1;                    // Clock
        E = 0;    
        }
     
    void LcdData( unsigned char C)
        {
        while(LcdBusy());        // Wait until not busy
        RS = 1;                    // Set to Data
        RW = 0;                    // Set to write
        LCDPORT = C;            // Write Data
        E = 1;                    // Clock
        E = 0;    
        }
    void LcdInit(void)
        {
        delayMs(50);    // Setting time
        RS = 0;            // Command
        RW = 0;            // Write
        LCDPORT = 0x33;    // Init value
        E = 1;            // Clock
        E = 0;
        delayMs(15);    // Twice
        E = 1;            // Clock
        E = 0;
        delayMs(5);        // Ready to start
        LcdCmd(0x38);    // Function set
        LcdCmd(0x0C);    // Display command
        LcdCmd(0x06);    // Cursor conmmand
        LcdCmd(0x01);    // Clear and Home command
        }
     
    void LcdClear(void)
        {
        LcdCmd(1);        // Clear and Home
        }
     
    void LcdPrintRam(unsigned char* str)
        {
        while(*str != 0)
            LcdData(*str++);    // Print until NULL
        }
     
    void LcdPrintConst( const char * str)
        {
        LcdPrintRam((unsigned char *) str);    //Convert to ram and print
        }
     
    void LcdGoto(unsigned char x, unsigned char y)
        {
        x += 0x80;                // Line 1
        if(y==1) x+=0x40;        // Line 2
        LcdCmd(x);                // Line Command
        }                        // For 4 line use 0xD4 and 0x94 as well..
     
    void main(void)            // Main entry point
        {
        LcdInit();            // Get screen ready
     
        while(1)            // Forever loop
            {
            LcdGoto(0,0);
            LcdPrintConst("   PRESS A KEY   ");
            LcdGoto(0,1);
            LcdPrintConst("YOU PRESSED:     ");
            LcdGoto(13,1);
            if(!P0_0) LcdData(0x31);
            if(!P0_1) LcdData(0x32);    
            }
        }
     
    I am hopefully going to upload the serial communication tutorial next. Simple micro ~ PC stuff....

    Enjoy!
    RonaldB, DonPriceTech, absf and 2 others like this.

Recent Reviews

  1. RonaldB
    RonaldB
    5/5,
    Great stuff in here. I'd surely try this. Since the Arduino made this so simple using LCD libraries, it would be very interesting to implement an LCD application using this tutorial and do some practice to learn the basics on how the code interacts with the hardware.