Path: Home => AVR-EN => Applications => Long-timer tn25 => Source code   Diese Seite in Deutsch: Flag DE Logo
Long-time timer with an ATtiny25 AVR applications

Long timer with ATtiny25
Assembler source code

Assembler source code for the long-time timer with ATtiny25

Long-time timer with ATtiny25 The source code in assembler format is here, the associated gamut include file here.

;
; *******************************************
; * Long time timer with LED and beeps      *
; * Written for an ATtiny25, V1, March 2021 *
; * (C)2021 by http://avr-asm-tutorial.net  *
; *******************************************
;
.nolist
.include "tn25def.inc" ; Define device ATtiny25
.list
;
; Task: Timer with adjustable interval time
;       LED is red over a longer time and
;       green over a shorter time (1/16 th
;       of the interval time
;       Tone generation with two tones of
;       programmable frequency, select from
;       a gamut table or input frequencies
;       in mHz
;
; **********************************
;    D E B U G   H A R D W A R E
; **********************************
;
; Debug the Duo-LED
.equ cDebugLed = 0 ; 1 = Blink the LED
;
; Debug the speaker output
.equ cDebugSpk = 0 ; 1 = On, 0 = Normal
;
; Debug the ADC
.equ cDebugAdc = 0 ; 1 = Debug, 0 = Normal
; 
; Debug the jumper input pin PA2
.equ cDebugPA2 = 0 ; 1 = Debug, 0 = Normal
;
.equ cDebug =(cDebugLed+cDebugSpk+cDebugAdc+cDebugPA2)>0
;
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Device: ATtiny25, Package: 8-pin-PDIP_SOIC
;
;            _________
;         1 /         |8
; Reset o--|RESET  VCC|--o + Vop
;  Poti o--|ADC3   PB2|--o J1
;   SPK o--|OC1B   PB1|--o LED red anode, MISO
;  -Vop o--|GND    PB0|--o LED green anode, MOSI
;        4 |__________|5
;
; **********************************
;  P O R T S   A N D   P I N S
; **********************************
;
.equ pO = PORTB ; Output port
.equ pD = DDRB ; Direction port
.equ pI = PINB ; Input port
.equ bAnRdO = PORTB1
.equ bAnRdD = DDB1
.equ bAnGnO = PORTB0
.equ bAnGnD = DDB0
.equ bSpkO = PORTB4
.equ bSpkD = DDB4
.equ bJ1O = PORTB2
.equ bJ1D = DDB2
.equ bJ1I = PINB2
;
; **********************************
;   A D J U S T A B L E   C O N S T
; **********************************
;
; If you want to use gamut frequencies
.include "gamut.inc" ; Included file
;
; Default clock frequency
.equ clock = 1000000 ; Frequency in Hz
;
; Prescaler for TC0 PWM
.equ cTc0Presc = 64 ; Can be 1, 8, 64, 256 or 1,024
;
; (Frequencies see the gamut frequency table at the end)
.equ fG2R = fA4 ; Beep frequency green to red = A7
.equ fR2G = fA6 ; ; Beep frequency red to green = A8
;
; cDurMax selects the maximum duration in hours
.equ cDurMax = 2 ; Two hours by default
.if (cDurMax == 0)||(cDurMax>64)
  .error "cDurMax out of range, can be 1 to 64!"
  .endif 
;
; Duration of tones
.equ cDur = 750 ; Duration in ms
;
; **********************************
;  F I X  &  D E R I V.  C O N S T
; **********************************
;
; Convert the prescaler value
.if cTc0Presc == 1
  .message "Prescaler=1, jumper 1 does not work!"
  .equ cTc0Cs = 1<<CS00
  .else
  .if cTc0Presc == 8
    .message "Prescaler=8: jumper 1 accelerates by 8!"
	.equ cTc0Cs = 1<<CS01
	.else
	.if cTc0Presc == 64
	  .message "Prescaler=64: jumper 1 accelerates by 64!"
	  .equ cTc0Cs = (1<<CS01)|(1<<CS00)
	  .else
      .if cTc0Presc == 256
	    .message "Prescaler=256: jumper 1 accelerates by 256!"
		.equ cTc0Cs = 1<<CS02
		.else
        .if cTc0Presc == 1024
		   .message "Prescaler=1,024: jumper 1 accelerates by 1,024!"
		   .equ cTc0Cs = (1<<CS02)|(1<<CS00)
		   .else
		   .error "Wrong prescaler value in cTc0Presc!"
		   .endif
		.endif
	  .endif
	.endif
  .endif 
; PWM characteristica
.equ fPwm = ((clock + 128) / 256 + cTc0Presc/2) / cTc0Presc ; Frequency PWM in Hz
.equ tPwm = (1000000*256*cTc0Presc+clock/2)/clock ; PWM cycle duration in ms
.equ fColorChange = 1000*fPwm / 256 ; Color change frequency in mHz
.equ tColorChange = (256 * tPwm +500) / 1000 ; Color change time in ms
;
; The green to red switching constants
.equ csG2R = LOG2((((1000*clock+fG2R/2)/fG2R)+128)/255)+1 ; CS bits
.equ cpG2R = EXP2(csG2R-1)
.equ cdG2R = (((1000*clock+fG2R/2)/fG2R+cpG2R/2)/cpG2R+1)/2-1 ; Divider
.equ cTc1G2R = csG2R | (1<<CTC1) ; TC1CR
.equ cDurG2R = (cDur*fG2R+250000)/500000 ; Duration
;
; The red to green switching constants
.equ csR2G = LOG2((((1000*clock+fR2G/2)/fR2G)+128)/255)+1 ; CS bits
.equ cpR2G = EXP2(csR2G-1) ; Prescaler value
.equ cdR2G = (((1000*clock+fR2G/2)/fR2G+cpR2G/2)/cpR2G+1)/2-1 ; Divider
.equ cTc1R2G = csR2G | (1<<CTC1)
.equ cDurR2G = (cDur*fR2G+250000)/500000 ; Duration
;
; Checks
.if (cdG2R>255)||(cdR2G>255)
  .error "Divider value too large, change frequency!"
  .endif
.if (cDurG2R>65535)||(cDurR2G>65535)
  .error "Durantion value too large, change frequency or duration"
  .endif
;
; **********************************
;       R E G I S T E R S
; **********************************
;
; free: R0 to R7
.def rDelayL = R8 ; Current delay, LSB
.def rDelayH = R9 ; dto., MSB
.def rRedL = R10 ; Red delay, LSB
.def rRedH = R11 ; dto., MSB
.def rGreenL = R12 ; Green delay, LSB
.def rGreenH = R13 ; dto., MSB
.def rAdcL = R14 ; ADC sum, LSB
.def rAdcH = R15 ; dto., MSB
.def rmp = R16 ; Define multipurpose register
.def rAdcCnt = R17 ; ADC counter
.def rCmp = R18 ; PWM compare value
; free: R19 to R23
.def rCntL = R24 ; Phase counter, LSB
.def rCntH = R25 ; dto., MSB
; used: X = R27:R26 as halfwave counter for tone generation
; used: R31:R30 = Z for ...
;
; **********************************
;           S R A M
; **********************************
;
.dseg
.org SRAM_START
; (No SRAM used)
;
; **********************************
;         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, not used
	rjmp PcioIsr ; PCI0
	reti ; OC1A, not used
	reti ; OVF1, not used
	rjmp Tc0OvfIsr ; OVF0
	reti ; ERDY, not used
	reti ; ACI, not used
	rjmp AdcIsr ; ADCC
	rjmp Oc1bIsr ; OC1B
	reti ; OC0A, not used
	reti ; OC0B, not used
	reti ; WDT, not used
	reti ; USI_START, not used
	reti ; USI_OVF, not used
;
; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************
;
; PCINT jumper changed, re-load prescaler
PcioIsr:
  sbic pI,bJ1I ; Jumper on?
  rjmp PcioIsr1 ; No
  ldi rmp,1<<CS00 ; Load fast prescaler
  out TCCR0B,rmp
  reti
PcioIsr1:
  ldi rmp,cTc0Cs ; Load default prescaler
  out TCCR0B,rmp
  reti
;
; Overflow TC0 interrupt service routine
Tc0OvfIsr:
  sbiw rCntL,1 ; Count down
  brne Tc0OvfIsrRet ; Time not yet over
  ; Check if last period was processed
  inc rCmp ; Next color intensity
  out OCR0A,rCmp ; To compare A
  out OCR0B,rCmp ; And to compare B
  brne Tc0OvfIsrSetCnt ; Not at zero, restart counter
  brts Tc0OvfIsrG2Ra ; Switch from red to green
  ldi rmp,(1<<COM0B1)|(1<<COM0B0)|(1<<WGM01)|(1<<WGM00) ; Fast PWM, OC0B inverted
  out TCCR0A,rmp ; To TC0 control register A
  set ; Next phase: switch from red to green
  mov rDelayH,rRedH ; Load red counter value to delay, MSB
  mov rDelayL,rRedL ; dto., LSB
  cbi pO,bAnGnO ; Green anode to low
  ldi XH,High(cDurG2R) ; Load duration, MSB
  ldi XL,Low(cDurG2R) ; dto., LSB
  ldi rmp,cdG2R ; Green to red tone
  out OCR1C,rmp
  ldi rmp,1<<COM1B0 ; Toggle
  out GTCCR,rmp
  ldi rmp,cTc1G2R ; TC0 prescaler
  out TCCR1,rmp ; Set prescaler
  in rmp,TIMSK ; Read interrupt mask
  sbr rmp,1<<OCIE1B ; OC interrupt enable
  out TIMSK,rmp 
  rjmp Tc0OvfIsrSetCnt ; Set counter value
Tc0OvfIsrG2Ra:
  ; Switch from red to green
  ldi rmp,(1<<COM0A1)|(1<<WGM01)|(1<<WGM00) ; Fast PWM, OC0A non-inverted
  out TCCR0A,rmp ; To TC0 control register A
  clt ; Next phase: switch to red
  mov rDelayH,rGreenH ; Load green delay, MSB
  mov rDelayL,rGreenL ; dto., LSB
  cbi pO,bAnRdO ; Red anode to low
  ldi XH,High(cDurR2G) ; Load duration, MSB
  ldi XL,Low(cDurR2G) ; dto., LSB
  ldi rmp,cdR2G ; red to green tone
  out OCR1C,rmp
  ldi rmp,1<<COM1B0 ; Toggle
  out GTCCR,rmp
  ldi rmp,cTc1R2G ; Default TC0 prescaler
  out TCCR1,rmp ; Set prescaler
  in rmp,TIMSK ; Read interrupt mask
  sbr rmp,1<<OCIE1B ; OC interrupt enable
  out TIMSK,rmp 
Tc0OvfIsrSetCnt:
  mov rCntH,rDelayH ; Restart down-counter, MSB
  mov rCntL,rDelayL ; dto., LSB
Tc0OvfIsrRet:
  reti
;
; ADC interrupt service routine
AdcIsr:
  in rmp,ADCL
  add rAdcL,rmp
  in rmp,ADCH
  adc rAdcH,rmp
  dec rAdcCnt
  brne AdcIsrRet
  ldi rAdcCnt,cDurMax ; Restart counter
  mov rRedH,rAdcH ; Copy sum of measurements to red, MSB
  mov rRedL,rAdcL ; dto., LSB
  mov rGreenH,rAdcH ; Sum also to green 
  mov rGreenL,rAdcL
  clr rAdcH ; Restart sum, MSB
  clr rAdcL ; dto., LSB
  lsr rGreenH ; Divide by 2, MSB
  ror rGreenL ; dto., LSB
  lsr rGreenH ; Divide by 4, MSB
  ror rGreenL ; dto., LSB
  lsr rGreenH ; Divide by 8, MSB
  ror rGreenL ; dto., LSB
  lsr rGreenH ; Divide by 16, MSB
  ror rGreenL ; dto., LSB
  mov rmp,rGreenL
  or rmp,rGreenH
  brne AdcIsr1
  clr rGreenL
  inc rGreenL
  clr rGreenH
AdcIsr1:
  sub rRedL,rGreenL ; Subtract green from red, LSB
  sbc rRedH,rGreenH ; dto., MSB
  brcs AdcIsr2
  mov rmp,rRedL
  or rmp,rRedH
  brne AdcIsr3
AdcIsr2:
  clr rRedL ; Set red to one
  inc rRedL
  clr rRedH
AdcIsr3:
  in rmp,TIMSK ; Read interrupt mask
  sbrc rmp,TOIE0 ; Interrupts off?
  rjmp AdcIsrRet
  brtc AdcIsrSet
  clr rCntH ; provoke switching, MSB = 0
  ldi rCntL,1 ; LSB = 1
  ldi rCmp,0xFF ; Compare to max
  sbr rmp,1<<TOIE0 ; OC interrupt enable on
  out TIMSK,rmp
  clt ; Start with green to red
  reti
AdcIsrSet:
  set ; Wait for second set
AdcIsrRet:
  reti
;
; OC1B ISR, counts half waves and switches tone off
Oc1BIsr:
  sbiw XL,1 ; Count down half waves
  brne Oc1BIsrRet
  ldi rmp,1<<COM1B1 ; OCR1B to clear
  out GTCCR,rmp
  in rmp,TIMSK ; Read int masks
  cbr rmp,1<<OCIE1B ; Clear TC1 Compare int
  out TIMSK,rmp
Oc1BIsrRet:
  reti   
;
; **********************************
;  M A I N   P R O G R A M   I N I T
; **********************************
;
Main:
  ; Init ports
  sbi pD,bAnGnD ; Green LED output
  cbi pO,bAnGnO ; Green output clear
  sbi pD,bAnRdD ; Red LED output
  cbi pO,bAnRdO ; Red output clear
  sbi pD,bSpkD ; Speaker = output
  cbi pO,bSpkO ; Speaker output clear
  cbi pD,bJ1D ; Jumper as input
  sbi pO,bJ1O ; Jumper pull-up high 
.if cDebug
  rjmp Debug
  .endif
.ifdef SPH ; If tiny85 at work
    ldi rmp,High(RAMEND) ; Stack MSB
    out SPH,rmp
  .endif
  ldi rmp,Low(RAMEND)
  out SPL,rmp ; Init LSB stack pointer
  ; Start TC1 as tone generator
  ldi XH,High(cDurG2R) ; Load duration, MSB
  ldi XL,Low(cDurG2R) ; dto., LSB
  ldi rmp,cdG2R ; Green to red tone
  out OCR1C,rmp
  ldi rmp,1<<COM1B0 ; Toggle
  out GTCCR,rmp
  ldi rmp,cTc1G2R ; Default TC0 prescaler
  out TCCR1,rmp ; Set prescaler
  in rmp,TIMSK ; Read interrupt mask
  sbr rmp,1<<OCIE1B ; OC interrupt enable
  out TIMSK,rmp 
  ; Start TC0 as LED timer in fast PWM mode
  clt ; Start with ignoring the first ADC set
  ldi rCmp,0xFF ; Start with the highest PWM stage
  out OCR0A,rCmp ; To compare A
  out OCR0B,rCmp ; and to compare B
  ldi rmp,(1<<WGM01)|(1<<WGM00) ; Fast PWM, OC outputs off
  out TCCR0A,rmp ; To TC0 control port A
  ldi rmp,cTc0Cs ; Load prescaler
  sbis pI,bJ1I ; Jumper 1 closed?
  ldi rmp,1<<CS00 ; Yes, accelerate
  out TCCR0B,rmp ; To TC0 control port B
  ; Do not enable interrupt yet, interrupts are switched
  ; on when the ADC has read enough values
  ; Start ADC
  ldi rAdcCnt,cDurMax ; Start value ADC measurements
  clr rAdcH ; ADC sum = 0, MSB
  clr rAdcL ; dto., LSB
  ldi rmp,(1<<MUX1)|(1<<MUX0) ; Mux to ADC3 channel
  out ADMUX,rmp
  ldi rmp,1<<ADTS2 ; TC0 overflow as trigger
  out ADCSRB,rmp
  ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  out ADCSRA,rmp
  ; Enable PCINT on PA2 pin
  ldi rmp,1<<PCINT2 ; Pin2 mask bit
  out PCMSK,rmp
  ldi rmp,1<<PCIE ; Enable PCI interrupt
  out GIMSK,rmp
  ; Sleep mode idle
  ldi rmp,1<<SE ; Sleep enable
  out MCUCR,rmp
  ; Interrupt enable
  sei ; Enable interrupts
;
; **********************************
;    P R O G R A M   L O O P
; **********************************
;
Loop:
  sleep
  rjmp loop ; On wake-up: sleep again
;
; **********************************
;    H A R D W A R E   D E B U G
; **********************************
;
; All hardware debugging routines here
Debug:
.if cDebugLed == 1
  DebugLed:
   sbi pO,bAnRdO
    cbi pO,bAnGnO
  DebugLed1:
    sbiw ZL,1
    brne DebugLed1
    cbi pO,bAnRdO
    sbi pO,bAnGnO
  DebugLed2:
    sbiw ZL,1
    brne DebugLed2
    rjmp DebugLed
  .endif
.if cDebugSpk == 1  ;
    ldi rmp,200
    out OCR1C,rmp
    ldi rmp,1<<COM1B0 ; Toggle
    out GTCCR,rmp
    ldi rmp,(1<<CS12)|(1<<CTC1)
    out TCCR1,rmp
  DebugSpk:
    rjmp DebugSpk
  .endif
.if cDebugAdc == 1
  test:
	ldi XL,(1<<bAnGnO)|(1<<bJ1O)
    out pO,XL
    ldi rmp,(1<<MUX1)|(1<<MUX0)
	out ADMUX,rmp
	ldi rmp,0
	out ADCSRB,rmp
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp
  DebugAdc:
    sbic ADCSRA,ADSC
	rjmp DebugAdc1
	in ZL,ADCL
	in ZH,ADCH
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp
	lsr ZH
	ror ZL
	ldi XL,(1<<bAnGnO)|(1<<bJ1O)
	add XL,ZH
  DebugAdc1:
    out pO,XL
	clr rmp
  DebugAdc2:
    cp rmp,ZL
	brne DebugAdc3
	ldi XH,4
	out pO,XH
  DebugAdc3:
    inc rmp
	breq DebugAdc
	rjmp DebugAdc2
  .endif
.if cDebugPA2 == 1
  DebugPA2:
    sbic pI,bJ1I
    rjmp DebugPA2a
	sbi pO,bAnRdO
	cbi pO,bAnGnO
	rjmp DebugPA2
  DebugPA2a:
	cbi pO,bAnRdO
	sbi pO,bAnGnO
	rjmp DebugPA2
  .endif
DebugLoop:
  rjmp DebugLoop
;
; End of source code
;
; Copyright information.
 .db "(C)2021 by Gerhard Schmidt  " ; Source code readable
 .db "C(2)20 1ybG reahdrS hcimtd  " ; Machine code format
;



Page top


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