![]() |
Applications of AVR Single chip controllers AT90S, ATtiny, ATmega and ATxmega Audio sensor with an ATtiny45 Assembler source code |
;
; ************************************
; * Follow me with an ATtiny25/45/85 *
; * Version 1 as of April 2018 *
; * (C)2018 by avr-asm-tutorial.net *
; ************************************
;
; Works either with an ATtiny45 or on an
; ATtiny85 without any changes,
; can be adapted to an ATtiny25 by
; setting the respective switch
; Can work on an ATtiny25 if the
; collection and storage of the 16 bit
; time stamps is omitted
;
; **********************************
; D E V I C E S E L E C T I O N
; **********************************
;
; Select the target devce, either ATtiny45
; or ATtiny85 work normal, the ATtiny25
; version does not store time stamps
.equ dev_sel = 45 ; can be 25, 45 or 85
;
;
.nolist
.if dev_sel == 25
.include "tn25def.inc"
.set tstmp = 0
.endif
.if dev_sel == 45
.include "tn45def.inc"
.set tstmp = 1
.endif
.if dev_sel == 85
.include "tn85def.inc"
.set tstmp = 1
.endif
.list
;
; Enforce setting/omitting timestamps
; overwrites default setting in device selection
;.set tstamp = 0 ; Select timestamp
;
.if (tstamp == 1)&&(dev_sel==25)
.error "Necessary EEPROM space too large!"
.endif
;
; **********************************
; D E B U G S W I T C H E S
; **********************************
; All debug switches = 0 in final version
;
; Test the hardware connected to the board
;
; Switch both red LEDs on and loop forever
.equ debug_ledsRed = 0
;
; Switch green LED on and loop forever
.equ debug_ledGreen = 0
;
; Turn the speaker on and loop forever
.equ debug_speaker = 0
;
; Test the mike and ADC function
;
; Collect ADC data sets, average those and
; use the average als pulse width for
; both red LED
.equ debug_mikeadc = 0
;
; Collect 64 data sets, disable interrupts,
; copy results to EEPROM, set red LED on
; and loop forever
.equ debug_eep = 1
;
; Collect 64 raw data sets, disable interrupts,
; copy results to EEPROM, set red LED on
; and loop forever
.equ debug_eep_raw = 0
;
; **********************************
; H A R D W A R E
; **********************************
;
; Device: ATtiny25/45/85, Package: 8-pin-PDIP_SOIC
;
; ________
; 1 / |8
; RESET o--|RES VCC|--o VCC
; 2| |7
; Udiff - o--|ADC3 PB2|--o Led red anode
; 3| |6
; UMike + o--|ADC2 PB1|--o Led green andode
; 4| |5
; GND o--|GND OC0A|--o Speaker, LED red cathode
; |_________|
;
;
; **********************************
; P O R T S A N D P I N S
; **********************************
;
; LED ports and pins
.equ pLedO = PORTB ; Led port
.equ bLedRCO = PORTB0 ; Red led cathode pin
.equ bLedRGRAO = PORTB2 ; Red/green red anode pin
.equ bLedRGRCO = PORTB1 ; Red/green red cathode pin
.equ bLedRGGAO = PORTB1 ; Red/green green anode pin
.equ bLedRGGCO = PORTB2 ; Red/green green cathode pin
.equ pLedD = DDRB ; Led direction port
.equ bLedRCD = DDB0 ; Red led cathode direction pin
.equ bLedRGRAD = DDB2 ; Red/green red anode direction pin
.equ bLedRGGAD = DDB1 ; Red/green green anode direction pin
; Speaker ports and pins
.equ pSpkO = PORTB ; Speaker output port
.equ bSpkO = PORTB0 ; Speaker output pin
.equ pSpkD = DDRB ; Speaker direction port
.equ bSpkD = DDB0 ; Speaker direction pin
;
; **********************************
; A D J U S T A B L E C O N S T
; **********************************
;
; Clock frequency, changed during start-up
; by writing to the CLKPR port
.equ clock=2000000 ; Define clock frequency
;
; Number of trigger exceedences that lead to
; alarm activity, 1 to 100
.equ cTriggr = 50 ; Trigger level exceedences in %
;
; Alarm duration in tenth of seconds
; Can be set to 1..41
.equ cAlrm = 10 ; Multiples of 0.1 seconds
;
; Alarm tone, 62 Hz .. 15 kHz
.equ cTone = 880 ; Tone in Hz
;
; **********************************
; F I X & D E R I V. C O N S T
; **********************************
;
; Alarm counter for TC1
.equ cAlrmC = clock / 128 /256 * cAlrm / 10
;
; Alarm tone conversion
.equ cToneC = clock / 64 / cTone ; CTC A TC0
;
; Trigger level count
.equ cTrigger = (64*cTriggr+50)/100 ; Trigger counts
;
;
; Check ranges
.if cAlrmC > 255
.error "Alarm time constant too large!"
.endif
.if (cToneC<1)||(cToneC>255)
.error "Alarm frequncy out of range!"
.endif
.if (cTrigger<1) || (cTrigger>64)
.error "Trigger level out of range!"
.endif
;
; **********************************
; T I M I N G
; **********************************
;
; Timing of the ADC:
; The ADC runs with a prescaler of 2 and
; collects differential ADC data on the
; inputs (ADC2-ADC3) with a gain of 20.
; Clock controller = 2 MHz
; Precaler ADC = 2
; Conversion clock = 1 MHz
; Conversion steps = 13
; Conversion time = 13 us
; Time from ADC start to int = 13 us
; Clock cycles for that = 26
; Clocks for restarting ADC: 21/23/25/26/35/38
; Minimum delay restart = 10.5 us
; Maximum delay restart = 19 us
; Conversion time minimum = 23.5 us
; Conversion time maximum = 32 us
; Conversion time with TC0 int = 39 us
; Minimum conversion frequency = 25.64 kHz
; Maximum conversion frequency = 42.55 kHz
;
; 8-bit timer TC0 as time base and as tone generator
; As time base during signal strength measurements:
; Mode: 8-bit normal counting
; Controller clock = 2 MHz
; Prescaler = 256
; TC0-Tick = 128 us
; Overflow time = 32.77 ms
; Overflow frequency = 30.5 Hz
; Overflow of TC0H = 8.38 sec
;
; As tone generator during alarm
; Mode: 8-bit CTC with OCR0A and OC0A toggling
; Controller clock = 2 MHz
; Precaler = 64
; Tone frequency, OCR0A=0x01 = 15.6 kHz
; Tone frequency, OCR0A=0xFF = 61 Hz
;
; 8 bit timer TC1 as PWM for tone strength
; display on the red/green LED and as
; duration control for alarm duration
; As LED-PWM (normal operation):
; Controller clock = 2 MHz
; Prescaler = 128
; Time per tick = 64 us
; Time for 256 ticks = 16.384 ms
; Time per overflow = 16,4 ms
; PWM-Frequency red/green LED = 61 Hz
;
; As alarm time counter (alarm condition):
; Controller clock = 2 MHz
; Prescaler = 128
; Time per Tick = 64 us
; Time for 256 ticks = 16.384 ms
; Counter for 1 second alarm = 61
; Counter for 4 seconds alarm = 244
;
; **********************************
; R E G I S T E R S
; **********************************
;
; Used: R1:R0 for adding to average
; free: R2 to R8
.def rMaxA = R9 ; Maximum collected
.def rLast = R10 ; Last measured value
.def rTrigger = R11 ; Trigger value
.def rTc0L = R12 ; TC0, LSB
.def rTc0H = R13 ; TC0, MSB
.def rTop = R14 ; Top value inside Int
.def rSreg = R15 ; Save/Restore status port
.def rmp = R16 ; Define multipurpose register
.def rimp = R17 ; Multipurpose inside ints
.def rFlag = R18 ; Flag register
.equ bOvf = 0 ; Overflow condition on ADC
.equ bDwn = 1 ; Signal on ADC is decreasing
.equ bDsc = 2 ; Update buffer, save data set
.equ bAlrm = 3 ; Alarm phase activated
.def rOvfC = R19 ; Overflow counter
.def rDscC = R20 ; Data set counter
.def rAlrmC = R21 ; Alarm counter
; free: R22 to R25
; used: R27:R26 = X for EEPROM write
; used: R29:R28 = Y as buffer pointer to SRAM
; used: R31:R30 = Z for ...
;
; **********************************
; S R A M
; **********************************
;
.dseg
.org SRAM_START
.equ nDataSets = 64 ; data set=2 byte time, one byte top value
sBuffer:
.if tstmp == 1
.byte 3*nDataSets ; Reserve 192 bytes for data
.else
.byte nDataSets ; Reserve 64 bytes for data
.endif
sBufferDataEnd:
.byte 1 ; Average
.byte 1 ; Maximum
.byte 1 ; Trigger
sBufferEnd:
;
; **********************************
; C O D E
; **********************************
;
.cseg
.org 000000
;
; **********************************
; R E S E T & I N T - V E C T O R S
; **********************************
rjmp Main ; Reset vector
reti ; INT0
reti ; PCI0
reti ; OC1A
rjmp Ovf1Int ; OVF1
rjmp Ovf0Int ; OVF0
reti ; ERDY
reti ; ACI
rjmp AdcRdy ; ADCC
reti ; OC1B
reti ; OC0A
reti ; OC0B
reti ; WDT
reti ; USI_START
reti ; USI_OVF
;
; **********************************
; I N T - S E R V I C E R O U T .
; **********************************
;
; Overflow Int TC0: 8-bit counter
; Measuring: Normal counter, provides timestamp MSB
; Alarm: Interrupt disabled, CTC for speaker output
Ovf0Int: ; 7 clocks for interrupt&vector jump
in rSreg,SREG ; Save SREG, +1=8
inc rTc0H ; increase MSB, #1=9
out SREG,rSreg ; Restore SREG, +1=10
reti ; +4=14
;
; Overflow Int TC1
; Measuring: 8-bit PWM, interrupt disabled
; Alarm: Normal counting, count alarm time down
Ovf1Int:
in rSreg,SREG ; Save SREG
tst rAlrmC ; Alarm at zero?
breq Ovf1Int1 ; No timeout
dec rAlrmC ; Count down
Ovf1Int1:
out SREG,rSreg ; Restore SREG
reti
;
; ADC ready interrupt service
; Clock cycles: +n=nc: ISR execution
; #n: clock cycles from last ADC start to next start
AdcRdy: ; 7 clocks for interrupt, #12
in rSreg,SREG ; Save SREG, +1=8, #13
in rTc0L,TCNT0 ; Store LSB time, +1=9, #14
in rimp,ADCH ; Read MSB ADC, +1=10, #15
.if debug_eep_raw == 0
tst rimp ; Zero? +1=11, #16
breq AdcZero ; Yes, clear dwn flag +1/2=12/13, #17/18
cp rimp,rLast ; Compare with last, +1=13, #18
mov rLast,rimp ; Last=current, +1=14, #19
brcc AdcGreater ; Increasing, +1/2=15/16, #20/21
cp rimp,rTop ; Smaller than ITop +1=16, #21
brcc AdcGreater ; No more decreasing +1/2=17/18, #22/23
sbrc rFlag,bDwn ; Skip if not downwards, +1/2=18/19, #23/24
rjmp AdcRet ; +2=20, #25
.endif
.if tstmp == 1 ; Store timestamp im buffer
st Y+,rTc0L ; Store LSB time in buffer, +2=21, #26
st Y+,rTc0H ; Store MSB time in buffer, +2=23, #28
.endif
.if debug_eep_raw == 1
st Y+,rimp ; Store measured value in buffer, +2=25, #30
.else
st Y+,rTop ; Store Top in buffer, +2=25, #30
.endif
sbr rFlag,1<<bDwn ; Set data flag, +1=26, #31
dec rDscC ; Decrease counter, +1=27, #32
brne AdcRet ; +1/2=28/29, #33/34
ldi YH,High(sBuffer) ; +1=29, #34
ldi YL,Low(sBuffer) ; +1=30, #35
ldi rDscC,nDataSets ; +1=31, #36
sbr rFlag,1<<bDsc ; +1=32, #37
AdcRet: ; 19/20/21/30/32 cycles, #23/24/25/34/37
ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS0); +1=20/21/22/31/33, #25/26/27/35/38
out ADCSRA,rimp ; Start next conversion, +1=22/32/34, #0
out SREG,rSreg ; Restore SREG, +1=23/33/35, #1
reti ; +4=27/37/39, #5
AdcGreater: ; 16/18 cycles, #21/23
cpi rimp,0x10 ; ignore small values, +1=17/19, #22/24
brcs AdcClrTop ; +1/2=18/19/20/21, #23/24/25/26
mov rTop,rimp ; Set new top 1=19/21, #24/26
ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS0); +1=20/22, #25/27
out ADCSRA,rimp ; Start next conversion, +1=19/21, #0
out SREG,rSreg ; Restore SREG, +1=20/22, #1
reti ; +4=24/26, #5
AdcZero: ; ADC is zero, clear down flag, 13 cycles, #18
cbr rFlag,1<<bDwn ; Clear down flag, +1=14, #19
AdcClrTop:
clr rTop ; +1=15, #20
ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS0); +1=16, #21
out ADCSRA,rimp ; Start next conversion, +1=17, #0
out SREG,rSreg ; Restore SREG, +1=18, #1
reti ; +4=22, #5
;
; **********************************
; M A I N P R O G R A M I N I T
; **********************************
;
Main:
ldi rmp,High(RAMEND) ; Init MSB stack
out SPH,rmp ; Init MSB stack pointer
ldi rmp,Low(RAMEND) ; Init LSB stack
out SPL,rmp ; Init LSB stack pointer
; Set clock speed to 2 MHz
ldi rmp,1<<CLKPCE ; Clock prescaler enable
out CLKPR,rmp
ldi rmp,1<<CLKPS1 ; Clock division factor 4
out CLKPR,rmp
; Init red LED port pin
sbi pLedD,bLedRCD ; Red led cathode is output
sbi pLedO,bLedRCO ; Red led cathode off
; Init red/green Duo-LED
sbi pLedD,bLedRGRAD ; Red/green red anode is output
sbi pLedD,bLedRGGAD ; Red/green green anode is output
cbi pLedO,bLedRGRAO ; Red/green red anode output is low
cbi pLedO,bLedRGGAO ; Red/green green anode output is low
.if debug_ledsRed == 1 ; Test the two red LEDs
cbi pLedO,bLedRCO ; Red led cathode on
sbi pLedO,bLedRGRAO ; Red anode high
cbi pLedO,bLedRGGAO ; Green anode low
test_leds_Red_Loop:
rjmp test_leds_Red_Loop ; Loop forever
.endif
.if debug_ledGreen == 1 ; Test the green LED
sbi pLedO,bLedRGGAO ; Green anode high
cbi pLedO,bLedRGRAO ; Red anode low
test_led_green_loop:
rjmp test_led_green_loop ; Loop forever
.endif
; Init speaker port
sbi pSpkD,bSpkD ; Speaker portpin is output
sbi pSpkO,bSpkO ; Speaker output portpin is high, red LED off
.if debug_speaker == 1
ldi rmp,clock / 8 / 2000 ; Compare match A to 1000 Hz
out OCR0A,rmp
ldi rmp,(1<<COM0A0)|(1<<WGM01) ; CTC on OCR0A, Toggle OC0A
out TCCR0A,rmp
ldi rmp,(1<<CS01) ; Prescaler = 8
out TCCR0B,rmp
test_speaker_loop:
rjmp test_speaker_loop ; Loop forever
.endif
; Switch off pin drivers for ADC2 and ADC3
ldi rmp,(1<<ADC2D)|(1<<ADC3D)
out DIDR0,rmp
; Init the ADC and Timer to start measurements
rcall InitAdcTimer
; Interrupts and sleep mode idle
ldi rmp,1<<SE ; Sleep mode idle
out MCUCR,rmp
sei ; Enable interrupts
;
; **********************************
; P R O G R A M L O O P
; **********************************
;
Loop:
sleep ; go to sleep
nop ; dummy after wakeup
sbrc rFlag,bAlrm ; Active alarm?
rcall Alarm
sbrc rFlag,bDsc ; Update flag set?
rcall Update ; Yes, handle update
rjmp loop
;
; Active alarm
Alarm:
tst rAlrmC ; Alarm counter at zero?
brne AlarmRet ; No, continue
ldi rmp,0 ; Clear the buffer
ldi ZH,High(sBuffer)
ldi ZL,Low(sBuffer)
Alarm1:
st Z+,rmp
cpi ZH,High(sBufferEnd)
brcs Alarm1
cpi ZL,Low(sBufferEnd)
brcs Alarm1
ldi rmp,0xFF ; Disable alarm for next round
mov rTrigger,rmp
cbr rFlag,1<<bAlrm ; Clear alarm flag
rcall InitAdcTimer ; Restart timers and ADC
AlarmRet:
ret
;
; Handle data table update
Update:
cbr rFlag,1<<bDsc ; Clear flag
.if (debug_eep == 1)||(debug_eep_raw == 1)
clr rmp ; Disable ADC, timers and interrupts
out ADCSRA,rmp
out TCCR0A,rmp
out TCCR0B,rmp
out TCCR1,rmp
out GTCCR,rmp
out TIMSK,rmp ; Disable timer interrupts
.endif
; Calculate average, maximum and trigger
; level exceedences in SRAM buffer
ldi XH,High(sBuffer+2) ; Point to first top value
ldi XL,Low(sBuffer+2)
clr ZH ; Clear the sum
clr ZL
clr rMaxA ; Clear maximum
clr R1 ; Clear MSB adder
clr R2 ; Number of exceedences
ldi rmp,64
UpDate1:
ld R0,X+ ; Read data byte from buffer
cp R0,rMaxA ; Compare data with maximum
brcs UpDate2
mov rMaxA,R0
Update2:
cp R0,rTrigger ; Compare with trigger value
brcs UpDate3
inc R2
UpDate3:
add ZL,R0 ; Add to LSB
adc ZH,R1 ; Add to MSB if carry
.if tstamp == 1
adiw XL,2 ; Point on next data byte
.endif
dec rmp
brne Update1
lsl ZL ; Multiply by 2
rol ZH
lsl ZL
rol ZH
mov rTrigger,ZH ; Copy MSB
out OCR1A,ZH ; to TC1 compare A
mov ZL,ZH
lsr ZL ; Div by 2
add rTrigger,ZL ; Add to MSB
brcc UpDate4 ; No overflow
ldi ZL,0xFF ; Set trigger level to max
mov rTrigger,ZL ; And to trigger
UpDate4:
.if (debug_eep==0)&&(debug_eep_raw==0) ; Debug switches clear
mov rmp,rTrigger ; Copy trigger value
cpi rmp,0xFF ; On its maximum
breq UpDate5 ; Do not alarm
ldi rmp,cTrigger ; Number of exceedences
cp R2,rmp ; Exceedences of trigger level
brcs UpDate5 ; Smaller, do not alarm
; Alarm level reached, start alarm phase
cbi ADCSRA,ADIE ; Disable ADC interrupts
ldi rmp,cToneC ; Load alarm tone frequency
out OCR0A,rmp ; To TC0
ldi rmp,(1<<COM0A0)|(1<<WGM01) ; CTC, toggle OC0A
out TCCR0A,rmp ; To TC0 control A
ldi rmp,(1<<CS01)|(1<<CS00) ; Prescaler =64
out TCCR0B,rmp ; To TC0 control B
ldi rAlrmC,cAlrmC ; Set alarm counter value
ldi rmp,1<<TOIE1 ; Interrupts TC1 only
out TIMSK,rmp ; To timer int mask
sbi pLedO,bLedRGRAO ; Set RG red anode high
cbi pLedO,bLedRGGAO ; RG green anode low
sbr rFlag,1<<bAlrm ; Alarm flag
Update5:
ret ; End of handling updates
.else
; Write the content of the SRAM to EEPROM
Write2Eep:
st X,rTrigger
.if tstmp == 0
adiw XL,2
.endif
st -X,rMaxA ; Store maximum
st -X,ZH ; Store average
clr rmp ; Disable ADC and interrupts
out ADCSRA,rmp
out TIMSK,rmp ; Disable timer interrupts
cli ; No interrupts any more
EepWait:
sbic EECR,EEPE ; Wait until EEPROM ready
rjmp EepWait
ldi rmp,0 ; Set EEPROM atomic erase/write
out EECR,rmp
ldi XH,0 ; Point X to EEPROM
ldi XL,0
ldi ZH,HIGH(sBuffer) ; Point Z to buffer
ldi ZL,Low(sBuffer)
EepWr:
sbic EECR,EEPE
rjmp EepWr
out EEARH,XH ; Set EEPROM address MSB
out EEARL,XL ; Set LSB address
ld rmp,Z+ ; Read data byte from SRAM
out EEDR,rmp ; Set EEPROM data
sbi EECR,EEMPE ; Program enable
sbi EECR,EEPE ; Program EEPROM
adiw XL,1 ; Next address
cpi ZH,High(sBufferEnd)
brne EepWr
cpi ZL,Low(sBufferEnd)
brne EepWr
EepLedOn:
cbi pLedO,bLedRCO ; Red led on
EepLoop:
rjmp EepLoop ; Indefinite loop
.endif
;
; Init the ADC and the timer for normal operation
; during startup and following alarm condition
InitAdcTimer:
; Init ADC
ldi YH,High(sBuffer) ; Y Pointer to SRAM buffer start
ldi YL,Low(sBuffer)
ldi rDscC,nDataSets
; Internal Reference 1.1V, ADLAR, Differential ADC2-ADC3, gain=20
ldi rmp,(1<<REFS1)|(1<<ADLAR)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0)
out ADMUX,rmp
; Enable, Start conversion, int enable, clock/2
ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS0)
out ADCSRA,rmp
; Start TC0 as 8-bit time base with a prescaler of 256
ldi rmp,(1<<COM0A1)|(1<<COM0A0); 8-bit counter, clr OC1A on match, mode normal
out TCCR0A,rmp
ldi rmp,1<<CS02 ; Prescaler = 256
out TCCR0B,rmp
; Start TC1 as inverted PWM with a prescaler of 128
ldi rmp,0x00 ; Compare match very short
out OCR1A,rmp
ldi rmp,0xFF ; Count to 256
out OCR1C,rmp
ldi rmp,(1<<COM1A1)|(1<<CS13)|(1<<PWM1A) ; PWM signal OC1A, prescaler 128
out TCCR1,rmp
clr rmp ; No PWM signal on OC1B
out GTCCR,rmp
ldi rmp,1<<TOIE0 ; Overflow interrupt TC0 only
out TIMSK,rmp
cbi pLedO,bLedRGRAO ; Red anode low
sbi pLedO,bLedRGGAO ; Green anode high
ret
;
; End of source code
;