Pfad: Home => AVR-Übersicht => Anwendungen => Timer2313 => Assembler-Sourcecode
Timer mit tiny2313

Assembler-Software Timer ATtiny2313/Siebensegmentanzeige


;
; ********************************************
; * ATtiny2313 timer with 7-segment display  *
; * Muxed four digit output, digikey op      *
; * (C)2012 by avr-asm-tutorial.net          *
; ********************************************
;
; Included header file for target AVR type ATtiny2313
.NOLIST
.INCLUDE "tn2313def.inc" ; Header for AT90S2313
.LIST
;
; ============================================
;   H A R D W A R E   I N F O R M A T I O N
; ============================================
;
;                  _____________
;                 /             |
;    Reset    o--|              |--รถ + 3 V
; Key 1 Dwn   o--|              |--o LEDs active low
; Key 2 Up    o--|              |--o Segment g
; S2A Cnt-mod o--|     A T      |--o Segment f
;    Output   o--|              |--o Segment e
; Key 3 Go    o--|   t i n y    |--o Segment d
; Anode Dig1  o--|              |--o Segment c
; Anode Dig2  o--|   2 3 1 3    |--o Segment b
; Anode Dig3  o--|              |--o Segment a
;        GND  o--|              |--o Anode Dig4
;                |______________|
;
; =========================
;  H O W   I T   W O R K S
; =========================
;
; Description:
;   This provides a timer with a four digit 7-segment
;   display. It can count down or up, depending from
;   the switch 2A. During active counting, the two
;   LEDs in between the minutes and seconds blinks
;   fast and the output pin is active high.
;
; Internal Functioning
; 0: Processor
;    Runs with internal RC oscillator at default,
;    clock rate 1 Mcs/s
;
; 1. 16-bit-Timer 1
;    Runs with internal clock, prescaled by 8, and
;    in CTC mode, with ICR1 set to 625, so an
;    interrupt occurs every 0.005 seconds to drive
;    the mux of the four digits with 50 Hz.
;    The timer sets the f5ms flag. If a second is
;    over, the timer sets the f1s flag additionally.
;
; 2. Flags
;    2a) 5 ms flag:
;        Inactive mode (counter is not started):
;        If set following a timer int, the routine
;        scans the key inputs. If one of the keys is
;        pressed, the respective routine is called:
;        - Key Down pressed: this reduces the alarm
;          time by 5 seconds. The new alarm time is
;          written to the EEPROM.
;        - Key Up pressed: This increases the alarm
;          time by 5 seconds. The new alarm time is
;          written to the EEPROM.
;        - Key Go pressed: This starts the timer. If
;          counting down is selected (switch 2A off)
;          the time is set to the alarm time. If up-
;          counting is selected (switch 2A on) the
;          time is cleared. The output pin A0 is set
;          to high, the double-LED in the middle of
;          the displays blinks. Each second pulse
;          advances the time count. If the time reaches
;          zero (down counting) resp. the alarm time (up
;          counting) the output pin A0 is switched off.
;        Active mode (counter is started):
;        - Key Down pressed: The elaspsed time is
;          reduced by 5.
;        - Key Up pressed: The elapsed time is advanced
;          by 5.
;        - Key Go pressed: The counter restarts.
;        Any active key blocks further key processing
;        until none of the keys is pressed for at least
;        20 ms duration.
;     2b) 1 s flag:
;         If not activated, the inactive time counter
;         is decreased. If it reaches zero (after 20
;         seconds) the digit displays are switched off
;         and the two LEDs in the middle are switched
;         to 1/4 to save power.
;         If activated: The time elapsed is advanced
;         by one and is compared with the alarm time.
;         If the alarm time is reached the output pin
;         is switched off (zero).
;
; ============================================
;      P O R T S   A N D   P I N S
; ============================================
;
.EQU ppSegment = PORTB ; segments output port
.EQU pdSegment = DDRB ; segments direction port
.EQU ppAnodes = PORTD ; anode driver port
.EQU pdAnodes = DDRD ; anode driver and key inputs direction
.EQU piKeys = PIND ; key input port
.EQU biKey1 = 0 ; key 1 input bit
.EQU biKey2 = 1 ; key 2 input bit
.EQU biKey3 = 2 ; key 3 input bit
.EQU ppOutput = PORTA ; output port
.EQU pdOutput = DDRA ; direction output port
.EQU boOutput = 0 ; output bit
.EQU piJumper = PINA ; jumper input port
.EQU biJumper = 1 ; jumper input bit
;
; ============================================
;    C O N S T A N T S   T O   C H A N G E
; ============================================
;
.EQU cMux = 50 ; MUX frequency for all four digits
;
; ============================================
;  F I X + D E R I V E D   C O N S T A N T S
; ============================================
;
.EQU cClock = 1000000 ; processor clock
.EQU cPresc = 8 ; prescaler TC1
.EQU cTc1Clk = cClock / cPresc ; TC1 clock
.EQU cCtc = cTc1Clk / cMux / 4 ; CTC TC1 value
.EQU c1s = 4 * cMux
;
; ============================================
;   R E G I S T E R   D E F I N I T I O N S
; ============================================
;
; used: R0..R1 for 7-segment conversion
; used: R2..R9 as MUX sequence storage
; free: R10..R12
.DEF rDOff = R13 ; downcounter to switch display off
.DEF rDisp = R14 ; counter to display alarm time instead of time
.DEF rSreg = R15 ; Save SREG register
.DEF rmp = R16 ; Multipurpose register
.DEF rimp = R17 ; Multi purpose inside interrupts
.DEF rFlg = R18 ; Flag register
  .EQU f5ms = 0 ; 5 ms over flag
  .EQU f1s = 1 ; 1 second over flag
  .EQU fAct = 2 ; count and output is active
  .EQU fKey = 3 ; key blocked
