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

Assembly Programming an LCD with AVR

Status
Not open for further replies.

Colossus

New Member
The following assembly code is for a Mini DDS that generates sine, sawtooth, triangle and square waves. Frequency range is from 0.07 Hz up to 200 kHz or more. It allows you to control the frequency with 2 push buttons( up increases frequency by 10 and down decreases by 1).

I am attempting to add an LCD HD44780 to display the frequency but my assembly programming skills are pretty bad. I was wondering if someone could help me with the code?

Here is a link to the schematic.

Code:
;*******************************************************************
;*******************************************************************
;
;Description
;
; Poor-mans DDS Synthesizer
;
; Author = Jesper Hansen
; Target = AT90S2313
; Date   = 2001-02-15
;
;
; Extensively modified by Leon Heller
;
; 4/10/02   Modified for the Atmel AVR assembler
; 18/08/02    Modified for the ATtiny2313
; 18/08/02    RS-232 stuff removed
; 19/08/06    Timer0 interrupt used for timing
; 21/08/06   Pushbuttons for frequency up/down added
;
; PB0..7 = D/A Data out
; PD2 (pin 6) SW1 input
; PD3 (pin 7) SW2 input
; PD0 (pin 2) LED output
;
;*******************************************************************
;*******************************************************************
;
;
;
; Output frequency (using 24 bit accumulator) :
;
;   f = deltaPhase * fClock/2^24
;
;   fClock is in this case the CPU clock divided by the
;   number of cycles to output the data ( 9 cycles )
;
;   f = r24/r25/r26 * (11059200/9)/16777216
;
;   f = r24/r25/r26 * 0.073242188
;
;   fMax (theoretical) = 0.5 * fClock
;


#define Temp r16
#define Flags r18
#define Delay_count 0xFF
#define sw1 0  

;******************************************************************************
; start of code
;******************************************************************************

   .nolist
   .include "tn2313def.inc"
   .list

   .cseg

   .org 0
      rjmp   RESET
   .org 0x0005
      rjmp   TIMER1_OVF
   .org 0x0006
      rjmp   TIMER0_OVF


;******************************************************************************
; code
;******************************************************************************


RESET:
      ldi      Temp, RAMEND
      out      SPL, Temp      ; setup stack pointer

      ser      Temp            ;
      out      DDRB,Temp      ; set all PORTB bits as output
      sbi      PORTD,PD2      ; pullup on PD2 input
      sbi      PORTD,PD3      ; pullup on PD3 input
      sbi      DDRD,PD0      ; PD0 output


      ; set up Timer/Counter 0

      ldi    Temp,0x01
      out    TCCR0,Temp       ; Timer clock = system clock (no prescale)
      ldi    Temp,1<<TOV0
      out    TIFR,Temp       ; Clear TOV0/ clear pending interrupts
      ldi    Temp,1<<TOIE0
      out    TIMSK,Temp       ; Enable Timer/Counter0 Overflow Interrupt
      ldi      Temp,Delay_count
      out      TCNT0,Temp
      sei                  ; global enable interrupts
        

      ; set sinewave output as default
      
      ldi      ZH,high(2*sine)   ; setup Z pointer hi
      ldi      ZL,low(2*sine)   ; setup Z pointer lo


      ; clear accumulator

      ldi    r29,0x00      ; clear accumulator
      ldi    r28,0x00      ; clear accumulator

      ; setup adder registers      
      
      ldi    r24,0x00      ; setup adder value
      ldi    r25,0x88      ; to 1 kHz
      ldi    r26,0x08      ;
      
      
      
 

  
loop1:
      rcall   test_sw1
      sbrs   Flags,sw1
      rjmp   loop1
      rcall   up
      rjmp   loop1



  
test_sw1:
      sbic   PIND,PD2      ; SW1 pressed?
      rjmp   test1         ; no, return with flag = 0
      rcall   dly            ; debounce delay
      sbic   PIND,PD2      ; SW1 still closed?
      rjmp   test1         ; no, return with flag = 0
      sbr      Flags,sw1      ; sw1 flag <- 1
      rjmp   test2         ; return with dlaf <- 1
