Add twiboot bridge

This commit is contained in:
Olaf Rempel 2020-01-13 22:18:33 +01:00
parent cfef6e9a72
commit 144016f65d
7 changed files with 683 additions and 5 deletions

View File

@ -23,7 +23,7 @@
#if (USE_DISPLAY) #if (USE_DISPLAY)
static display_mode_t m_mode = DISPLAY_MODE_OFF; 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_length = 0;
static uint8_t m_buffer_pos = 0; static uint8_t m_buffer_pos = 0;

322
ispprog.c
View File

@ -26,8 +26,11 @@
#include "display.h" #include "display.h"
#include "spi_isp.h" #include "spi_isp.h"
#include "target.h" #include "target.h"
#include "twi_master.h"
#include "uart.h" #include "uart.h"
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define TIMER_IRQFREQ_MS 10 #define TIMER_IRQFREQ_MS 10
/* convert milliseconds to timer ticks */ /* convert milliseconds to timer ticks */
@ -41,12 +44,14 @@
#define EV_TIMEOUT 0x08 #define EV_TIMEOUT 0x08
#define EV_PROG_ENTER 0x10 #define EV_PROG_ENTER 0x10
#define EV_PROG_LEAVE 0x20 #define EV_PROG_LEAVE 0x20
#define EV_PROG_ENTER_TWI 0x40
#define STATE_IDLE 0x00 /* nothing */ #define STATE_IDLE 0x00 /* nothing */
#define STATE_RESET_SYNC 0x01 #define STATE_RESET_SYNC 0x01
#define STATE_RESET_RETRY 0x02 #define STATE_RESET_RETRY 0x02
#define STATE_RESET_PROGMODE 0x03 #define STATE_RESET_PROGMODE 0x03
#define STATE_TWI_CHECK_BL 0x04
#define STATE_TWI_PROGMODE 0x05
#define LED_OFF 0x00 #define LED_OFF 0x00
#define LED_SLOW 0x20 #define LED_SLOW 0x20
@ -63,6 +68,11 @@ static uint8_t m_page_buf[256];
static avr_device_t m_device; static avr_device_t m_device;
static uint16_t m_address = 0x0000; 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) static void reset_statemachine(uint8_t events)
{ {
@ -105,12 +115,20 @@ static void reset_statemachine(uint8_t events)
timer = TIMER_MSEC2IRQCNT(0); timer = TIMER_MSEC2IRQCNT(0);
spi_init(0); spi_init(0);
#if (USE_TWI_SUPPORT)
twi_init(0);
#endif
/* put device in RUN mode */ /* put device in RUN mode */
RESET_INACTIVE(); RESET_INACTIVE();
m_led_mode = LED_OFF; 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; reset_cause = events;
events &= ~(EV_BUTTON_PRESSED | EV_PROG_ENTER); events &= ~(EV_BUTTON_PRESSED | EV_PROG_ENTER);
@ -122,6 +140,29 @@ static void reset_statemachine(uint8_t events)
m_state = STATE_RESET_SYNC; 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; break;
case STATE_RESET_SYNC: case STATE_RESET_SYNC:
@ -204,6 +245,101 @@ static void reset_statemachine(uint8_t events)
} }
break; 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: default:
m_state = STATE_IDLE; m_state = STATE_IDLE;
break; break;
@ -380,6 +516,7 @@ static void cmd_handler_isp(uint8_t cmd)
size |= uart_recv(); size |= uart_recv();
type = uart_recv(); type = uart_recv();
size = MIN(size, sizeof(m_page_buf));
uart_recv_buf(m_page_buf, size); uart_recv_buf(m_page_buf, size);
if (type == 'F') if (type == 'F')
@ -487,6 +624,124 @@ static void cmd_handler_isp(uint8_t cmd)
} /* cmd_handler_isp */ } /* 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) __attribute__ ((noreturn));
static void cmdloop(void) static void cmdloop(void)
{ {
@ -596,12 +851,67 @@ static void cmdloop(void)
uart_send(sizeof(m_page_buf) & 0xFF); uart_send(sizeof(m_page_buf) & 0xFF);
break; 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 */ /* ESC */
case 0x1B: case 0x1B:
break; break;
default: 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; break;
} }
} }
@ -683,6 +993,10 @@ int main(void)
spi_init(0); spi_init(0);
#if (USE_TWI_SUPPORT)
twi_init(0);
#endif
TIMER_INIT(); TIMER_INIT();
/* init statemachine */ /* init statemachine */

View File

