630 lines
22 KiB
NASM
630 lines
22 KiB
NASM
; A digital 4x7 digit display witch keeps track of button presses
|
|
;
|
|
; 2020 R. Branten
|
|
; Written for use with the AtTiny26L-8pi
|
|
;
|
|
; Clock set to 1Mhz internal clock
|
|
|
|
.NOLIST
|
|
.include "tn26def.inc"
|
|
.LIST
|
|
|
|
;===============================================================================
|
|
; REGISTERS
|
|
;
|
|
; Register | Name | Function
|
|
;-----------|-------------------------------------------------------
|
|
; R1 | arithmicRegisterA | Calculations
|
|
; R2 | arithmicRegisterB | Calculations
|
|
; R3 | arithmicRegisterC | Calculations
|
|
; R4 | arithmicRegisterD | Calculations
|
|
; R16 | registerA | General purpose
|
|
; R17 | registerB | General purpose
|
|
; R18 | buttonPushRegister | Button push detection
|
|
; R19 | delayRegisterB | Delaying program execution
|
|
; R20 | digit1 | Storing ones of 7-segment
|
|
; R21 | digit2 | Storing tens of 7-segment
|
|
; R22 | digit3 | Storing hundreds of 7-segment
|
|
; R23 | digit4 | Storing thousands of 7-segment
|
|
; R24 | counterLow | LSB of counter
|
|
; R25 | counterHigh | MSB of counter
|
|
; R10 | incrementButtonStatus | Button debounce state for increment
|
|
; R11 | decrementButtonStatus | Button debounce state for decrement
|
|
;
|
|
.DEF registerA = R16
|
|
.DEF registerB = R17
|
|
.DEF arithmicRegisterA = R1
|
|
.DEF arithmicRegisterB = R2
|
|
.DEF arithmicRegisterC = R3
|
|
.DEF arithmicRegisterD = R4
|
|
.DEF buttonPushRegister = R18
|
|
.DEF counterHigh = R25
|
|
.DEF counterLow = R24
|
|
.DEF delayRegisterA = R18
|
|
.DEF delayRegisterB = R19
|
|
.DEF incrementButtonStatus = R10
|
|
.DEF decrementButtonStatus = R11
|
|
|
|
; Digit numbering
|
|
; +-----+ +-----+ +-----+ +-----+
|
|
; | | | | | | | |
|
|
; | 4 | | 3 | | 2 | | 1 |
|
|
; | R23 | | R22 | | R21 | | R22 |
|
|
; +-----+ +-----+ +-----+ +-----+
|
|
;
|
|
.DEF digit1 = R20
|
|
.DEF digit2 = R21
|
|
.DEF digit3 = R22
|
|
.DEF digit4 = R23
|
|
|
|
;===============================================================================
|
|
; PINS
|
|
;
|
|
; Port | Name | Function | Direction | Init
|
|
;-------|---------------|-------------------------------|-----------|------
|
|
; PA0 | display4 | Ground fourth segment | OUTPUT | LOW
|
|
; PA1 | display2 | Ground third segment | OUTPUT | LOW
|
|
; PA2 | display3 | Ground second segment | OUTPUT | LOW
|
|
; PA3 | display1 | Ground first segment | OUTPUT | LOW
|
|
; PB0 | incrementPin | Trigger ISR | INPUT | LOW
|
|
; PB1 | decrementPin | Trigger ISR | INPUT | LOW
|
|
; PB4 | latchPin | Latching of shift register | OUTPUT | LOW
|
|
; PB5 | clockPin | Clock of shift register | OUTPUT | LOW
|
|
; PB6 | dataPin | Data to shift register | OUTPUT | LOW
|
|
;
|
|
; Inputs
|
|
.EQU incrementPin = PB0 ; PCINT0
|
|
.EQU decrementPin = PB1 ; PCINT0
|
|
|
|
; Outputs
|
|
.EQU latchPin = PB4 ; pin 07 of the Attiny to pin number 12 of the 74HC595
|
|
.EQU clockPin = PB5 ; pin 08 of the Attiny to pin number 11 of the 74HC595
|
|
.EQU dataPin = PB6 ; pin 09 of the Attiny to pin number 14 of the 74HC595
|
|
.EQU display4 = PA0
|
|
.EQU display2 = PA1
|
|
.EQU display3 = PA2
|
|
.EQU display1 = PA3
|
|
|
|
;===============================================================================
|
|
; CONSTANTS
|
|
; Segment numbers
|
|
; +--[1]--+
|
|
; | |
|
|
;[2] [3]
|
|
; | |
|
|
; +--[4]--+
|
|
; | |
|
|
;[5] [6]
|
|
; | |
|
|
; +--[7]--+ [8]
|
|
;
|
|
|
|
.EQU displayDigit0 = 0b00000011
|
|
.EQU displayDigit1 = 0b01111011
|
|
.EQU displayDigit2 = 0b00100101
|
|
.EQU displayDigit3 = 0b00110001
|
|
.EQU displayDigit4 = 0b01011001
|
|
.EQU displayDigit5 = 0b10010001
|
|
.EQU displayDigit6 = 0b10000001
|
|
.EQU displayDigit7 = 0b00111011
|
|
.EQU displayDigit8 = 0b00000001
|
|
.EQU displayDigit9 = 0b00010001
|
|
|
|
; Button debounce states
|
|
.EQU BUTTON_IDLE = 0
|
|
.EQU BUTTON_DEBOUNCE = 1
|
|
.EQU BUTTON_PRESSED = 2
|
|
|
|
;===============================================================================
|
|
; VECTORS
|
|
;
|
|
.CSEG
|
|
.ORG $0000
|
|
RJMP RESET ; Hardware Pin and Watchdog Reset
|
|
RETI ; External Interrupt Request 0
|
|
RJMP PIN_CHANGE ; Pin Change Interrupt
|
|
RETI ; Timer/Counter1 Compare Match 1A
|
|
RETI ; Timer/Counter1 Compare Match 1B
|
|
RETI ; Timer/Counter1 Overflow
|
|
RJMP TIM0_OVF ; Timer/Counter0 Overflow
|
|
RETI ; USI Start
|
|
RETI ; USI Overflow
|
|
RETI ; EEPROM Ready
|
|
RETI ; Analog Comparator
|
|
RETI ; ADC Conversion Complete
|
|
|
|
;===============================================================================
|
|
; SETUP
|
|
;
|
|
RESET:
|
|
CLI ; Disable interrupts
|
|
|
|
LDI registerA, RAMEND
|
|
OUT SP, registerA ; Set stack pointer, 8 bits on the attiny26
|
|
|
|
; Port A, direction
|
|
LDI registerA, (1<<display4) | (1<<display3) | (1<<display2) | (1<<display1)
|
|
OUT DDRA, registerA
|
|
|
|
; Port A, init status
|
|
CBI PORTA, display1
|
|
CBI PORTA, display2
|
|
CBI PORTA, display3
|
|
CBI PORTA, display1
|
|
|
|
; Port B, direction
|
|
LDI registerA, (1<<latchPin) | (1<<clockPin) | (1<<dataPin)
|
|
OUT DDRB, registerA
|
|
|
|
; Port B, init status
|
|
CBI PORTB, latchPin
|
|
CBI PORTB, clockPin
|
|
CBI PORTB, dataPin
|
|
|
|
; Interrupt PCINT1 enable
|
|
LDI registerA, (1<<PCIE0)
|
|
OUT GIMSK, registerA
|
|
|
|
; Set counter to zero
|
|
LDI counterLow, 0
|
|
LDI counterHigh, 0
|
|
|
|
; Initialize button states
|
|
LDI registerA, BUTTON_IDLE
|
|
MOV incrementButtonStatus, registerA
|
|
MOV decrementButtonStatus, registerA
|
|
|
|
SEI ; Enable interrupts
|
|
RJMP main
|
|
|
|
|
|
;===============================================================================
|
|
; MAIN PROGRAM LOOP
|
|
;
|
|
main:
|
|
RCALL checkButtonRelease ; Check for button releases
|
|
RCALL setDigits
|
|
RCALL outputCounter
|
|
RJMP main
|
|
|
|
;===============================================================================
|
|
; REGULAR SUBROUTINES
|
|
;
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; INCREMENT COUNTER
|
|
;
|
|
incrementCounter:
|
|
LDI registerA, HIGH(9999)
|
|
CP counterHigh, registerA
|
|
BRLO doIncrement
|
|
LDI registerA, LOW(9999) ; Above 9984, check lower half
|
|
CP counterLow, registerA
|
|
BRSH maximumReached
|
|
|
|
doIncrement:
|
|
ADIW counterHigh:counterLow, 1
|
|
BRCS maximumReached
|
|
RET
|
|
maximumReached:
|
|
LDI counterLow, LOW(9999) ; Above 65535, fill low bytes
|
|
LDI counterHigh, HIGH(9999) ; Above 65535, fill high bytes
|
|
RET
|
|
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; DECREMENT COUNTER
|
|
;
|
|
decrementCounter:
|
|
LDI registerA, 0
|
|
CP registerA, counterHigh
|
|
BRSH doDecrement
|
|
CP registerA, counterLow
|
|
BREQ minimumReached
|
|
|
|
doDecrement:
|
|
SBIW counterHigh:counterLow, 1
|
|
BRCS minimumReached
|
|
RET
|
|
minimumReached:
|
|
CLR counterLow
|
|
CLR counterHigh
|
|
RET
|
|
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; GETTING DIGITS FROM COUNTER
|
|
;
|
|
setDigits:
|
|
; Push original counter to stack
|
|
PUSH counterHigh
|
|
PUSH counterLow
|
|
RCALL setThousands
|
|
RCALL setHundreds
|
|
RCALL setTens
|
|
RCALL setOnes
|
|
; Pop original counter from stack
|
|
POP counterLow
|
|
POP counterHigh
|
|
RET
|
|
|
|
setThousands:
|
|
; Should be above 00000011 11101000 (1000)
|
|
LDI digit4, 0 ; Default digit 4 to zero
|
|
LDI registerA, 0b00000011
|
|
CP counterHigh, registerA
|
|
BRSH thousandStart ; More than 1000, start counting
|
|
RET
|
|
thousandStart:
|
|
INC digit4
|
|
SUBI counterLow, LOW(1000) ; Subtract two low bytes: AL=AL-LOW(420)
|
|
SBCI counterHigh, HIGH(1000) ; Subtract high bytes and include Carry Flag
|
|
CP counterHigh, registerA
|
|
BRSH thousandStart ; More than 100, substract some more
|
|
RET
|
|
|
|
setHundreds:
|
|
; Should be above 00000000 01100100 (100)
|
|
LDI digit3, 0 ; Default digit 3 to zero
|
|
LDI registerA, 0b00000001
|
|
LDI registerB, 0b01100100
|
|
CP counterHigh, registerA
|
|
BRSH hundredStart ; More than 100, start counting
|
|
CP counterLow, registerB
|
|
BRSH hundredStart ; More than 100, start counting
|
|
RET
|
|
hundredStart:
|
|
INC digit3
|
|
SBIW counterHigh:counterLow, 50
|
|
SBIW counterHigh:counterLow, 50
|
|
LDI registerA, 0b00000001
|
|
CP counterHigh, registerA
|
|
BRSH hundredStart ; More than 100, substract some more
|
|
CP counterLow, registerB
|
|
BRSH hundredStart ; More than 100, substract some more
|
|
RET
|
|
|
|
setTens:
|
|
; Should be above 00000000 00001010 (10)
|
|
LDI digit2, 0 ; Default digit 4 to zero
|
|
LDI registerA, 0b00001010
|
|
CP counterLow, registerA
|
|
BRSH tenStart ; More than 10, start counting
|
|
RET
|
|
tenStart:
|
|
INC digit2
|
|
SBCI counterLow, 10
|
|
CP counterLow, registerA
|
|
BRSH tenStart ; More than 10, substract some more
|
|
RET
|
|
|
|
setOnes:
|
|
; Should be above 00000000 00000001 (1)
|
|
LDI digit1, 0
|
|
LDI registerA, 0b00000001
|
|
CP counterLow, registerA
|
|
BRSH oneStart
|
|
RET
|
|
oneStart:
|
|
INC digit1
|
|
DEC counterLow
|
|
CP counterLow, registerA
|
|
BRSH oneStart ; More then 1, substract some more
|
|
RET
|
|
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; DRIVING SEGMENT DISPLAY
|
|
;
|
|
outputCounter:
|
|
CLI ; Disable interrupts
|
|
; drive digit 1 [X][ ][ ][ ]
|
|
MOV registerA, digit4
|
|
RCALL getShiftValue ; Get value to be shifted out
|
|
MOV arithmicRegisterA, registerB
|
|
LDI registerB, (1<<display1)
|
|
RCALL driveDisplay
|
|
|
|
; drive digit 2 [ ][X][ ][ ]
|
|
MOV registerA, digit3
|
|
RCALL getShiftValue ; Get value to be shifted out
|
|
MOV arithmicRegisterA, registerB
|
|
LDI registerB, (1<<display2)
|
|
RCALL driveDisplay
|
|
|
|
; drive digit 3 [ ][ ][X][ ]
|
|
MOV registerA, digit2
|
|
RCALL getShiftValue ; Get value to be shifted out
|
|
MOV arithmicRegisterA, registerB
|
|
LDI registerB, (1<<display3)
|
|
RCALL driveDisplay
|
|
|
|
; drive digit 4 [ ][ ][ ][X]
|
|
MOV registerA, digit1
|
|
RCALL getShiftValue ; Get value to be shifted out
|
|
MOV arithmicRegisterA, registerB
|
|
LDI registerB, (1<<display4)
|
|
RCALL driveDisplay
|
|
|
|
SEI ; Enable interrupts
|
|
|
|
RET
|
|
|
|
driveDisplay:
|
|
; Shift in bits
|
|
RCALL shiftStart
|
|
; Set correct display pin high, see registerB (display(n))
|
|
OUT PORTA, registerB
|
|
NOP ; Wait
|
|
NOP ; Wait
|
|
RCALL delay
|
|
; Stop driving display pin
|
|
EOR registerB, registerB
|
|
OUT PORTA, registerB
|
|
RET
|
|
|
|
shiftStart:
|
|
; Repeat shift 8 times
|
|
LDI registerA, 8
|
|
MOV arithmicRegisterB, registerA
|
|
LDI registerA, 0
|
|
shiftIn:
|
|
; Logic shift left to get value to be shifted in SREG 'C' flag
|
|
LSL arithmicRegisterA
|
|
BRCS shiftHigh
|
|
CBI PORTB, dataPin
|
|
RJMP doShift
|
|
shiftHigh:
|
|
SBI PORTB, dataPin
|
|
doShift:
|
|
; Cycle clock
|
|
SBI PORTB, clockPin
|
|
NOP
|
|
NOP
|
|
CBI PORTB, clockPin
|
|
; End if all shifted out
|
|
DEC arithmicRegisterB
|
|
CP arithmicRegisterB, registerA
|
|
BRNE shiftIn
|
|
; Toggle latch
|
|
SBI PORTB, latchPin
|
|
NOP
|
|
NOP
|
|
CBI PORTB, latchPin
|
|
RET
|
|
|
|
getShiftValue:
|
|
; Set data to be shifted out
|
|
; The digit should be loaded in registerA
|
|
LDI registerB, 0
|
|
CP registerA, registerB
|
|
BRNE shiftValue_1
|
|
LDI registerB, displayDigit0 ; Set shift bits to 0
|
|
RET
|
|
shiftValue_1:
|
|
INC registerB
|
|
CP registerA, registerB
|
|
BRNE shiftValue_2
|
|
LDI registerB, displayDigit1 ; Set shift bits to 1
|
|
RET
|
|
shiftValue_2:
|
|
INC registerB
|
|
CP registerA, registerB
|
|
BRNE shiftValue_3
|
|
LDI registerB, displayDigit2 ; Set shift bits to 2
|
|
RET
|
|
shiftValue_3:
|
|
INC registerB
|
|
CP registerA, registerB
|
|
BRNE shiftValue_4
|
|
LDI registerB, displayDigit3 ; Set shift bits to 3
|
|
RET
|
|
shiftValue_4:
|
|
INC registerB
|
|
CP registerA, registerB
|
|
BRNE shiftValue_5
|
|
LDI registerB, displayDigit4 ; Set shift bits to 4
|
|
RET
|
|
shiftValue_5:
|
|
INC registerB
|
|
CP registerA, registerB
|
|
BRNE shiftValue_6
|
|
LDI registerB, displayDigit5 ; Set shift bits to 5
|
|
RET
|
|
shiftValue_6:
|
|
INC registerB
|
|
CP registerA, registerB
|
|
BRNE shiftValue_7
|
|
LDI registerB, displayDigit6 ; Set shift bits to 6
|
|
RET
|
|
shiftValue_7:
|
|
INC registerB
|
|
CP registerA, registerB
|
|
BRNE shiftValue_8
|
|
LDI registerB, displayDigit7 ; Set shift bits to 7
|
|
RET
|
|
shiftValue_8:
|
|
INC registerB
|
|
CP registerA, registerB
|
|
BRNE shiftValue_9
|
|
LDI registerB, displayDigit8 ; Set shift bits to 8
|
|
RET
|
|
shiftValue_9:
|
|
LDI registerB, displayDigit9 ; Set shift bits to 9
|
|
RET
|
|
|
|
delay:
|
|
LDI delayRegisterA, 255
|
|
LDI delayRegisterB, 0
|
|
delayStart:
|
|
DEC delayRegisterA
|
|
CP delayRegisterA, delayRegisterB
|
|
BRNE delayStart
|
|
RET
|
|
|
|
setTimer:
|
|
; Use the 8-bit timer0
|
|
; We want to wait about 1.6ms for faster button response
|
|
; Using prescaler of 64 gives about 1.6ms which is much faster
|
|
LDI registerA, 0b00000011 ; CS01=1, CS00=1 for prescaler /64
|
|
OUT TCCR0, registerA ; Set prescaler to /64
|
|
LDI registerA, (1<<TOV0) ; Load timer overflow flag
|
|
OUT TIFR, registerA ; Reset by writing a 1
|
|
LDI registerA, (1<<TOIE0) ; Load overflow interrupt flag for timer 0
|
|
OUT TIMSK, registerA ; Enable overflow interrupt flag for timer 0
|
|
RET
|
|
|
|
;===============================================================================
|
|
; ISR SUBROUTINES
|
|
;
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; CHECK BUTTON RELEASE
|
|
; This routine should be called periodically to detect button releases
|
|
;
|
|
checkButtonRelease:
|
|
; Check increment button release
|
|
LDI registerA, BUTTON_PRESSED
|
|
CP incrementButtonStatus, registerA
|
|
BRNE checkDecrementRelease ; Not pressed, check decrement
|
|
|
|
IN registerA, PINB ; Read pin states
|
|
LDI registerB, (1<<incrementPin)
|
|
AND registerA, registerB ; Check if increment button is still pressed
|
|
CP registerA, registerB
|
|
BRNE incrementReleased ; Button released
|
|
|
|
RJMP checkDecrementRelease ; Still pressed, check decrement
|
|
|
|
incrementReleased:
|
|
LDI registerA, BUTTON_IDLE
|
|
MOV incrementButtonStatus, registerA ; Reset to idle state
|
|
|
|
checkDecrementRelease:
|
|
LDI registerA, BUTTON_PRESSED
|
|
CP decrementButtonStatus, registerA
|
|
BRNE releaseExit ; Not pressed, exit
|
|
|
|
IN registerA, PINB ; Read pin states
|
|
LDI registerB, (1<<decrementPin)
|
|
AND registerA, registerB ; Check if decrement button is still pressed
|
|
CP registerA, registerB
|
|
BRNE decrementReleased ; Button released
|
|
|
|
RJMP releaseExit ; Still pressed, exit
|
|
|
|
decrementReleased:
|
|
LDI registerA, BUTTON_IDLE
|
|
MOV decrementButtonStatus, registerA ; Reset to idle state
|
|
|
|
releaseExit:
|
|
RET
|
|
|
|
;
|
|
PIN_CHANGE:
|
|
CLI ; Disable interrupt
|
|
; Push registers on stack
|
|
PUSH registerA
|
|
PUSH registerB
|
|
|
|
; Check which button caused the interrupt
|
|
IN registerA, PINB ; Read current pin states
|
|
LDI registerB, (1<<incrementPin)
|
|
AND registerA, registerB ; Check increment button
|
|
CP registerA, registerB
|
|
BRNE checkDecrementPin ; Not increment, check decrement
|
|
|
|
; Increment button was pressed
|
|
LDI registerA, BUTTON_IDLE
|
|
CP incrementButtonStatus, registerA ; Check if button was idle
|
|
BRNE pinChangeExit ; Already processing, ignore
|
|
|
|
LDI registerA, BUTTON_DEBOUNCE
|
|
MOV incrementButtonStatus, registerA ; Set to debounce state
|
|
RCALL setTimer ; Start debounce timer
|
|
RJMP pinChangeExit
|
|
|
|
checkDecrementPin:
|
|
IN registerA, PINB ; Read current pin states again
|
|
LDI registerB, (1<<decrementPin)
|
|
AND registerA, registerB ; Check decrement button
|
|
CP registerA, registerB
|
|
BRNE pinChangeExit ; Neither button, exit
|
|
|
|
; Decrement button was pressed
|
|
LDI registerA, BUTTON_IDLE
|
|
CP decrementButtonStatus, registerA ; Check if button was idle
|
|
BRNE pinChangeExit ; Already processing, ignore
|
|
|
|
LDI registerA, BUTTON_DEBOUNCE
|
|
MOV decrementButtonStatus, registerA ; Set to debounce state
|
|
RCALL setTimer ; Start debounce timer
|
|
|
|
pinChangeExit:
|
|
; Pop registers from stack
|
|
POP registerB
|
|
POP registerA
|
|
SEI ; Enable interrupt
|
|
RETI
|
|
|
|
TIM0_OVF:
|
|
CLI ; Disable interrupt
|
|
; Push registers on stack
|
|
PUSH registerA
|
|
PUSH registerB
|
|
|
|
; Clear stop the timer by setting Clock select to 0
|
|
LDI registerA, 0x000000
|
|
OUT TCCR0, registerA
|
|
|
|
; Check current pin states after debounce period
|
|
IN registerA, PINB ; Read current pin states
|
|
|
|
; Check increment button
|
|
LDI registerB, BUTTON_DEBOUNCE
|
|
CP incrementButtonStatus, registerB
|
|
BRNE checkDecrementTimer ; Not in debounce state, check decrement
|
|
|
|
LDI registerB, (1<<incrementPin)
|
|
AND registerA, registerB ; Check if increment button is still pressed
|
|
CP registerA, registerB
|
|
BRNE incrementNotPressed ; Button not pressed, reset state
|
|
|
|
; Button is still pressed after debounce, process it
|
|
LDI registerA, BUTTON_PRESSED
|
|
MOV incrementButtonStatus, registerA
|
|
RCALL incrementCounter
|
|
RJMP timerExit
|
|
|
|
incrementNotPressed:
|
|
LDI registerA, BUTTON_IDLE
|
|
MOV incrementButtonStatus, registerA ; Reset to idle state
|
|
|
|
checkDecrementTimer:
|
|
LDI registerB, BUTTON_DEBOUNCE
|
|
CP decrementButtonStatus, registerB
|
|
BRNE timerExit ; Not in debounce state, exit
|
|
|
|
IN registerA, PINB ; Read current pin states again
|
|
LDI registerB, (1<<decrementPin)
|
|
AND registerA, registerB ; Check if decrement button is still pressed
|
|
CP registerA, registerB
|
|
BRNE decrementNotPressed ; Button not pressed, reset state
|
|
|
|
; Button is still pressed after debounce, process it
|
|
LDI registerA, BUTTON_PRESSED
|
|
MOV decrementButtonStatus, registerA
|
|
RCALL decrementCounter
|
|
RJMP timerExit
|
|
|
|
decrementNotPressed:
|
|
LDI registerA, BUTTON_IDLE
|
|
MOV decrementButtonStatus, registerA ; Reset to idle state
|
|
|
|
timerExit:
|
|
; Pop registers from stack
|
|
POP registerB
|
|
POP registerA
|
|
SEI ; Enable interrupt
|
|
RETI
|