Assembly Programming an LCD and AVR

Thread Starter

Colossus

Joined Aug 12, 2009
18
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.

Rich (BB 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   
;******************************************************************************
 

Thread Starter

Colossus

Joined Aug 12, 2009
18
I am a senior majoring in computer science but i am not very good at assembly level programming. I did take RISC assembly language programming but my strengths are in high level programming.
 

Thread Starter

Colossus

Joined Aug 12, 2009
18
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.

Rich (BB 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]
 

eblc1388

Joined Nov 28, 2008
1,542
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.
I'm sorry to say thing is not looking good. :(

The original designer Jesper Hansen had selected the AT90S2313 or in its replacement Tiny2313 for very good reasons. So you or someone else would think it is must be a piece of cake just to use a bigger chip like Mega8 or Mega88 to replace it, you could be in for a big surprise.

One should be aware that the selected MCU must satisfy the following requirement:

1. it has a 8-bit dedicated output port for the external R-2R DAC network
2. Rx/Tx pins for serial communication with PC
3. external crystal connection

Unfortunate in your case you have got point 1&3 of the above requirement wrong. Pin#9 and pin#10 on the ATMega8 can be either external crystal connections or PB6 & PB7, but can't be both at the same time.



Now the remaining full 8-bit port available on the M8 is PortD. However, using it would violate requirement 2 of the above and you have no serial communication with the PC.

So what would be the possible solution? There are at least two.

1. Use the Mega8 PortD to drive the external R-2R DAC, and bit-bang the serial communication in software. But this leave you with no connection of external Push Button. Or Do NOT use any serial communication at all.

2. Use a 40-pin chip that satisfy the above requirement, like Mega16, Mega32...
 

Attachments

Last edited:

Thread Starter

Colossus

Joined Aug 12, 2009
18
Could I do:

RS -->PC7
RW-->PC6
E -->PB4
D4-->PB0
D5-->PB1
D6-->PB2
D7-->PB3

And then PD0 and PD1 will be free for the MAX202,
and the AD557 will connect pins 0-7 to PA0-PA7.
 

Attachments

Last edited:

eblc1388

Joined Nov 28, 2008
1,542
Yes you can but you can also do it (better) this way.

Port A : 8-bit, Free, for I/O or ADC use if required
Port B : Push buttons, LCD 4-bit Data
Port C : 8-bit DAC
Port D : Rx, Tx, LCD controls

Edited: With hindsight, the above will not work. The main program is running in an endless loop and external stimulus must be via interrupt to break the MCU operation. This is the amended recommendation.

Port A : 8-bit, Free, for I/O or ADC use if required
Port B : LCD 4-bit Data and control
Port C : 8-bit DAC
Port D : Rx, Tx, push buttons (PD2 & PD3)
 
Last edited:

eblc1388

Joined Nov 28, 2008
1,542
Ok how is this?
How are you going to program the Mega16 in circuit? Your current ICSP connections are all wrong. You would need proper connections to MOSI, MISO, SCK, GND & VCC pins of Mega16.

Will these changes effect the original program?
Sure it does. You would need to change the reference of PORT in the original software to that of your new configuration.

Also you would need additional coding to take care of the push buttons. I have overlooked the operation of the main program in allocating the pin resources in the above post. The main program is running in an endless loop and INTERRUPT must be used for external input like push button.

It would be more appropriate to allocate the push buttons to PD2 & PD3 of PORT D instead because you can then use INT0 & INT1 on PORT D to read the push button.
 

Thread Starter

Colossus

Joined Aug 12, 2009
18
I give up.

I'll just use an Arduino and program it with the freqout function and buttons to generate the various frequencies.
 

eblc1388

Joined Nov 28, 2008
1,542
I give up.
Not a bad decision afterall. :)

If you look at the following relationship between actual output frequency and the phase accumulator(r24/r25/r26) value, you would have a hard time using assembly language to calculate the frequency for display on the LCD.

Rich (BB code):
;   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 
;
;   f is the output frequency to be displayed on LCD
 
Top