From 9f3781a3eba5d4c31e573777f17aa54b7f1a6b83 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Mon, 26 Oct 2020 13:51:07 +0100 Subject: [PATCH] Implement USI peripheral slave statemachine - Implement a TWI slave using the USI peripheral found in AVR tiny MCUs - using attiny85 as target (has no bootloader section!) - USI peripheral in general needs clock stretching support from the master since the whole statemachine is software based. For now the actual writing to flash/eeprom is also done during clock stretching (like original twiboot implementation). This might be changed later. - ACK/NAK handling is different: For TWI peripheral the ACK/NAK of the *next* byte has to be returned by TWI_data_write(). For USI peripheral the ACK/NAK of the *current* byte needs to be returned. For now the TWI version remains in the code and might be changed later. --- Makefile | 16 ++++- main.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3613287..0233b45 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ TARGET = twiboot SOURCE = $(wildcard *.c) # select MCU -MCU = atmega88 +MCU = attiny85 AVRDUDE_PROG := -c avr910 -b 115200 -P /dev/ttyUSB0 #AVRDUDE_PROG := -c dragon_isp -P usb @@ -58,10 +58,22 @@ AVRDUDE_FUSES=lfuse:w:0xc2:m hfuse:w:0xdc:m efuse:w:0xfd:m BOOTLOADER_START=0x7C00 endif +ifeq ($(MCU), attiny85) +# attiny85: +# Fuse L: 0xe2 (8Mhz internal RC-Osz.) +# Fuse H: 0xdd (2.7V BOD) +# Fuse E: 0xfe (self programming enable) +AVRDUDE_MCU=t85 +AVRDUDE_FUSES=lfuse:w:0xe2:m hfuse:w:0xdd:m efuse:w:0xfe:m + +BOOTLOADER_START=0x1C00 +CFLAGS_TARGET=-DUSE_CLOCKSTRETCH=1 +endif + # --------------------------------------------------------------------------- CFLAGS = -pipe -g -Os -mmcu=$(MCU) -Wall -fdata-sections -ffunction-sections -CFLAGS += -Wa,-adhlns=$(*F).lst -DBOOTLOADER_START=$(BOOTLOADER_START) +CFLAGS += -Wa,-adhlns=$(*F).lst -DBOOTLOADER_START=$(BOOTLOADER_START) $(CFLAGS_TARGET) LDFLAGS = -Wl,-Map,$(@:.elf=.map),--cref,--relax,--gc-sections,--section-start=.text=$(BOOTLOADER_START) LDFLAGS += -nostartfiles diff --git a/main.c b/main.c index e5062e2..f51e3a3 100644 --- a/main.c +++ b/main.c @@ -59,6 +59,33 @@ #define LED_OFF() #endif /* LED_SUPPORT */ +#if !defined(TWCR) && defined(USICR) +#define USI_PIN_INIT() { PORTB |= ((1< prepare ACK/NAK */ + else if (state == USI_STATE_SLA) + { + bcnt = 0; + + /* SLA+W received -> send ACK */ + if (data == ((TWI_ADDRESS<<1) | 0x00)) + { + LED_RT_ON(); + usi_state = USI_STATE_SLAW_ACK | USI_WAIT_FOR_ACK | USI_ENABLE_SDA_OUTPUT | USI_ENABLE_SCL_HOLD; + USIDR = 0x00; + } + /* SLA+R received -> send ACK */ + else if (data == ((TWI_ADDRESS<<1) | 0x01)) + { + LED_RT_ON(); + usi_state = USI_STATE_SLAR_ACK | USI_WAIT_FOR_ACK | USI_ENABLE_SDA_OUTPUT | USI_ENABLE_SCL_HOLD; + USIDR = 0x00; + } + /* not addressed -> send NAK */ + else + { + usi_state = USI_STATE_NAK | USI_WAIT_FOR_ACK | USI_ENABLE_SDA_OUTPUT | USI_ENABLE_SCL_HOLD; + USIDR = 0x80; + } + } + /* sent NAK -> go to idle */ + else if (state == USI_STATE_NAK) + { + usi_state = USI_STATE_IDLE; + } + /* sent ACK after SLA+W -> wait for data */ + /* sent ACK after DAT+W -> wait for more data */ + else if ((state == USI_STATE_SLAW_ACK) || + (state == USI_STATE_DATW_ACK) + ) + { + usi_state = USI_STATE_DATW | USI_ENABLE_SCL_HOLD; + } + /* data received -> send ACK/NAK */ + else if (state == USI_STATE_DATW) + { + if (TWI_data_write(bcnt++, data)) + { + usi_state = USI_STATE_DATW_ACK | USI_WAIT_FOR_ACK | USI_ENABLE_SDA_OUTPUT | USI_ENABLE_SCL_HOLD; + USIDR = 0x00; + } + else + { + usi_state = USI_STATE_NAK | USI_WAIT_FOR_ACK | USI_ENABLE_SDA_OUTPUT | USI_ENABLE_SCL_HOLD; + USIDR = 0x80; + } + } + /* sent ACK after SLA+R -> send data */ + /* received ACK after DAT+R -> send more data */ + else if ((state == USI_STATE_SLAR_ACK) || + ((state == USI_STATE_DATR_ACK) && !(data & 0x01)) + ) + { + USIDR = TWI_data_read(bcnt++); + usi_state = USI_STATE_DATR | USI_ENABLE_SDA_OUTPUT | USI_ENABLE_SCL_HOLD; + } + /* sent data after SLA+R -> receive ACK/NAK */ + else if (state == USI_STATE_DATR) + { + usi_state = USI_STATE_DATR_ACK | USI_WAIT_FOR_ACK | USI_ENABLE_SCL_HOLD; + USIDR = 0x80; + } + /* received NAK after DAT+R -> go to idle */ + else if ((state == USI_STATE_DATR_ACK) && (data & 0x01)) + { + usi_state = USI_STATE_IDLE; + } + /* default -> go to idle */ + else + { + usi_state = USI_STATE_IDLE; + } + + /* set SDA direction according to current state */ + if (usi_state & USI_ENABLE_SDA_OUTPUT) + { + USI_PIN_SDA_OUTPUT(); + } + else + { + USI_PIN_SDA_INPUT(); + } + + if (usi_state & USI_ENABLE_SCL_HOLD) + { + /* Enable TWI Mode, hold SCL low after counter overflow, count both SCL edges */ + USICR = (1<