test1:
      cbr      Flags,sw1      ; sw1 flag <- 0
test2:
      ret


up:
      ldi      r16,10
      add      r25,r16
      ret

down:
      ldi      r16,1  
      sub      r25,r16
      
      ret

dly:
      ret
      ldi      r16,0x10
dly1:
      ldi      r17,0xFF
dly2:
      dec      r17
      brne   dly2
      dec      r16
      brne   dly1
      ret


; Timer0 overflow interrupt service routine
; r28,r29,r30 is the phase accumulator
; r24,r25,r26 is the adder value determining frequency
;

TIMER0_OVF:
      add      r28,r24      ; add value to accumulator
      adc      r29,r25  
      adc      r30,r26
      lpm               ; load byte from current table in ROM
      out      PORTB,r0   ; output byte to port
      ldi      Temp,Delay_count
      out      TCNT0,Temp
      reti


TIMER1_OVF:


      reti

;******************************************************************************
; data tables
;******************************************************************************

   ; force table to begin at 256 byte boundary

   .org 0x200

sine:      ; 256 step sinewave table
   .db   0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae
   .db   0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8
   .db   0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5
   .db   0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7
   .db   0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc
   .db   0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3
   .db   0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83
   .db   0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51
   .db   0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27
   .db   0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a
   .db   0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08
   .db   0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23
   .db   0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c
   .db   0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c

square:      ; 256 step squarewave table
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff


;******************************************************************************
; end of file  
;******************************************************************************
 

Colossus

New Member
I made a diagram using the original PDF file, could you please let me know if the buttons and LCD are connected to the ATMEGA8 correctly?

http://onfinite.com/libraries/1515060/741.jpg

Here is the code i added to output the frequency to the LCD. AVR studio says there are a couple errors but let me know if i'm on the right track.

Code:
;*******************************************************************
;*******************************************************************

; Output frequency (using 24 bit accumulator) :
;
;   f = deltaPhase * fClock/2^24
;
;   fClock is in this case the CPU clock divided by the
;   number of cycles to output the data ( 9 cycles )
;
;   f = r24/r25/r26 * (11059200/9)/16777216
;
;   f = r24/r25/r26 * 0.073242188
;
;   fMax (theoretical) = 0.5 * fClock
;


#define Temp r16
#define Flags r18
#define Delay_count 0xFF
#define sw1 0   

;******************************************************************************
; start of code
;******************************************************************************

   .nolist
   ;.include "tn2313def.inc"
   .include "m32def.inc" ; *** 
   .list

   .equ lcden = 7 ; LCD Enable (Act as clk)
   .equ lcdrs = 6 ; LCD register select
   .equ lcdrw = 5 ; LCD read/write
   .cseg

   .org 0
      rjmp   RESET
   .org 0x0005
      rjmp   TIMER1_OVF
   .org 0x0006
      rjmp   TIMER0_OVF


;******************************************************************************
; code
;******************************************************************************

RESET:


      ldi      Temp, RAMEND
      out      SPL, Temp      ; setup stack pointer

      ser      Temp           ;
      out      DDRB,Temp      ; set all PORTB bits as output
      sbi      PORTD,PD2      ; pullup on PD2 input
      sbi      PORTD,PD3      ; pullup on PD3 input
      sbi      DDRD,PD0       ; PD0 output


      ; set up Timer/Counter 0

      ldi    Temp,0x01
      out    TCCR0,Temp       ; Timer clock = system clock (no prescale)
      ldi    Temp,1<<TOV0
      out    TIFR,Temp       ; Clear TOV0/ clear pending interrupts
      ldi    Temp,1<<TOIE0
      out    TIMSK,Temp       ; Enable Timer/Counter0 Overflow Interrupt
      ldi    Temp,Delay_count
      out    TCNT0,Temp
      sei                  ; global enable interrupts
         

      ; set sinewave output as default
      
      ldi      ZH,high(2*sine)   ; setup Z pointer hi
      ldi      ZL,low(2*sine)   ; setup Z pointer lo


      ; clear accumulator

      ldi    r29,0x00      ; clear accumulator
      ldi    r28,0x00      ; clear accumulator

      ; setup adder registers      
      
      ldi    r24,0x00      ; setup adder value
      ldi    r25,0x88      ; to 1 kHz
      ldi    r26,0x08      ;
      
    ;*** LCD Reset ***
 	ldi r16, low (ramend) ;Stack setup
 	out spl, r16
 	ldi r16, high (ramend)
 	out sph, r16

 	ser r16
 	out DDRD,r16        ; Setup port D as output for data
    ldi r16, 0b11100000
 	out DDRC,r16        ; Setup port C for ctrl
 	rcall lcd_init      ; Call to LCD initialization
      


