;********************************* ;Copyright(C)2002,2003, 2004 by Richard Cappels projects@cappels.org ;http://www.projects.cappels.org ; USES 32 bit binary to packed BCD routine from Atmel applination note. ;Uses AT90S2313 or similar operating at 10 MHz. ;Input, D5, is connected to prescaler. See schematic on website. ;********************************* ;ffmtr040916A current version .include "2313def.inc" .equ clock = 10000000 ;clock frequency .equ baudrate = 9600 ;choose a baudrate .equ baudconstant = (clock/(16*baudrate))-1 .def delay0 = r2 ;delay0..delay2 are delay counters. .def delay1 = r3 .def delay2 = r4 .def delayreload0 = r6 ;delayreload0..delayreload2 are reload values for delay counters. .def delayreload1 = r7 .def delayreload2 = r8 .def iterations = r9 ;Number of 1 second iterations. .def temp = r16 ;General purpose scratch register. .def byte0 = r18 ;Byte0..Byte4 for binary to bcd conversion. .def byte1 = r19 .def byte2 = r20 .def byte3 = r21 .def byte4 = r22 .def flagreg = r23 ;Definition of flagregister bits ;0 lsb of mode select ;1 middle bit of mode select ;2 msb of mode select ;3 ;4 set to indicate that counting is to stop. ;5 set to indicate counting mode (increment ZH,ZL in interrupt) ;6 set to indicate no more leading zeros ;7 set if 32 bit overflow occured. ;PortD assignments ;0 RXD weak pullup (required by circuit). ;1 TXD input ;2 not used weak pullup ;3 prescaler input gate High to disable, high Z to enable. Toggle to increment. ;4 prescaler reset output high to reset ;5 prescaler out Input ;6 Gate counter (aux) Output ;7 not available on AT90S2313 .equ DDRDdata = 0b01011010 .equ PORTDdata = 0b11001001 .org $00 rjmp reset ;Power-on and reset vector .org $05 rjmp T1OFLW ;16 Bit timer overflow .org $07 rjmp UARTRCV ;UART receive interrupt reset: ldi temp,low(ramend) out spl,temp ;set spl. ldi temp,baudconstant out ubrr,temp ;Load baud rate. ldi temp,$00 out DDRB,temp ;Set up PORTB. ldi temp,$FF out PORTB,temp ldi temp,DDRDdata ;Set up PORTD. out ddrd,temp ldi temp,PORTDdata ;Weak pullup on UART receive pin. out PORTD,temp sbi ucr,txen ;Enable UART transmitter. sbi ucr,rxen ;Enable UART receiver. sbi ucr,rxcie ;Enable UART receive interrupts. ldi temp,0b10000110 ;Enable input to 16 bit counter (Timer/Counter 1). . out tccr1b,temp rcall Delay1Second ;Wait 2 seconds for display to come up. rcall Delay1Second rcall crlf ;Start new line rcall typemessage ;Send ID message for two seconds before starting. rcall Delay1Second ;Display for 2 seconds before starting measurements. ldi flagreg,1 ;Set measurement mode to 1 second frequency measurement. wdr ;Reset the watchdog timer -make sure its not about to trip. ldi temp,0b00001111 ;Enable the watchdog out wdtcr,temp sei ;Enable interrupts rjmp Main ;Start main execution loop. TypeSeconds: ;Type symbol 's' to UART. push temp ldi temp,'s' rcall EmitUart pop temp ret Delay: ;Maximum delay is 50529045 cycles (5.05 seconds at 10 MHz). mov delay2,delayreload2 ;Minimum delays occurs when constants are 1,1,1. moretime3: ;Maximum delay occurs when constants are 0,0,0. mov delay1,delayreload1 moretime2: mov delay0,delayreload0 moretime1: dec delay0 brne moretime1 dec delay1 brne moretime2 dec delay2 brne moretime3 ret Delay1Second: ;Delay 1 second. ;With the values 50,0,255; 235,21,1 this routine delays 10003509 machine cycles. ;This is a little long, but it is the best timing for the prototype with its ;particular crystal. When using a precision 10 MHz oscillator, these delay ;numbers can be set to get closer 10,000,000 machine cycles. ldi temp,50 ;Call Delay routine with one set of constants. mov delayreload0,temp ldi temp,0 mov delayreload1,temp ldi temp,255 mov delayreload2,temp rcall Delay ldi temp,235 ;Call Delay routine with another set of constants. mov delayreload0,temp ldi temp,21 mov delayreload1,temp ldi temp,1 mov delayreload2,temp rcall Delay wdr ;Reset the watchdog timer. ret measurefreq: ;Measure frequency for 1, 10, and 100 seconds. ;100 milliseconds has its own routine. ldi temp,$00 ;Clear 16 bit counter. out tcnt1h,temp out tcnt1l,temp clr ZL ;Clear ZL which is the overflow counter. sbi PORTD,4 ;Pulse prescaler reset cbi PORTD,4 ldi temp,0b10000000 ;Enable overflow interrupts from 16 bit timer. out TIMSK,temp cbi DDRD,3 ;Make gate pin high impedance. cbi PORTD,3 ;Gate signal into prescaler - turn off pullup. AnotherSecond: rcall Delay1Second ;Delay 1 second. dec iterations ;Measure more than 1 second if necessary/ brne AnotherSecond sbi DDRD,3 ;Make gate signal low Z. sbi PORTD,3 ;Disable signal into prescaler. clr temp out TIMSK,temp ;Disable overflow interrupts. ;Note: using tccr1b to disable counter causes 1+. ret Meas1Sec: ;Label measurement as 1 second timebase. ldi temp,'1' rcall EmitUart rcall TypeSeconds rcall crlf ldi temp,1 ;Take 1 second measurement. mov iterations,temp rcall measurefreq rcall displaycounter rjmp main Meas10Sec: ;Label measurement as 10 second timebase. ldi temp,'1' rcall EmitUart ldi temp,'0' rcall EmitUart rcall TypeSeconds rcall crlf ldi temp,10 ;Take 10 second measurement. mov iterations,temp rcall measurefreq rcall displaycounter rjmp main Meas100Sec: ;Label measurement as 100 second timebase. ldi temp,'1' rcall EmitUart ldi temp,'0' rcall EmitUart ldi temp,'0' rcall EmitUart rcall TypeSeconds rcall crlf ldi temp,100 ;Take 100 second measurement. mov iterations,temp rcall measurefreq rcall displaycounter rjmp main Main: ;Main execution loop. Called routines jump back here. mov temp,flagreg ;Get mode number from flagreg. andi temp,0b00000111 cpi temp,0 ;If mode 0, measure with 100 ms timebase. breq Meas100ms cpi temp,1 ;If mode 1, measure with 1 second timebase. breq Meas1Sec cpi temp,2 ;If mode 2, measure with 10 second timebase. breq Meas10Sec cpi temp,3 ;If mode 3, measure with 100 second timebase. breq Meas100Sec cpi temp,4 ;If mode 4, count until reset or mode left.. breq counting rjmp main Meas100ms: ;Take 100 millisecond measurement -special case. ;Label measurement as with 1 second timebase. ldi temp,'0' rcall EmitUart ldi temp,'.' rcall EmitUart ldi temp,'1' rcall EmitUart rcall TypeSeconds rcall crlf cbr flagreg,0b10000000 ;Clear 32 bit overflow flag. ldi temp,$00 ;Clear 16 bit counter. out tcnt1h,temp out tcnt1l,temp clr ZL ;Clear ZL which is the overflow counter. sbi PORTD,4 ;Pulse prescaler reset cbi PORTD,4 ldi temp,0b10000000 ;Enable overflow interrupts from 16 bit timer. out TIMSK,temp cbi DDRD,3 ;Make gate pin high impedance. cbi PORTD,3 ;Gate signal into prescaler - turn off pullup. rcall Delay100ms ;Delay 100 milliseconds second. sbi DDRD,3 ;Make gate signal low Z. sbi PORTD,3 ;Disable signal into prescaler. clr temp out TIMSK,temp ;Disable overflow interrupts. ;Note: using tccr1b to disable counter causes 1+. rcall delay100ms ;Limit updates to 5 times per second. rcall displaycounter rjmp main Delay100ms: ldi temp,130 ;100 ms delay routine. mov delayreload0,temp ldi temp,254 mov delayreload1,temp ldi temp,10 mov delayreload2,temp rcall Delay ldi temp,1 mov delayreload0,temp ldi temp,1 mov delayreload1,temp ldi temp,229 mov delayreload2,temp rcall Delay wdr ;Reset the watchdog timer. ret Counting: ;Count and display results until a flag goes true. ldi flagreg,$24 ;Establish flagreg values. cbi DDRD,3 ;Make gate pin high impedance. cbi PORTD,3 ;Gate signal into prescaler - turn off pullup. sbi PORTD,4 ;Make sure prescaler counter is in reset. cbi PORTD,6 ;Make sure counting input (aux input) is gated on. ldi temp,$00 ;Clear 16 bit counter. out tcnt1h,temp out tcnt1l,temp clr ZL ;Clear Z register, which is the overflow counter. clr ZH ldi temp,0b10000000 ;Enable overflow interrupts from 16 bit timer. out TIMSK,temp KeepCounting: ;Wait for flag to go high, signaling end of counting. sbrc flagreg,4 rjmp CountingDone ;At this point, ;LSB is in tcnt1l, Move to byte0 ;Next B is in tcnt1h. Move to byte1 ;Next B in in ZL. Move to byte2 ;MSB is in ZH clr XL ;Use XL to count the digits for placement of commans and decimal points andi flagreg,0b10111111 ;Clear nonzero flag. ;move counter contents to input for number conversion in byte0,tcnt1l ;Move contents of on-chip 16 bit coutner into conversion registers. in byte1,tcnt1h mov byte2,ZL ;Move MSB into conversion register. mov byte3,ZH rcall Conv32bit2bcd ;Get binary data in byte0..byte 3 into byte0..byte4. mov temp,byte4 rcall bcdEmitUart mov temp,byte3 rcall bcdEmitUart mov temp,byte2 rcall bcdEmitUart mov temp,byte1 rcall bcdEmitUart mov temp,byte0 rcall bcdEmitUart sbrc flagreg,7 ;If flagreg bit 7 (32 bit overflow) is set, send warning. rcall warnoflow rcall crlf ;New line on display. rcall TypeCounting ;Type word "Counting" on display via UART. rcall Delay100ms ;Display 10 times per second. wdr ;Reset the watchdog timer. rjmp KeepCounting CountingDone: sbi DDRD,3 ;Make gate signal low Z. sbi PORTD,3 ;Disable signal into prescaler. clr temp out TIMSK,temp ;Disable overflow interrupts. sbi PORTD,3 ;Make input a high impedance - temporarily ungate the prescaler. sbi DDRD,3 cbi PORTD,4 ;Make sure prescaler counter is not in reset. sbi PORTD,6 ;Make sure counting input (aux input) is gated off. rjmp main displaycounter: ;Display contents of counter via UART. ;At this point, ;LSB is in external prescaler, Move to byte0 ;Next B is in tcnt1l. Move to byte1 ;Next B in in tcnt1h. Move to byte2 ;MSB is in ZL clr XL ;Use XL to count the digits for placement of commans and decimal points andi flagreg,0b10111111 ;Clear nonzero flag. ;move counter contents to input for number conversion in byte1,tcnt1l ;Move contents of on-chip 16 bit coutner into conversion registers. in byte2,tcnt1h mov byte3,ZL ;Move MSB into conversion register. ldi byte0,$FF ;First move contents of off-chip prescaler to byte0 ldi temp,$00 ;Clear 16 bit counter. out tcnt1h,temp out tcnt1l,temp MorPs: ;if count timer not equal to zero, toggle. in temp,tcnt1l tst temp brne PsDone dec byte0 ;Decrement byte0 cbi PORTD,3 ;Advance prescaler one count sbi PORTD,3 rjmp MorPs PsDone: rcall Conv32bit2bcd ;Get binary data in byte0..byte 3 into byte0..byte4. mov temp,byte4 rcall bcdEmitUart mov temp,byte3 rcall bcdEmitUart mov temp,byte2 rcall bcdEmitUart mov temp,byte1 rcall bcdEmitUart mov temp,byte0 rcall bcdEmitUart sbrc flagreg,7 ;If flagreg bit 7 (32 bit overflow) is set, send warning. rcall warnoflow rcall crlf ret bcdEmitUart: ;Emit two digite BCD in temp through UART. push temp swap temp andi temp,0b00001111 ori temp,$30 rcall EmitNoLeadZero pop temp andi temp,0b00001111 ori temp,$30 rcall EmitNoLeadZero ret PlaceMark: ;Place comma or period as appropriate. ;based on digit count (XL) and mode (flagreg 0..2) MakeModeByte: push temp ;Preserve temp.- must pop temp from stack in calling routine. mov temp,flagreg ;Make state byte from mode and digit counter andi temp,0b00000111 ;Mask off all but mode value from flagreg. cpi temp,4 brne NotMode4 ;If in mode 4, make it look like mode 1. ldi temp,1 NotMode4: swap temp ;Get mode into upper nybble. or temp,XL ;Get digit count into lower nybble. cpi temp,$02 ;Place commas and decimal point depending upon breq docomma ;the mode and digit count. cpi temp,$05 breq docomma cpi temp,$08 breq docomma cpi temp,$11 breq docomma cpi temp,$14 breq docomma cpi temp,$17 breq docomma cpi temp,$23 breq docomma cpi temp,$26 breq docomma ; cpi temp,$32 breq docomma cpi temp,$35 breq docomma cpi temp,$1A breq dodpoint cpi temp,$29 breq dodpoint cpi temp,$38 breq dodpoint cpi temp,$0A breq doohdot pop temp ret docomma: ;Type a comma on the display via UART. ldi temp,',' rcall EmitUart pop temp ret dodpoint: ;Type a decimal point on display via UART. ldi temp,'.' rcall EmitUart pop temp ret doohdot: ;Type "0." on display via UART. ldi temp,'0' rcall EmitUart ldi temp,'.' rcall EmitUart pop temp ret EmitNoLeadZero: ;Emit number if not a leading zero, otherwise a space. inc XL ;Increment digit count ;Make sure decimal point and zeros shown in modes 2,3. mov XH,flagreg ;Make state byte from mode and digit counter andi XH,0b00000111 ;Mask off all but mode value from flagreg. swap XH ;Get mode into upper nybble. or XH,XL ;Get digit count into lower nybble. cpi XH,$29 ;Set flag to show zeros to rt of decimal point brne NO29 ;in mode 2. ori flagreg,0b01000000 ;Set flagreg to allow sending leading zeros NO29: cpi XH,$38 ;Set flag to show zeros to ret of decimal point brne NO38 ;In mode 3. ori flagreg,0b01000000 ;Set flagreg to allow sending leading zeros NO38: cpi temp,'0' breq ItsAZero ori flagreg,0b01000000 ;Set nonzero flag. rcall EmitUart rcall PlaceMark ret ItsAZero: sbrs flagreg,6 rjmp QX21 rcall emitUart rcall PlaceMark ret QX21: cpi XL,10 breq EmitUart ldi temp,$20 rjmp EmitUart EmitUart: ;Send RFChar via UART, preserves content of RFChar sbis usr,udre ;Wait for data to arrive. rjmp EmitUart out udr,temp ;Send byte. ret crlf: ldi temp,$0A rcall EmitUart ldi temp,$0D rcall EmitUart ret TypeMessage: ;Type greeting push ZL push ZH ldi ZH,high(2*Message) ;Load high part of byte address into ZH ldi ZL,low(2*Message) ;Load low part of byte address into ZL rcall SendRomstring ;Send it pop ZH pop ZL ret Message: .db "ffmtr040916A" .db $0A,$0D .db $00,$00 TypeCounting: ;Type the word "Counting" via UART. push ZL push ZH ldi ZH,high(2*Countmessage) ;Load high part of byte address into ZH ldi ZL,low(2*Countmessage) ;Load low part of byte address into ZL rcall SendRomstring ;Send it pop ZH pop ZL ret Countmessage: .db "Counting" .db $0A,$0D .db $00,$00 SendRomstring: ;Send string pointed to by ZH,ZL. lpm ; Load byte from program memory into r0 tst r0 ; Check if we've reached the end of the message breq stringdone ; If so, return mov temp,r0 rcall EmitUart adiw ZL,1 ; Increment Z registers rjmp SendRomstring stringdone: ret WarnOflow: ;Type "+" to indicate overflow push temp ldi temp,'+' rcall EmitUart pop temp ret Conv32bit2bcd: ;Input: r18.19,20.21 (binary); out: r18,19,20,21,22 (bcd) ;Listed above as lsB first, msB last. push r16 ;Save registers not needed for push r17 push r23 ;input or output. push r24 rcall Bin4BCD ;Do the conversion mov r18,r20 mov r19,r21 mov r20,r22 mov r21,r23 mov r22,r24 pop r24 ;Restore saved registers. pop r23 pop r17 pop r16 ret ;******** 32 bit binary to packed BCD routines from Atmel application note ************* ; Call bin4bcd r18r19r20r21 >>> r20r21r22r23r24 Bin2BCD20: mov r16,r20 ;for compatibility with Math32 mov r17,r21 ; Bin2BCD16: ldi r22,0xff ;initialize digit 4 binBCD_4: inc r22 ; subi r16,low(10000);subiw fbin,10000 sbci r17,high(10000) brcc binBCD_4 ; ldi r21,0x9f ;initialize digits 3 and 2 binBCD_3: subi r21,0x10 ; subi r16,low(-1000);subiw fbin,1000 sbci r17,high(-1000) brcs binBCD_3 ; binBCD_2: inc r21 ; subi r16,low(100) ;subiw fbin,100 sbci r17,high(100) ; brcc binBCD_2 ; ldi r20,0xa0 ;initialize digits 1 and 0 binBCD_1: subi r20,0x10 ; subi r16,-10 ;subi fbin,10 brcs binBCD_1 ; add r20,r16 ; binBCD_ret: ret ;* r18:r19:r20:r21 >>> r20:r21:r22:r23:r24 <== this one. ;* hex dec ;* r18r19r20r21 >>> r20r21r22r23r24 Bin4BCD: rcall Bin2BCD20 ; clr r23 ;initial highest bytes of result ldi r24,0xfe ; binBCD_loop: subi r20,-0x33 ;add 0x33 to digits 1 and 0 sbrs r20,3 ;if bit 3 clear subi r20,0x03 ; sub 3 sbrs r20,7 ;if bit 7 clear subi r20,0x30 ; sub $30 subi r21,-0x33 ;add 0x33 to digits 3 and 2 sbrs r21,3 ;if bit 3 clear subi r21,0x03 ; sub 3 sbrs r21,7 ;if bit 7 clear subi r21,0x30 ; sub $30 subi r22,-0x33 ;add 0x33 to digits 5 and 4 sbrs r22,3 ;if bit 3 clear subi r22,0x03 ; sub 3 sbrs r22,7 ;if bit 7 clear subi r22,0x30 ; sub $30 lsl r18 ; rol r19 ;shift lower word rol r20 ;through all bytes rol r21 ; rol r22 ; rol r23 ; rol r24 ; brmi binBCD_loop ;7 shifts w/o correction of MSD rol r17 ;since Bin2BCD r17 = 0xff brcc binBCD_ret ;so as to do 16_lsl in total subi r23,-0x33 ;add 0x33 to digits 7 and 6 sbrs r23,3 ;if bit 3 clear subi r23,0x03 ; sub 3 sbrs r23,7 ;if bit 7 clear subi r23,0x30 ; sub $30 subi r24,-0x03 ;add 0x03 to digit 8 only sbrs r24,3 ;if bit 3 clear subi r24,0x03 ; sub 3 rjmp binBCD_loop ; ;************End of binary to bcd conversion routine********** T1OFLW: push temp ;Save temp then save status register in temp. in temp,sreg sbrc flagreg,5 rjmp countermode inc ZL ;Increment 8 bit ZL register upon overflow of 16 bit counter. rjmp fmetermodes countermode: adiw ZL,1 fmetermodes: brne NoOverflow sbr flagreg,0b10000000 ;Set 32 bit overflow flag (very reare event) NoOverflow: out sreg,temp ;Restore status regiser then restore temp. pop temp reti UARTRCV: ;UART receive interrupt push temp ;Save temp then save status register. in temp,sreg push temp in temp,usr in temp,udr ;Read the char. andi temp,$DF ;Make upper-case ascii cpi temp,'R' ;If 'R' reset counters if mode 4 (Counting mode). breq resetcounter conint2: ;Branch above returns here if not 'R' in mode 4. mov temp,flagreg ;Increment mode (flagret 0..2) andi temp,0b00000111 ;Maximum count is 4 inc temp cpi temp,$05 ;If its mode 5, that's one too many, warp around to zero. breq modemaxed contint: ;Branch above returns here after resetting temp to 1. andi flagreg,0b11111000 or flagreg,temp ori flagreg,0b00010000 ;Set flagreg bit 4 to leave counting mode. ldi temp,1 ;Set timer counters to time out early. mov iterations,temp mov delayreload0,temp mov delayreload1,temp mov delayreload2,temp intdone: pop temp ;Restore status regiser then restore temp. out sreg,temp pop temp reti modemaxed: ldi temp,0 ;Lowest mode number is 0. rjmp contint resetcounter: ;If in mode 4, counter mode, reset counter. mov temp,flagreg andi temp,0b00000111 cpi temp,4 brne conint2 ;If not mode 4, continue as if any other key. ldi temp,$00 ;Clear 16 bit counter. out tcnt1h,temp out tcnt1l,temp clr ZL ;Clear ZL,ZH which is the overflow counter. clr ZH rjmp intdone .exit ;Assemble stops at this line in document.