Full Version : Cornell 2000 Digital Thermometer (AVR ASM)
avr >>HOME & TIME & TEMPERATURE PROJECTS >>Cornell 2000 Digital Thermometer (AVR ASM)


Admin3- 04-18-2006
Project description:

Our final project involved the design of a fully functional, multi-purpose digital thermometer. This was our attempt at producing a portable device that could be widely used for a variety of different purposes. For example, think of the many situations where the precise measurement of temperature is of high importance. Temperature control and monitoring is important in homes for the comfort of its occupants… it is important for gardeners who want to carefully monitor the atmospheric conditions within a greenhouse… it is important in ensuring the correct operation of various electronic devices where many components may have a sensitive dependence on temperature.

LM34

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) ------------------




Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.