; *** Output Freqencuy to LCD ***
;  run calculation every time the frequency changes, 
;  and output the number to the LCD
;  f = r24/r25/r26 * 0.073242188

main:


 	  ldi      r16,0x00     ; place cursor in 1st row, 1st char
      rcall    lcd_cmd
  
      add      r28,r24      ; Calculate f 
      adc      r29,r25   
      adc      r30,r26
      lpm                   ; load byte from current table in ROM
      out      PORTB,r0     ; output byte to port
      ldi      Temp,Delay_count
      out      TCNT0,Temp

      ldi      r16, TCNT0	; load the freq value into regester 
      rcall    lcd_prt      ; display to screen 
	  ret


loop:
 rjmp loop


;*** LCD initialization ***
lcd_init:
 ldi   r16,0x38       ;Function Set:8 bit mode & 5 x 7 dot font
 rcall lcd_cmd
 ldi   r16,0x38       ;Function Set:8 bit mode & 5 x 7 dot font
 rcall lcd_cmd
 ldi   r16,0x38       ;Function Set:8 bit mode & 5 x 7 dot font
 rcall lcd_cmd
 ldi   r16, 0x0F      ;Display on, cursor on, cursor blink
 rcall lcd_cmd
 ldi   r16, 0x06      ;auto increment cursor
 rcall lcd_cmd
 rcall main
 ret
 

;*** Send data to be displayed ***
lcd_prt:
 cbi PORTC, lcdrs ; RS=1 sending instruction
 nop
 cbi PORTC, lcdrw ; RW=0 write operation
 nop
 out PORTD,r16    ; put data on bus
 nop
 sbi PORTC, lcden ; simulate +ve clk edge
 nop
 nop
 nop
 cbi PORTC, lcden ; simulate -ve clk edge
 ret
    

;*** Send commmand to LCD ***
lcd_cmd:
 cbi PORTC, lcdrs ; RS=0 sending instruction
 nop
 cbi PORTC, lcdrw ; RW=0 write operation
 nop
 out PORTD,r16    ; put data on bus
 nop
 sbi PORTC, lcden ; simulate +ve clk edge
 nop
 nop
 nop
 cbi PORTC, lcden ; simulate -ve clk edge
 ret 



loop1:
      rcall   test_sw1
      sbrs   Flags,sw1
      rjmp   loop1
      rcall   up
      rjmp   loop1



   
test_sw1:
      sbic   PIND,PD2      ; SW1 pressed?
      rjmp   test1         ; no, return with flag = 0
      rcall  dly           ; debounce delay
      sbic   PIND,PD2      ; SW1 still closed?
      rjmp   test1         ; no, return with flag = 0
      sbr    Flags,sw1     ; sw1 flag <- 1
      rjmp   test2         ; return with dlaf <- 1
test1:
      cbr    Flags,sw1     ; sw1 flag <- 0
test2:
      ret


up:
      ldi      r16,10
      add      r25,r16
      ret

down:
      ldi      r16,1   
      sub      r25,r16
      
      ret

dly:
      ret
      ldi      r16,0x10
dly1:
      ldi      r17,0xFF
dly2:
      dec      r17
      brne    dly2
      dec      r16
      brne    dly1
      ret


; Timer0 overflow interrupt service routine
; r28,r29,r30 is the phase accumulator
; r24,r25,r26 is the adder value determining frequency
;

