.device ATmega88 .def temp = R16; temporary storage .def temp1 = R17 .def txbyte = R18 .def rxbyte = R19 .def spibyte = R20 .def bit_count = R21 .def get_bit_count = R22 ; ; pins ; .equ button = PD2 .equ button_port = PORTD .equ button_pin = PIND .equ button_ddr = DDRD .macro init_button cbi button_ddr, button sbi button_port, button .endmacro .equ led = PC4 .equ led_port = PORTC .equ led_pin = PINC .equ led_ddr = DDRC .macro init_led sbi button_ddr, led ; source to light cbi button_port, button .endmacro .macro led_on sbi button_port, button .endmacro .macro led_off cbi button_port, button .endmacro ; (me in) slave out .equ audio_so = PD5 ; on PCINT21 (PCINT2) .equ audio_so_port = PORTD .equ audio_so_pin = PIND .equ audio_so_ddr = DDRD ; (me out) slave in .equ audio_si = PB1 ; .equ audio_si_port = PORTB ; .equ audio_si_pin = PINB ; .equ audio_si_ddr = DDRB ; .macro init_audio sbi audio_si_ddr, audio_si cbi audio_si_port, audio_si cbi audio_so_ddr, audio_so .endmacro ; (me in) slave out .equ radio_so = PD3 ; on INT1 .equ radio_so_port = PORTD ; .equ radio_so_pin = PIND ; .equ radio_so_ddr = DDRD ; ; (me out) slave in .equ radio_si = PC0 ; .equ radio_si_port = PORTC ; .equ radio_si_pin = PINC ; .equ radio_si_ddr = DDRC ; .macro init_radio sbi radio_si_ddr, radio_si cbi radio_si_port, radio_si cbi radio_so_ddr, radio_so .endmacro ; master in (me out -- I am slave to computer) .equ serial_si = PC2 ; .equ serial_si_port = PORTC ; .equ serial_si_ddr = DDRC ; .equ serial_si_pin = PINC ; ; master out (me in) .equ serial_so = PC3 .equ serial_so_port = PORTC .equ serial_so_ddr = DDRC .equ serial_so_pin = PINC .macro init_serial ; computer is "master" for serial sbi serial_so_ddr, serial_so cbi serial_so_port, serial_so cbi serial_si_ddr, serial_si .endmacro ; ; constants ; .equ SER_BAUD_115200 = 68 ; (8000000 / 115200.) .equ BAUD_RATE = SER_BAUD_115200 .equ STOP_BITS = 1 ; ; code ; .cseg .org 0 ; ; vector table ; rjmp reset ; Reset Handler rjmp button_down ; IRQ0 Handler rjmp igetchar ; IRQ1 Handler .dw 0 ; PCINT0 Handler .dw 0 ; PCINT1 Handler .dw 0 ; PCINT2 Handler .dw 0 ; Watchdog Timer Handler rjmp getbit ; Timer2 Compare A Handler .dw 0 ; Timer2 Compare B Handler .dw 0 ; Timer2 Overflow Handler .dw 0 ; Timer1 Capture Handler .dw 0 ; Timer1 Compare A Handler .dw 0 ; Timer1 Compare B Handler .dw 0 ; Timer1 Overflow Handler rjmp putbit ; Timer0 Compare A Handler .dw 0 ; Timer0 Compare B Handler .dw 0 ; Timer0 Overflow Handler .dw 0 ; SPI Transfer Complete Handler .dw 0 ; USART, RX Complete Handler .dw 0 ; USART, UDR Empty Handler .dw 0 ; USART, TX Complete Handler .dw 0 ; ADC Conversion Complete Handler .dw 0 ; EEPROM Ready Handler .dw 0 ; Analog Comparator Handler .dw 0 ; 2-wire Serial Interface Handler .dw 0 ; Store Program Memory Ready Handler ; ; putchar ; assumes no line driver (doesn't invert bits) ; putchar: ldi bit_count, 9 + stop_bits; 1 + 8 + sb com txbyte; invert everything sec ; set start bit putchar0: brcc putchar1 ; if carry set sbi serial_so_port, serial_so; send a '0' rjmp putchar2; else putchar1: cbi serial_so_port, serial_so ; send a '1' nop ; even out timing putchar2: rcall bit_delay ; one bit delay rcall bit_delay lsr txbyte ; get next bit dec bit_count ; if not all bits sent brne putchar0 ; set next bit ret ; ; bit delay ; serial bit delay ; ;.equ b = 13 ; 9600 baud (clock /8) ;.equ b = 130 ; 9600 baud (clock /1) .equ b = 8 ; 115200 baud (clock /1) ;.equ b = 6 ; crazy speed ;.equ b = 4 ; ludicrous speed bit_delay: ldi temp, b bitloop: dec temp brne bitloop ret button_down: push temp push temp1 in temp, sreg ; debounce ldi temp1, 50 button_debounce: dec temp1 brne button_debounce ldi txbyte, 0b11111111 rcall iputchar out sreg, temp pop temp1 pop temp reti light_if_pressed: sbis button_pin, button sbi led_port, led sbic button_pin, button cbi led_port, led ret ; ; iputchar ; interrupt driven putchar ; assumes no line driver (no bit inversion) ; iputchar: ldi bit_count, 8 + STOP_BITS com txbyte; invert so bit value == voltage value sbi serial_so_port, serial_so ldi temp1, 10 start_bit_extra: dec temp1 brne start_bit_extra ; start timer ldi temp1, (1 << OCIE0A) ; compare match interrupt on sts TIMSK0, temp1 ldi temp1, 0 out TCNT0, temp1 ; reset timer ; send start bit ret ; the rest is handled by putbit on timer interrupt ; ; send next bit for iputchar ; putbit: push temp push temp1 in temp, SREG lsr txbyte ; shift next bit into carry - lsb first ; put 0 brcc putbit_0v ; putbit 5v sbi serial_so_port, serial_so rjmp putbit_dec_count putbit_0v: cbi serial_so_port, serial_so putbit_dec_count: dec bit_count brne putbit_return ; stop timer if bit count is 0 ldi temp1, 0 sts TIMSK0, temp1 putbit_return: out SREG, temp ; restore SREG pop temp1 pop temp reti igetchar_on: ; enable int1 interrupt in temp, EIMSK sbr temp, (1 << INT1) out EIMSK, temp ret igetchar_off: ; disable int1 interrupt in temp, EIMSK cbr temp, (1 << INT1) out EIMSK, temp ret igetchar: ; this is called when start bit ends. push temp ; N.B. - SREG not protected (or modified) ; Disable int1 interrupt. rcall igetchar_off ; set bit count and half-delay for first bit. ldi get_bit_count, 8 ldi temp, BAUD_RATE / 2 ; half-bit delay to place frame sts OCR2A, temp ; compare match interrupt on ldi temp, (1 << OCIE2A) sts TIMSK2, temp ; reset timer ldi temp, 0 sts TCNT2, temp pop temp reti getbit: push temp push temp1 in temp, SREG ldi temp1, BAUD_RATE sts OCR2A, temp1 ; we should be mid-bit now clc sbis serial_si_pin, serial_si sec ror rxbyte ; shift carry into receive byte dec get_bit_count brne getbit_return ; disable timer2 interrupts ldi temp1, 0 sts TIMSK2, temp1 rcall igetchar_on rcall igetchar_done getbit_return: out SREG, temp pop temp1 pop temp reti igetchar_done: mov txbyte, rxbyte rcall putchar ret two_byte_delay: ldi temp, 255 long_loop1: ldi temp1, 3 long_loop2: dec temp1 brne long_loop2 dec temp brne long_loop1 ret iputchar_test: ldi txbyte, 0b00000000 rcall iputchar rcall two_byte_delay ldi txbyte, 0b00000001 rcall iputchar rcall two_byte_delay ldi txbyte, 0b00000011 rcall iputchar rcall two_byte_delay ldi txbyte, 0b00000111 rcall iputchar rcall two_byte_delay ldi txbyte, 0b00001111 rcall iputchar rcall two_byte_delay ldi txbyte, 0b00011111 rcall iputchar rcall two_byte_delay ldi txbyte, 0b00111111 rcall iputchar rcall two_byte_delay ldi txbyte, 0b01111111 rcall iputchar rcall two_byte_delay ldi txbyte, 0b11111111 rcall iputchar rcall two_byte_delay ldi txbyte, 0b10101010 rcall iputchar rcall two_byte_delay ret reset: ; ; set stack pointer to top of RAM ; ldi temp, high(RAMEND) out SPH, temp ldi temp, low(RAMEND) out SPL, temp ; ; set clock divider to /1 ; ldi temp, (1 << CLKPCE) ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0) sts CLKPR, temp sts CLKPR, temp1 ; ; pin inits ; init_led init_button init_audio init_radio init_serial ; iputchar timer: Timer/Counter 0 ; igetchar timer: Timer/Counter 1 ; ; Timer/Counter Control Register A: ; COMnA1, COMnAn, COMnB1, COMnBn, ---, ---, WGMn1, WGMn0 ldi temp, 0b00000010 out TCCR0A, temp sts TCCR2A, temp ; Timer/Counter Control Register B: ; FOCnA, FOCnB, ---, ---, WGMn2, CSn2, CSn1, CSn0 ldi temp, 0b00000001 out TCCR0B, temp sts TCCR2B, temp ; Timer/Counter interrupt mask: ; ---, ---, ---, ---, ---, OCIEnB, OCIEnA, TOIEn ldi temp, 0b00000000 sts TIMSK0, temp sts TIMSK2, temp ; Timer/Counter compare match ldi temp, SER_BAUD_115200 out OCR0A, temp sts OCR2A, temp ; will be changed by igetchar ; ; enable int0. ; External Interrupt Control Register A ; ---, ---, ---, ---, ISC11, ICS10, ISC01, ISC00 ; trigger int1 on low (end of start bit) ; trigger int0 on low (button press) ldi temp, 0b00000000 sts EICRA, temp ; ; External Interrupt Mask Register ; ---, ---, ---, ---, ---, ---, INT1, INT0 ldi temp, 0b00000001 ; int1 and int0 on out EIMSK, temp ; ; enable global interrupts ; sei ; ; main loop ; ;rcall igetchar_on main_loop: rcall iputchar_test rjmp main_loop