diff options
author | Daniel Friesel <derf@finalrewind.org> | 2014-12-21 17:52:43 +0100 |
---|---|---|
committer | Daniel Friesel <derf@finalrewind.org> | 2014-12-21 17:52:43 +0100 |
commit | 4dd3fef3763d0d48e5891ae211ebdbb9485d017d (patch) | |
tree | d562deb57e75d40490c69c91dacd1d0bad3d1a57 | |
parent | 39c8b7e9b8dcb469dab73c5773fd98d4396113d8 (diff) |
now with assembly
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | main.S | 354 |
2 files changed, 357 insertions, 3 deletions
@@ -6,7 +6,7 @@ AVRNM ?= avr-nm AVROBJCOPY ?= avr-objcopy AVROBJDUMP ?= avr-objdump -CFLAGS += -mmcu=attiny2313a -DF_CPU=20000000UL +CFLAGS += -mmcu=attiny2313a -DF_CPU=8000000UL #CFLAGS += -gdwarf-2 -fverbose-asm CFLAGS += -I. -std=gnu99 -Os -Wall -Wextra -pedantic CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums @@ -25,8 +25,8 @@ AVRFLAGS += -U flash:w:main.hex ${AVROBJCOPY} -j .eeprom --set-section-flags=.eeprom="alloc,load" \ --change-section-lma .eeprom=0 -O ihex $< $@ -main.elf: main.c - ${AVRCC} ${CFLAGS} -o $@ ${@:.elf=.c} -Wl,-Map=main.map,--cref +main.elf: main.S + ${AVRCC} ${CFLAGS} -o $@ ${@:.elf=.S} -Wl,-Map=main.map,--cref avr-size -d $@ program: main.hex main.eep @@ -0,0 +1,354 @@ +#include <avr/io.h> +#include <avr/interrupt.h> + +/* + * Onewire iButton / SmartButton slave. Has the 64bit ID set below. + * + * Tested and working with a DS2482. Should mostly adhere to the standard, + * but nothing is guaranteed. + */ + +/* + * Set the 64bit ID (including 8bit CRC) here, in the order in which they are + * printed on the button (see + * <https://wiki.chaosdorf.de/images/f/fc/Smartbutton.jpg>). + * Note: The bytes are sent in reversed order. This has also been observed + * on off-the-shelf smartbuttons / iButtons. + */ +#define ADDR1 0xC4 +#define ADDR2 0x00 +#define ADDR3 0x00 +#define ADDR4 0x09 +#define ADDR5 0x7d +#define ADDR6 0x79 +#define ADDR7 0x04 +#define ADDR8 0x01 + +/* + * You should not need to change things below, unless you're not using PD3 + * as OWI data pin. + */ + + +/* + * RAM access is time-expensive and requires the X / Y / Z registers. Since + * we have neither time nor many available registers (Y and Z are used + * otherwise during the main loop), all program variables are saved in + * registers. + * + * The LCNT registers count how many microseconds have passed + * since the last low-to-high / high-to-low transition. In the main loop, + * only r28 and r29 (Y) are incremented (precisely once per microsecond), + * their GPIO counterparts are used in the ISR and synced with the registers + * both at start and end. + * + * Note: The compiler knows that these registers are forbiden thanks to + * -ffixed-28 -ffixed-29 + * + * LCNT = r28 (YL), r29 (YH) + */ +#define LCNTH r29 +#define LCNTL r28 + +/* + * The last complete command byte received from the master. Once this is + * set, we start writing data onto the bus. Reset at bus resets and once a + * command is done + */ +#define LASTCMD r20 + +/* + * command buffer, always initialized to 0 + */ +#define BUF r21 + +/* + * bitmask for the current buffer (either BUF or BYTE) position. + * Left-shifted after each bit + */ +#define POS r22 + +/* + * Position in the 8-byte address sequence + */ +#define APOS r23 + +/* + * current byte in this sequence + */ +#define BYTE r24 + +/* + * the SEARCH ROM command consits of three steps: send a bit of our address, + * send the inverted bit of our address, receive the bit the master chose + * to proceed with. the current position in this cycle is stored here + */ +#define SEARCHSTEP r25 + +#define CMD_READROM 0x33 +#define CMD_SEARCHROM 0xf0 + +#define SEARCHSTEP_BIT 0 +#define SEARCHSTEP_INV 1 +#define SEARCHSTEP_DIRECTION 2 + +#define NULLREG r1 + +.text + +.global main + +; r1: 0 +; SPL: set to end of mem by avr-gcc + +main: + ; watchdog reset after ~4 seconds + out _SFR_IO_ADDR(MCUSR), NULLREG + ldi r16, (_BV(WDCE) | _BV(WDE)) + out _SFR_IO_ADDR(WDTCR), r16 + ldi r16, (_BV(WDE) | _BV(WDP3)) + out _SFR_IO_ADDR(WDTCR), r16 + + ; rising edge for reset/presence signals and reading data, + ; falling edge for writing. + ldi r16, _BV(ISC10) + out _SFR_IO_ADDR(MCUCR), r16 + ldi r16, _BV(INT1) + out _SFR_IO_ADDR(GIMSK), r16 + + ; disable Analog Comparator + sbi _SFR_IO_ADDR(ACSR), ACD + + ; disable USI / USART + sbi _SFR_IO_ADDR(PRR), PRUSI + sbi _SFR_IO_ADDR(PRR), PRUSART + + clr POS + clr APOS + clr _SFR_IO_ADDR(DDRD) + clr _SFR_IO_ADDR(PORTD) + sbi _SFR_IO_ADDR(DDRB), PB2 + cbi _SFR_IO_ADDR(PORTB), PB2 + + sei + + clr LCNTL + clr LCNTH +loop: + adiw LCNTL, 1 + wdr + nop + nop + nop + nop + rjmp loop + +delay_short: + ldi r16, 15 + wdr + wdr + wdr + wdr + subi r16, 1 + cpi r16, 0 + brne .-14 + ret + +delay_long: + ldi r16, 120 + wdr + wdr + wdr + wdr + subi r16, 1 + cpi r16, 0 + brne .-14 + ret + +.global INT1_vect + +INT1_vect: + sbis _SFR_IO_ADDR(PIND), PD3 + rjmp check_cmd + + ; Read OWI command + cpi LCNTH, 0 + breq check_lastcmd + + ; send presence signal + sbi _SFR_IO_ADDR(DDRD), PD3 + rcall delay_long + cbi _SFR_IO_ADDR(DDRD), PD3 + clr LASTCMD + clr BUF + ldi POS, 1 + clr APOS + wdr + in r16, _SFR_IO_ADDR(GIFR) + ori r16, _BV(INTF1) + out _SFR_IO_ADDR(GIFR), r16 + reti + +check_lastcmd: + cpi LASTCMD, 0 + brne check_lastcmd_nope + cpi LCNTL, 16 + brsh check_lastcmd_lcntl_done + or BUF, POS +check_lastcmd_lcntl_done: + cpi POS, 0x80 + breq command_received + lsl POS +check_lastcmd_nope: + reti + +command_received: + mov LASTCMD, BUF + ldi POS, 1 + clr APOS + ldi BYTE, ~ADDR8 + ldi SEARCHSTEP, SEARCHSTEP_BIT + in r16, _SFR_IO_ADDR(GIFR) + ori r16, _BV(INTF1) + out _SFR_IO_ADDR(GIFR), r16 + reti + +check_cmd: + cpi LASTCMD, CMD_READROM + brne check_cmd_searchrom + + ; we got READ ROM + mov r16, BYTE + and r16, POS + breq pos_bit_is_null + + ; (BYTE & POS) is true -> ADDRx has a 0 bit, keep data low + sbi _SFR_IO_ADDR(DDRD), PD3 + rcall delay_short + cbi _SFR_IO_ADDR(DDRD), PD3 + wdr + in r16, _SFR_IO_ADDR(GIFR) + ori r16, _BV(INTF1) + out _SFR_IO_ADDR(GIFR), r16 + +pos_bit_is_null: + cpi POS, 0x80 + breq pos_is_0x80 + lsl POS + rjmp check_cmd_cleanup + +pos_is_0x80: + ldi POS, 1 + ; Put next ADDRx into BYTE or reset state if we sent all 8 + ; bytes. Again, a RAM array is too expensive, and both + ; if/elseif and case/when chains are expensive too. What + ; happens here is the following: + ; + ; APOS is stored in r28, BYTE in r29 (both are written + ; back at the end of the block). Then APOS is checked for + ; 1 / 2 / 3 /... in turn and the corresponding address set + ; where appropriate. Since there are no shortcuts, this + ; block has a constant execution time of 4us + ; (compared to ~10us with an if/else if chain and -Os) + inc APOS ;APOS++ + cpi APOS, 1 + brne .+2 ; if (APOS == 1) { + ldi BYTE, ~ADDR7 + cpi APOS, 2 ; } + brne .+2 ; else if (APOS == 2) { + ldi BYTE, ~ADDR6 + cpi APOS, 3 ; } + brne .+2 ; else if (APOS == 3) { + ldi BYTE, ~ADDR5 + cpi APOS, 4 ; } + brne .+2 ; else if (APOS == 4) { + ldi BYTE, ~ADDR4 + cpi APOS, 5 ; } + brne .+2 ; else if (APOS == 5) { + ldi BYTE, ~ADDR3 + cpi APOS, 6 ; } + brne .+2 ; else if (APOS == 6) { + ldi BYTE, ~ADDR2 + cpi APOS, 7 ; } + brne .+2 ; else if (APOS == 7) { + ldi BYTE, ~ADDR1 + cpi APOS, 8 ; } + brne .+4 ; if (APOS == 8) { + clr LASTCMD + clr BUF ; } + rjmp check_cmd_cleanup + + +check_cmd_searchrom: + cpi LASTCMD, CMD_SEARCHROM + brne check_cmd_cleanup + + cpi SEARCHSTEP, SEARCHSTEP_BIT + brne check_searchstep_2 + mov r16, BYTE + and r16, POS + brne check_searchstep_2 + rjmp send_ack + +check_searchstep_2: + cpi SEARCHSTEP, SEARCHSTEP_INV + brne check_search_lt_direction + mov r16, BYTE + and r16, POS + breq check_search_lt_direction + +send_ack: + rcall delay_short + cbi _SFR_IO_ADDR(DDRD), PD3 + wdr + in r16, _SFR_IO_ADDR(GIFR) + ori r16, _BV(INTF1) + out _SFR_IO_ADDR(GIFR), r16 + +check_search_lt_direction: + cpi SEARCHSTEP, SEARCHSTEP_DIRECTION + brge check_pos + inc SEARCHSTEP +check_pos: + cpi POS, 0x80 + breq check_pos_else + lsl POS + ldi SEARCHSTEP, SEARCHSTEP_BIT + rjmp check_cmd_cleanup +check_pos_else: + ldi SEARCHSTEP, SEARCHSTEP_BIT + ldi POS, 1 + + ; see comments above + inc APOS + cpi APOS, 1 + brne .+2 + ldi BYTE, ~ADDR7 + cpi APOS, 2 + brne .+2 + ldi BYTE, ~ADDR6 + cpi APOS, 3 + brne .+2 + ldi BYTE, ~ADDR5 + cpi APOS, 4 + brne .+2 + ldi BYTE, ~ADDR4 + cpi APOS, 5 + brne .+2 + ldi BYTE, ~ADDR3 + cpi APOS, 6 + brne .+2 + ldi BYTE, ~ADDR2 + cpi APOS, 7 + brne .+2 + ldi BYTE, ~ADDR1 + cpi APOS, 8 + brne .+4 + clr LASTCMD + clr BUF + + +check_cmd_cleanup: + clr LCNTH + ldi LCNTL, 1 + reti |