TIMER0_OVF:
      add      r28,r24      ; add value to accumulator
      adc      r29,r25   
      adc      r30,r26
      lpm                   ; load byte from current table in ROM
      out      PORTB,r0     ; output byte to port
      ldi      Temp,Delay_count
      out      TCNT0,Temp
      reti


TIMER1_OVF:


      reti


;******************************************************************************
; data tables
;******************************************************************************

   ; force table to begin at 256 byte boundary

   .org 0x200

square:      ; 256 step squarewave table
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
   .db   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff


;******************************************************************************
; end of file   
;******************************************************************************
[/code]
 
Last edited:

Pommie

Well-Known Member
Most Helpful Member
You are on the right track but your code and diagram don't agree with each other.

You diagram is for 4 bit data on C0-C3. You code is for 8 bit data on D0-D7.
Your diagram has the R/W line grounded, your code is using it.

The way your diagram is wired, you need 4 bit code that uses delays instead of reading the busy signal. Try here.

Mike.
 

Colossus

New Member
Thanks for the link.

As you might have guessed i'm rather new to assembly programming. That don't teach you this stuff in school and i major in computer science. There was one course, assembly level programming where we briefly used SAL/MAL/TAL to program RISC but that's about it. Everything else we learn is high level programming mainly with C/C++/Java.

So, in the code where exactly is the frequency variable stored and which of the following routines do I need to send it to the LCD?

Thanks for all your help!

Code:
; ******************************************************************
;    L C D    D I S P L A Y    D E M O
; ******************************************************************
;
;   Provides the entry point for a simple "Hello World" impelmentation
;   using my LCD routines.
;
;   Author:   Adam Swann <[email protected]>
;   Homepage: http://www.velocity2.com/~adam/
;
;   http://www.adamswann.com/projects/avr-lcd/
;
;   See LCD.asm for more information.
;
; ******************************************************************

   .nolist
   .include "8515def.inc"
   .list

   .cseg



.def    Temp      = r16
.def    Temp2      = r17

