| CODE |
; <<Digital Thermometer>> ; Alan Nawoj, Donald Chai ; EE 476 - FINAL PROJECT ; *Description* ; Temperature range: 0F - 300F ; Aref = 4.096 V ; LM 34 --> 10 mV / 1 degree F ; PortC = LCD display ; PortD = pushbuttons ; PortB = LEDs ; Only 1 pushbutton can be pressed at a given time ;------------------------------ .nolist .include "8535def.inc" .list ;***************** ;REGISTERS: .def save = r1 .def reload = r2 .def lcdstat = r3 .def TXchar = r4 .def isneg = r14 ;says if we're below 32 F .def temp = r16 .def timeout = r17 .def timeout2= r18 .def LCDcharpos=r19 ;***************** ;Program constants: .equ lcdrs = PC6 ;LCD rs pin connected to PC6 .equ lcdrw = PC5 ;LCD r/w pin connected to PC5 .equ lcde = PC4 ;LCD e pin connected to PC4 ;***************** ;BUTTONS: ;DC - you might think we could simply store currbtn in SRAM as our state, ; as SRAM is plentiful. this reduces code size by ALOT. .equ toggle = ~(1<<7); toggle between temp scales .equ decmin = ~(1<<6); dec. minimum alarm temp .equ incmin = ~(1<<5); inc. minimum alarm temp .equ decmax = ~(1<<4); dec. maximum alarm temp .equ incmax = ~(1<<3); inc. maximum alarm temp .equ dispmin= ~(1<<2); display the minimum temp recorded .equ dispmax= ~(1<<1); display the maximum temp recorded ;-------------------------------------- .cseg .org $0000 ;8535 vector table rjmp RESET;reset entry vector reti reti reti reti reti reti;TIMER1A interrupt reti reti rjmp TIMER0 reti reti reti reti reti reti reti ;---------------------------------------------- RESET:;ALWAYS setup a stack pointer ldi temp, LOW(RAMEND);setup stack pointer out SPL, temp ldi temp, HIGH(RAMEND) out SPH, temp ;PortD is all inputs (pushbuttons) clr temp out DDRD, temp ser temp out PortD, temp ser temp out DDRB, temp; make PortB all outputs (LEDs) clr temp out PortB, temp ;setup timer0 as a 1ms delay ldi temp, 255-62 mov reload, temp ldi temp, 3 out TCCR0, temp ldi temp, exp2(TOIE0); timer 0 overflow interrupt enable out TIMSK, temp ;set up analog converter to read channel zero (read temperature as voltage) ldi temp, 0 out ADMUX, temp clr LCDcharpos clr timeout2 ;clear timeout2 to read pushbuttons ldi temp, 'F' sts (scale), temp ;default scale is Fahrenheit ldi temp, 0 sts (state), temp ;zero state (start) ldi temp, LOW(605) sts (mintemp), temp ; initialize MIN alarm temp. to 60.5°F ldi temp, HIGH(605) sts (mintemp+1), temp ldi temp, LOW(802) sts (maxtemp), temp ; initialize MAX alarm temp. to 80.2°F ldi temp, HIGH(802) sts (maxtemp+1), temp clr temp sts (hitemp), temp ; initialize MAX ($0000) sts (hitemp+1), temp ser temp sts (lotemp), temp ; initialize MIN ($FFFF) sts (lotemp+1), temp clr temp sts (flashcnt), temp sei rcall lcdinit rcall lcdclr ;------------------------------------ ; ******** MAIN PROGRAM LOOP ******** MAIN: tst timeout2 brne MAIN ;-------------- rcall ButtonActionListener;call this every 50mS (get button state) ldi timeout2, 50 rcall MeasureTemp; read in the current temp... rcall UpdateExtremes; update the measured HI/LO temps rcall lcdclr ; clear the LCD clr LCDcharpos; rcall UpdateSettings rcall ConvertTemp; convert temp value to appropriate scale... rcall DisplayTemp; display the temp. rcall DisplayScale; display °[F, C, K, or R] rcall CheckAlarm rjmp MAIN ;---------------------------------------------- .cseg MaxalarmMsg: .db "Max Temp:", 0 MinalarmMsg: .db "Min Temp:", 0 HotMsg: .db " HOT!", 0 ColdMsg: .db " COLD", 0 HiMsg: .db "HI: ", 0 LoMsg: .db "LO: ", 0 ;---------------------------------------------- ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ; PROCEDURES: ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ;********************************************************* UpdateExtremes: ; compares current temperature with the stored max and min temperatures ; updates them in SRAM [hitemp or lotemp] (if necessary) ;LOCALS: .def currL = r5 .def currH = r6 .def hiL = r7 .def hiH = r8 .def loL = r9 .def loH = r10 ;------------------------------- .dseg currtemp: .byte 2; the current temperature hitemp: .byte 2; the highest temp recorded by the thermometer lotemp: .byte 2; the lowest temp recorded by the thermometer ;------------------------------- .cseg push currL; save registers push currH push hiL push hiH push loL push loH lds currL, (currtemp); load values into registers lds currH, (currtemp+1) lds hiL, (hitemp) lds hiH, (hitemp+1) lds loL, (lotemp) lds loH, (lotemp+1) cp currL, hiL cpc currH, hiH brsh updatehigh; currtemp > hitemp cp loL, currL cpc loH, currH brsh updatelow; currtemp < lotemp rjmp updateexit updatehigh: sts (hitemp), currL sts (hitemp+1), currH rjmp updateexit updatelow: sts (lotemp), currL sts (lotemp+1), currH updateexit: pop loH; restore registers pop loL pop hiH pop hiL pop currH pop currL ret ; ENDP ;**************** ;********************************************************** CheckAlarm: ; compares current temperature with stored alarm temps (has alarm threshold been reached?) ; if alarm threshold reached...FLASH LEDs!!!! .def maxL = r6; ALARM max temp .def maxH = r7 .def minL = r8; ALARM min temp .def minH = r9 .def currL = r10; current temp .def currH = r11 .def flashcnt = r20 ;------------------------------- .dseg flashcnt: .byte 1; counter for flashing messages ;------------------------------- .cseg push currL; save registers push currH push maxL push maxH push minL push minH push flashcnt lds currL, (currtemp); load values into registers lds currH, (currtemp+1) lds maxL, (maxtemp) lds maxH, (maxtemp+1) lds minL, (mintemp) lds minH, (mintemp+1) lds flashcnt, (flashcnt) ser temp out PortB, temp; turn off LEDs cp currL, maxL cpc currH, maxH brsh toohot ; currtemp > maxtemp cp minL, currL cpc minH, currH brsh toocold ; currtemp < mintemp rjmp alarmexit toohot: ldi temp, (1<<6) out PortB, temp; turn on LED[6] mov temp, flashcnt andi temp, 2; display every 16 * 50 ms = ~1sec breq alarmexit ldi ZL, LOW(HotMsg<<1) ldi ZH, HIGH(HotMsg<<1) rcall lcdputflashstr rjmp alarmexit toocold: ldi temp, (1<<7); turn on LED[7] out PortB, temp mov temp, flashcnt andi temp, 2; display every 16 * 50 ms = ~1sec breq alarmexit ldi ZL, LOW(ColdMsg<<1) ldi ZH, HIGH(ColdMsg<<1) rcall lcdputflashstr alarmexit: inc flashcnt sts (flashcnt), flashcnt pop flashcnt pop minH; restore registers pop minL pop maxH pop maxL pop currH pop currL ret ; ENDP ;**************** ;****************************************************************** UpdateSettings: ; updates stored alarm temperatures if necessary --> (ALARM UPDATE) ; NOTE: if update alarm button is PRESSED: inc/dec by 1 ; if update alarm button is HELD: inc/dec by 5 ; if toggle button was pushed, update that --------> (SCALE UPDATE) ; NOTE: only toggle scale on a button release ;LOCALS: ;r20s don't conflict. first I use state to detect which state, and ;then iff state is in btnreleased, use scale .def state = r28 .def scale = r20 .def button = r21 .def maxL = r24; ALARM max temp .def maxH = r25 .def minL = r26; ALARM min temp .def minH = r27 .def UStemp = r5 ;---------------------- .dseg maxtemp: .byte 2; ALARM HI/LO temps... mintemp: .byte 2 scale: .byte 1;'F', 'C', 'K', 'R' ;---------------------- .cseg push state push button push scale push maxL push maxH push minL push minH lds state, (state) ; get state (0=start, 1=push, 2=stillp, 3=held, 4=rel) lds button, (button) lds maxL, (maxtemp) ; get alarm max value lds maxH, (maxtemp+1) lds minL, (mintemp) ; get alarm min value lds minH, (mintemp+1) cpi state, 2 brlo USjmpexit ;if state == 0 or state == 1, no action brne USbtnheld ;if state != 2... see if it's held/released instead... breq USbtnpush USjmpexit: rjmp USexit ;button pushed (state = 2) USbtnpush: ;** button is PRESSED ** USp0: cpi button, toggle ;NOTE: Don't do anything if holding down toggle. ;update ALARM MIN (if necessary)... USp1: cpi button, decmin brne USp2 sbiw minL, 1 ; decrease ALARM MIN by .1° (every 50 ms if held) rjmp MinPrompt; display min alarm text USp2: cpi button, incmin brne USp3 adiw minL, 1 ; increase ALARM MIN by .1° (every 50 ms if held) rjmp MinPrompt ;update ALARM MAX (if necessary)... USp3: cpi button, decmax brne USp4 sbiw maxL, 1 ; decrease ALARM MAX by .1° (every 50 ms if held) rjmp MaxPrompt; display max alarm text USp4: cpi button, incmax brne USp5 adiw maxL, 1 ; increase ALARM MAX by .1° (every 50 ms if held) rjmp MaxPrompt ;display the MIN recorded temp value (if necessary)... USp5: cpi button, dispmin brne USp6 lds UStemp, (lotemp) sts (disptemp), ustemp lds ustemp, (lotemp+1) sts (disptemp+1), ustemp ldi ZL, LOW(lomsg<<1) ldi ZH, HIGH(lomsg<<1) rcall lcdputflashstr ;display the MAX recorded temp value (if necessary)... USp6: cpi button, dispmax brne USjmpexit lds ustemp, (hitemp) sts (disptemp), ustemp lds ustemp, (hitemp+1) sts (disptemp+1), ustemp ldi ZL, LOW(himsg<<1) ldi ZH, HIGH(himsg<<1) rcall lcdputflashstr rjmp USexit ;button either held or released (state = 3 or 4)... USbtnheld: cpi state, 3 brne USbtnreleased ; button is released... ;** button is HELD down ** USh0: cpi button, toggle ;NOTE: Don't do anything if holding down toggle. ;<<<<<change ALARM values 5 X faster>>>>> ;update ALARM MIN (if necessary)... USh1: cpi button, decmin brne USh2 sbiw minL, 5 ; decrease ALARM MIN by .5° (every 50 ms if held) rjmp MinPrompt; display min alarm text USh2: cpi button, incmin brne USh3 adiw minL, 5 ; increase ALARM MIN by .5° (every 50 ms if held) rjmp MinPrompt ;update ALARM MAX (if necessary)... USh3: cpi button, decmax brne USh4 sbiw maxL, 5 ; decrease ALARM MAX by .5° (every 50 ms if held) rjmp MaxPrompt; display max alarm text USh4: cpi button, incmax brne USh5 adiw maxL, 5 ; increase ALARM MAX by .5° (every 50 ms if held) rjmp MaxPrompt; display max alarm text USh5: cpi button, dispmin brne USh6 lds ustemp, (lotemp) sts (disptemp), ustemp lds ustemp, (lotemp+1) sts (disptemp+1), ustemp ldi ZL, LOW(lomsg<<1) ldi ZH, HIGH(lomsg<<1) rcall lcdputflashstr USh6: cpi button, dispmax brne USexit lds ustemp, (hitemp) sts (disptemp), ustemp lds ustemp, (hitemp+1) sts (disptemp+1), ustemp ldi ZL, LOW(himsg<<1) ldi ZH, HIGH(himsg<<1) rcall lcdputflashstr rjmp USexit ; button released (state = 4) USbtnreleased: cpi button, toggle brne USexit ; EXIT... ;toggle the scale... lds scale, (scale) cpi scale, 'F' brne UStogC ldi scale, 'C' rjmp USstoretog UStogC: cpi scale, 'C' brne UStogR ldi scale, 'R' rjmp USstoretog UStogR: cpi scale, 'R' brne UStogK ldi scale, 'K' rjmp USstoretog UStogK: ldi scale, 'F' USstoretog: sts (scale), scale USexit: sts (maxtemp), maxL ; update ALARM temps in SRAM sts (maxtemp+1), maxH sts (mintemp), minL sts (mintemp+1), minH pop minH pop minL pop maxH pop maxL pop scale pop button pop state ret MinPrompt: ; display min alarm text ;if out of range, put it in range ;if (min<0) min=0 ;if (min>2048) min = 2048 clr UStemp cp minL, UStemp cpc minH, UStemp brge skipzerol clr minL clr minH skipzerol: clr UStemp cp minL, UStemp ldi temp, HIGH(2048) cpc minH, temp brlt skipmaxl clr minL ldi minH, HIGH(2048) skipmaxl: ldi ZL, LOW(MinalarmMsg<<1) ldi ZH, HIGH(MinalarmMsg<<1) rcall lcdputflashstr sts (disptemp), minL sts (disptemp+1), minH rjmp USexit MaxPrompt: ; display max alarm text ;if out of range, put it in range ;if (max<0) max=0 ;if (max>2048) max = 2048 clr UStemp cp maxL, UStemp cpc maxH, UStemp brge skipzeroh clr maxL clr maxH skipzeroh: clr UStemp cp maxL, UStemp ldi temp, HIGH(2048) cpc maxH, temp brlt skipmaxh clr maxL ldi maxH, HIGH(2048) skipmaxh: ldi ZL, LOW(MaxalarmMsg<<1) ldi ZH, HIGH(MaxalarmMsg<<1) rcall lcdputflashstr sts (disptemp), maxL sts (disptemp+1), maxH rjmp USexit ; ENDP ;**************** ;********************************************************* MeasureTemp: ; reads a value from the ADC and converts it to millivolts ; each mV = .1 deg F ; each bit increment of the ADC = 2 mV ;----------------- ;LOCALS: .def analo = r21 .def anahi = r20 ;----------------- push anahi push analo ;start A to D conversion (get a voltage) -- Aref = 2.048 V swait: ldi temp, 0b11100101 ;free run out ADCSR, temp await: in temp, ADCSR ;wait for A to D done andi temp, 0b01000000 ;by checking ADSC bit brne await ;this bit is cleared by the AtoD hardware in AnaLo, ADCL ;read the voltage in AnaHi, ADCH lsl analo ; multiply by 2 to get # of mV rol anahi sts (currtemp), analo; store the current temp value into SRAM sts (currtemp+1), anahi ;by default, display the current temperature sts (disptemp), analo sts (disptemp+1), anahi pop analo pop anahi ret ; ENDP ;**************** ;********************************************************** ConvertTemp: ; converts a temperature in mV (or Fahrenheit) from disptemp ; into a specified temperature scale and puts it back in ; .def scale = r20 .def tempL = r26 .def tempH = r27 .def temp1 = r24 .def temp2 = r25 .def countL = r28; will hold the quotient .def countH = r29 ;----------------------- .dseg disptemp: .byte 2 ;----------------------- .cseg push scale push tempL push tempH push temp1 push temp2 lds scale, (scale) lds tempL, (disptemp) lds tempH, (disptemp+1) clr isneg ;assume positive cpi scale, 'F' breq convertexit ; if Fahrenheit, don't change.... cpi scale, 'C' breq converttoC ; convert to °C cpi scale, 'K' breq converttoC ; if °K, first do the converttoC code... ;converttoR default: ldi temp1, LOW(4600) ldi temp2, HIGH(4600) add tempL, temp1 adc tempH, temp2 ; °R = °F + 460 cpi scale, 'R' breq convertexit ; if Rankine, exit... converttoC: ldi temp1, LOW(320) ldi temp2, HIGH(320) sub tempL, temp1; °F - 32 sbc tempH, temp2 mov temp, tempH andi temp, 0x80 breq Cnotneg ; check to see if °F < 32... ;Kelvin is never negative, but toggle isneg flag anyway ser temp mov isneg, temp cpi scale, 'K' breq Cnotneg ;get two's complement (1's complement + 1) ... restore # to positive com tempL com tempH adiw tempL, 1 Cnotneg: mov temp1, tempL; keep a copy of the temp (temp2:temp1 = °F - 32) mov temp2, tempH clc lsl tempL rol tempH clc lsl tempL rol tempH add tempL, temp1; 5 * (°F -32) adc tempH, temp2 ; ----- divide by 9 ------- (fast enough for our purposes) clr countL clr countH ; Y register div9: sbiw XL, 9 brcs divend adiw YL, 1 ; increment count (quotient) rjmp div9 divend: nop ; tempH:tempL holds 5/9 * (°F - 32) mov tempL, countL mov tempH, countH ; --------------------------- cpi scale, 'K' breq converttoK;convert to °K.... °K = °C + 273 rjmp convertexit;else exit with °C... converttoK: ldi temp1, LOW(2730) ldi temp2, HIGH(2730) add tempL, temp1 adc tempH, temp2;°K = °C + 273 tst isneg breq convertexit ldi temp1, LOW(7280) ;compensate for funny problems ldi temp2, HIGH(7280) ;with negative numbers sub tempL, temp1 sbc tempH, temp2;°K = °C + 273 clr isneg ;clear it so we don't display a - sign convertexit: sts (disptemp), tempL sts (disptemp+1), tempH pop temp2 pop temp1 pop tempH pop tempL pop scale ret ; ENDP ;**************** ;************************************************************ DisplayTemp: ; displays temperature in disptemp as «xxx.x°» (no scale here) .def tempL = r20 .def tempH = r21 lds tempL, (disptemp) lds tempH, (disptemp+1) rcall Bin2ASC ldi YL, LOW(ASCIItemp) ldi YH, HIGH(ASCIItemp) rcall lcdputRAMstr ret ; ENDP ;**************** ;*********************************************** DisplayScale: ; displays temperature scale as F, C, K, T ldi temp, 223;degree symbol, ° mov TXchar, temp rcall TXputC lds TXchar, (scale) rcall TXputC ret ;ENDP ;**************** ;************************************************ ButtonActionListener: ; gets called every 50 mS ; polls the button and does debouncing (internally) ; stores the (internal) state ;LOCALS: .def state = r21 .def count = r22 .def lastbtn = r15 .def currbtn = r20 ;---------------------- .dseg state: .byte 1 button: .byte 1 count: .byte 1 ;---------------------- .cseg push state push count push lastbtn push currbtn lds state, (state) lds lastbtn, (button) lds count, (count) ; Stored Machine States: ; 0--> 000=start ; 1--> 001=push detected ; 2--> 010=same button pressed ; 3--> 011=button is held (if pushed for 2s) ; 4--> 100=button is released (use for toggle button) ; ;switch (state) { ;case START: BALstart: cpi state, 0 brne BALpushed ;*state = START in currbtn, PinD ;in button, PinD cpi currbtn, 0xFF ;if (button==0xFF) break (no press) breq BALexit sts (button), currbtn;else store button ldi state, 0x01 ;state = PUSHED; sts (state), state rjmp BALexit ;break ;case PUSHED: BALpushed: cpi state, 1 brne BALstillpush ; *state = PUSHED in currbtn, PinD ;read the current button press (if any) ;use an AND here in case multiple buttons are/were pressed. and lastbtn, currbtn;check to see if same button is still pushed cp currbtn, lastbtn brne BALnotsame ;if (yes) ldi state, 0x02 ; state = STILLPUSH; sts (state), state clr count ; count = 0; (count how long button pressed) sts (count), count rjmp BALexit ;break ;case STILLPUSH: BALstillpush: cpi state, 2 brne BALheld ; *state = STILLPUSHED in currbtn, PinD and lastbtn, currbtn cp currbtn, lastbtn brne BALreleased ;if (button = same) inc count ; count ++; sts (count), count cpi count, 20 ;NOTE ---> 20 * 50ms = 1 sec brne BALexit ;if (count = 1 sec) ldi state, 0x03 ; state = held; sts (state), state rjmp BALexit ;case HELD: BALheld: cpi state, 3 brne BALnotsame in currbtn, PinD and lastbtn, currbtn cp currbtn, lastbtn brne BALreleased rjmp BALexit ; case RELEASED: BALreleased: ldi state, 4 sts (state), state rjmp BALexit BALnotsame: clr state; *state = START (reset the state) sts (state), state BALexit: pop currbtn pop lastbtn pop count pop state ret ;************************************************************** ; -------- TIMER0 ISR -------- ;************************************************************** ;Timer0 ISR decrements timeout TIMER0: in save, SREG out TCNT0, reload; keeps clock ticking at 1 ms dec timeout ; subtract another ms dec timeout2 out SREG, save reti ;back to background tasks ;************************************************************** ;************************************************************** Bin2ASC: ;NOTE: Compute temperature with base unit of .1 degrees (any scale) ;convert binary temp value to decimal and store in RAM .def temp = r20 .def power10L = r21 .def power10H = r22 .def degsL = r5; temp to display .def degsH = r6 ;----------------------- .dseg ASCIItemp: .byte 6; temp in ASCII (ready for display) ;----------------------- .cseg lds degsL, (disptemp); load the temp to display lds degsH, (disptemp+1) ldi power10L, LOW(1000) ldi power10H, HIGH(1000) clr temp ; temp will count each place value digit thousands: sub degsL, power10L sbc degsH, power10H brcs hundreds inc temp ; * temp actually holds the 100's temp. digit * rjmp thousands hundreds: add degsL, power10L ; restore the number adc degsH, power10H subi temp, -'0' sts (ASCIItemp), temp; put ASCII value of thousands digit into SRAM tst isneg ; see if number is negative..... breq __hundreds ldi temp, '-' sts (ASCIItemp), temp __hundreds: ldi power10L, LOW(100) ldi power10H, HIGH(100) clr temp _hundreds: sub degsL, power10L sbc degsH, power10H; subtract 100 from the temp value brcs tens ; dvdh:dvdL is less than 100, so goto tens.... inc temp ; * temp holds the 10's digit * rjmp _hundreds tens: add degsL, power10L adc degsH, power10H; restore the number (make it positive again) subi temp, -'0'; convert 10's digit to ASCII sts (ASCIItemp+1), temp; store 10's digit into RAM ldi power10L, 10 clr temp ; reset digit counter _tens: sub degsL, power10L brcs ones inc temp ; * temp holds the 1's digit * rjmp _tens ones: add degsL, power10L; restore the number back to positive (now holds tenths digit) subi temp, -'0'; convert ones digit to ASCII sts (ASCIItemp+2), temp; store 1's digit into RAM ldi temp, '.' sts (ASCIItemp+3), temp; store decimal point into RAM mov temp, degsL subi temp, -'0' ; convert tenth's digit to ASCII sts (ASCIItemp+4), temp; store tenths digit into RAM clr temp sts (ASCIItemp+5), temp; zero terminate ret ;============================================== ; Extra LCD functions ;============================================== ; NOTE: Expects register Y to hold the address in SRAM ; Y = char* lcdputRAMstr: nextc: ld TXchar, Y+ ; get next character from flash tst TXchar ; ...reach terminator byte yet? breq end1 ; ...if so, then get 4 digits rcall TXputC rjmp nextc ; get more characters... end1: ret ;============================================== ; NOTE: Expects register Z to hold the address in flash ; Z = char* lcdputflashstr: nextfc: lpm adiw ZL, 1 mov TXchar, r0 tst TXchar ; ...reach terminator byte yet? breq endf1 ; ...if so, then get 4 digits rcall TXputC rjmp nextfc ; get more characters... endf1: ret ;============================================== ;Character transfer routines TXputC: push temp cpi LCDcharpos, 8 ; addressing changes at char #8! brne writechar ; at char 8, fix the addressing ldi temp, 0xC0 ; set address to last 8 chars (start at $40) rcall lcdcmd writechar: mov temp, TXchar rcall lcdput ; displays character to LCD inc LCDcharpos pop temp ret ;************************************************** .include "lcdc.asm" |
| CODE |
;**** MODIFIED LCD CODE --- uses Port C for LCD display **** ;=================================== ; *** LCD support routines *** ;=================================== ;Clear entire LCD and delay for a bit .equ LCDPort = PortC .equ DataDir = DDRC .equ InputPin = PINC lcdclr: ldi temp,1 ;Clear LCD command rcall lcdcmd ldi timeout,3 ;Delay 3 mS for clear command rcall delay ret ;================================================ ;Initialize LCD module lcdinit: ; cbi PORTB,0 ;Turn on LED 0 ldi temp,0 ;Setup port pins out LCDPort,temp ;Pull all pins low ldi temp,0xff ;All pins are outputs out DataDir,temp ldi timeout,15 ;Wait at least 15 mS at power up rcall delay ; LCD specs call for 3 repetitions as follows: ;first rep ldi temp,3 ;Function set out LCDPort,temp ;to 8-bit mode nop ;nop is data setup time sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde ldi timeout,15 ;Wait at least 15 mS rcall delay ;second rep ldi temp,3 ;Function set out LCDPort,temp nop sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde ldi timeout,15 ;Wait at least 15 ms rcall delay ;third rep ldi temp,3 ;Function set out LCDPort,temp nop sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde ldi timeout,15 ;Wait at least 15 ms rcall delay ;Now change to 4-wire interface mode ldi temp,2 ;Function set, 4 wire databus out LCDPort,temp nop sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde ; Finally, at this point, ; the normal 4 wire command routine (lcdcmd) can be used ldi temp,0b00100000;Function set, 4 wire, 1 line, 5x7 font rcall lcdcmd ldi temp,0b00001100;Display on, no cursor, no blink rcall lcdcmd ldi temp,0b00000110;Address increment, no scrolling rcall lcdcmd ; sbi PORTB,0 ;Turn off LED 0 ret ;============================================ ;Wait for LCD to go unbusy lcdwait: ;cbi PORTB,1 ;Turn on LED 1 ldi temp,0xF0 ;Make 4 data lines inputs out DataDir,temp sbi LCDPort,lcdrw ;Set r/w pin to read cbi LCDPort,lcdrs ;Set register select to command waitloop: sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde in lcdstat,InputPin ;Read busy flag ;Read, and ignore lower nibble sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde sbrc lcdstat,3 ;Loop until done rjmp waitloop ; sbi PORTB,1 ;Turn off LED 1 ret ;============================================= ;Send command in temp to LCD lcdcmd: push temp ;Save character rcall lcdwait ldi temp,0xFF ;Make all port D pins outputs out DataDir,temp pop temp ;Get character back push temp ;Save another copy swap temp ;Get upper nibble andi temp,0x0F ;Strip off upper bits out LCDPort,temp ;Put on port nop ;wait for data setup time sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde pop temp ;Recall character andi temp,0x0F ;Strip off upper bits out LCDPort,temp ;Put on port nop sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde ret ;============================================= ;Send character data in temp to LCD lcdput: push temp ;Save character rcall lcdwait ldi temp,0xFF ;Make all port D pins outputs out DataDir,temp pop temp ;Get character back push temp ;Save another copy swap temp ;Get upper nibble andi temp,0x0F ;Strip off upper bits out LCDPort,temp ;Put on port sbi LCDPort,lcdrs ;Register select set for data nop sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde pop temp ;Recall character andi temp,0x0F ;Strip off upper bits out LCDPort,temp ;Put on port sbi LCDPort,lcdrs ;Register select set for data nop sbi LCDPort,lcde ;Toggle enable line nop cbi LCDPort,lcde ret ;============================================= ;Subroutine waits for time equal to value in register timeout ;the register 'timeout' should be loaded before the call delay: tst timeout brne delay ret ; ---------------- (end of program) ------------------ |