From 144016f65d6465723cb0009d2b0cd509d2d3c06c Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Mon, 13 Jan 2020 22:18:33 +0100 Subject: [PATCH] Add twiboot bridge --- display.c | 2 +- ispprog.c | 322 ++++++++++++++++++++++++++++++++++++++++++++++++++- target.h | 3 + twi_master.c | 286 +++++++++++++++++++++++++++++++++++++++++++++ twi_master.h | 62 ++++++++++ uart.c | 12 ++ uart.h | 1 + 7 files changed, 683 insertions(+), 5 deletions(-) create mode 100644 twi_master.c create mode 100644 twi_master.h diff --git a/display.c b/display.c index 54cdc2c..5ff43e3 100644 --- a/display.c +++ b/display.c @@ -23,7 +23,7 @@ #if (USE_DISPLAY) static display_mode_t m_mode = DISPLAY_MODE_OFF; -static char m_buffer[24]; +static char m_buffer[32]; static uint8_t m_buffer_length = 0; static uint8_t m_buffer_pos = 0; diff --git a/ispprog.c b/ispprog.c index b2199cf..7dbaebe 100644 --- a/ispprog.c +++ b/ispprog.c @@ -26,8 +26,11 @@ #include "display.h" #include "spi_isp.h" #include "target.h" +#include "twi_master.h" #include "uart.h" +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + #define TIMER_IRQFREQ_MS 10 /* convert milliseconds to timer ticks */ @@ -41,12 +44,14 @@ #define EV_TIMEOUT 0x08 #define EV_PROG_ENTER 0x10 #define EV_PROG_LEAVE 0x20 +#define EV_PROG_ENTER_TWI 0x40 #define STATE_IDLE 0x00 /* nothing */ #define STATE_RESET_SYNC 0x01 #define STATE_RESET_RETRY 0x02 #define STATE_RESET_PROGMODE 0x03 - +#define STATE_TWI_CHECK_BL 0x04 +#define STATE_TWI_PROGMODE 0x05 #define LED_OFF 0x00 #define LED_SLOW 0x20 @@ -63,6 +68,11 @@ static uint8_t m_page_buf[256]; static avr_device_t m_device; static uint16_t m_address = 0x0000; +#if (USE_TWI_SUPPORT) +static twi_chipinfo_t m_twi_chipinfo; +static uint8_t m_twi_address; +#endif /* (USE_TWI_SUPPORT) */ + static void reset_statemachine(uint8_t events) { @@ -105,12 +115,20 @@ static void reset_statemachine(uint8_t events) timer = TIMER_MSEC2IRQCNT(0); spi_init(0); - +#if (USE_TWI_SUPPORT) + twi_init(0); +#endif /* put device in RUN mode */ RESET_INACTIVE(); m_led_mode = LED_OFF; } - else if (events & (EV_BUTTON_PRESSED | EV_PROG_ENTER)) + else if ((events & EV_PROG_ENTER) || +#if (USE_TWI_SUPPORT) + ((events & EV_BUTTON_PRESSED) && (m_twi_address == 0x00)) +#else + (events & EV_BUTTON_PRESSED) +#endif /* (USE_TWI_SUPPORT) */ + ) { reset_cause = events; events &= ~(EV_BUTTON_PRESSED | EV_PROG_ENTER); @@ -122,6 +140,29 @@ static void reset_statemachine(uint8_t events) m_state = STATE_RESET_SYNC; } +#if (USE_TWI_SUPPORT) + else if ((events & EV_PROG_ENTER_TWI) || + ((events & EV_BUTTON_PRESSED) && (m_twi_address != 0x00)) + ) + { + uint8_t result; + + reset_cause = events; + events &= ~(EV_BUTTON_PRESSED | EV_PROG_ENTER_TWI); + + reset_retries = 5; + + twi_init(1); + result = twi_switch_application(m_twi_address, BOOTTYPE_BOOTLOADER); + if (result == TWI_ERROR) + { + /* no response from target, do normal reset */ + RESET_ACTIVE(); + } + + m_state = STATE_TWI_CHECK_BL; + } +#endif /* (USE_TWI_SUPPORT) */ break; case STATE_RESET_SYNC: @@ -204,6 +245,101 @@ static void reset_statemachine(uint8_t events) } break; +#if (USE_TWI_SUPPORT) + case STATE_TWI_CHECK_BL: + if (events & EV_STATE_ENTER) + { + events &= ~(EV_STATE_ENTER); + + timer = TIMER_MSEC2IRQCNT(10); + } + else if (events & EV_TIMEOUT) + { + uint8_t result; + + events &= ~(EV_TIMEOUT); + + /* put target in RUN mode */ + RESET_INACTIVE(); + m_led_mode = LED_ON; + + result = twi_read_chipinfo(m_twi_address, &m_twi_chipinfo); + if (result == TWI_SUCCESS) + { +#if (USE_DISPLAY) + char twi_version[16 +1]; + + twi_read_version(m_twi_address, twi_version, + sizeof(twi_version) -1); + twi_version[16] = '\0'; + + display_show_string(twi_version, 0); + + avrdevice_get_by_signature(&m_device, m_twi_chipinfo.sig); + if (m_device.name[0] != '\0') + { + display_show_string(" ", 1); + display_show_string(m_device.name, 1); + } + else + { + display_show_string(" 0X", 1); + display_show_hex(m_twi_chipinfo.sig[0], 1); + display_show_hex(m_twi_chipinfo.sig[1], 1); + display_show_hex(m_twi_chipinfo.sig[2], 1); + } + + display_set_mode(DISPLAY_MODE_SCROLL_ONCE); +#endif /* (USE_DISPLAY) */ + + m_state = STATE_TWI_PROGMODE; + } + else + { + reset_retries--; + if (reset_retries > 0) + { + timer = TIMER_MSEC2IRQCNT(10); + } + else + { +#if (USE_DISPLAY) + display_show_string("0x", 0); + display_show_hex(m_twi_address, 1); + display_show_string(":NAK", 1); + display_set_mode(DISPLAY_MODE_SCROLL_ONCE); +#endif /* (USE_DISPLAY) */ + + m_state = STATE_IDLE; + } + } + } + break; + + case STATE_TWI_PROGMODE: + if (events & EV_STATE_ENTER) + { + events &= ~(EV_STATE_ENTER); + + if (reset_cause == EV_BUTTON_PRESSED) + { + m_state = STATE_IDLE; + } + } + else if (events & EV_PROG_LEAVE) + { + events &= ~(EV_PROG_LEAVE); + + m_state = STATE_IDLE; + } + + if (m_state == STATE_IDLE) + { + twi_switch_application(m_twi_address, BOOTTYPE_APPLICATION); + } + break; +#endif /* (USE_TWI_SUPPORT) */ + default: m_state = STATE_IDLE; break; @@ -380,6 +516,7 @@ static void cmd_handler_isp(uint8_t cmd) size |= uart_recv(); type = uart_recv(); + size = MIN(size, sizeof(m_page_buf)); uart_recv_buf(m_page_buf, size); if (type == 'F') @@ -487,6 +624,124 @@ static void cmd_handler_isp(uint8_t cmd) } /* cmd_handler_isp */ +#if (USE_TWI_SUPPORT) +static void cmd_handler_twi(uint8_t cmd) +{ + switch (cmd) + { + /* Enter programming mode */ + case 'P': + reset_statemachine_wait(EV_PROG_ENTER_TWI); + uart_send((m_state == STATE_TWI_PROGMODE) ? '\r' : '!'); + break; + + /* Chip erase */ + case 'e': + uart_send('\r'); + break; + + /* Read signature bytes */ + case 's': + uart_send(m_twi_chipinfo.sig[2]); + uart_send(m_twi_chipinfo.sig[1]); + uart_send(m_twi_chipinfo.sig[0]); + break; + + /* Block Write */ + case 'B': + { + uint16_t write_pos = 0; + uint16_t size; + uint8_t type; + + m_led_mode = LED_FAST; + + size = uart_recv() << 8; + size |= uart_recv(); + type = uart_recv(); + + size = MIN(size, sizeof(m_page_buf)); + uart_recv_buf(m_page_buf, size); + + memset(m_page_buf + size, 0xFF, sizeof(m_page_buf) - size); + + while (write_pos < size) + { + if (type == 'F') + { + twi_write_memory(m_twi_address, MEMTYPE_FLASH, + (m_address << 1), + m_page_buf + write_pos, + m_twi_chipinfo.page_size); + + /* when accessing flash, m_address is a word address */ + m_address += (m_twi_chipinfo.page_size >> 1); + write_pos += m_twi_chipinfo.page_size; + } + else + { + uint8_t write_size; + + write_size = MIN(size, m_twi_chipinfo.page_size); + + twi_write_memory(m_twi_address, MEMTYPE_EEPROM, + m_address, + m_page_buf + write_pos, + write_size); + + /* when accessing eeprom, m_address is a byte address */ + m_address += write_size; + write_pos += write_size; + } + } + + uart_send('\r'); + break; + } + + /* Block Read */ + case 'g': + { + uint16_t size; + uint8_t type; + + m_led_mode = LED_SLOW; + + size = uart_recv() << 8; + size |= uart_recv(); + type = uart_recv(); + + size = MIN(size, sizeof(m_page_buf)); + + if (type == 'F') + { + twi_read_memory(m_twi_address, MEMTYPE_FLASH, + (m_address << 1), + m_page_buf, size); + + m_address += (size >> 1); + } + else + { + twi_read_memory(m_twi_address, MEMTYPE_EEPROM, + m_address, + m_page_buf, size); + + m_address += size; + } + + uart_send_buf(m_page_buf, size); + break; + } + + default: + uart_send('?'); + break; + } +} /* cmd_handler_twi */ +#endif /* (USE_TWI_SUPPORT) */ + + static void cmdloop(void) __attribute__ ((noreturn)); static void cmdloop(void) { @@ -596,12 +851,67 @@ static void cmdloop(void) uart_send(sizeof(m_page_buf) & 0xFF); break; +#if (USE_TWI_SUPPORT) + case 'I': + m_twi_address = uart_recv() & 0x7F; + + if (m_twi_address != 0x00) + { + reset_statemachine_wait(EV_PROG_ENTER_TWI); + if (m_state == STATE_TWI_PROGMODE) + { + uart_send('\r'); + } + else + { + m_twi_address = 0x00; + uart_send('!'); + } + } + else + { + uart_send('\r'); + } + break; + + case 'i': + { + uint8_t twi_addr; + uint8_t write_size; + uint8_t read_size; + uint8_t result; + + twi_addr = uart_recv(); + write_size = uart_recv(); + read_size = uart_recv(); + + uart_recv_buf(m_page_buf, write_size); + + result = twi_generic(twi_addr, + m_page_buf, write_size, + m_page_buf, read_size); + + uart_send_buf(m_page_buf, read_size); + + uart_send((result == TWI_SUCCESS) ? '\r' : '!'); + } +#endif /* (USE_TWI_SUPPORT) */ + /* ESC */ case 0x1B: break; default: - cmd_handler_isp(cmd); +#if (USE_TWI_SUPPORT) + if (m_twi_address != 0x00) + { + cmd_handler_twi(cmd); + } + else +#endif /* (USE_TWI_SUPPORT) */ + { + cmd_handler_isp(cmd); + } break; } } @@ -683,6 +993,10 @@ int main(void) spi_init(0); +#if (USE_TWI_SUPPORT) + twi_init(0); +#endif + TIMER_INIT(); /* init statemachine */ diff --git a/target.h b/target.h index ee1269c..86ba17d 100644 --- a/target.h +++ b/target.h @@ -34,6 +34,7 @@ #define ISP_CHECK() (PIND & (1< +#include + +#include "target.h" +#include "twi_master.h" + +#if (USE_TWI_SUPPORT) + +#define TWI_SLA_W(addr) (addr << 1) +#define TWI_SLA_R(addr) ((addr << 1) | 0x01) + + +/* *********************************************************************** + * twi_start + * *********************************************************************** */ +static uint8_t twi_start(void) +{ + TWCR = (1< 0) + { + result = twi_master_start(TWI_SLA_W(twi_addr)); + if (result == TWI_SUCCESS) + { + uint16_t i; + + for (i = 0; i < write_size; i++) + { + result = twi_master_tx(*p_wr_data++); + if (result != TWI_SUCCESS) + { + break; + } + } + } + } + + if ((read_size > 0) && + (result == TWI_SUCCESS) + ) + { + result = twi_master_start(TWI_SLA_R(twi_addr)); + if (result == TWI_SUCCESS) + { + uint16_t i; + + for (i = 0; i < read_size; i++) + { + uint8_t ack; + + ack = (i != (read_size -1)); + result = twi_master_rx(p_rd_data++, ack); + if (result != TWI_SUCCESS) + { + break; + } + } + } + } + + twi_stop(); + return result; +} /* twi_generic */ + + +/* *********************************************************************** + * twi_switch_application + * *********************************************************************** */ +uint8_t twi_switch_application(uint8_t twi_addr, uint8_t app) +{ + uint8_t cmd[2] = { CMD_SWITCH_APPLICATION, app }; + + return twi_generic(twi_addr, cmd, sizeof(cmd), NULL, 0); +} /* twi_switch_application */ + + +/* *********************************************************************** + * twi_read_version + * *********************************************************************** */ +uint8_t twi_read_version(uint8_t twi_addr, char * p_version, + uint8_t version_length) +{ + uint8_t cmd[1] = { CMD_READ_VERSION }; + + return twi_generic(twi_addr, cmd, sizeof(cmd), + (uint8_t *)p_version, version_length); +} /* twi_read_version */ + + +/* *********************************************************************** + * twi_read_chipinfo + * *********************************************************************** */ +uint8_t twi_read_chipinfo(uint8_t twi_addr, twi_chipinfo_t * p_chipinfo) +{ + uint8_t cmd[4] = { CMD_READ_MEMORY, MEMTYPE_CHIPINFO, 0x00, 0x00 }; + + return twi_generic(twi_addr, cmd, sizeof(cmd), + (uint8_t *)p_chipinfo, sizeof(twi_chipinfo_t)); +} /* twi_read_chipinfo */ + + +/* *********************************************************************** + * twi_read_memory + * *********************************************************************** */ +uint8_t twi_read_memory(uint8_t twi_addr, uint8_t memory_type, uint16_t memory_addr, + uint8_t * p_data, uint16_t data_length) +{ + uint8_t cmd[4] = { CMD_READ_MEMORY, memory_type, + (memory_addr >> 8) & 0xFF, + (memory_addr & 0xFF) }; + + return twi_generic(twi_addr, cmd, sizeof(cmd), p_data, data_length); +} /* twi_read_memory */ + + +/* *********************************************************************** + * twi_write_memory + * *********************************************************************** */ +uint8_t twi_write_memory(uint8_t twi_addr, uint8_t memory_type, uint16_t memory_addr, + const uint8_t * p_data, uint16_t data_length) +{ + uint8_t cmd[4] = { CMD_WRITE_MEMORY, memory_type, + (memory_addr >> 8) & 0xFF, + (memory_addr & 0xFF) }; + uint8_t result; + + result = twi_master_start(TWI_SLA_W(twi_addr)); + if (result == TWI_SUCCESS) + { + uint16_t i; + + for (i = 0; i < sizeof(cmd); i++) + { + result = twi_master_tx(cmd[i]); + if (result != TWI_SUCCESS) + { + break; + } + } + } + + if (result == TWI_SUCCESS) + { + uint16_t i; + + for (i = 0; i < data_length; i++) + { + result = twi_master_tx(*p_data++); + if (result != TWI_SUCCESS) + { + break; + } + } + } + + twi_stop(); + return result; +} /* twi_read_memory */ + + +/* *********************************************************************** + * twi_init + * *********************************************************************** */ +void twi_init(uint8_t enable) +{ + if (enable) + { + TWBR = ((F_CPU / 100000) -16) / 2; + TWCR = (1< + +/* *********************************************************************** */ + +/* SLA+R */ +#define CMD_WAIT 0x00 +#define CMD_READ_VERSION 0x01 +#define CMD_READ_MEMORY 0x02 + +/* SLA+W */ +#define CMD_SWITCH_APPLICATION CMD_READ_VERSION +#define CMD_WRITE_MEMORY CMD_READ_MEMORY + +/* CMD_SWITCH_APPLICATION parameter */ +#define BOOTTYPE_BOOTLOADER 0x00 +#define BOOTTYPE_APPLICATION 0x80 + +/* CMD_{READ|WRITE}_* parameter */ +#define MEMTYPE_CHIPINFO 0x00 +#define MEMTYPE_FLASH 0x01 +#define MEMTYPE_EEPROM 0x02 + +#define TWI_SUCCESS 0x00 +#define TWI_ERROR 0x01 + +typedef struct twi_chipinfo_s +{ + uint8_t sig[3]; + uint8_t page_size; + uint16_t flash_size; + uint16_t eeprom_size; +} twi_chipinfo_t; + +/* *********************************************************************** */ + +uint8_t twi_generic (uint8_t twi_addr, + const uint8_t * p_wr_data, uint16_t write_size, + uint8_t * p_rd_data, uint16_t read_size); + +uint8_t twi_switch_application (uint8_t addr, uint8_t app); + +uint8_t twi_read_version (uint8_t addr, char * p_version, + uint8_t version_length); + +uint8_t twi_read_chipinfo (uint8_t addr, twi_chipinfo_t * p_chipinfo); + +uint8_t twi_read_memory (uint8_t twi_addr, uint8_t memory_type, + uint16_t memory_addr, + uint8_t * p_data, uint16_t data_length); + +uint8_t twi_write_memory (uint8_t twi_addr, uint8_t memory_type, + uint16_t memory_addr, + const uint8_t * p_data, uint16_t data_length); + +void twi_init (uint8_t enable); + +/* *********************************************************************** */ + +#endif /* TWI_MASTER_H_ */ diff --git a/uart.c b/uart.c index a87b5b4..92423be 100644 --- a/uart.c +++ b/uart.c @@ -66,6 +66,18 @@ uint8_t uart_rx_ready(void) } /* uart_rx_ready */ +/* *********************************************************************** + * uart_send_buf + * *********************************************************************** */ +void uart_send_buf(const uint8_t * p_data, uint16_t data_length) +{ + while (data_length--) + { + uart_send(*p_data++); + } +} /* uart_send_buf */ + + /* *********************************************************************** * uart_recv_buf * *********************************************************************** */ diff --git a/uart.h b/uart.h index d86ad1d..3ba3e2a 100644 --- a/uart.h +++ b/uart.h @@ -9,6 +9,7 @@ void uart_send (uint8_t data); uint8_t uart_recv (void); uint8_t uart_rx_ready (void); +void uart_send_buf (const uint8_t * p_data, uint16_t data_length); void uart_recv_buf (uint8_t * p_data, uint16_t data_length); void uart_init (void);