.DEF rTimeL = R19 ; time counter
.DEF rTimeH = R20 ;
.DEF rAlrmL = R21 ; alarm time storage
.DEF rAlrmH = R22
.DEF rKey = R23 ; for key counting
.DEF rC1sL = R24 ; downcounter for seconds
.DEF rC1sH = R25
; used: X (XH:XL) as mux pointer to SRAM
; used; Y (YH:YL) for conversion to 7-segment
; used: Z (ZH:ZL) multipurpose outside ints
;
; ============================================
;       S R A M   D E F I N I T I O N S
; ============================================
;
; (none, SRAM only used for stack operations)
;
; ============================================
;   R E S E T   A N D   I N T   V E C T O R S
; ============================================
;
.CSEG
.ORG $0000
	rjmp Main ; Reset vector
	reti ; INT0 vector
	reti ; INT1 vector
	rjmp Tc1CaptIsr ; TC1 capture vector
	reti ; TC1COMPA vector
	reti ; TC1OVF vector
	reti ; TC0OVF vector
	reti ; USART-RX vector
	reti ; USART-UDRE vector
	reti ; USART-TX vector
	reti ; ANACOMP vector
	reti ; PCINT vector
	reti ; TC1COMPB vector
	reti ; TC0COMPA vector
	reti ; TC0COMPB vector
	reti ; USI-START vector
	reti ; USI-OVF vector
	reti ; EE-READY vector
	reti ; WDT-OVF vector
;
; ============================================
;     I N T E R R U P T   S E R V I C E S
; ============================================
;
; TC1 ICF Isr (MUX and timing)
Tc1CaptIsr:
	in rSreg,SREG ; save SREG
	ldi rimp,0b01111111 ; clear anode drivers
	out ppAnodes,rimp
	ld rimp,X+ ; read next mux bytes
	bst rC1sL,5 ; copy bit 7 of second counter to T
	sbrc rFlg,fAct
	bld rimp,7 ; copy T flag to bit 7 to blink
	out ppSegment,rimp ; output to segments
	ld rimp,X+ ; read next anode driver
	out ppAnodes,rimp ; set anode drivers
	sbr rFlg,1<<f5ms ; set 5-ms-flag
	cpi XL,10 ; End of mux field in register?
	brcs Tc1CaptIsr1 ; no, go on
	ldi XL,2 ; start new
Tc1CaptIsr1:
	sbiw rC1sL,1 ; count til end of second
	brne Tc1CaptIsr2 ; not end of second
	ldi rC1sH,HIGH(c1s) ; start counter new
	ldi rC1sL,LOW(c1s)
	sbr rFlg,1<<f1s ; set 1-s-flag
Tc1CaptIsr2:
	out SREG,rSreg ; restore SREG
	reti ; return
