;my_lcd.asm ;Synopsis ;An LCD display driver example for a 4-bit mode ;LCD display and DTMF decoder to Processor interface ;LCD to AVR Processor connections: ;PB7 PB6 PB5 PB4 PB3 PB2 ;D7 D6 D5 D4 E RS ;LCD input R/W is grounded so the LCD can only be written to. ; ;DTMF decoder connections: ;PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 ;D3 D2 D1 D0 _ _ OE DSO .include "2313def.inc" .def temp=r16 .def temp1=r17 .def low_del=r18 .def hi_del=r19 .def count=r20 .def lcd_cmd=r21 .def lcd_dat=r22 .def dtmf=r23 .def char_count=r24 .cseg .org 0 rjmp RESET ;Reset Handle RESET: ldi temp, low(RAMEND) out SPL, temp ;Init the Stack Pointer ldi temp, 0b11111110 ;configure PORT B for 7 outputs out DDRB, temp ;except PB0 rcall hi_delay ;********************************************************** ;Main Part of the Program ;********************************************************** main_loop: rcall init_lcd rcall print_intro ldi temp, 0b11111110 ;configure PORT B for 7 outputs out DDRB, temp ;except PB0 which is input ldi temp, 0b00000001 ;configure PORT B pullup on bit 0 out PORTB, temp ;send to port xdet: sbis PINB,0 ;loop looking for valid DTMF signal rjmp xdet ; not found try again rcall LcdClear ; as this is first time through ; the sign-on message is cleared ldi char_count,0 ; reset ,clear display and counter dtmf_loop: ; this is the main loop waiting for valid DTMF tone ; from the DTMF decoder ; if you want to do anything when DTMF is valid put it ; here and jump here when char has been displayed det: sbis PINB,0 ; loop again waiting for valid dtmf rjmp det ; ; valid dtmf has been found time to change pins to inputs ; ldi temp, 0b00001110 ;configure PORT B high nibble as inputs out DDRB, temp ldi temp, 0b00000010 ;enable OE on DTMF chip out PORTB, temp ldi dtmf,0b00110000 ;preload rx nibble with 30h or ascii '0' sbic PINB,4 ori dtmf,0b00000001 ; sbic PINB,5 ;read each bit and 'or' into nibble ori dtmf,0b00000010 sbic PINB,6 ori dtmf,0b00000100 sbic PINB,7 ori dtmf,0b00001000 cpi dtmf, 0b00111010 ;if dtmf > '9' (39h), ajust so proper char will breq ascii_0 ;be printed. cpi dtmf, 0b00111011 breq ascii_star cpi dtmf, 0b00111100 breq ascii_hash ; Ascii char now in buffer, precheck if still within LCD screen limits. (2x16 char lcd) ascii_done: ldi temp,0b11111110 ;configure PORT B for all outputs out DDRB, temp ;except PB0 inc char_count ;inc overall character count cpi char_count,17 ;check if over width on line 1 breq doline2 cpi char_count,33 ;check if over width on line 2 brne printch ;within limits so just print character ; Whole LCD screen if full so clear line one and loop back to start . ldi char_count,1 ldi lcd_cmd, $80 ;set lcd to start rcall lcd_all_cmd ldi count, 16 ;set parameters for printing a blank line ldi ZH, high(msg2*2) ldi ZL, low(msg2*2) rcall more2 ;print blank line ldi lcd_cmd, $80 ;set lcd to start of top line rcall lcd_all_cmd rjmp printch ;at last print our character ; Clear line two and continue printing characters. doline2: ldi ZH, high(msg2*2) ;set parameters for printing a blank line ldi ZL, low(msg2*2) rcall ms2 ;print blank line ldi lcd_cmd, $c0 ;set lcd to start of second line rcall lcd_all_cmd printch: mov lcd_dat,dtmf ;load our rx'ed character for printing rcall lcd_all_dat ;print to LCD (LCD auto increments for each character) fdet: sbic PINB,0 ;wait for dtmf tone to disappear rjmp fdet rjmp det ;tone is gone, loop right back to start. ; Patchs to print correct ascii characters. ascii_0: ldi dtmf,0b00110000 ;dtmf tone 0 = 3ah, so change to ascii '0' (30h) rjmp ascii_done ascii_star: ldi dtmf,0b00101010 ;dtmf tone ? = 3bh, so change to ascii '*' (2Ah) rjmp ascii_done ascii_hash: ldi dtmf,0b00100011 ;dtmf tone ? = 3ch, so change to ascii '#' (23h) rjmp ascii_done ;********************************************************** ;Init_Lcd: Initializes the 16 X 2 LCD module in 4 bit data ;transfer mode ;********************************************************** init_lcd: ldi lcd_cmd, 3 rcall lcd_low_cmd rcall hi_delay ldi lcd_cmd, 3 rcall lcd_low_cmd rcall low_delay ldi lcd_cmd, 3 rcall lcd_low_cmd rcall low_delay ldi lcd_cmd, $28 ;set 4-bit interface rcall lcd_all_cmd ldi lcd_cmd, 8 ;set DDRAM address to 00 rcall lcd_all_cmd ldi lcd_cmd, $0c rcall lcd_all_cmd ldi lcd_cmd, 6 rcall lcd_all_cmd ret ;********************************************************** ;Print_Lcd: Prints strings on the LCD display module ;********************************************************** print_intro: ldi lcd_cmd, $80 rcall lcd_all_cmd ldi count, 16 ldi ZH, high(msg1*2) ldi ZL, low(msg1*2) more1: lpm mov lcd_dat, r0 rcall lcd_all_dat adiw ZL, 1 dec count cpi count, 0 brne more1 ms2: ldi count, 16 ldi lcd_cmd, $c0 rcall lcd_all_cmd more2: lpm mov lcd_dat, r0 rcall lcd_all_dat adiw ZL, 1 dec count cpi count, 0 brne more2 ret ;********************************************************** ;LcdClear: ;********************************************************** LcdClear: ldi lcd_cmd, $01 rcall lcd_all_cmd rcall hi_delay ret ;********************************************************** ;Low_Delay: A 250 us delay 4MHz clock????? ;********************************************************** low_delay: push low_del ; save 2nd delay counters push hi_del ldi low_del, 25 ; 25 times 1us = 25us.... ld_hi: ldi hi_del, 10 ; 10 times 25us =250us..... loop_in: ; each time through is 1us dec hi_del ; 1~ 0.25us cpi hi_del, 0 ; 1~ 0.25us brne loop_in ; 2~ if true 0.5us 1~ if not true dec low_del ; dec low delay and see if 0 cpi low_del, 0 brne ld_hi ; do hi delay again pop hi_del ; recover 2nd delay counters pop low_del ret ;********************************************************** ;Hi_Delay: A 5 ms delay ;********************************************************** hi_delay: push low_del ldi low_del, 20 more_call: rcall low_delay dec low_del cpi low_del, 0 brne more_call pop low_del ret ;********************************************************** ;Big_delay: A 1 s delay ;********************************************************** big_delay: ldi low_del, 200 more_big: rcall hi_delay dec low_del cpi low_del, 0 brne more_big ret ;********************************************************** ;Lcd_Low_Cmd: Sends a command to higher 4 bits of the LCD. ;The command nibble must be in the lower nibble of the ;variable 'lcd_cmd' ;********************************************************** lcd_low_cmd: mov temp, lcd_cmd lsl temp lsl temp lsl temp lsl temp andi temp, $f0 out PORTB, temp ori temp, $08 out PORTB, temp andi temp, $f7 out PORTB, temp ret ;********************************************************** ;Lcd_All_Cmd: sends an 8 bit command to the LCD ;********************************************************** lcd_all_cmd: push lcd_cmd lsr lcd_cmd lsr lcd_cmd lsr lcd_cmd lsr lcd_cmd rcall lcd_low_cmd pop lcd_cmd andi lcd_cmd, $0f rcall lcd_low_cmd rcall low_delay ret ;********************************************************** ;Lcd_Low_Dat: Sends a data nibble to higher 4 bits of the ;LCD. The data nibble must be in the lower nibble of the ;variable 'lcd_dat' ;********************************************************** lcd_low_dat: mov temp, lcd_dat lsl temp lsl temp lsl temp lsl temp andi temp, $f0 ori temp, 4 out PORTB, temp ori temp, 8 out PORTB, temp andi temp, $f7 out PORTB, temp ret ;********************************************************** ;Lcd_All_Dat: Sends a data byte to the LCD. The data byte ;is stored in variable 'lcd_dat' ;********************************************************** lcd_all_dat: push lcd_dat lsr lcd_dat lsr lcd_dat lsr lcd_dat lsr lcd_dat andi lcd_dat, $0f rcall lcd_low_dat pop lcd_dat andi lcd_dat, $0f rcall lcd_low_dat rcall low_delay ret ;********************************************************** ;Message #1 ;********************************************************** msg1: .db " DTMF Decoder ZL1VK " ; 12345678123456781234567812345678 ;********************************************************** ;Message #2 ;********************************************************** msg2: .db " " ; 12345678123456781234567812345678