| CODE |
.include "c:\users\jason\8535def.inc" .device AT90S8535 ;========================================================== ; Simple Thermostat ;Takes readings at 1 second intervals using the ADC ;Displays temperature, target, and time of day on ;LCD, uses keypad for the following functions: ; Set Time ; Set Target ; Reset Min/Max Temperatures ; Display Min/Max Temperatures ; ;Port A - ADC - A0 Voltage Signal ;Port B - Power - B7 LCD V+, B0 Heater On ;Port C - KeyPd - As Per Lab 5 ;Port D - LCD - As Per Lab 5 ; ;Intended for AT90S8535 ;5/3/99, EE476, Jason Pettiss Steve Shannon ;========================================================== ;========================================================= ; Register definitions ;Low .def CharCnt = r15 ;lcd .def LCDStat = r14 ;lcd .def LastSec = r13 .def Current = r12 ;therm (ADCDone) .def Target = r11 ;therm (Main) .def TimeH = r10 ;therm (t1cmpA) .def TimeM = r9 ;therm (t1cmpA) .def TimeS = r8 ;therm (t1cmpA) .def AH = r7 ;mul,add .def AL = r6 ;mul,add .def Negative = r5 ;mul .def CX = r4 ;mul .def CH = r3 ;mul .def CL = r2 ;mul ;High ;LCD .def DelayL = r18 ;lcd .def DelayH = r19 ;lcd ;KeyBd .def Press = r20 ;keypd .def InitPrs = r21 ;keypd .def Sure = r22 ;keypd .def KeyPd = r23 ;keypd .def KeyState = r24 ;keypd .def Mode = r25 ;therm (Main) .def Char = r16 ;Always Save/Restore If Function Uses .def Temp = r16 ;Unless are arguments or returns from function .def TempL = r16 ;(In which case higher level saves/restores) .def TL = r16 .def Temp2 = r17 .def TempH = r17 .def TH = r17 .def BL = r26 .def HiTemp = r26;Main .def BH = r27 .def LoTemp = r27;Main .def Round = r28 .def Num = r29 .def Temp3 = r29 ;========================================================== ; Constants ;Device .equ lcdrs = PD6 .equ lcdrw = PD5 .equ lcde = PD4 .equ TSTOP = 0 .equ TCK1 = 1 .equ TCK8 = 2 .equ TCK64 = 3 .equ TCK256 = 4 .equ TCK1024 = 5 .equ TEXF = 6 .equ TEXR = 7 ;Delay .equ D1500H = 22 .equ D1500L = 210 .equ D500H = 7 .equ D500L = 140;? .equ D15H = 0 .equ D15L = 59 ;KeyState .equ KS_IDLE = 0 .equ KS_PRESS = 1 .equ KS_DEPRESS = 2 .equ KS_KEYREADY = 3 ;Key press tolerance .equ KEYTIME = 100 .equ KEYTOL = 30 ;Button Equivalents .equ B_EXIT = 1 .equ B_HILO = 2 .equ B_HILORST = 3 .equ B_SETTIME = 5 .equ B_SETTGT = 6 .equ B_LAST = 16 .equ B_0 = 8 .equ B_1 = 1 .equ B_2 = 5 .equ B_3 = 9 .equ B_4 = 2 .equ B_5 = 6 .equ B_6 = 10 .equ B_7 = 3 .equ B_8 = 7 .equ B_9 = 11 ;Mode .equ S_NORM = 0 .equ S_HILO = 1 .equ S_SETTIME = 2 .equ S_SETTGT = 3 ;ADC .equ ADC_ENABLE = 0b10001000 .equ ADC_START = 0b11001000 ;Power .equ PB_HOFF = 0b11000000 .equ PB_HON = 0b11000001 ;Delay 15 ms .macro xDelay15 ldi DelayH,D15H ldi DelayL,D15L rcall Delay .endmacro ;========================================================== ; Interrupt Vector .cseg .org $0000 rjmp reset reti;External Int0 reti;External Int1 reti;Timer2 Compare reti;Timer2 Overflow reti;Timer1 Capture rjmp t1cmpA;Timer1 CompA reti;Timer1 CompB reti;Timer1 Overflow rjmp t0int;Timer0 Overflow reti;Serial Txfer Complete reti;UART Rx reti;UART UDRE reti;UART Tx rjmp ADCdone;ADC Conversion Complete reti;EEPROM Rdy reti;Analog Comparator ;========================================================== ; Reset reset: cli ; Disable Interrupts ldi Mode,S_NORM ldi Temp, LOW(RAMEND) ; initialize stack out SPL, Temp ldi Temp, HIGH(RAMEND) out SPH, Temp ldi Temp, TSTOP ; Timer 0 off out TCCR0, Temp ; Stop Timer ldi Temp, 0b00000001 ; Enable Timer0 overflow & Timer1 A Match out TIMSK, Temp ldi Temp, TCK1024;CK/1024 out TCCR0, Temp ldi Temp, 255 out TCNT0, Temp ldi Temp,0b1000000 ;Bit 0 is analog signal out ADMUX,Temp ldi Temp,ADC_ENABLE;Enable ADC out ADCSR,Temp sei ; Enable Interrupts ldi KeyState,KS_IDLE ; Delay 1.5 seconds ldi DelayH, D1500H ldi DelayL, D1500L rcall Delay ; Power up LCD ldi Temp, 0xFF out PORTB, Temp ; Delay 1.5 seconds ldi DelayH, D1500H ldi DelayL, D1500L rcall Delay rcall LCDInit rcall LCDClr rcall LCDReady ldi DelayH, D1500H ldi DelayL, D1500L rcall Delay ;For 1 sec Timer1: ;TCCR1B = 0b00001011 (CK/64) ;OCR1AH = 244 ;OCR1AL = 36 (62500) ldi Temp,244 out OCR1AH,Temp ldi Temp,36 out OCR1AL,Temp ldi Temp, 0b00001011 ; CTC1 & CK/64 out TCCR1B, Temp ldi Temp, 0b00010001 ; Enable Timer0 overflow & Timer1 A Match out TIMSK, Temp ;========================================================== ; Main ;A main: clr Press clr KeyPd clr Current clr Target ;Init time to 1:25:40pm ldi Temp,13 mov TimeH,Temp ldi Temp,25 mov TimeM,Temp ldi Temp,40 mov TimeS,Temp clr LastSec ;Reset Min/Max temps ldi HiTemp,0 ldi LoTemp,99 ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;========================================================== ;MainMenu (S_NORM) MainMenu: ldi Mode,S_NORM rcall GetKey cpi KeyPd,B_HILO breq disphilo cpi KeyPd,B_HILORST breq hilorst cpi KeyPd,B_SETTIME breq settime cpi KeyPd,B_SETTGT breq settgt rjmp MainMenu hilorst: ldi HiTemp,0 ldi LoTemp,99 rjmp MainMenu ;========================================================== ;Display Hi/Lo Temperatures (S_HILO) disphilo: ldi Mode,S_HILO rcall GetKey cpi KeyPd,B_EXIT breq MainMenu rjmp disphilo ;========================================================== ;Set Time (S_SETTIME) settime: ldi Mode,S_SETTIME rcall LCDClr rcall GetTwoDigits mov TimeH,Temp ldi Temp,':' rcall LCDPut rcall GetTwoDigits mov TimeM,Temp clr TimeS rjmp MainMenu ;========================================================== ;Set Target (S_SETTGT) settgt: ldi Mode,S_SETTGT rcall LCDClr rcall GetTwoDigits mov Target,Temp rjmp MainMenu ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;========================================================== ;Update display if 1 second has elapsed ;========================================================== DispTest: cp LastSec,TimeS breq dsptstout rcall StdDisplay mov LastSec,TimeS dsptstout: ret ;================================================= ;StdDisplay Routine ;cc->ttC Pp hh:mm S_NORM ;1234567812345678 ;hhH llL hh:mm S_HILO ;1234567812345678 ;================================================= StdDisplay: push Temp push Temp2 rcall LCDClr cpi Mode,S_HILO breq stddisphilo stddispnorm: mov Temp,Current rcall TwoDigit ldi Temp,'-' rcall LCDPut ldi Temp,'>' rcall LCDPut mov Temp,Target rcall TwoDigit ldi Temp,'C' rcall LCDPut ldi Temp,' ' rcall LCDPut ldi Temp,'P' rcall LCDPut ldi Temp,0;Program ldi Temp2,'0' add Temp,Temp2 rcall LCDPut ldi Temp,' ' rcall LCDPut rjmp stddisptime stddisphilo: mov Temp,HiTemp rcall TwoDigit ldi Temp,'H' rcall LCDPut ldi Temp,' ' rcall LCDPut mov Temp,LoTemp rcall TwoDigit ldi Temp,'L' rcall LCDPut ldi Temp,' ' rcall LCDPut ldi Temp,' ' rcall LCDPut ldi Temp,' ' rcall LCDPut ldi Temp,' ' rcall LCDPut stddisptime: mov Temp,TimeH rcall TwoDigit sbrc TimeS,0 rjmp stdoddsec ldi Temp,':' rjmp stdsec stdoddsec: ldi Temp,' ' stdsec: rcall LCDPut mov Temp,TimeM rcall TwoDigit pop Temp2 pop Temp ret ;================================================= ;Wait for a key press (main 'message loop'). ;While waiting, update display once a second. ;================================================= GetKey: cpi Mode,S_NORM breq dstst cpi Mode,S_HILO breq dstst rjmp dsnotst dstst: rcall DispTest dsnotst: cpi KeyState,KS_KEYREADY brne GetKey ldi KeyState,KS_IDLE ret ;================================================= ; Temp -> xx decimal on lcd ;================================================= TwoDigit: push Temp2 push Temp3 mov Temp2,Temp clr Temp tens: cpi Temp2,10 brlo tensout inc Temp subi Temp2,10 rjmp tens tensout: ldi Temp3,'0' add Temp,Temp3 rcall LCDPut ones: mov Temp,Temp2 add Temp,Temp3 rcall LCDPut pop Temp3 pop Temp2 ret ;================================================= ; xx decimal from keypad -> Temp (& Echo) ;================================================= GetTwoDigits: push Temp2 push Temp3 clr Temp3 getnext: rcall GetKey cpi KeyPd,B_0 breq got0 cpi KeyPd,B_1 breq got1 cpi KeyPd,B_2 breq got2 cpi KeyPd,B_3 breq got3 cpi KeyPd,B_4 breq got4 cpi KeyPd,B_5 breq got5 cpi KeyPd,B_6 breq got6 cpi KeyPd,B_7 breq got7 cpi KeyPd,B_8 breq got8 cpi KeyPd,B_9 breq got9 rjmp getnext got0: ldi Temp2,0 rjmp gotnumber got1: ldi Temp2,1 rjmp gotnumber got2: ldi Temp2,2 rjmp gotnumber got3: ldi Temp2,3 rjmp gotnumber got4: ldi Temp2,4 rjmp gotnumber got5: ldi Temp2,5 rjmp gotnumber got6: ldi Temp2,6 rjmp gotnumber got7: ldi Temp2,7 rjmp gotnumber got8: ldi Temp2,8 rjmp gotnumber got9: ldi Temp2,9 rjmp gotnumber gotnumber: push Temp2 ldi Temp,'0' add Temp2,Temp push Temp push Temp2 push Temp3 mov Temp,Temp2 rcall LCDPut pop Temp3 pop Temp2 pop Temp inc Temp3 cpi Temp3,2 breq gottwodigits rjmp getnext gottwodigits: ldi Temp3,10 pop Temp pop Temp2 doingtens: cpi Temp2,0 breq donetens dec Temp2 add Temp,Temp3 rjmp doingtens donetens: pop Temp3 pop Temp2 ret ;================================================= ; t1cmpA fires at 1 second intervals ;================================================= t1cmpA: push Temp in Temp,SREG push Temp push Temp2 push Temp3 ;Start an ADC conversion ldi Temp,ADC_START out ADCSR,Temp ;Time routine: if seconds gets to 60, set it to ;zero and increment minutes, and so on. mov Temp,TimeS inc Temp cpi Temp,60 brne t1cmpAout clr Temp mov Temp2,TimeM inc Temp2 cpi Temp2,60 brne t1cmpAminout clr Temp2 mov Temp3,TimeH inc Temp3 cpi Temp3,24 brne t1cmpAhrout clr Temp3 t1cmpAhrout: mov TimeH,Temp3 t1cmpAminout: mov TimeM,Temp2 t1cmpAout: mov TimeS,Temp pop Temp3 pop Temp2 pop Temp out SREG,Temp pop Temp reti ;================================================= ;ADC Conversion: ;================================================= ADCdone: push Temp push TempH in Temp,SREG push Temp push AL push AH push CL push CH push CX push Negative push BL push BH ;RawValue is 10 bit voltage in ;range ~200 to 1024. This is mapped ;to a range of 12 to 99 degrees celsius ;with a linear function. in TempL,ADCL in TempH,ADCH;Read mov AL,TempL mov AH,TempH;A = Raw Value ldi BL,0xBE ldi BH,0xE2;B = -29.26 rcall mul ldi BL,0 ldi BH,130;B = 129 rcall add ;Temp = RawValue * -29.26 + 129 ;Temp = Raw * -0.1143 + 129 mov TempL,AL mov TempH,AH ;Round 0.5+ to 1 and 0.49- to 0 cpi TempL,0x80 brlo skiproundup roundup: inc TempH skiproundup: mov Current,TempH pop BH pop BL ;If our measured temperature is ;bigger than max, or less than min, ;change max or min reading. cp Current,HiTemp brlo adcnomax mov HiTemp,Current adcnomax: cp Current,LoTemp brge adcnomin mov LoTemp,Current adcnomin: ;If we're less than our target, heater ;should be on. Else turn it off. cp Current,Target brge noheater ldi Temp,PB_HON rjmp heaterset noheater: ldi Temp,PB_HOFF heaterset: out PORTB,Temp pop Negative pop CX pop CH pop CL pop AH pop AL pop Temp out SREG,Temp pop TempH pop Temp reti ;Following code reused from Labs 2-6. ;========================================================== ;Prototypes ;Return = Function (Arguments);DestroysReg;Subcalls ;Delay(DelayH, DelayL);; ;t0int ();KeyPd,Press,Sure,InitPrs,KeyState;ReadKeyPd ;Saves/Restores: Temp,Temp2,ZL,ZH,r0,SREG ;LCDClr ();CharCnt,Temp;lcdcmd ;LCDInit ();Temp,Temp2 ;lcdcmd ;LCDWait ();Temp,lcdstat ;LCDPut (Temp, &CharCnt);;lcdwait,lcdcmd ;LCDMsgPut (ZL, ZH);ZL,ZH,r0;lcdput ;LCDCmd ();Temp;lcdwait ;LCDReady ();Temp,CharCnt,ZL,ZH;lcdmsgput ;Press = ReadKeyPd ();Temp,Temp2,ZL,ZH,r0 ;A = add ( A+B );; ;A = sub ( A-B );; ;A = mul ( A*B );C,CX,Negative; ;========================================================== ;Delay ; Delay: cpi DelayH,0 brne Delay cpi DelayL,0 brne Delay ret ;========================================================== ; Timer 0 Overflow ISR ; t0int: push Temp push Temp2 in Temp, SREG push Temp push ZL push ZH push r0 ;Keyboard Routine ;To get key, wait for KeyState==KS_KEYREADY ;Key is KeyPd ;Set KeyState <- KS_IDLE when done cpi KeyState,KS_IDLE breq t0idle cpi KeyState,KS_PRESS breq t0press cpi KeyState,KS_DEPRESS breq t0depress rjmp t0keyout t0idle: rcall ReadKeyPd cpi Press,0 breq t0keyout mov KeyPd,Press ldi InitPrs,KEYTIME clr Sure ldi KeyState,KS_PRESS rjmp t0keyout t0press: rcall ReadKeyPd cp Press,KeyPd brne t0cont inc Sure t0cont: dec InitPrs brne t0keyout ;done! cpi Sure,KEYTOL brge t0pressed ;was not a valid press clr KeyPd clr Press ldi KeyState,KS_IDLE rjmp t0keyout t0pressed: ;valid press, wait for depress ldi KeyState,KS_DEPRESS ldi InitPrs,KEYTIME rjmp t0keyout t0depress: rcall ReadKeyPd cpi Press,0 brne t0keyout dec InitPrs brne t0keyout ldi KeyState,KS_KEYREADY t0keyout: ;Delay Routine cpi DelayH,0 brne t0hinonzero cpi DelayL,0 breq t0allzero rjmp t0out t0hinonzero: cpi DelayL,0 brne t0out dec DelayH rjmp t0out t0out: dec DelayL t0allzero: ldi Temp, 255 out TCNT0, Temp pop r0 pop ZH pop ZL pop Temp out SREG, Temp pop Temp2 pop Temp reti ;========================================================== ;LCDClr LCDClr: clr CharCnt ldi Temp,1 rcall LCDCmd xDelay15 ret ;========================================================== ; Initialize LCD module ;lcd0 LCDInit: ldi Temp,0 ;Setup port pins out PORTD,Temp ;Pull all pins low ldi Temp,0xFF ;All pins are outputs out DDRD,Temp ; LCD specs call for 3 repetitions as follows ldi Temp2,0 lcd0loop: inc Temp2 xDelay15 ldi Temp,3 ;Function set out PORTD,Temp ;to 8-bit mode ; xDelay15;or nop nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde cpi Temp2,3 brne lcd0loop ldi Temp,2 ;Function set, 4 line interface out PORTD,Temp ; xDelay15;or nop nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde ldi Temp,0b11110000;Make 4 data lines inputs out DDRD,Temp ; Finally, ; At this point, the normal 4 wire command routine 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 ret ;========================================================== ; Wait for LCD to go unbusy ;lcd1 LCDWait: ldi Temp,0xF0 ;Make 4 data lines inputs out DDRD,Temp sbi PORTD,lcdrw;Set r/w pin to read cbi PORTD,lcdrs;Set register select to command lcd1loop: sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde in lcdstat,PIND ;Read busy flag ;Read, and ignore lower nibble sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde sbrc lcdstat,3 ;Loop until done rjmp lcd1loop ret ;========================================================== ; Send character data in wreg to LCD ;lcd2 LCDPut: push Temp ;Save character rcall LCDWait ;Wait for LCD to be ready ldi Temp,0xFF ;Make all port D pins outputs out DDRD,Temp pop Temp ;Get character back push Temp ;Save another copy swap Temp ;Get upper nibble andi Temp,0x0F ;Strip off upper bits out PORTD,Temp ;Put on port sbi PORTD,lcdrs ;Register select set for data nop nop ; nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde pop Temp ;Recall character andi Temp,0x0F ;Strip off upper bits out PORTD,Temp ;Put on port sbi PORTD,lcdrs ;Register select set for data nop nop ; nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde ;cbi PORTD,lcdrs ;-- ldi Temp,0xF0 ;Make 4 data lines inputs out DDRD,Temp inc CharCnt mov Temp,CharCnt cpi Temp,8 brne lcd2out ldi Temp,0xC0 ;Set address to last 8 chars rcall LCDCmd lcd2out: ret ;========================================================== ; Write Message From Memory To LCD ;lcd3 LCDMsgPut: lcd3nextchar: lpm tst R0 breq lcd3out mov Char,R0 rcall LCDPut adiw ZL,1 rjmp lcd3nextchar lcd3out: ret ;========================================================== ; Send command in wreg to LCD LCDCmd: push Temp ;Save character rcall LCDWait ;Wait for LCD to be ready ldi Temp,0xFF ;Make all port D pins outputs out DDRD,Temp pop Temp ;Get character back push Temp ;Save another copy swap Temp ;Get upper nibble andi Temp,0x0F ;Strip off upper bits out PORTD,Temp ;Put on port nop ;wait for data setup time sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde pop Temp ;Recall character andi Temp,0x0F ;Strip off upper bits out PORTD,Temp ;Put on port nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde ldi Temp,0xF0 ;Make 4 data lines inputs out DDRD,Temp ret ;========================================================== ; LCD Ready LCDReady: clr CharCnt ldi Char, 0x20 ldi ZH, high(msgReady*2) ldi ZL, low(msgReady*2) rcall LCDMsgPut ret ;=============================================; text data msgReady: .db " --- Ready --- ", 0x00 msgTime: .db "Set Time: ",0x00 msgTgt: .db "Target: ",0x00 ;========================================================== ;The following table is used to convert raw button-press high/low ;values to a sequence number. Invalid codes caused by multiple ;button presses are ignored. keytbl: .db 0b11101110, 0b11101101, 0b11101011, 0b11100111 .db 0b11011110, 0b11011101, 0b11011011, 0b11010111 .db 0b10111110, 0b10111101, 0b10111011, 0b10110111 .db 0b01111110, 0b01111101, 0b01111011, 0b01110111 ;========================================================== ; Keypad Checking Routine ReadKeyPd: ldi Temp,0x0F ;set lower four lines to output out DDRC, Temp ldi Temp, 0xF0 ;and turn on the pullups on the inputs out PORTC, Temp nop;Need some time for the pullups to nop;charge the port pins nop nop in Temp, PINC ;read the high nibble mov Temp2, Temp ;and store it (with zeros in the low nibble) ldi Temp, 0xF0 ;set upper four lines to outputs out DDRC, Temp ldi Temp, 0x0F ;and turn on pullups on the inputs out PORTC, Temp nop;As before wait for the pin to charge nop nop nop in Temp, PINC ;read the low nibble or Temp2, Temp ;combine to make key code ;At the point the raw key code should have exactly one zero each in ;the lower and upper nibbles. Any other number of zeros indicates ;either no-button pressed or multiple-button pressed. ;Now search the table for a match to the raw key code ;and exit with a button number ldi ZL, low(keytbl*2) ;table pointer in FLASH ldi ZH, high(keytbl*2) ;so convert from word to byte addr ldi Press, 0 tbllp: lpm ;get the table entry cp Temp2, r0 ;match? breq foundit inc Press ;if not, have we exhaused the cpi Press, 0x10 ;table breq illegal adiw ZL, 1 ;if not, get the next table entry rjmp tbllp foundit: inc Press rjmp keyout illegal: clr Press keyout: ret ;====================================================== ; Math Routines ;====================================================== ;====================================================== ; 8.8 Fixed-Point Signed Add ;Arguments: A,B ;Returns: A add: clc add AL,BL adc AH,BH ret ;====================================================== ; 8.8 Fixed-Point Signed Subtract ;Arguments: A,B ;Returns: A sub: clc sub AL,BL sbc AH,BH ret ;====================================================== ; 8.8 Fixed-Point Signed Multiply ;Arguments: A,B ;Destroys: C,CX,Negative ;Returns: A mul: ;Save Registers push Round push Temp push Temp2 push BL push BH clr Temp2 clr Negative clr Round clr CX clr CH ldi Temp,1 mov CL,Temp ;Ensure Positive arguments multstb: cpi BH,0 brge multsta ;b is negative, make positive com Negative com BH com BL clc add BL,CL adc BH,CH multsta: cp AH,Temp2 brge multstout ;a is negative, make positive com Negative com AH com AL clc add AL,CL adc AH,CH;+0 unless carry multstout: clr CL mulnextround: ;Test B(0) sbrs BL,0 rjmp mullowbitoff mullowbiton: ;On: CH = CH + A clc add CH,AL adc CX,AH mullowbitoff: ;Shift B Right clc asr BH ror BL ;Shift C Right clc asr CX ror CH ror CL ;Loop 16 Times inc Round cpi Round,16 brne mulnextround mov AH,CH mov AL,CL ;Imbue correct signage cp Negative,Temp2 breq mulout com AH com AL clr CH ldi Temp,1 mov CL,Temp clc add AL,CL adc AH,CH mulout: ;Restore Registers pop BH pop BL pop Temp2 pop Temp pop Round ret |