; ; i0.3.serial.44.byte.asm ; three-wire byte bridge ; ; Neil Gershenfeld ; CBA MIT 12/1/07 ; ; (c) Massachusetts Institute of Technology 2007 ; Permission granted for experimental and personal use; ; license for commercial sale available from MIT. ; .include "tn44def.inc" ; ; definitions ; .equ click_pin = PA0 ; i0 click pin .equ led_sink_pin = PA1 ; LED sink pin .equ led_source_pin = PA2 ; LED source pin .equ tx_pin = PA6; transmit pin .equ rx_pin = PA7; receive pin .equ click_count = 5 ; loop count to wait during click .equ settle_count = 10 ; loop count to wait for click to settle .equ delay_count = 15 ; loop count to wait between clicks ; ; registers ; .def double_count = R1 ; double loop count .def triple_space = R2 ; triple click spacing .def bit_count = R16; bit counter .def temp = R17; temporary storage .def temp1 = R18; temporary storage .def txbyte = R19; transmit byte .def rxbyte = R20; receive byte .def click_space = R21 ; click spacing .def count = R22 ; loop counter .cseg .org 0 rjmp reset ; ; click_duration ; click_duration: ldi temp, click_count click_duration_loop: dec temp brne click_duration_loop ret ; ; click_delay ; click_delay: ldi temp, delay_count click_delay_loop: dec temp brne click_delay_loop 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 click_space, (settle_count+1) ; +1 for overhead getclick_time_start: inc click_space breq getclick_timeout ; check for overflow sbic PINA, click_pin ; check for click rjmp getclick_time_start mov triple_space, click_space add triple_space, click_space add triple_space, click_space ; ; decode data clicks ; clr rxbyte ldi bit_count, 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 PINA, click_pin ; check for click rjmp getclick_time ; ; determine bit delay ; mov double_count, count add double_count, count cp double_count, triple_space brsh getclick_zero ; ; one bit ; sec ; set carry ror rxbyte ; shift in carry ; ; even out 0/1 timing ; mov count, click_space getclick_space: dec count nop ; to even out timing for breq brne getclick_space ; ; decrement counter and output if byte received ; dec bit_count 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 bit_count brne getclick_bitloop getclick_end: ; ; wait for stop clicks ; getclick_end_0_up: sbis PINA, click_pin rjmp getclick_end_0_up getclick_end_1_down: sbic PINA, click_pin rjmp getclick_end_1_down getclick_end_1_up: sbis PINA, click_pin rjmp getclick_end_1_up getclick_end_2_down: sbic PINA, click_pin rjmp getclick_end_2_down getclick_end_2_up: sbis PINA, click_pin rjmp getclick_end_2_up ret getclick_timeout: ldi rxbyte, 0 ret ; ; putclick ; send char in txbyte clicks ; putclick: ldi bit_count, 8 sec; set start bit ; ; set click pin to output ; sbi DDRA, click_pin ; ; send start clicks ; cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay ; ; send data clicks ; putclick0: lsr txbyte; get next bit brcc putclick1 ; if carry set, send a 1 click cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay sbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay rjmp putclick2; otherwise ... putclick1: sbi PORTA, click_pin ; ... send a 0 click rcall click_duration sbi PORTA, click_pin rcall click_delay cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay putclick2: dec bit_count; if not all bits sent brne putclick0; send next bit ; ; send stop clicks ; cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay ; ; set click pin to input with pull-up ; cbi DDRA, click_pin ; ; return ; ret ; ; bit delay ; serial bit delay ; ;.equ b = 13 ; 9600 baud (8 MHz clock /8) ;.equ b = 8 ; 115200 baud (8 MHz clock /1) .equ b = 25 ; 115200 baud (20 MHz clock /1) bitdelay: ldi temp, b bitloop: dec temp brne bitloop ret ; ; getchar ; assumes no line driver (doesn't invert bits) ; getchar: ldi bit_count,9 ; 8 data bit + 1 stop bit getchar1: sbis PINA, rx_pin ; wait for start bit rjmp getchar1 rcall bitdelay ; 0.5 bit delay getchar2: rcall bitdelay ; 1 bit delay rcall bitdelay ; clc ; clear carry sbis PINA, rx_pin ; if RX pin high skip sec ; otherwise set carry dec bit_count breq getchar3 ; return if all bits read ror rxbyte ; otherwise shift bit into receive byte rjmp getchar2 ; go get next bit getchar3: ret ; ; putchar ; assumes no line driver (doesn't invert bits) ; .equ sb = 1; number of stop bits putchar: ldi bit_count, 9+sb; 1+8+sb com txbyte; invert everything sec; set start bit putchar0: brcc putchar1; if carry set sbi PORTA, tx_pin; send a '0' rjmp putchar2; else putchar1: cbi PORTA, tx_pin ; send a '1' nop ; even out timing putchar2: rcall bitdelay; one bit delay rcall bitdelay lsr txbyte; get next bit dec bit_count; if not all bits sent brne putchar0; send next bit ret; ; ; main program ; reset: ; ; set fuse low byte to 0x7E for 20 MHz resonator ; ; set clock divider to /1 ; ldi temp, (1 << CLKPCE) ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0) out CLKPR, temp out CLKPR, temp1 ; ; set stack pointer to top of RAM ; ldi temp, high(RAMEND) out SPH, temp ldi temp, low(RAMEND) out SPL, temp ; ; init RS-232 pin for output ; cbi PORTA, tx_pin sbi DDRA, tx_pin ; ; init click pin for input with pull-up ; sbi PORTA, click_pin cbi DDRA, click_pin ; ; init LED pins ; cbi PORTA, led_source_pin sbi DDRA, led_source_pin cbi PORTA, led_sink_pin sbi DDRA, led_sink_pin ; ; start main loop ; main_loop: ; ; wait for i0 or serial bytes ; wait_loop: sbis PINA, click_pin ; check i0 pin for click rjmp i0_to_serial sbis PINA, rx_pin ; check serial pin for start bit rjmp wait_loop serial_to_i0: sbi PORTA, led_source_pin ; turn on LED rcall getchar ; read the serial character mov txbyte, rxbyte ; send the i0 character rcall putclick ; cbi PORTA, led_source_pin ; turn off LED rjmp main_loop ; return to main loop i0_to_serial: sbi PORTA, led_source_pin ; turn on LED rcall getclick ; read the i0 character mov txbyte, rxbyte ; send the serial character rcall putchar ; cbi PORTA, led_source_pin ; turn off LED rjmp main_loop ; return to main loop