.nolist
.include "m168def.inc" ; Same family as 328P
.list
.macro poke
.message "No parameters"
.endm
.macro poke_i_8_i
ldi @1, @2
sts @0, @1
.endm
.macro poke_i_16_i
ldi @1, low(@3)
sts @0+0, @1
ldi @2, high(@3)
sts @0+1, @2
.endm
;; Load a 16-bit *word* address into the given register a a byte address
.macro loadp_16_i
ldi @0, high(2 * @2)
ldi @1, low(2 * @2)
.endm
.macro load_16_i
ldi @0, high(@2)
ldi @1, low(@2)
.endm
;; Interrupt Vector
.org 0x0000
rjmp Main
;; Libraries
.include "div.inc" ; Division routines
;; Serial
.set SERIAL_BAUD = 103 ; 9.6k @ 16Mhz
;; Initialize the UART for
Serial_Init:
; Set up control registers
; Single-speed
poke [UCSR0A, r16, (0 << U2X0)]
; Async
; n parity
; 1 stop
; 8 bits
poke [UCSR0C, r16, (0b00 << UMSEL00) | (0b00 << UPM00) | (0 << USBS0) | (0b11 << UCSZ00)]
; Baud rate
poke [UBRR0L, r16:r17, SERIAL_BAUD]
; Enable RX
; Enable TX
; 8 bits
poke [UCSR0B, r16, (1 << RXEN0) | (1 << TXEN0) | (0 << UCSZ02)]
; Done
ret
;; Send a single byte on serial port
; Input byte in r16
Serial_Send:
; Wait for idle
lds r0, UCSR0A
sbrs r0, UDRE0
rjmp Serial_Send
; Copy byte to buffer
sts UDR0, r16
; Done
ret
;; Read a single byte from serial port
; Output byte in r16
Serial_Recv:
; Wait for recv
lds r0, UCSR0A
sbrs r0, RXC0
rjmp Serial_Recv
; Copy byte from buffer
lds r16, UDR0
; Done
ret
;; Write nul-terminated string to serial port
; Input string in Z
Serial_Write_s:
; Load byte to r16
lpm r16, Z+
; Quit if nul
tst r16
breq _serial_writes_end
; Write it
rcall Serial_Send
; Continue
rjmp Serial_Write_s
_serial_writes_end:
ret
;; Write char to serial port
; Input byte in r16
Serial_Write_c:
; ASCII offset for '0'
ldi r17, 48
mov r4, r17
; Convert
ldi r17, 100
call div8u
add r16, r4
rcall Serial_Send
mov r16, r15
ldi r17, 10
call div8u
add r16, r4
rcall Serial_Send
mov r16, r15
add r16, r4
rcall Serial_Send
ret
;; Program
message: .db "Hello World", 13, 10, 0
Main:
; Initialization
; Stack
poke [SPL, r16:r17, RAMEND]
; Enable interrupts
sei
; Init
rcall Serial_Init
; Main program
ldi ZH, high(message * 2)
ldi ZL, low(message * 2)
rcall Serial_Write_s
; Echo out
sbi DDRB, PORTB5
ldi r20, 0
ldi r21, 0
Main_Echo:
; blink LED
out PORTB, r20
; read
rcall Serial_Recv
push r16
; running counter
inc r21
mov r16, r21
rcall Serial_Write_c
; ' '
ldi r16, 32
rcall Serial_Send
pop r16
rcall Serial_Send
; '\r\n'
ldi r16, 13
rcall Serial_Send
ldi r16, 10
rcall Serial_Send
; toggle
com r20
; continue
rjmp Main_Echo