; **************************************************** ; * Frequenzzaehler, Drehzahlmesser und Voltmeter * ; * fuer ATmega8 bei 16 MHz Taktfrequenz (Quarzosz.) * ; * ohne Autorange, mit Vorteiler /1 oder /16 * ; * Version 0.3 (C)2009 by info@avr-asm-tutorial.net * ; **************************************************** ; .INCLUDE "m8def.inc" ; .EQU debug = 0 .EQU debugpulse = 0 ; ; Switches for connected hardware ; .EQU cDisplay = 1 ; LCD display connected .EQU cDisplay8 = 0 ; displays 8 characters per line instead of 16 .EQU cDisplay2 = 1 ; two line LCD display connected .EQU cUart = 1 ; Uart active ; attached prescaler on port C .EQU pPresc = PORTC ; prescaler by 16 output attached to port C .EQU pPrescD = DDRC ; data direction of prescaler .EQU bPresc = 5 ; bit 5 enables prescaler by 16 ; ; ================================================ ; Other hardware depending stuff ; ================================================ ; .EQU cFreq = 16000000 ; Clock frequency processor in cycles/s .IF cUart .EQU cBaud = 9600 ; If Uart active, define Baudrate .ENDIF .EQU bLcdE = 5 ; LCD E port bit on Port B .EQU bLcdRs = 4 ; Lcd RS port bit on Port B ; ; ================================================ ; Constants for voltage measurement ; ================================================ ; ; Resistor network as pre-divider for the ADC ; -------------------------------------- ; R1 R2(k) Meas Accur. MaxVoltage ; kOhm kOhm Volt mV/dig Volt ; -------------------------------------- ; 1000 1000 5,12 5 10 ; 1000 820 5,68 6 11 ; 1000 680 6,32 6 12 ; 1000 560 7,13 7 14 ; 1000 470 8,01 8 15 ; 1000 330 10,32 10 20 ; 1000 270 12,04 12 23 ; 1000 220 14,20 14 27 ; 1000 180 16,78 16 32 ; 1000 150 19,63 19 38 ; 1000 120 23,98 23 46 ; 1000 100 28,16 28 55 ; .EQU cR1 = 1000 ; Resistor between ADC input and measured voltage .EQU cR2 = 1000 ; Resistor between ADC input and ground .EQU cRin = 8250 ; Input resistance ADC, experimental ; ; Other sSoft switches ; .EQU cNMode = 3 ; number o0f measurements before mode changes .EQU cDecSep = ',' ; decimal separator for numbers displayed .EQU c1kSep = '.' ; thousands separator .EQU nMeasm = 4 ; number of measurements per second .IF (nMeasm<4)||(nMeasm>7) .ERROR "Number of measurements outside acceptable range" .ENDIF ; ; ================================================ ; Hardware connections ; ================================================ ; ___ ___ ; RESET |1 |_| 28| Prescaler divide by 16 output ; RXD |2 A 27| ; TXD |3 T 26| ; Time inp |4 M 25| ; |5 E 24| Mode select input, 0..2.56 V ; Count in |6 L 23| Voltage input, 0..2.56 V ; VCC |7 22| GND ; GND |8 A 21| AREF (+2.56 V, output) ; XTAL1 |9 T 20| AVCC input ; XTAL2 |10 m 19| SCK/LCD-E ; |11 e 18| MISO/LCD-RS ; |12 g 17| MOSI/LCD-D7 ; |13 a 16| LCD-D6 ; LCD-D4 |14 8 15| LCD-D5 ; |_________| ; ; ; ================================================ ; Derived constants ; ================================================ ; .EQU cR2c = (cR2 * cRin) / (cR2+cRin) .EQU cMultiplier = (641 * (cR1+cR2c))/cR2c ; used for voltage multiplication .EQU cMaxVoltage = 1024*cMultiplier/256 ; in mV .EQU cSafeVoltage = (cMaxVoltage * 5000) / 2560 .EQU cTDiv = 1000/nMeasm ; interval per measurement update ; calculating the CTC and prescaler values for TC1 (frequency measurement) .SET cCmp1F = cFreq/32 ; CTC compare value with counter prescaler = 8 .SET cPre1F = (1<2097120 .SET cCmp1F = cFreq/256 ; CTC compare value with counter prescaler = 64 .SET cPre1F = (1<16776960 .SET cCmp1F = cFreq/1024 ; CTC compare value with counter prescaler = 256 .SET cPre1F = (1<2040000 .SET cCmp2 = cFreq/32000 .SET cPre2 = (1<8160000 .SET cCmp2 = cFreq/64000 .SET cPre2 = (1<16320000 .SET cCmp2 = cFreq/128000 ; counter prescaler = 128 .SET cPre2 = (1< Presc2 => TC2 => CTC => rTDiv => ; ADC0 conversion => ADC1 conversion => bAdc-flag ; ; Main interval timer TC2 ; - uses TC2 as 8-bit-CTC, with compare interrupt ; - starts a ADC conversion ; - on ADC conversion complete: ; * store ADC result ; * convert ADC result ; * if a new counter result: convert this ; * if Uart connected and monitoring f/U: display on Uart ; * if LCD connected and display mode: display f/U result ; ; Operation at 16 MHz clock: ; cFreq => Prescaler/128 => CTC(125) => rTDiv(250) ; 16MHz => 125 kHz => 1 kHz => 4 Hz ; ; Frequeny counting modes (Mode = 0 and 1) ; - uses TC0 as 8-bit-counter to count positive edges ; - uses TC1 as 16-bit-counter to time-out the counter after 250 ms ; ; Timer modes (Mode = 2 to 8) ; - uses edge detection on external INT0 for timeout ; - uses TC1 as 16-bit-counter to time-out from edge to edge ; ; Voltage only (Mode = 9) ; - Timers TC0 and TC1 off ; - Timer TC2 times interval ; ; ============================================== ; Reset and Interrupt Vectors starting here ; ============================================== ; .CSEG .ORG $0000 ; ; Reset/Intvectors ; rjmp Main ; Reset rjmp Int0Int; Int0 reti ; Int1 rjmp TC2CmpInt ; TC2 Comp reti ; TC2 Ovf reti ; TC1 Capt rjmp Tc1CmpAInt ; TC1 Comp A reti ; TC1 Comp B rjmp Tc1OvfInt ; TC1 Ovf rjmp TC0OvfInt ; TC0 Ovf reti ; SPI STC .IF cUart rjmp SioRxcIsr ; USART RX .ELSE reti ; USART RX .ENDIF reti ; USART UDRE reti ; USART TXC rjmp AdcCcInt ; ADC Conv Compl reti ; EERDY reti ; ANA_COMP reti ; TWI reti ; SPM_RDY ; ; ============================================= ; ; Interrupt Service Routines ; ; ============================================= ; ; TC2 Compare Match Interrupt ; counts rTDiv down, if zero: starts an AD conversion ; TC2CmpInt: in rSreg,SREG ; save SREG dec rTDiv ; count down brne TC2CmpInt1 ; not zero, interval not ended ldi rimp,(1< 1000 brne CalcPwE tst rRes3 brne CalcPwE ldi rmp,LOW(1001) cp rRes1,rmp ldi rmp,HIGH(1001) cpc rRes2,rmp brcc CalcPwE clc ; no error ret CalcPwE: ; error sec ret ; ; Display the binary in R2:R1 in the form " 100,0%" ; DisplPw: ldi XH,HIGH(sResult) ldi XL,LOW(sResult) ldi rmp,' ' st X+,rmp st X+,rmp clr R0 ldi ZH,HIGH(1000) ldi ZL,LOW(1000) rcall DisplDecX2 ldi ZH,HIGH(100) ldi ZL,LOW(100) rcall DisplDecX2 ldi ZL,10 inc R0 rcall DisplDecX2 ldi rmp,cDecSep st X+,rmp ldi rmp,'0' add rmp,rRes1 st X+,rmp ldi rmp,'%' st X+,rmp .IF ! cDisplay8 ldi rmp,8 ldi ZL,' ' DisplPw1: st X+,ZL dec rmp brne DisplPw1 .ENDIF ret ; ; If the first characters in the result buffer are empty, ; place the character in ZL here and add equal, if possible ; DisplMode: ldi XH,HIGH(sResult+1) ; point to result buffer ldi XL,LOW(sResult+1) ld rmp,X ; read second char cpi rmp,' ' brne DisplMode1 ldi rmp,'=' st X,rmp DisplMode1: sbiw XL,1 ld rmp,X ; read first char cpi rmp,' ' brne DisplMode2 st X,ZL DisplMode2: ret ; ;================================================= ; Display binary numbers as decimal ;================================================= ; ; Converts a binary in R2:R1 to a digit in X ; binary in Z ; DecConv: clr rmp DecConv1: cp R1,ZL ; smaller than binary digit? cpc R2,ZH brcs DecConv2 ; ended subtraction sub R1,ZL sbc R2,ZH inc rmp rjmp DecConv1 DecConv2: tst rmp brne DecConv3 tst R0 brne DecConv3 ldi rmp,' ' ; suppress leading zero rjmp DecConv4 DecConv3: subi rmp,-'0' DecConv4: st X+,rmp ret ; ; Display fractional number in R3:R2:(Fract)R1 ; DisplFrac: ldi XH,HIGH(sResult) ldi XL,LOW(sResult) .IF ! cDisplay8 ldi rmp,' ' st X+,rmp st X+,rmp .ENDIF clr R0 ldi ZH,HIGH(10000) ldi ZL,LOW(10000) rcall DisplDecY2 ldi ZH,HIGH(1000) ldi ZL,LOW(1000) rcall DisplDecY2 .IF ! cDisplay8 ldi rmp,c1kSep tst R0 brne DisplFrac0 ldi rmp,' ' DisplFrac0: st X+,rmp .ENDIF ldi ZL,100 rcall DisplDecY1 ldi ZL,10 rcall DisplDecY1 ldi rmp,'0' add rmp,R2 st X+,rmp tst R1 ; fraction = 0? brne DisplFrac1 ldi rmp,' ' st X+,rmp ldi rmp,'H' st X+,rmp ldi rmp,'z' st X+,rmp .IF ! cDisplay8 ldi rmp,' ' st X+,rmp st X+,rmp st X+,rmp st X+,rmp .ENDIF ret DisplFrac1: ldi rmp,cDecSep st X+,rmp .IF cDisplay8 ldi ZL,2 .ELSE ldi ZL,3 .ENDIF DisplFrac2: clr rRes3 clr rRes2 mov R0,rRes1 ; * 1 lsl rRes1 ; * 2 adc rRes2,rRes3 lsl rRes1 ; * 4 rol rRes2 add rRes1,R0 ; * 5 adc rRes2,rRes3 lsl rRes1 ; * 10 rol rRes2 ldi rmp,'0' add rmp,rRes2 st X+,rmp dec ZL brne DisplFrac2 .IF ! cDisplay8 ldi rmp,' ' st X+,rmp ldi rmp,'H' st X+,rmp ldi rmp,'z' st X+,rmp ldi rmp,' ' st X+,rmp .ENDIF ret ; ; Convert a decimal in R4:R3:R2, decimal in ZH:ZL ; DisplDecY2: clr rDiv1 ; rDiv1 is counter clr rDiv2 ; overflow byte DisplDecY2a: cp rRes2,ZL cpc rRes3,ZH cpc rRes4,rDiv2 brcs DisplDecY2b ; ended sub rRes2,ZL ; subtract sbc rRes3,ZH sbc rRes4,rDiv2 inc rDiv1 rjmp DisplDecY2a DisplDecY2b: ldi rmp,'0' add rmp,rDiv1 add R0,rDiv1 tst R0 brne DisplDecY2c ldi rmp,' ' DisplDecY2c: st X+,rmp ret ; ; Convert a decimal decimal in R:R2, decimal in ZL ; DisplDecY1: clr rDiv1 ; rDiv1 is counter clr rDiv2 ; overflow byte DisplDecY1a: cp rRes2,ZL cpc rRes3,rDiv2 brcs DisplDecY1b ; ended sub rRes2,ZL ; subtract sbc rRes3,rDiv2 inc rDiv1 rjmp DisplDecY1a DisplDecY1b: ldi rmp,'0' add rmp,rDiv1 add R0,rDiv1 tst R0 brne DisplDecY1c ldi rmp,' ' DisplDecY1c: st X+,rmp ret ; ; Display a 4-byte-binary in decimal format on result line 1 ; 8-bit-display: "12345678" ; 16-bit-display: " 12.345.678 Hz " ; Displ4Dec: ldi rmp,BYTE1(100000000) ; check overflow cp rRes1,rmp ldi rmp,BYTE2(100000000) cpc rRes2,rmp ldi rmp,BYTE3(100000000) cpc rRes3,rmp ldi rmp,BYTE4(100000000) cpc rRes4,rmp brcs Displ4Dec1 rjmp CycleOvf Displ4Dec1: clr R0 ; suppress leading zeroes ldi XH,HIGH(sResult) ; X to result buffer ldi XL,LOW(sResult) .IF ! cDisplay8 ldi rmp,' ' ; clear the first two digits st X+,rmp st X+,rmp .ENDIF ldi ZH,BYTE3(10000000) ; 10 mio ldi ZL,BYTE2(10000000) ldi rmp,BYTE1(10000000) rcall DisplDecX3 ldi ZH,BYTE3(1000000) ; 1 mio ldi ZL,BYTE2(1000000) ldi rmp,BYTE1(1000000) rcall DisplDecX3 .IF ! cDisplay8 ldi rmp,c1kSep ; set separator tst R0 brne Displ4Dec2 ldi rmp,' ' Displ4Dec2: st X+,rmp .ENDIF ldi ZH,BYTE3(100000) ; 100 k ldi ZL,BYTE2(100000) ldi rmp,BYTE1(100000) rcall DisplDecX3 ldi ZH,HIGH(10000) ; 10 k ldi ZL,LOW(10000) rcall DisplDecX2 ldi ZH,HIGH(1000) ; 1 k ldi ZL,LOW(1000) rcall DisplDecX2 .IF ! cDisplay8 ldi rmp,c1kSep ; set separator tst R0 brne Displ4Dec3 ldi rmp,' ' Displ4Dec3: st X+,rmp .ENDIF ldi ZL,100 ; 100 rcall DisplDecX1 ldi ZL,10 rcall DisplDecX1 ldi rmp,'0' ; 1 add rmp,R1 st X+,rmp ret ; ; Convert a decimal in R3:R2:R1, decimal in ZH:ZL:rmp ; DisplDecX3: clr rDiv1 ; rDiv1 is counter clr rDiv2 ; subtractor for byte 4 DisplDecX3a: cp rRes1,rmp ; compare cpc rRes2,ZL cpc rRes3,ZH cpc rRes4,rDiv2 brcs DisplDecX3b ; ended sub rRes1,rmp ; subtract sbc rRes2,ZL sbc rRes3,ZH sbc rRes4,rDiv2 inc rDiv1 rjmp DisplDecX3a DisplDecX3b: ldi rmp,'0' add rmp,rDiv1 add R0,rDiv1 tst R0 brne DisplDecX3c ldi rmp,' ' DisplDecX3c: st X+,rmp ret ; ; Convert a decimal in R3:R2:R1, decimal in ZH:ZL ; DisplDecX2: clr rDiv1 ; rDiv1 is counter clr rDiv2 ; next byte overflow DisplDecX2a: cp rRes1,ZL cpc rRes2,ZH cpc rRes3,rDiv2 brcs DisplDecX2b ; ended sub rRes1,ZL ; subtract sbc rRes2,ZH sbc rRes3,rDiv2 inc rDiv1 rjmp DisplDecX2a DisplDecX2b: ldi rmp,'0' add rmp,rDiv1 add R0,rDiv1 tst R0 brne DisplDecX2c ldi rmp,' ' DisplDecX2c: st X+,rmp ret ; ; Convert a decimal in R2:R1, decimal in ZL ; DisplDecX1: clr rDiv1 ; rDiv1 is counter clr rDiv2 ; next byte overflow DisplDecX1a: cp rRes1,ZL cpc rRes2,rDiv2 brcs DisplDecX1b ; ended sub rRes1,ZL ; subtract sbc rRes2,rDiv2 inc rDiv1 rjmp DisplDecX1a DisplDecX1b: ldi rmp,'0' add rmp,rDiv1 add R0,rDiv1 tst R0 brne DisplDecX1c ldi rmp,' ' DisplDecX1c: st X+,rmp ret ; ;================================================= ; Delay routines ;================================================= ; Delay10ms: ldi rDelH,HIGH(10000) ldi rDelL,LOW(10000) rjmp DelayZ Delay15ms: ldi rDelH,HIGH(15000) ldi rDelL,LOW(15000) rjmp DelayZ Delay4_1ms: ldi rDelH,HIGH(4100) ldi rDelL,LOW(4100) rjmp DelayZ Delay1_64ms: ldi rDelH,HIGH(1640) ldi rDelL,LOW(1640) rjmp DelayZ Delay100us: clr rDelH ldi rDelL,100 rjmp DelayZ Delay40us: clr rDelH ldi rDelL,40 rjmp DelayZ ; ; Delays execution for Z microseconds ; DelayZ: .IF cFreq>18000000 nop nop .ENDIF .IF cFreq>16000000 nop nop .ENDIF .IF cFreq>14000000 nop nop .ENDIF .IF cFreq>12000000 nop nop .ENDIF .IF cFreq>10000000 nop nop .ENDIF .IF cFreq>8000000 nop nop .ENDIF .IF cFreq>6000000 nop nop .ENDIF .IF cFreq>4000000 nop nop .ENDIF sbiw rDelL,1 ; 2 brne DelayZ ; 2 ret ; ; ========================================= ; Main Program Start ; ========================================= ; main: ldi rmp,HIGH(RAMEND) ; set stack pointer out SPH,rmp ldi rmp,LOW(RAMEND) out SPL,rmp clr rFlg ; set flags to default ; .IF debug .EQU number = 100000000 ldi rmp,BYTE4(number) mov rRes4,rmp mov rDiv4,rmp ldi rmp,BYTE3(number) mov rRes3,rmp mov rDiv3,rmp ldi rmp,BYTE2(number) mov rRes2,rmp mov rDiv2,rmp ldi rmp,BYTE1(number) mov rRes1,rmp mov rDiv1,rmp rcall CycleM6 beloop: rjmp beloop .ENDIF .IF debugpulse .EQU nhigh = 100000000 .EQU nlow = 15000 ldi rmp,BYTE4(nhigh) sts sCtr+3,rmp ldi rmp,BYTE3(nhigh) sts sCtr+2,rmp ldi rmp,BYTE2(nhigh) sts sCtr+1,rmp ldi rmp,BYTE1(nhigh) sts sCtr,rmp ldi rmp,BYTE4(nlow) mov rRes4,rmp mov rDiv4,rmp ldi rmp,BYTE3(nlow) mov rRes3,rmp mov rDiv3,rmp ldi rmp,BYTE2(nlow) mov rRes2,rmp mov rDiv2,rmp ldi rmp,BYTE1(nlow) mov rRes1,rmp mov rDiv1,rmp sbr rFlg,1<elp",cCr,cLf txtUartCursor: .DB cCr,cLf,"i> ",cNul txtUartUnknown: .DB cCr,cLf,"Unknown command!",cNul,cNul txtUartUOff: .DB "Voltage monitoring is off.",cNul,cNul txtUartUOn: .DB "Voltage monitoring is on. ",cNul,cNul txtUartFOff: .DB "Frequency monitoring is off.",cNul,cNul txtUartFOn: .DB "Frequency monitoring is on. ",cNul,cNul txtUartErr: .DB "Error in parameter! ",cNul,cNul txtUartHelp: .DB cCr,cLf,"Help: ",cCr,cLf .DB "U[=N](on) or u(Off): monitor voltage output, N=1..255,",cCr,cLf .DB "F[=N](On) or f(Off): monitor frequency output N=1..255, ",cCr,cLf .DB "p: display monitoring parameters, ",cCr,cLf .DB "h or ?: this text." txtUartNul: .DB cNul,cNul .ENDIF ; ; End of source code ;