.nolist
.include "m168def.inc" ; Same family as 328P
.list
;; Interrupt Vector
.org 0x00
rjmp init
.org SPIaddr
rjmp SPI_Interrupt
;; SPI
.equ SPI_DDR = DDRB
.equ SPI_PORT = PORTB
.equ SPI_SCK = PORTB5
.equ SPI_MISO = PORTB4
.equ SPI_MOSI = PORTB3
.equ SPI_SS = PORTB2
.equ SPI_FLAGS = GPIOR0
.equ SPI_BUSY = 0
;; Initialize SPI subsystem for master operation
SPI_Init:
; Set modes
sbi SPI_DDR, SPI_SCK ; Out
sbi SPI_DDR, SPI_MOSI ; Out
sbi SPI_DDR, SPI_SS ; Out
; Drive SS high (off)
sbi SPI_PORT, SPI_SS
; Set control mode
; Enable interrupt
; Enable SPI
; MSB first
; Master mode
; Polarity/phase: Mode 0 (sample on rising edge)
; Clock rate 1/16
ldi r16, (1 << SPIE) | (1 << SPE) | (0 << DORD) | (1 << MSTR) | (0 << CPOL) | (0 << CPHA) | (0b01 << SPR0)
out SPCR, r16
; Flags
clr r0
out SPI_FLAGS, r0
; Done
ret
;; Send byte
;; Input: r16
;; XXX: should not be busy...
SPI_Send:
; Flag
sbi SPI_FLAGS, SPI_BUSY
; Enable slave (low)
cbi SPI_PORT, SPI_SS
; Write byte (starts SCK)
out SPDR, r16
; Wait for interrupt
; Done
ret
;; Wait for byte to be sent
SPI_Wait:
wait_idle:
sbic SPI_FLAGS, SPI_BUSY ; Test for busy flag
rjmp wait_idle ; loop
; Done
ret
;; Service SPI interrupt
SPI_Interrupt:
; Store SREG
in r16, SREG
; Drive SS high (off)
sbi SPI_PORT, SPI_SS
; Flag
cbi SPI_FLAGS, SPI_BUSY
; Done
out SREG, r16
reti
;; LCD
.equ LCD_DDR = DDRB
.equ LCD_PORT = PORTB
.equ LCD_OE = PORTB1 ; Output Enable (Low)
; Output font for 7-segment display
LCD_Font:
.db 0b00111111, 0b00000110 ; 0, 1
.db 0b01011011, 0b01001111 ; 2, 3
.db 0b01100110, 0b01101101 ; 4, 5
.db 0b01111101, 0b00000111 ; 6, 7
.db 0b01111111, 0b01100111 ; 8, 9
.db 0b10000000, 0b00000000 ; .,
;.db 0b00111111, ; 0
; 0b00000110, ; 1
; 0b01011011, ; 2
; 0b01001111, ; 3
; 0b01100110, ; 4
; 0b01101101, ; 5
; 0b01111101, ; 6
; 0b00000111, ; 7
; 0b01111111, ; 8
; 0b01100111, ; 9
; 0b10000000, ; .
; 0b01000000 ;
.equ LCD_0 = 0
.equ LCD_1 = 1
.equ LCD_2 = 2
.equ LCD_3 = 3
.equ LCD_4 = 4
.equ LCD_5 = 5
.equ LCD_6 = 6
.equ LCD_7 = 7
.equ LCD_8 = 8
.equ LCD_9 = 9
.equ LCD_DOT = 10
.equ LCD_EMPTY = 11
;; Initialize LCD to empty, and enable
LCD_Init:
; Setup ENable port
sbi LCD_PORT, LCD_OE ; Disabled (Low)
sbi LCD_DDR, LCD_OE ; Out
; empty
ldi r16, 0b11111111
; Output
rcall SPI_Send
rcall SPI_Wait
; Enable
cbi LCD_PORT, LCD_OE
; Done
ret
;; Display a single digit on the display
;; Input: r16
LCD_Show:
clr r0, 0
; Prep address
; base addr for font table
ldi ZH, high(2*LCD_Font)
ldi ZL, low(2*LCD_Font)
; offset
add ZL, r16
adc ZH, r0
; Load char
lpm r16, Z
; Invert
com r16
; Display
rcall SPI_Send
; Done
ret
;; Delay for approx. 1s
Delay_1s:
; 20 * 255 * 255 = 1.3M cycles
ldi r20, 40
ldi r21, 255
ldi r22, 255
delay:
dec r22
brne delay
dec r21
brne delay
dec r20
brne delay
ret
Main:
init:
; Stack
ldi r16, high(RAMEND)
ldi r17, low(RAMEND)
out SPH, r16
out SPL, r17
; Enable interrupts
sei
; SPI
rcall SPI_Init
; LCD (requires interrupts)
rcall LCD_Init
;; Count down from 9
count:
; init from 9
ldi r24, LCD_9
loop:
; display
mov r16, r24
rcall LCD_Show
; exit if zero
tst r24
breq blink
; count down
dec r24
; wait...
rcall Delay_1s
; next
rjmp loop
;; Blink between dot and empty
blink:
rcall Delay_1s
; dot
ldi r16, LCD_DOT
rcall LCD_Show
; wait...
rcall Delay_1s
; empty
ldi r16, LCD_EMPTY
rcall LCD_Show
; loop
rjmp blink
end:
rjmp end