;
; ============================================
;     M A I N    P R O G R A M    I N I T
; ============================================
;
Main:
; Init stack
	ldi rmp, LOW(RAMEND) ; Init LSB stack
	out SPL,rmp
; Init segment driver port
	ldi rmp,0xFF ; Direction segment driver port
	out pdSegment,rmp ; set all port bits to 1
	out pdSegment,rmp ; set all port bits output
; Init anode driver port
	ldi rmp,0b01111000 ; set anode driver bits to be output
	out pdAnodes,rmp
	ldi rmp,0b01111111 ; set anode drivers off and inputs high
	out ppAnodes,rmp
; Init switch and output port
	sbi pdOutput,boOutput
	sbi ppOutput,biJumper
; Read stored time from EEPROM
  	ldi rmp,0 ; set address
	out EEAR,rmp ; to EEPROM
  	ldi rmp,1<<EERE ; set read enable
	out EECR,rmp
	in rAlrmL,EEDR ; read data
	ldi rmp,1 ; set address
	out EEAR,rmp ; to EEPROM
	ldi rmp,1<<EERE ; set read enable
	out EECR,rmp
	in rAlrmH,EEDR ; read data
; inactivate display off time
	ldi rmp,20
	mov rDOff,rmp
; clear flag register
	clr rFlg
; restart the counter
	clr rDisp ; show display time for 2 sec
	inc rDisp
	inc rDisp
	rcall Restart
; X is mux position counter
	ldi XH,HIGH(2)
	ldi XL,LOW(2)
	ldi rC1sH,HIGH(c1s) ; start counter new
	ldi rC1sL,LOW(c1s)
; Init and start timer
	ldi rmp,HIGH(cCtc) ; set ICR1 register
	out ICR1H,rmp
	ldi rmp,LOW(cCtc)
	out ICR1L,rmp
	ldi rmp,0 ; set mode
	out TCCR1A,rmp
	ldi rmp,(1<<WGM12)|(1<<WGM13)|(1<<CS11) ; CTC, presc=8
	out TCCR1B,rmp
	ldi rmp,1<<ICIE1 ; enable ICI interrupt
	out TIMSK,rmp
; Start loop
	ldi rmp,1<<SE ; enable sleep
	out MCUCR,rmp
	sei ; enable interrupts
;
; ============================================
;         P R O G R A M    L O O P
; ============================================
;
Loop:
	sleep ; go to sleep
	nop ; dummy for wake up
	sbrc rFlg,f5ms
	rcall Flag5ms ; call 5-ms-flag
	sbrc rFlg,f1s
	rcall Flag1s ; call 1 second flag
	rjmp loop ; go back to loop
;
; Interrupt generated 5-ms time pulse
;
.EQU cKey = (1<<biKey1)|(1<<biKey2)|(1<<biKey3)
;
Flag5ms:
	cbr rFlg,1<<f5ms ; clear flag
	sbrc rFlg,fKey ; key is logged?
	rjmp Flag5ms1 ; yes
	in rmp,piKeys ; read keys
	andi rmp,cKey ; clear anything but key bits
	cpi rmp,cKey ; no key pressed?
	brne Flag5msKey ; key pressed
	ret
Flag5msKey:
	clr rKey ; set rKey to 20 ms
	inc rKey
	inc rKey
	inc rKey
	inc rKey
	sbr rFlg,1<<fKey ; set key logged
	sbrs rmp,biKey3 ; key 3 pressed?
	rjmp Flag5msKey3
	sbrc rFlg,fAct ; count active?
	rjmp Flag5msKeyAct
	sbrs rmp,biKey2 ; key 2 pressed?
	rjmp Flag5msKey2
	sbrs rmp,biKey1 ; key 1 pressed?
	rjmp Flag5msKey1
	cbr rFlg,1<<fKey ; clear key flag
	ret
Flag5msKeyAct: ; key during active
	sbrs rmp,biKey2 ; key 2 active?
	rjmp Flag5msKey2Act ; yes
	sbrs rmp,biKey1 ; key 1 active?
	rjmp Flag5msKey1Act ; yes
	ret
; Down key is pressed during inactive
Flag5msKey1:
	ldi rmp,5 ; add 5 seconds
	sub rAlrmL,rmp
	ldi rmp,0
	sbc rAlrmH,rmp
	brcc Flag5msUpdate
	ldi rAlrmL,5 ; underflow
	clr rAlrmH