@ -34,6 +34,7 @@
#define ISP_CHECK() (PIND & (1<<RESET_IN)) #define ISP_CHECK() (PIND & (1<<RESET_IN))
#define USE_DISPLAY 0 #define USE_DISPLAY 0
#define USE_TWI_SUPPORT 0
#define TIMER_DIVISOR 1024 #define TIMER_DIVISOR 1024
#define TIMER_INIT() { /* timer0, FCPU/1024, overflow interrupt */ \ #define TIMER_INIT() { /* timer0, FCPU/1024, overflow interrupt */ \
@ -95,6 +96,8 @@
#define DISP_D5 PORTD6 #define DISP_D5 PORTD6
#define DISP_D6 PORTD7 #define DISP_D6 PORTD7
#define USE_TWI_SUPPORT 1
#define TIMER_DIVISOR 1024 #define TIMER_DIVISOR 1024
#define TIMER_INIT() { /* timer0, FCPU/1024, overflow interrupt */ \ #define TIMER_INIT() { /* timer0, FCPU/1024, overflow interrupt */ \
TCCR0B = (1<<CS02) | (1<<CS00); \ TCCR0B = (1<<CS02) | (1<<CS00); \

286
twi_master.c Normal file
View File

@ -0,0 +1,286 @@
/***************************************************************************
* Copyright (C) 2006 - 2020 by Olaf Rempel *
* razzor AT kopf MINUS tisch DOT de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License, *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <avr/io.h>
#include <string.h>
#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<<TWINT) | (1<<TWSTA) | (1<<TWEN);
loop_until_bit_is_set(TWCR, TWINT);
switch (TWSR & 0xF8)
{
case 0x08: /* START transmitted */
case 0x10: /* repeated START transmitted */
return TWI_SUCCESS;
default:
return TWI_ERROR;
}
} /* twi_start */
/* ***********************************************************************
* twi_stop
* *********************************************************************** */
static void twi_stop(void)
{
TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
} /* twi_stop */
/* ***********************************************************************
* twi_master_tx
* *********************************************************************** */
static uint8_t twi_master_tx(uint8_t value)
{
TWDR = value;
TWCR = (1<<TWINT) | (1<<TWEN);
loop_until_bit_is_set(TWCR, TWINT);
switch (TWSR & 0xF8)
{
case 0x18: /* SLA+W transmitted, ACK received */
case 0x28: /* Data transmitted, ACK received */
case 0x40: /* SLA+R transmitted, ACK received */
return TWI_SUCCESS;
default:
return TWI_ERROR;
}
} /* twi_master_tx */
/* ***********************************************************************
* twi_master_rx
* *********************************************************************** */
static uint8_t twi_master_rx(uint8_t * p_value, uint8_t ack)
{
TWCR = (1<<TWINT) | (1<<TWEN) | ((ack) ? (1<<TWEA) : 0x00);
loop_until_bit_is_set(TWCR, TWINT);
*p_value = TWDR;
switch (TWSR & 0xF8)
{
case 0x50: /* Data received, ACK returned */
case 0x58: /* Data received, NAK returned */
return TWI_SUCCESS;
default:
return TWI_ERROR;
}
} /* twi_master_rx */
/* ***********************************************************************
* twi_master_start
* *********************************************************************** */
static uint8_t twi_master_start(uint8_t twi_addr)
{
if (twi_start() == TWI_SUCCESS)
{
if (twi_master_tx(twi_addr) == TWI_SUCCESS)
{
return TWI_SUCCESS;
}
}
twi_stop();
return TWI_ERROR;
} /* twi_master_start */
/* ***********************************************************************
* twi_generic
* *********************************************************************** */
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 result = TWI_ERROR;
if (write_size > 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<<TWSTO) | (1<<TWEN);
}
else
{
TWCR = 0x00;
}
} /* twi_init */
#endif /* (USE_TWI_SUPPORT) */

62
twi_master.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef TWI_MASTER_H_
#define TWI_MASTER_H_
#include <stdint.h>
/* *********************************************************************** */
/* 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_ */

12
uart.c
View File

@ -66,6 +66,18 @@ uint8_t uart_rx_ready(void)
} /* uart_rx_ready */ } /* 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 * uart_recv_buf
* *********************************************************************** */ * *********************************************************************** */

1
uart.h
View File

@ -9,6 +9,7 @@ void uart_send (uint8_t data);
uint8_t uart_recv (void); uint8_t uart_recv (void);
uint8_t uart_rx_ready (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_recv_buf (uint8_t * p_data, uint16_t data_length);
void uart_init (void); void uart_init (void);