Compare commits
4 Commits
c5f5a74783
...
baf5a895b5
Author | SHA1 | Date |
---|---|---|
Olaf Rempel | baf5a895b5 | |
Olaf Rempel | f4952aaa63 | |
Olaf Rempel | 9f3781a3eb | |
Olaf Rempel | df56c54697 |
16
Makefile
16
Makefile
|
@ -8,7 +8,7 @@ TARGET = twiboot
|
||||||
SOURCE = $(wildcard *.c)
|
SOURCE = $(wildcard *.c)
|
||||||
|
|
||||||
# select MCU
|
# select MCU
|
||||||
MCU = atmega88
|
MCU = attiny85
|
||||||
|
|
||||||
AVRDUDE_PROG := -c avr910 -b 115200 -P /dev/ttyUSB0
|
AVRDUDE_PROG := -c avr910 -b 115200 -P /dev/ttyUSB0
|
||||||
#AVRDUDE_PROG := -c dragon_isp -P usb
|
#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
|
BOOTLOADER_START=0x7C00
|
||||||
endif
|
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 -DVIRTUAL_BOOT_SECTION=1
|
||||||
|
endif
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
CFLAGS = -pipe -g -Os -mmcu=$(MCU) -Wall -fdata-sections -ffunction-sections
|
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 = -Wl,-Map,$(@:.elf=.map),--cref,--relax,--gc-sections,--section-start=.text=$(BOOTLOADER_START)
|
||||||
LDFLAGS += -nostartfiles
|
LDFLAGS += -nostartfiles
|
||||||
|
|
||||||
|
|
26
README.md
26
README.md
|
@ -1,5 +1,5 @@
|
||||||
# twiboot - a TWI / I2C bootloader for AVR MCUs ##
|
# twiboot - a TWI / I2C bootloader for AVR MCUs ##
|
||||||
twiboot is a simple/small bootloader for AVR MCUs with integrated TWI peripheral written in C.
|
twiboot is a simple/small bootloader for AVR MCUs written in C. It uses the integrated TWI or USI peripheral of the controller to implement a I2C slave.
|
||||||
It was originally created to update I2C controlled BLMCs (Brushless Motor Controller) without an AVR ISP adapter.
|
It was originally created to update I2C controlled BLMCs (Brushless Motor Controller) without an AVR ISP adapter.
|
||||||
|
|
||||||
twiboot acts as a slave device on a TWI/I2C bus and allows reading/writing of the internal flash memory.
|
twiboot acts as a slave device on a TWI/I2C bus and allows reading/writing of the internal flash memory.
|
||||||
|
@ -10,6 +10,7 @@ Currently the following AVR MCUs are supported:
|
||||||
|
|
||||||
AVR MCU | Flash bytes used (.text + .data) | Bootloader region size
|
AVR MCU | Flash bytes used (.text + .data) | Bootloader region size
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
|
attiny85 | 956 (0x3BC) | 512 words
|
||||||
atmega8 | 802 (0x322) | 512 words
|
atmega8 | 802 (0x322) | 512 words
|
||||||
atmega88 | 826 (0x33A) | 512 words
|
atmega88 | 826 (0x33A) | 512 words
|
||||||
atmega168 | 826 (0x33A) | 512 words
|
atmega168 | 826 (0x33A) | 512 words
|
||||||
|
@ -19,9 +20,10 @@ atmega328p | 826 (0x33A) | 512 words
|
||||||
|
|
||||||
|
|
||||||
## Operation ##
|
## Operation ##
|
||||||
twiboot is installed in the bootloader memory region and executed directly after reset (BOOTRST fuse is programmed).
|
twiboot is installed in the bootloader section and executed directly after reset (BOOTRST fuse is programmed).
|
||||||
|
For MCUs without bootloader section see [Virtual bootloader section](#virtual-bootloader-section) below.
|
||||||
|
|
||||||
While running, twiboot configures the TWI peripheral as slave device and waits for valid protocol messages
|
While running, twiboot configures the TWI/USI peripheral as slave device and waits for valid protocol messages
|
||||||
directed to its address on the TWI/I2C bus. The slave address is configured during compile time of twiboot.
|
directed to its address on the TWI/I2C bus. The slave address is configured during compile time of twiboot.
|
||||||
When receiving no messages for 1000ms after reset, the bootloader exits and executes the main application at address 0x0000.
|
When receiving no messages for 1000ms after reset, the bootloader exits and executes the main application at address 0x0000.
|
||||||
|
|
||||||
|
@ -38,6 +40,19 @@ One LED will flash with a frequency of 20Hz while twiboot is active (including b
|
||||||
A second LED will flash when the bootloader is addressed on the TWI/I2C bus.
|
A second LED will flash when the bootloader is addressed on the TWI/I2C bus.
|
||||||
|
|
||||||
|
|
||||||
|
### Virtual Bootloader Section ###
|
||||||
|
For MCUs without bootloader section twiboot will patch the vector table on the fly during flash programming to stay active.
|
||||||
|
The reset vector is patched to execute twiboot instead of the application code.
|
||||||
|
|
||||||
|
Another vector entry will be patched to store the original entry point of the application.
|
||||||
|
This vector entry is overridden and MUST NOT be used by the application.
|
||||||
|
twiboot uses this vector to start the application after the initial timeout.
|
||||||
|
|
||||||
|
This live patching changes the content of the vector table, which would result in a verification error after programming.
|
||||||
|
To counter this kind of error, twiboot caches the original vector table entries in RAM and return those on a read command.
|
||||||
|
The real content of the vector table is only returned after a reset.
|
||||||
|
|
||||||
|
|
||||||
## Build and install twiboot ##
|
## Build and install twiboot ##
|
||||||
twiboot uses gcc, avr-libc and GNU Make for building, avrdude is used for flashing the MCU.
|
twiboot uses gcc, avr-libc and GNU Make for building, avrdude is used for flashing the MCU.
|
||||||
The build and install procedures are only tested under linux.
|
The build and install procedures are only tested under linux.
|
||||||
|
@ -102,8 +117,3 @@ Please note that there are some TWI/I2C masters that do not support clockstretch
|
||||||
|
|
||||||
## Development ##
|
## Development ##
|
||||||
Issue reports, feature requests, patches or simply success stories are much appreciated.
|
Issue reports, feature requests, patches or simply success stories are much appreciated.
|
||||||
|
|
||||||
|
|
||||||
## Roadmap ##
|
|
||||||
Some ideas that I want to investigate / implement in twiboot:
|
|
||||||
- support AVR TINYs (USI peripheral, no bootloader fuse, no Read-While-Write flash)
|
|
||||||
|
|
340
main.c
340
main.c
|
@ -1,5 +1,5 @@
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Copyright (C) 11/2019 by Olaf Rempel *
|
* Copyright (C) 10/2020 by Olaf Rempel *
|
||||||
* razzor@kopf-tisch.de *
|
* razzor@kopf-tisch.de *
|
||||||
* *
|
* *
|
||||||
* This program is free software; you can redistribute it and/or modify *
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
@ -21,27 +21,38 @@
|
||||||
#include <avr/boot.h>
|
#include <avr/boot.h>
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
|
|
||||||
#define VERSION_STRING "TWIBOOT v3.0"
|
#define VERSION_STRING "TWIBOOT v3.1"
|
||||||
#define EEPROM_SUPPORT 1
|
#define EEPROM_SUPPORT 1
|
||||||
#define LED_SUPPORT 1
|
#define LED_SUPPORT 1
|
||||||
#define USE_CLOCKSTRETCH 0
|
|
||||||
|
|
||||||
#define F_CPU 8000000ULL
|
#ifndef USE_CLOCKSTRETCH
|
||||||
#define TIMER_DIVISOR 1024
|
#define USE_CLOCKSTRETCH 0
|
||||||
#define TIMER_IRQFREQ_MS 25
|
#endif
|
||||||
#define TIMEOUT_MS 1000
|
|
||||||
|
#ifndef VIRTUAL_BOOT_SECTION
|
||||||
|
#define VIRTUAL_BOOT_SECTION 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TWI_ADDRESS
|
||||||
|
#define TWI_ADDRESS 0x29
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define F_CPU 8000000ULL
|
||||||
|
#define TIMER_DIVISOR 1024
|
||||||
|
#define TIMER_IRQFREQ_MS 25
|
||||||
|
#define TIMEOUT_MS 1000
|
||||||
|
|
||||||
#define TIMER_MSEC2TICKS(x) ((x * F_CPU) / (TIMER_DIVISOR * 1000ULL))
|
#define TIMER_MSEC2TICKS(x) ((x * F_CPU) / (TIMER_DIVISOR * 1000ULL))
|
||||||
#define TIMER_MSEC2IRQCNT(x) (x / TIMER_IRQFREQ_MS)
|
#define TIMER_MSEC2IRQCNT(x) (x / TIMER_IRQFREQ_MS)
|
||||||
|
|
||||||
#if LED_SUPPORT
|
#if (LED_SUPPORT)
|
||||||
#define LED_INIT() DDRB = ((1<<PORTB4) | (1<<PORTB5))
|
#define LED_INIT() DDRB = ((1<<PORTB4) | (1<<PORTB5))
|
||||||
#define LED_RT_ON() PORTB |= (1<<PORTB4)
|
#define LED_RT_ON() PORTB |= (1<<PORTB4)
|
||||||
#define LED_RT_OFF() PORTB &= ~(1<<PORTB4)
|
#define LED_RT_OFF() PORTB &= ~(1<<PORTB4)
|
||||||
#define LED_GN_ON() PORTB |= (1<<PORTB5)
|
#define LED_GN_ON() PORTB |= (1<<PORTB5)
|
||||||
#define LED_GN_OFF() PORTB &= ~(1<<PORTB5)
|
#define LED_GN_OFF() PORTB &= ~(1<<PORTB5)
|
||||||
#define LED_GN_TOGGLE() PORTB ^= (1<<PORTB5)
|
#define LED_GN_TOGGLE() PORTB ^= (1<<PORTB5)
|
||||||
#define LED_OFF() PORTB = 0x00
|
#define LED_OFF() PORTB = 0x00
|
||||||
#else
|
#else
|
||||||
#define LED_INIT()
|
#define LED_INIT()
|
||||||
#define LED_RT_ON()
|
#define LED_RT_ON()
|
||||||
|
@ -50,10 +61,50 @@
|
||||||
#define LED_GN_OFF()
|
#define LED_GN_OFF()
|
||||||
#define LED_GN_TOGGLE()
|
#define LED_GN_TOGGLE()
|
||||||
#define LED_OFF()
|
#define LED_OFF()
|
||||||
|
#endif /* LED_SUPPORT */
|
||||||
|
|
||||||
|
#if !defined(TWCR) && defined(USICR)
|
||||||
|
#define USI_PIN_INIT() { PORTB |= ((1<<PORTB0) | (1<<PORTB2)); \
|
||||||
|
DDRB |= (1<<PORTB2); \
|
||||||
|
}
|
||||||
|
#define USI_PIN_SDA_INPUT() DDRB &= ~(1<<PORTB0)
|
||||||
|
#define USI_PIN_SDA_OUTPUT() DDRB |= (1<<PORTB0)
|
||||||
|
#define USI_PIN_SCL() (PINB & (1<<PINB2))
|
||||||
|
|
||||||
|
#if (USE_CLOCKSTRETCH == 0)
|
||||||
|
#error "USI peripheral requires enabled USE_CLOCKSTRETCH"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef TWI_ADDRESS
|
#define USI_STATE_MASK 0x0F
|
||||||
#define TWI_ADDRESS 0x29
|
#define USI_STATE_IDLE 0x00 /* wait for Start Condition */
|
||||||
|
#define USI_STATE_SLA 0x01 /* wait for Slave Address */
|
||||||
|
#define USI_STATE_SLAW_ACK 0x02 /* ACK Slave Address + Write (Master writes) */
|
||||||
|
#define USI_STATE_SLAR_ACK 0x03 /* ACK Slave Address + Read (Master reads) */
|
||||||
|
#define USI_STATE_NAK 0x04 /* send NAK */
|
||||||
|
#define USI_STATE_DATW 0x05 /* receive Data */
|
||||||
|
#define USI_STATE_DATW_ACK 0x06 /* transmit ACK for received Data */
|
||||||
|
#define USI_STATE_DATR 0x07 /* transmit Data */
|
||||||
|
#define USI_STATE_DATR_ACK 0x08 /* receive ACK for transmitted Data */
|
||||||
|
#define USI_WAIT_FOR_ACK 0x10 /* wait for ACK bit (2 SCL clock edges) */
|
||||||
|
#define USI_ENABLE_SDA_OUTPUT 0x20 /* SDA is output (slave transmitting) */
|
||||||
|
#define USI_ENABLE_SCL_HOLD 0x40 /* Hold SCL low after clock overflow */
|
||||||
|
#endif /* !defined(TWCR) && defined(USICR) */
|
||||||
|
|
||||||
|
#if (VIRTUAL_BOOT_SECTION)
|
||||||
|
/* unused vector to store application start address */
|
||||||
|
#define APPVECT_NUM EE_RDY_vect_num
|
||||||
|
|
||||||
|
/* each vector table entry is a 2byte RJMP opcode */
|
||||||
|
#define RSTVECT_ADDR 0x0000
|
||||||
|
#define APPVECT_ADDR (APPVECT_NUM * 2)
|
||||||
|
#define RSTVECT_PAGE_OFFSET (RSTVECT_ADDR % SPM_PAGESIZE)
|
||||||
|
#define APPVECT_PAGE_OFFSET (APPVECT_ADDR % SPM_PAGESIZE)
|
||||||
|
|
||||||
|
/* create RJMP opcode for the vector table */
|
||||||
|
#define OPCODE_RJMP(addr) (((addr) & 0x0FFF) | 0xC000)
|
||||||
|
|
||||||
|
#elif (!defined(ASRE) && !defined (RWWSRE))
|
||||||
|
#error "Device without bootloader section requires VIRTUAL_BOOT_SECTION"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SLA+R */
|
/* SLA+R */
|
||||||
|
@ -135,6 +186,12 @@ static uint8_t cmd = CMD_WAIT;
|
||||||
static uint8_t buf[SPM_PAGESIZE];
|
static uint8_t buf[SPM_PAGESIZE];
|
||||||
static uint16_t addr;
|
static uint16_t addr;
|
||||||
|
|
||||||
|
#if (VIRTUAL_BOOT_SECTION)
|
||||||
|
/* reset/application vectors received from host, needed for verify read */
|
||||||
|
static uint8_t rstvect_save[2];
|
||||||
|
static uint8_t appvect_save[2];
|
||||||
|
#endif /* (VIRTUAL_BOOT_SECTION) */
|
||||||
|
|
||||||
/* *************************************************************************
|
/* *************************************************************************
|
||||||
* write_flash_page
|
* write_flash_page
|
||||||
* ************************************************************************* */
|
* ************************************************************************* */
|
||||||
|
@ -144,6 +201,29 @@ static void write_flash_page(void)
|
||||||
uint8_t size = SPM_PAGESIZE;
|
uint8_t size = SPM_PAGESIZE;
|
||||||
uint8_t *p = buf;
|
uint8_t *p = buf;
|
||||||
|
|
||||||
|
#if (VIRTUAL_BOOT_SECTION)
|
||||||
|
if (pagestart == (RSTVECT_ADDR & ~(SPM_PAGESIZE -1)))
|
||||||
|
{
|
||||||
|
/* save original vectors for verify read */
|
||||||
|
rstvect_save[0] = buf[RSTVECT_PAGE_OFFSET];
|
||||||
|
rstvect_save[1] = buf[RSTVECT_PAGE_OFFSET + 1];
|
||||||
|
appvect_save[0] = buf[APPVECT_PAGE_OFFSET];
|
||||||
|
appvect_save[1] = buf[APPVECT_PAGE_OFFSET + 1];
|
||||||
|
|
||||||
|
/* replace reset vector with jump to bootloader address */
|
||||||
|
uint16_t rst_vector = OPCODE_RJMP(BOOTLOADER_START -1);
|
||||||
|
buf[RSTVECT_PAGE_OFFSET] = (rst_vector & 0xFF);
|
||||||
|
buf[RSTVECT_PAGE_OFFSET + 1] = (rst_vector >> 8) & 0xFF;
|
||||||
|
|
||||||
|
/* replace application vector with jump to original reset vector */
|
||||||
|
uint16_t app_vector = rstvect_save[0] | (rstvect_save[1] << 8);
|
||||||
|
app_vector = OPCODE_RJMP(app_vector - APPVECT_NUM);
|
||||||
|
|
||||||
|
buf[APPVECT_PAGE_OFFSET] = (app_vector & 0xFF);
|
||||||
|
buf[APPVECT_PAGE_OFFSET + 1] = (app_vector >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
#endif /* (VIRTUAL_BOOT_SECTION) */
|
||||||
|
|
||||||
if (pagestart < BOOTLOADER_START)
|
if (pagestart < BOOTLOADER_START)
|
||||||
{
|
{
|
||||||
boot_page_erase(pagestart);
|
boot_page_erase(pagestart);
|
||||||
|
@ -160,7 +240,11 @@ static void write_flash_page(void)
|
||||||
|
|
||||||
boot_page_write(pagestart);
|
boot_page_write(pagestart);
|
||||||
boot_spm_busy_wait();
|
boot_spm_busy_wait();
|
||||||
|
|
||||||
|
#if defined (ASRE) || defined (RWWSRE)
|
||||||
|
/* only required for bootloader section */
|
||||||
boot_rww_enable();
|
boot_rww_enable();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} /* write_flash_page */
|
} /* write_flash_page */
|
||||||
|
|
||||||
|
@ -365,7 +449,33 @@ static uint8_t TWI_data_read(uint8_t bcnt)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_ACCESS_FLASH:
|
case CMD_ACCESS_FLASH:
|
||||||
data = pgm_read_byte_near(addr++);
|
switch (addr)
|
||||||
|
{
|
||||||
|
/* return cached values for verify read */
|
||||||
|
#if (VIRTUAL_BOOT_SECTION)
|
||||||
|
case RSTVECT_ADDR:
|
||||||
|
data = rstvect_save[0];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (RSTVECT_ADDR + 1):
|
||||||
|
data = rstvect_save[1];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case APPVECT_ADDR:
|
||||||
|
data = appvect_save[0];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (APPVECT_ADDR + 1):
|
||||||
|
data = appvect_save[1];
|
||||||
|
break;
|
||||||
|
#endif /* (VIRTUAL_BOOT_SECTION) */
|
||||||
|
|
||||||
|
default:
|
||||||
|
data = pgm_read_byte_near(addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#if (EEPROM_SUPPORT)
|
#if (EEPROM_SUPPORT)
|
||||||
|
@ -383,6 +493,7 @@ static uint8_t TWI_data_read(uint8_t bcnt)
|
||||||
} /* TWI_data_read */
|
} /* TWI_data_read */
|
||||||
|
|
||||||
|
|
||||||
|
#if defined (TWCR)
|
||||||
/* *************************************************************************
|
/* *************************************************************************
|
||||||
* TWI_vect
|
* TWI_vect
|
||||||
* ************************************************************************* */
|
* ************************************************************************* */
|
||||||
|
@ -403,6 +514,9 @@ static void TWI_vect(void)
|
||||||
case 0x80:
|
case 0x80:
|
||||||
if (TWI_data_write(bcnt++, TWDR) == 0x00)
|
if (TWI_data_write(bcnt++, TWDR) == 0x00)
|
||||||
{
|
{
|
||||||
|
/* the ACK returned by TWI_data_write() is not for the current
|
||||||
|
* data in TWDR, but for the next byte received
|
||||||
|
*/
|
||||||
control &= ~(1<<TWEA);
|
control &= ~(1<<TWEA);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -411,6 +525,7 @@ static void TWI_vect(void)
|
||||||
case 0xA8:
|
case 0xA8:
|
||||||
bcnt = 0;
|
bcnt = 0;
|
||||||
LED_RT_ON();
|
LED_RT_ON();
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
/* prev. SLA+R, data sent, ACK returned -> send data */
|
/* prev. SLA+R, data sent, ACK returned -> send data */
|
||||||
case 0xB8:
|
case 0xB8:
|
||||||
|
@ -465,6 +580,156 @@ static void TWI_vect(void)
|
||||||
|
|
||||||
TWCR = (1<<TWINT) | control;
|
TWCR = (1<<TWINT) | control;
|
||||||
} /* TWI_vect */
|
} /* TWI_vect */
|
||||||
|
#endif /* defined (TWCR) */
|
||||||
|
|
||||||
|
#if defined (USICR)
|
||||||
|
/* *************************************************************************
|
||||||
|
* usi_statemachine
|
||||||
|
* ************************************************************************* */
|
||||||
|
static void usi_statemachine(uint8_t usisr)
|
||||||
|
{
|
||||||
|
static uint8_t usi_state;
|
||||||
|
static uint8_t bcnt;
|
||||||
|
|
||||||
|
uint8_t data = USIDR;
|
||||||
|
uint8_t state = usi_state & USI_STATE_MASK;
|
||||||
|
|
||||||
|
/* Start Condition detected */
|
||||||
|
if (usisr & (1<<USISIF))
|
||||||
|
{
|
||||||
|
/* wait until SCL goes low */
|
||||||
|
while (USI_PIN_SCL());
|
||||||
|
|
||||||
|
usi_state = USI_STATE_SLA | USI_ENABLE_SCL_HOLD;
|
||||||
|
state = USI_STATE_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop Condition detected */
|
||||||
|
if (usisr & (1<<USIPF))
|
||||||
|
{
|
||||||
|
LED_RT_OFF();
|
||||||
|
usi_state = USI_STATE_IDLE;
|
||||||
|
state = USI_STATE_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == USI_STATE_IDLE)
|
||||||
|
{
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
/* Slave Address received => 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<<USIWM1) | (1<<USIWM0) | (1<<USICS1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Enable TWI, hold SCL low only after start condition, count both SCL edges */
|
||||||
|
USICR = (1<<USIWM1) | (1<<USICS1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear start/overflow/stop condition flags */
|
||||||
|
usisr &= ((1<<USISIF) | (1<<USIOIF) | (1<<USIPF));
|
||||||
|
if (usi_state & USI_WAIT_FOR_ACK)
|
||||||
|
{
|
||||||
|
/* count 2 SCL edges (ACK/NAK bit) */
|
||||||
|
USISR = usisr | ((16 -2)<<USICNT0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* count 16 SCL edges (8bit data) */
|
||||||
|
USISR = usisr | ((16 -16)<<USICNT0);
|
||||||
|
}
|
||||||
|
} /* usi_statemachine */
|
||||||
|
#endif /* defined (USICR) */
|
||||||
|
|
||||||
|
|
||||||
/* *************************************************************************
|
/* *************************************************************************
|
||||||
|
@ -491,7 +756,11 @@ static void TIMER0_OVF_vect(void)
|
||||||
} /* TIMER0_OVF_vect */
|
} /* TIMER0_OVF_vect */
|
||||||
|
|
||||||
|
|
||||||
static void (*jump_to_app)(void) __attribute__ ((noreturn)) = 0x0000;
|
#if (VIRTUAL_BOOT_SECTION)
|
||||||
|
static void (*jump_to_app)(void) __attribute__ ((noreturn)) = (void*)APPVECT_ADDR;
|
||||||
|
#else
|
||||||
|
static void (*jump_to_app)(void) __attribute__ ((noreturn)) = (void*)0x0000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* *************************************************************************
|
/* *************************************************************************
|
||||||
|
@ -542,6 +811,14 @@ int main(void)
|
||||||
LED_INIT();
|
LED_INIT();
|
||||||
LED_GN_ON();
|
LED_GN_ON();
|
||||||
|
|
||||||
|
#if (VIRTUAL_BOOT_SECTION)
|
||||||
|
/* load current values (for reading flash) */
|
||||||
|
rstvect_save[0] = pgm_read_byte_near(RSTVECT_ADDR);
|
||||||
|
rstvect_save[1] = pgm_read_byte_near(RSTVECT_ADDR + 1);
|
||||||
|
appvect_save[0] = pgm_read_byte_near(APPVECT_ADDR);
|
||||||
|
appvect_save[1] = pgm_read_byte_near(APPVECT_ADDR + 1);
|
||||||
|
#endif /* (VIRTUAL_BOOT_SECTION) */
|
||||||
|
|
||||||
/* timer0: running with F_CPU/1024 */
|
/* timer0: running with F_CPU/1024 */
|
||||||
#if defined (TCCR0)
|
#if defined (TCCR0)
|
||||||
TCCR0 = (1<<CS02) | (1<<CS00);
|
TCCR0 = (1<<CS02) | (1<<CS00);
|
||||||
|
@ -551,16 +828,30 @@ int main(void)
|
||||||
#error "TCCR0(B) not defined"
|
#error "TCCR0(B) not defined"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined (TWCR)
|
||||||
/* TWI init: set address, auto ACKs */
|
/* TWI init: set address, auto ACKs */
|
||||||
TWAR = (TWI_ADDRESS<<1);
|
TWAR = (TWI_ADDRESS<<1);
|
||||||
TWCR = (1<<TWEA) | (1<<TWEN);
|
TWCR = (1<<TWEA) | (1<<TWEN);
|
||||||
|
#elif defined (USICR)
|
||||||
|
USI_PIN_INIT();
|
||||||
|
usi_statemachine(0x00);
|
||||||
|
#else
|
||||||
|
#error "No TWI/USI peripheral found"
|
||||||
|
#endif
|
||||||
|
|
||||||
while (cmd != CMD_BOOT_APPLICATION)
|
while (cmd != CMD_BOOT_APPLICATION)
|
||||||
{
|
{
|
||||||
|
#if defined (TWCR)
|
||||||
if (TWCR & (1<<TWINT))
|
if (TWCR & (1<<TWINT))
|
||||||
{
|
{
|
||||||
TWI_vect();
|
TWI_vect();
|
||||||
}
|
}
|
||||||
|
#elif defined (USICR)
|
||||||
|
if (USISR & ((1<<USISIF) | (1<<USIOIF) | (1<<USIPF)))
|
||||||
|
{
|
||||||
|
usi_statemachine(USISR);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined (TIFR)
|
#if defined (TIFR)
|
||||||
if (TIFR & (1<<TOV0))
|
if (TIFR & (1<<TOV0))
|
||||||
|
@ -579,8 +870,13 @@ int main(void)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined (TWCR)
|
||||||
/* Disable TWI but keep address! */
|
/* Disable TWI but keep address! */
|
||||||
TWCR = 0x00;
|
TWCR = 0x00;
|
||||||
|
#elif defined (USICR)
|
||||||
|
/* Disable USI peripheral */
|
||||||
|
USICR = 0x00;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* disable timer0 */
|
/* disable timer0 */
|
||||||
#if defined (TCCR0)
|
#if defined (TCCR0)
|
||||||
|
@ -593,10 +889,12 @@ int main(void)
|
||||||
|
|
||||||
LED_OFF();
|
LED_OFF();
|
||||||
|
|
||||||
|
#if (LED_SUPPORT)
|
||||||
uint16_t wait = 0x0000;
|
uint16_t wait = 0x0000;
|
||||||
do {
|
do {
|
||||||
__asm volatile ("nop");
|
__asm volatile ("nop");
|
||||||
} while (--wait);
|
} while (--wait);
|
||||||
|
#endif /* (LED_SUPPORT) */
|
||||||
|
|
||||||
jump_to_app();
|
jump_to_app();
|
||||||
} /* main */
|
} /* main */
|
||||||
|
|
Loading…
Reference in New Issue