Flag5msUpdate: ; Write to EEPROM
	sbic EECR,EEPE ; write clear?
	rjmp Flag5msUpdate
	ldi rmp,0 ; address to 0
	out EEAR,rmp
	out EEDR,rAlrmL ; data
	cli ; clear interrupt flag
	sbi EECR,EEMPE ; enable write
	sbi EECR,EEPE ; start write
	sei
Flag5msWriteMSB:
	sbic EECR,EEPE ; write clear?
	rjmp Flag5msWriteMsb
	ldi rmp,1 ; address = 1
	out EEAR,rmp
	out EEDR,rAlrmH ; data
	cli ; clear interrupt flag
	sbi EECR,EEMPE ; enable write
	sbi EECR,EEPE ; start write
	sei
	inc rDisp
	inc rDisp
	rjmp Restart
Flag5msKey2:
	ldi rmp,5
	add rAlrmL,rmp
	ldi rmp,0
	adc rAlrmH,rmp
	cpi rAlrmL,LOW(3599)
	brcs Flag5msUpdate
	cpi rAlrmH,HIGH(3599)
	brcs Flag5msUpdate
	ldi rAlrmL,LOW(3599)
	ldi rAlrmH,HIGH(3599)
	rjmp Flag5msUpdate
Flag5msKey3:
	rcall Restart
	ldi rC1sH,HIGH(c1s) ; start counter new
	ldi rC1sL,LOW(c1s)
	sbr rFlg,1<<fAct ; set active flag
	sbi ppOutput,boOutput ; set output port pin high
	ret
Flag5msKey1Act:
	ldi rmp,5
	sub rTimeL,rmp
	ldi rmp,0
	sbc rTimeH,rmp
	brcc UpdateDisplay
	ldi rTimeL,1
	ldi rTimeH,0
	rjmp UpdateDisplay
Flag5msKey2Act:
	ldi rmp,5
	add rTimeL,rmp
	ldi rmp,0
	adc rTimeH,rmp
	rjmp UpdateDisplay
; Key is logged, wait until key is inactive for 20 ms
Flag5ms1:
	in rmp,piKeys ; read keys
	andi rmp,cKey ; clear anything but key bits
	cpi rmp,cKey ; no key pressed?
	brne Flag5msSetNew ; key still pressed
	dec rKey ; count 10 ms down
	brne Flag5msRet ; not zero
	cbr rFlg,1<<fKey ; clear key flag
	ret
Flag5msSetNew:
	ldi rmp,4
	mov rKey,rmp
Flag5msRet:
	ret
;
; Interrupt generated second pulse
;
Flag1s:
	cbr rFlg,1<<f1s ; clear flag
	sbrs rFlg,fAct ; counting active?
	rjmp Flag1sOff ; no, check display off
	sbis piJumper,biJumper ; count down?
	rjmp Flag1sUp ; count up
	subi rTimeL,1 ; decrease LSB counter
	brcc UpDateDisplay ; no underflow
	subi rTimeH,1 ; decrease MSB counter
	brcc UpDateDisplay
	mov rTimeL,rAlrmL ; restart timer
	mov rTimeH,rAlrmH
	cbr rFlg,1<<fAct ; clear active flag
	cbi ppOutput,boOutput ; clear output portbit
	rcall SetDisplayOffTime
	rjmp UpDateDisplay
Flag1sUp:
	inc rTimeL ; next second
	brne Flag1sEq
	inc rTimeH
Flag1sEq:
	cp rTimeL,rAlrmL ; compare with alarm time
	brne UpDateDisplay
	cp rTimeH,rAlrmH
	brne UpDateDisplay
	clr rTimeL ; restart new
	clr rTimeH
	cbr rFlg,1<<fAct ; clear active flag
	cbi ppOutput,boOutput ; clear output portbit
	rcall SetDisplayOffTime
UpdateDisplay:
	ldi YH,HIGH(2) ; Point to mux buffer
	ldi YL,LOW(2)
	tst rDisp ; show display?
	breq UpdateDisplayNormal
	dec rDisp
	mov R0,rAlrmL ; display Alarm time
	mov R1,rAlrmH
	rjmp UpdateDisplayCalc
UpDateDisplayNormal:
	mov R0,rTimeL ; LSB to R0
	mov R1,rTimeH ; MSB to R1
