Autodetect SPI frequency

- add statemachine for ISP sync & signature read
- autoprobing of SPI frequency
- remove manual SPI frequency selection
- remove NVM code
This commit is contained in:
Olaf Rempel 2014-10-14 21:56:16 +02:00
parent 798fa7fbb1
commit e91782ec65
1 changed files with 134 additions and 269 deletions

421
ispprog.c
View File

@ -19,11 +19,9 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <avr/io.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <string.h>
#include <util/crc16.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
@ -114,21 +112,21 @@
#include <util/delay.h>
#define UART_CALC_BAUDRATE(baudRate) (((uint32_t)F_CPU) / (((uint32_t)baudRate)*16) -1)
/*
* To select SPI SPEED press RESET_IN until LED starts blinking (about 5s)
* press RESET_IN to cycle through SPI speeds (LED frequency changes)
* after timout (about 5s) the LED turns off and the new SPI speed is set
*/
/* F_CPU /4 (1.8432MHz) */
#define SPI_MODE4 ((1<<SPE) | (1<<MSTR))
/* F_CPU /16 (460.8kHz) */
#define SPI_MODE3 ((1<<SPE) | (1<<MSTR) | (1<<SPR1))
#define SPI_MODE3 ((1<<SPE) | (1<<MSTR) | (1<<SPR0))
/* F_CPU /64 (115.2kHz) */
#define SPI_MODE2 ((1<<SPE) | (1<<MSTR) | (1<<SPR0))
#define SPI_MODE2 ((1<<SPE) | (1<<MSTR) | (1<<SPR1))
/* F_CPU /128 (57.6kHz) */
#define SPI_MODE1 ((1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0))
static const uint8_t spi_modes[4] = { SPI_MODE1, SPI_MODE2, SPI_MODE3, SPI_MODE4 };
#define SPI_SPEED_PROBE 0xFF
static uint8_t spi_speed = SPI_SPEED_PROBE;
#define POLL_00 0x01 /* value 0x00 can not be polled from flash/eeprom */
#define POLL_7F 0x02 /* value 0x7F can not be polled from flash/eeprom */
#define POLL_80 0x04 /* value 0x80 can not be polled from flash/eeprom */
@ -240,20 +238,19 @@ static const struct _device devices[] PROGMEM = {
// { { 0x1E, 0xA7, 0x01 }, 0xFF, ???, ??? }, /* mega128rfa1 */
};
#define EV_NONE 0
#define EV_STATE_ENTER 1
#define EV_BUTTON_PRESSED 2
#define EV_BUTTON_RELEASED 3
#define EV_TIMEOUT 4
#define EV_NONE 0x00
#define EV_STATE_ENTER 0x01
#define EV_BUTTON_PRESSED 0x02
#define EV_BUTTON_RELEASED 0x03
#define EV_TIMEOUT 0x04
#define EV_PROG_ENTER 0x11
#define EV_PROG_LEAVE 0x12
#define STATE_IDLE 0x00 /* nothing */
#define STATE_RESET_SYNC 0x01
#define STATE_RESET_RETRY 0x02
#define STATE_RESET_PROGMODE 0x03
#define STATE_IDLE 0 /* nothing */
#define STATE_PRESSED 1 /* reset_in pressed, generating 50ms isp_reset pulse */
#define STATE_PRESSED2 2 /* reset_in still pressed, isp_reset pulse complete */
#define STATE_SPEED1 3 /* spi speed setting 1 */
#define STATE_SPEED2 4
#define STATE_SPEED3 5
#define STATE_SPEED4 6
#define STATE_NVRAM_STORE 7 /* start nvram_write (internal state) */
#define LED_OFF 0x00
#define LED_SLOW 0x20
@ -304,100 +301,6 @@ static const struct _device devices[] PROGMEM = {
#define CMD_WRITE_FUSE_E_1 0xAC /* not used */
#define CMD_WRITE_FUSE_E_2 0xA4 /* not used */
struct _nvdata {
uint8_t nvram_size; /* first */
uint8_t spi_mode;
uint16_t nvram_crc; /* last */
};
static uint8_t nvram_write_pos;
static struct _nvdata nvram_data;
static struct _nvdata nvram_eeprom EEMEM;
static const struct _nvdata nvram_defaults PROGMEM = { .spi_mode = SPI_MODE4 };
/* create crc and store nvram data to eeprom */
static void nvram_start_write(void)
{
uint8_t i;
uint16_t crc = 0x0000;
uint8_t *tmp = (uint8_t *)&nvram_data;
#if defined(__AVR_ATmega16__)
if (EECR & (1<<EEWE))
return;
#elif defined(__AVR_ATmega328P__)
if (EECR & (1<<EEPE))
return;
#endif
nvram_data.nvram_size = sizeof(struct _nvdata);
for (i = 0; i < sizeof(struct _nvdata) -2; i++) {
crc = _crc_ccitt_update(crc, *tmp++);
}
nvram_data.nvram_crc = crc;
nvram_write_pos = 0;
EEARL = nvram_write_pos;
EEARH = 0x00;
EEDR = ((uint8_t *)&nvram_data)[nvram_write_pos++];
cli();
#if defined(__AVR_ATmega16__)
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
#elif defined(__AVR_ATmega328P__)
EECR |= (1<<EEMPE);
EECR |= (1<<EEPE);
#endif
EECR |= (1<<EERIE);
sei();
}
/* store nvram data to eeprom */
#if defined(__AVR_ATmega16__)
ISR(EE_RDY_vect)
#elif defined(__AVR_ATmega328P__)
ISR(EE_READY_vect)
#endif
{
if (nvram_write_pos < sizeof(struct _nvdata)) {
EEARL = nvram_write_pos;
EEARH = 0x00;
EEDR = ((uint8_t *)&nvram_data)[nvram_write_pos++];
#if defined(__AVR_ATmega16__)
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
#elif defined(__AVR_ATmega328P__)
EECR |= (1<<EEMPE);
EECR |= (1<<EEPE);
#endif
EECR |= (1<<EERIE);
} else {
EECR &= ~(1<<EERIE);
}
}
/* read nvram from eeprom and check crc */
static void nvram_read(void)
{
uint8_t i;
uint16_t crc = 0x0000;
uint8_t *tmp = (uint8_t *)&nvram_data;
eeprom_read_block(&nvram_data, &nvram_eeprom, sizeof(struct _nvdata));
for (i = 0; i < sizeof(struct _nvdata); i++) {
crc = _crc_ccitt_update(crc, *tmp++);
}
/* if nvram content is invalid, overwrite with defaults */
if ((nvram_data.nvram_size != sizeof(struct _nvdata)) || (crc != 0x0000)) {
memcpy_P(&nvram_data, &nvram_defaults, sizeof(struct _nvdata));
nvram_start_write();
}
}
static volatile uint8_t led_mode = LED_OFF;
@ -510,59 +413,33 @@ static void mem_pagewrite(uint16_t addr)
poll();
}
static void reset_statemachine(uint8_t event);
static volatile uint16_t reset_timer = 0x0000;
static volatile uint8_t reset_state;
static void cmdloop(void) __attribute__ ((noreturn));
static void cmdloop(void)
{
static uint8_t page_buf[256];
uint16_t addr = 0;
/* disable ISP_RESET */
set_reset(1);
while (1) {
switch (ser_recv()) {
/* Enter programming mode */
case 'P': {
uint8_t sync, count = 0x20, retval = '!';
led_mode = LED_ON;
do {
set_reset(1);
_delay_ms(50);
set_reset(0);
_delay_ms(50);
reset_statemachine(EV_PROG_ENTER);
spi_rxtx(CMD_PROG_ENABLE_1);
spi_rxtx(CMD_PROG_ENABLE_2);
sync = spi_rxtx(0x00);
spi_rxtx(0x00);
while (1) {
if (reset_state == STATE_IDLE) {
ser_send('!');
break;
} while (sync != CMD_PROG_ENABLE_2 && count--);
memset(&device, 0x00, sizeof(struct _device));
if (sync == CMD_PROG_ENABLE_2) {
uint8_t i;
for (i = 0; i < 3; i++) {
device.sig[i] = mem_read(CMD_READ_SIG_1, (CMD_READ_SIG_2 << 8) | i);
}
for (i = 0; i < ARRAY_SIZE(devices); i++) {
if (memcmp_P(device.sig, devices[i].sig, sizeof(device.sig)) == 0) {
memcpy_P(&device, &devices[i], sizeof(struct _device));
retval = '\r';
} else if (reset_state == STATE_RESET_PROGMODE) {
/* device not supported */
ser_send('\r');
break;
}
}
}
/* device not supported */
if (retval == '!') {
set_reset(1);
led_mode = LED_OFF;
}
ser_send(retval);
break;
}
@ -687,8 +564,7 @@ static void cmdloop(void)
/* Exit Bootloader */
case 'E':
set_reset(1);
led_mode = LED_OFF;
reset_statemachine(EV_PROG_LEAVE);
ser_send('\r');
break;
@ -905,127 +781,126 @@ static void cmdloop(void)
}
}
static uint16_t button_statemachine(uint8_t event)
static void reset_statemachine(uint8_t event)
{
static uint8_t oldstate;
uint8_t state = oldstate;
uint16_t timer = 0; /* no change */
static uint8_t reset_retries;
static uint8_t reset_cause;
uint8_t state;
uint8_t oldstate;
uint16_t timer;
cli();
/* copy state, disable timer */
state = reset_state;
timer = reset_timer;
reset_timer = 0x0000;
sei();
do {
if (state != oldstate) {
event = EV_STATE_ENTER;
}
oldstate = state;
switch (state) {
case STATE_IDLE:
if (event == EV_STATE_ENTER) {
led_mode = LED_OFF;
timer = 0xFFFF; /* stop timer */
timer = 0; /* stop timer */
} else if (event == EV_BUTTON_PRESSED) {
state = STATE_PRESSED;
set_reset(0);
/* put device in RUN mode */
set_reset(1);
} else if ((event == EV_BUTTON_PRESSED) || (event == EV_PROG_ENTER)) {
memset(&device, 0x00, sizeof(struct _device));
reset_retries = 5;
reset_cause = event;
/* probe SPI speed of device */
if (spi_speed == SPI_SPEED_PROBE) {
spi_speed = 3;
}
state = STATE_RESET_SYNC;
}
break;
case STATE_PRESSED:
case STATE_RESET_SYNC:
if (event == EV_STATE_ENTER) {
led_mode = LED_ON;
timer = 5; /* timeout 50ms (== reset length) */
timer = 5; /* timeout 50ms */
} else if (event == EV_BUTTON_RELEASED) {
state = STATE_IDLE;
set_reset(1);
/* set SPI speed */
SPCR = spi_modes[spi_speed];
/* put device in ISP mode */
set_reset(0);
} else if (event == EV_TIMEOUT) {
state = STATE_PRESSED2;
set_reset(1);
uint8_t sync;
spi_rxtx(CMD_PROG_ENABLE_1);
spi_rxtx(CMD_PROG_ENABLE_2);
sync = spi_rxtx(0x00);
spi_rxtx(0x00);
if (sync == CMD_PROG_ENABLE_2) {
uint8_t i;
uint8_t sig[3];
for (i = 0; i < 3; i++) {
sig[i] = mem_read(CMD_READ_SIG_1, (CMD_READ_SIG_2 << 8) | i);
}
for (i = 0; i < ARRAY_SIZE(devices); i++) {
if (memcmp_P(sig, devices[i].sig, sizeof(device.sig)) == 0) {
memcpy_P(&device, &devices[i], sizeof(struct _device));
break;
}
}
state = (reset_cause == EV_PROG_ENTER) ? STATE_RESET_PROGMODE
: STATE_IDLE;
} else {
state = STATE_RESET_RETRY;
}
}
break;
case STATE_PRESSED2:
case STATE_RESET_RETRY:
if (event == EV_STATE_ENTER) {
led_mode = LED_OFF;
timer = 500; /* timeout in 5s */
timer = 5; /* timeout 50ms */
} else if (event == EV_BUTTON_RELEASED) {
/* put device in RUN mode */
set_reset(1);
} else if (event == EV_TIMEOUT) {
reset_retries--;
if (reset_retries > 0) {
/* try lower frequency */
if (spi_speed > 0) {
spi_speed--;
}
state = STATE_RESET_SYNC;
} else {
/* got no sync, probe speed again next time */
spi_speed = SPI_SPEED_PROBE;
state = STATE_IDLE;
}
}
break;
case STATE_RESET_PROGMODE:
if (event == EV_STATE_ENTER) {
} else if (event == EV_PROG_LEAVE) {
/* was in prog mode (osc changed?), probe speed next time */
spi_speed = SPI_SPEED_PROBE;
state = STATE_IDLE;
} else if (event == EV_TIMEOUT) {
switch (SPCR) {
case SPI_MODE1:
state = STATE_SPEED1;
break;
case SPI_MODE2:
state = STATE_SPEED2;
break;
case SPI_MODE3:
state = STATE_SPEED3;
break;
default:
case SPI_MODE4:
state = STATE_SPEED4;
break;
}
}
break;
case STATE_SPEED1:
if (event == EV_STATE_ENTER) {
led_mode = LED_SPEED1;
timer = 500; /* timeout in 5s */
} else if (event == EV_BUTTON_PRESSED) {
state = STATE_SPEED2;
} else if (event == EV_TIMEOUT) {
state = STATE_NVRAM_STORE;
SPCR = SPI_MODE1;
}
break;
case STATE_SPEED2:
if (event == EV_STATE_ENTER) {
led_mode = LED_SPEED2;
timer = 500; /* timeout in 5s */
} else if (event == EV_BUTTON_PRESSED) {
state = STATE_SPEED3;
} else if (event == EV_TIMEOUT) {
state = STATE_NVRAM_STORE;
SPCR = SPI_MODE2;
}
break;
case STATE_SPEED3:
if (event == EV_STATE_ENTER) {
led_mode = LED_SPEED3;
timer = 500; /* timeout in 5s */
} else if (event == EV_BUTTON_PRESSED) {
state = STATE_SPEED4;
} else if (event == EV_TIMEOUT) {
state = STATE_NVRAM_STORE;
SPCR = SPI_MODE3;
}
break;
case STATE_SPEED4:
if (event == EV_STATE_ENTER) {
led_mode = LED_SPEED4;
timer = 500; /* timeout in 5s */
} else if (event == EV_BUTTON_PRESSED) {
state = STATE_SPEED1;
} else if (event == EV_TIMEOUT) {
state = STATE_NVRAM_STORE;
SPCR = SPI_MODE4;
state = STATE_IDLE;
}
break;
@ -1034,19 +909,16 @@ static uint16_t button_statemachine(uint8_t event)
break;
}
if (state == STATE_NVRAM_STORE) {
state = STATE_IDLE;
nvram_data.spi_mode = SPCR;
nvram_start_write();
}
event = (oldstate != state) ? EV_STATE_ENTER
: EV_NONE;
if (event == EV_STATE_ENTER) {
oldstate = state;
}
} while (oldstate != state);
} while (state != oldstate);
return timer;
cli();
/* copy state back */
reset_timer = timer;
reset_state = state;
sei();
}
/* time keeping */
@ -1071,22 +943,15 @@ ISR(TIMER0_OVF_vect)
}
}
static uint16_t timer;
if (timer) {
timer--;
if (timer == 0) {
if (reset_timer) {
reset_timer--;
if (reset_timer == 0) {
event = EV_TIMEOUT;
}
}
if (event != EV_NONE) {
uint16_t new_timer = button_statemachine(event);
if (new_timer == 0xFFFF) {
timer = 0;
} else if (new_timer > 0) {
timer = new_timer;
}
reset_statemachine(event);
}
/* update LED */
@ -1141,11 +1006,8 @@ int main(void)
UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
#endif
/* read stored parameters */
nvram_read();
/* enable SPI master mode */
SPCR = nvram_data.spi_mode;
SPCR = SPI_MODE4;
#if defined(__AVR_ATmega16__)
/* timer0, FCPU/1024, overflow interrupt */
@ -1156,6 +1018,9 @@ int main(void)
TIMSK0 = (1<<TOIE0);
#endif
/* init statemachine */
reset_statemachine(EV_STATE_ENTER);
sei();
cmdloop();