; Define generic port names (change the 'A' to whatever you're using)
.equ   LCD_PORT  = PORTC
.equ   LCD_DDR     = DDRC
.equ   LCD_PIN     = PINC

; Define the pin numbers
.equ   LCD_E       = 1
.equ   LCD_RW      = 2
.equ   LCD_RS      = 3

.def   DelayTime = r20
.equ   DelayCount1 = 128


      .org   0
      rjmp   RESET

RESET:   ldi     Temp, low(RAMEND)
       out      SPL, Temp
       ldi      Temp, high(RAMEND)
       out      SPH, temp      ; Initialize Stackpointer

      ldi     Temp, 0x00
      out     LCD_PORT, Temp      ; Clear the outputs

      ldi     Temp, 0xFF
      out     LCD_DDR, Temp       ; Set the direction to output

      ldi     DelayTime, 255      ; Set the default delay length
                  ; (see my delay.asm)

      rcall   LCD_Init

      rcall    DELAY

      ldi     r30, low(strInit1*2)
      ldi     r31, high(strInit1*2)
      ldi     Temp2,   16
      rcall   LCD_PrintPM

      ; *** Send the cursor to beginning of second line
      ldi     Temp, 0b11000000
      rcall    LCD_SendCmd

      ldi     r30, low(strInit2*2)
      ldi     r31, high(strInit2*2)
      ldi     Temp2,   16
      rcall   LCD_PrintPM


      rcall    DELAY



FOREVER:   rjmp    FOREVER

strInit1:   .db   " Hello World!   "
strInit2:   .db   "It worked!!!    "

; ******************************************************************
;    L C D    D I S P L A Y    R O U T I N E S
; ******************************************************************
;
;   Interfaces the AVR '8515 microcontroller with LCDs controlled
;   by the Samsung KS0066U (and similiar) LCD driver.
;
;   Author:   Adam Swann <[email protected]>
;   Homepage: http://www.velocity2.com/~adam/
;
;   The code below is fairly straightforward and well-documented.
;   See my Web site or e-mail me if you need further instructions.
;
;   I used an 8515 at 4 MHz.  My LCD is Jameco Part #171715.
;
;   I wired the LCD display as follows (onto Port C)
;    
;      AVR   LCD
;      0 --> no connection
;      1 --> Enable on LCD
;      2 --> R/W on LCD
;      3 --> RS on LCD
;      4 --> Data4
;      5 --> Data5
;      6 --> Data6
;      7 --> Data7 
;
;   References: (URLs may be wrapped)
;    o KS0066U Datasheet <http://www.usa.samsungsemi.com/
;                          products/summary/charlcd/ks0066u.htm>
;
; ******************************************************************

; *** LCD_Init: Routine to initialize the LCD.
LCD_Init:   push   Temp

      ldi   DelayTime, 255

      ; Put the LCD in 8-bit mode.  Even though we want the display to
      ; operate in 4-bit mode, the only way to guarantee that our commands
      ; are aligned properly is to initialize in 8-bit.  (The user might have
      ; hit reset between nibbles of a dual 4-bit cycle.)
      ldi   Temp, 0b00110000
      out   LCD_PORT, Temp
      rcall   LCD_PulseE

      rcall   DELAY
      rcall   DELAY

      ; Now it's safe to go into 4-bit mode.
      ldi   Temp, 0b00100000
      out   LCD_PORT, Temp
      rcall   LCD_PulseE

      rcall    DELAY
      rcall   DELAY

      ; *** Send the 'FUNCTION SET' command
      ;         +------ Data:  0 = 4-bit; 1 = 8-bit
      ;         |+----- Lines: 0 = 1; 1 = 2
      ;         ||+---- Font:  0 = 5x8; 1 = 5x11
      ldi   Temp, 0b00101100
      rcall    LCD_SendCmd

      ; *** Send the 'CURSOR/DISPLAY SHIFT' command
      ;          +----- S/C:  0 = cursor; 1 = display
      ;          |+---- R/L:  0 = left; 1 = right
      ldi   Temp, 0b00010100
      rcall    LCD_SendCmd

      ; *** Send the 'DISPLAY ON/OFF' command
      ;           +---- Display: 0 = off; 1 = on
      ;           |+--- Cursor: 0 = off; 1 = on
      ;           ||+-- Blink: 0 = off; 1 = on
      ldi   Temp, 0b00001100
      rcall    LCD_SendCmd

      ; *** Send the 'ENTRY MODE' command
      ;            +--- Direction: 0 = left; 1 = right
      ;            |+-- Shift Dislay: 0 = off; 1 = on
      ldi   Temp, 0b00000110
      rcall    LCD_SendCmd

      rcall    LCD_Clear

      pop    Temp
      ret

; *** LCD_PrintMem: Prints from memory.
;       Put the starting memory location in Z (r31:r30)
;       Put the number of characters to print in Temp2
;   After execution, Z is at the character AFTER the last to be printed
;                    and Temp2 is zero.
;       This function will not wrap if you the string is bigger than the LCD.

LCD_PrintMem:   push   Temp

   LCD_MemRead: ld   Temp, Z+
      rcall   LCD_SendChar
      dec   Temp2
      brne   LCD_MemRead

      pop   Temp
      ret

; *** LCD_PrintEE: Prints from EEPROM
;LCD_PrintEE:   push   Temp
;
 ;   LCD_EERead: sbic    EECR, EEWE    ; Wait for EEWE to clear
;      rjmp    LCD_EERead
;
;      out    EEARH, r31    ;output address high byte, remove if no high byte exist
;      out    EEARL, r30    ;output address low byte
;
;      sbi    EECR, EERE    ;set EEPROM Read strobe
;      in    Temp, EEDR    ;get data
;
;      rcall   LCD_SendChar
;
;      dec   Temp2
;      brne   LCD_EERead
;
;      pop   Temp
;      ret

; *** LCD_PrintPM: Prints from program memory
LCD_PrintPM:   push   r0
      push   Temp

    LCD_PMRead: lpm
          mov   Temp, r0
      rcall   LCD_SendChar
      adiw   r30, 1
      dec   Temp2
      brne   LCD_PMRead

      pop   Temp
      pop   r0
      ret



; *** LCD_Clear: Clears the display and sends the cursor home.

LCD_Clear:   push    Temp

      ; *** Clear the display
      ldi   Temp, 0b00000001
      rcall    LCD_SendCmd

      ; *** Send the cursor home
      ldi   Temp, 0b00000010
      rcall    LCD_SendCmd

      pop   Temp
      ret

; *** LCD_SendCmd: Routine to write a command to the instruction register.
;       The value to be written should be stored in Temp.
;       The value is sent 4 bits at a time.

LCD_SendCmd:   push    Temp
      push    Temp2

      rcall   LCD_WaitBusy

      mov    Temp2, Temp      ; Make a backup copy
      andi    Temp2, 0b11110000   ; Only use the upper nibble
      out   LCD_PORT, Temp2      ; Send it
      rcall    LCD_PulseE      ; Pulse the enable

      swap   Temp         ; Swap upper/lower nibble
      andi    Temp, 0b11110000   ; Only use the upper nibble
      out   LCD_PORT, Temp      ; Send it
      rcall    LCD_PulseE      ; Pulse the enable

      pop    Temp2
      pop    Temp
      ret
; *** LCD_SendChar: Routine to write a character to the data register.
;       The value to be written should be stored in Temp.
;       The value is sent 4 bits at a time.

LCD_SendChar:   push    Temp
      push    Temp2

      mov    Temp2, Temp      ; Make a backup copy
      rcall   LCD_WaitBusy
      mov    Temp, Temp2      ; Make a backup copy

      andi    Temp2, 0b11110000   ; Only use the upper nibble
      ori   Temp2,  0b00001000
      out   LCD_PORT, Temp2      ; Send it
      rcall    LCD_PulseE      ; Pulse the enable

      swap   Temp         ; Swap upper/lower nibble
      andi    Temp,  0b11110000   ; Only use the upper nibble
      ori   Temp,  0b00001000
      out   LCD_PORT, Temp      ; Send it
      rcall    LCD_PulseE      ; Pulse the enable

      pop    Temp2
      pop    Temp
      ret

; *** LCD_WaitBusy: Wait for the busy flag to go low.
;       Waits for the busy flag to go low.  Since we're in 4-bit mode,
;       the register has to be read twice (for a total of 8 bits).  The
;       second read is never used.
;       If you need more code space, this function could be replaced with
;       a simple delay.

LCD_WaitBusy:   push    Temp

      ldi   Temp, 0b00001111   ; Disable data bit outputs
      out   LCD_DDR, Temp

      ldi   Temp, 0x00      ; Clear all outputs
      out   LCD_PORT, Temp

LCDWaitLoop:   ldi   Temp, 0b00000100   ; Enable only read bit
      out   LCD_PORT, Temp
      sbi   LCD_PORT, LCD_E      ; Raise the Enable signal.
      nop
      nop
      in   Temp, LCD_PIN      ; Read the current values
      cbi   LCD_PORT, LCD_E      ; Disable the enable signal.
      rcall    LCD_PulseE      ; Pulse the enable (the second nibble is discarded)
      sbrc   Temp, 7         ; Check busy flag
      rjmp   LCDWaitLoop

      ldi   Temp, 0b11111111   ; Enable all outputs
      out   LCD_DDR, Temp

      pop   Temp
      ret

LCD_PulseE:   sbi   LCD_PORT, LCD_E
      nop
      nop
      cbi   LCD_PORT, LCD_E
      ret

; *** Provide millisecond delay (DelayTicks specifies number of ms)
DELAY:   push DelayTime
   push r25

   ldi   r25, DelayCount1

DELAY1:   dec r25
   brne DELAY1
   dec DelayTime
   brne DELAY1

   pop r25
   pop DelayTime
   ret
 
Last edited:
Status
Not open for further replies.

Latest threads

EE World Online Articles

Loading
Top