UpDateDisplayCalc:
	ldi ZH,HIGH(600)
	ldi ZL,LOW(600)
	rcall GetDigit2
	ldi ZH,HIGH(60)
	ldi ZL,LOW(60)
	rcall GetDigit2
	ldi ZL,10
	rcall GetDigit1
	rcall GetDigit
	ret
;
; Inactive, downcount display off
;
Flag1sOff:
	tst rDOff ; check at zero
	breq Flag1sOffRet ; already zero
	dec rDOff ; decrease wait time
	brne Flag1sOffRet
	ldi rmp,0x7F ; switch all displays dark
	mov R3,rmp ; switch anode drivers off
	mov R5,rmp
	mov R7,rmp
	mov R9,rmp
	mov R2,rmp ; switch cathode drivers off
	ldi rmp,0xFF ; switch middle LEDs dark
	mov R4,rmp
	mov R6,rmp
	mov R8,rmp
Flag1sOffRet:
	ret
;
; Set display off time to 20 seconds
;
SetDisplayOffTime:
	ldi rmp,20 ; 20 seconds to display off
	mov rDOff,rmp
	ret
;
; Convert two-byte digits
;   R1:R0 is two-byte number of seconds
;   ZH:ZL is subtractor (ZH:ZL = 600 or 60)
;
GetDigit2:
	clr rmp ; rmp is counter
GetDigit2Count:
	sub R0,ZL ; subtract LSB
	sbc R1,ZH ; subtract MSB
	brcs GetDigit2Ok
	inc rmp
	rjmp GetDigit2Count
GetDigit2Ok:
	add R0,ZL ; restore last
	adc R1,ZH
ConvertDigit:
	ldi ZH,HIGH(2*SegTab) ; point Z to 7-seg-table
	ldi ZL,LOW(2*SegTab)
	add ZL,rmp ; add digit number
	lpm rmp,Z ; read result from table
	st Y,rmp ; write to MUX register
	adiw YL,2 ; point to next display position
	ret
; convert second last digit (ZL = 10)
GetDigit1:
	clr rmp ; rmp is counter
GetDigit1Count:
	sub R0,ZL ; subtract number base
	brcs GetDigit1Ok
	inc rmp
	rjmp GetDigit1Count
GetDigit1Ok:
	add R0,ZL ; add digit base
	rjmp ConvertDigit
; convert the last digit
GetDigit:
	mov rmp,R0 ; copy last digit
	rjmp ConvertDigit
;
; Restart the counter
;
Restart:
	sbis piJumper,biJumper ; start up?
	rjmp RestartZero ; no, down
	mov rTimeL,rAlrmL ;restart time
	mov rTimeH,rAlrmH
	rjmp RestartMux
RestartZero:
	clr rTimeL ; clear value for upcounting
	clr rTimeH
; Convert time to buffer in SRAM
RestartMux:
	rcall UpdateDisplay ; convert to 7-seg
	ldi rmp,0b00111111 ; display Digit 1
	mov R9,rmp
	ldi rmp,0b01011111 ; display Digit 2
	mov R7,rmp
	ldi rmp,0b01101111 ; display Digit 3
	mov R5,rmp
	ldi rmp,0b01110111 ; display Digit 4
	mov R3,rmp
	ret
;
; Seven-Segment table
;
;     a     0: _gFEDCBA 01000000
;    ---    1: _gfedCBa 01111001
;  f/g /b   2: _GfEDcBA 00100100
;   ---     3: _GfeDCBA 00110000
; e/  /c    4: _GFedCBa 00011001
;  ---      5: _GFeDCbA 00010010
;   d       6: _GFEDCba 00000011
;           7: _gfedCBA 01111000
;           8: _GFEDCBA 00000000
;           9: _GFedCBA 00011000
SegTab:
.DW 0b0111100101000000 ; 1, 0
.DW 0b0011000000100100 ; 3, 2
.DW 0b0001001000011001 ; 5, 4
.DW 0b0111100000000011 ; 7, 6
.DW 0b0001100000000000 ; 9, 8
;
; EEPROM content for starting up
;
.EQU cAlrm = 1*60+30 ; startup at 01:30
.ESEG
.ORG 0x0000
.DB LOW(cAlrm), HIGH(cAlrm)
;
; End of source code
;


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