Path:AVR-EN => Applications => ATtiny45 audio sensor => Source code
Audio sensor small Applications of
AVR Single chip controllers AT90S, ATtiny, ATmega and ATxmega
Audio sensor with an ATtiny45
Assembler source code

Logo

Assembler source code for the audio sensor with an ATtiny45

The source code is here in asm format.

;
; ************************************
; * 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
;



Praise, error reports, scolding and spam please via the comment page to me. Spammers: please note that behind this is not a dump standard script but I personally select the entries to be published. So, even if you try it five times (some spammers indeed do that) it will fail and ads for pharmacy will not occur here.

©2018 by http://www.avr-asm-tutorial.net