; ; i0.15.byte.asm ; ; Neil Gershenfeld CBA MIT 11/24/06 ; (c) Massachusetts Institute of Technology 2005 ; Permission granted for experimental and personal use; ; license for commercial use available from MIT ; .include "tn45def.inc" .equ rxpin = PB2; receive pin .equ txpin = PB1; transmit pin .equ clickbit = 4 ; click bit .equ clickpin = PB4 ; click pin .equ settle_count = 10 ; counts to wait for click to settle .def bitcount = R16 ; bit counter .def txbyte = R17 ; transmit byte .def rxbyte = R18 ; receive byte .def clickspace = R19 ; click spacing .def triplespace = R20 ; triple click spacing .def count = R21 ; loop counter .def doublecount = R22 ; double loop count .def temp = R23 ; temporary storage .def temp1 = R24 ; temporary storage .cseg .org 0 rjmp reset ; reset routine ; ; click_duration ; click_duration: ldi temp, 2 click_duration_loop: dec temp brne click_duration_loop ret ; ; click_delay ; click_delay: ldi temp, 15 click_delay_loop: dec temp brne click_delay_loop ret ; ; bitdelay ; serial bit delay ; ; .equ b = 13 ; 9600 baud (clock /8) .equ b = 8 ; 115200 baud (clock /1) bitdelay: ldi temp, b bitdelayloop: dec temp brne bitdelayloop ret ; ; getchar ; input a serial byte following first transition ; getchar: ldi bitcount, 9 ; 8 data bit + 1 stop bit rcall bitdelay ; 0.5 bit delay getchar_loop: rcall bitdelay ; 1 bit delay rcall bitdelay ; clc ; clear carry sbis PINB, rxpin ; if RX pin high skip sec ; otherwise set carry dec bitcount breq getchar_end ; return if all bytes read ror rxbyte ; otherwise shift bit into receive byte rjmp getchar_loop ; get next bit getchar_end: ret ; ; putchar ; output a serial byte ; putchar: .equ sb = 1 ; number of stop bits ldi bitcount, 9+sb; 1+8+sb com txbyte ; invert everything sec ; set start bit putchar0: brcc putchar1 ; if carry set sbi PORTB, txpin ; send a '0' rjmp putchar2 ; else putchar1: cbi PORTB, txpin ; send a '1' nop ; even out timing putchar2: rcall bitdelay ; one bit delay rcall bitdelay lsr txbyte ; get next bit dec bitcount ; if not all bits sent brne putchar0 ; send next bit ret ; ; getclick ; input an I0 byte following first click ; getclick: ; ; delay for first click to settle ; ldi count, settle_count getclick_settle_start: dec count nop ; to even out timing for breq brne getclick_settle_start ; ; time arrivial of second start click ; ldi clickspace, settle_count getclick_time_start: inc clickspace breq getclick_timeout ; check for overflow sbic PINB, clickpin ; check for click rjmp getclick_time_start mov triplespace, clickspace add triplespace, clickspace add triplespace, clickspace ; ; decode data clicks ; clr rxbyte ldi bitcount, 8 getclick_bitloop: ; ; delay for click to settle ; ldi count, settle_count getclick_settle: dec count nop ; to even out timing for breq brne getclick_settle ; ; time arrivial of next click ; ldi count, settle_count getclick_time: inc count breq getclick_timeout ; check for overflow sbic PINB, clickpin ; check for click rjmp getclick_time ; ; determine bit delay ; mov doublecount, count add doublecount, count cp doublecount, triplespace brsh getclick_zero ; ; one bit ; sec ; set carry ror rxbyte ; shift in carry ; ; even out 0/1 timing ; mov count, clickspace getclick_space: dec count nop ; to even out timing for breq brne getclick_space ; ; decrement counter and output if byte received ; dec bitcount brne getclick_bitloop rjmp getclick_end getclick_zero: ; ; zero bit ; clc ; clear carry ror rxbyte ; shift in carry ; ; decrement counter and output if byte received ; dec bitcount brne getclick_bitloop getclick_end: ; ; delay for stop clicks ; mov count, clickspace getclick_stop1: dec count nop ; to even out timing for breq brne getclick_stop1 mov count, clickspace getclick_stop2: dec count nop ; to even out timing for breq brne getclick_stop2 ret getclick_timeout: ldi rxbyte, 0 ret ; ; putclick ; send char in txbyte clicks ; putclick: ldi bitcount, 8 sec; set start bit ; ; get click pin ready for output ; cbi PORTB, clickpin ; ; send start clicks ; sbi DDRB, clickpin rcall click_duration cbi DDRB, clickpin rcall click_delay sbi DDRB, clickpin rcall click_duration cbi DDRB, clickpin rcall click_delay ; ; send data clicks ; putclick0: lsr txbyte; get next bit brcc putclick1 ; if carry set, send a 1 click sbi DDRB, clickpin rcall click_duration cbi DDRB, clickpin rcall click_delay cbi DDRB, clickpin rcall click_duration cbi DDRB, clickpin rcall click_delay rjmp putclick2; otherwise ... putclick1: cbi DDRB, clickpin ; ... send a 0 click rcall click_duration cbi DDRB, clickpin rcall click_delay sbi DDRB, clickpin rcall click_duration cbi DDRB, clickpin rcall click_delay putclick2: dec bitcount; if not all bits sent brne putclick0; send next bit ; ; send stop clicks ; sbi DDRB, clickpin rcall click_duration cbi DDRB, clickpin rcall click_delay sbi DDRB, clickpin rcall click_duration cbi DDRB, clickpin rcall click_delay ; ; return to pull-up on click pin and return ; sbi PORTB, clickpin ret ; ; main program ; reset: ; ; set clock divider ; ldi temp, (1 << CLKPCE) ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0) ; /1 out CLKPR, temp out CLKPR, temp1 ; ; init microcontroller ; ldi temp, high(RAMEND) ; set stack pointer to top of RAM out SPH, temp ; " ldi temp, low(RAMEND) ; " out SPL, temp ; " sbi PORTB, txpin ; init comm pin for output sbi DDRB, txpin ; sbi PORTB, clickpin ; init click pin for input with pull-up cbi DDRB, clickpin ; " ; ; main loop ; mainloop: ; ; wait for I0 or serial byte ; waitloop: sbis PINB, clickpin rjmp I0_to_serial sbis PINB, rxpin ; check serial pin for start bit rjmp waitloop serial_to_I0: rcall getchar ; read the serial character mov txbyte, rxbyte ; send the I0 character rcall putclick ; rjmp mainloop ; return to main loop I0_to_serial: rcall getclick ; read the I0 character mov txbyte, rxbyte ; send the serial character rcall putchar ; rjmp mainloop ; return to main loop