Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prompt for bootloader trigger keys #22

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 140 additions & 61 deletions etc/flash_firmware.ino
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#include <Kaleidoscope.h>
#include <util/crc16.h>
#include "Arduino.h"
extern "C" {
#include <twi.h>
#include <kaleidoscope/device/keyboardio/twi.h>
};

#include "attiny88_flasher.h"
Expand All @@ -14,7 +15,27 @@ extern "C" {
#define ENDTRANS_DATA_NACK 3
#define ENDTRANS_ERROR 4

#define write_cmd(addr, data, sendStop) \
twi_writeTo((addr), (data), ELEMENTS(data), true, (sendStop))

// Reset delay, in milliseconds. Also retry interval for initial
// commands to bootloader.
//
// The ATtinys in the Model 01 ship with fuses programmed for a
// 64 ms reset delay (actually 8192 watchdog timer cycles, so
// maybe 65ms to 70ms in practice, given temperature and voltage
// variations). Also, the original Model 01 bootloader times out
// in that same period. So delay once and retry with an interval
// a little less than that, to make sure we hit the bootloader
// before it times out.
//
// (We could delay slightly longer for the initial delay, but
// we would miss the bootloader timeout if someone changed
// the fuses to a shorter startup delay.)
#define RESET_DELAY 60

#define debug_print(...) Serial.print(__VA_ARGS__)
#define debug_println(...) Serial.println(__VA_ARGS__)

void debug_print_result(uint8_t result) {
switch(result) {
Expand All @@ -36,9 +57,31 @@ void debug_print_result(uint8_t result) {
}
}

void debug_println_result(uint8_t result) {
debug_print_result(result);
debug_println();
}

#define LEFT_LEDS 0x58
#define RIGHT_LEDS (LEFT_LEDS | 0x03)

void turn_leds_off(uint8_t addr) {
uint8_t leds_off[] = { 0x03, 0x00, 0x00, 0x00 };
(void)write_cmd(addr, leds_off, true);
}

void setup() {
delay(2000);
// Turn on LED (and right hand) power, in case bootloader didn't
// do that for us (e.g., not coming straight off an ATmega flash.)
// That lets us explicitly turn them off, in case they're lit up
// from power cycling or bootloader status updates.
DDRC |= _BV(7);
PORTC |= _BV(7);
delay(3 * RESET_DELAY);
twi_init();

turn_leds_off(LEFT_LEDS);
turn_leds_off(RIGHT_LEDS);
}

// The LEFT ATTiny has a reset pin directly connected to the ATMega
Expand All @@ -48,6 +91,7 @@ void reset_left_attiny() {
PORTC &= ~_BV(6);
delay(30);
DDRC &= ~_BV(6); // Turn the ATTiny back on
delay(RESET_DELAY);
}

// The RIGHT ATTiny is on the other side of the wired i2c bus.
Expand All @@ -59,21 +103,53 @@ void reset_right_attiny() {
PORTC &= ~_BV(7);
delay(1000);
PORTC |= _BV(7); // Turn the ATTiny back on
delay(RESET_DELAY);
// In case power cycling caused any to turn on
turn_leds_off(LEFT_LEDS);
}

#define PROMPT_COLOR 0x00, 0x7f, 0x7f
void prompt_trigger(uint8_t addr) {
uint8_t left_prompt0[] = { 0x04, 0, PROMPT_COLOR };
uint8_t left_prompt1[] = { 0x04, 30, PROMPT_COLOR };
uint8_t right_prompt0[] = { 0x04, 1, PROMPT_COLOR };
uint8_t right_prompt1[] = { 0x04, 31, PROMPT_COLOR };
debug_println(F("Exceeded retry count"));
debug_print(F("Try holding down the bootloader keys on the "));
if (addr == LEFT_ADDRESS) {
debug_println(F("left side"));
(void)write_cmd(LEFT_LEDS, left_prompt0, true);
delay(1);
(void)write_cmd(LEFT_LEDS, left_prompt1, true);
delay(1000);
reset_left_attiny();
} else {
debug_println(F("right side"));
(void)write_cmd(RIGHT_LEDS, right_prompt0, true);
delay(1);
(void)write_cmd(RIGHT_LEDS, right_prompt1, true);
delay(1000);
reset_right_attiny();
}
}



void leds_success(uint8_t addr) {
uint8_t data[] = { 0x03, 0x00, 0x3f, 0x00 };
(void)write_cmd(addr, data, true);
}

uint8_t read_crc16(byte addr, byte *version, uint16_t *crc16, uint16_t offset, uint16_t length) {
uint8_t result = ENDTRANS_ADDR_NACK;


// get version and CRC16 // addr (lo) // addr (hi) // len (lo) // len (hi)
uint8_t data[] = { (0x06), (uint8_t)(offset & 0xff), (uint8_t)(offset >> 8), (uint8_t)(length & 0xff), (uint8_t)(length >> 8) };
result = twi_writeTo(addr, data, ELEMENTS(data), true, true);

// get version and CRC16
uint8_t data[] = {
(0x06),
(uint8_t)(offset & 0xff), (uint8_t)(offset >> 8), // addr
(uint8_t)(length & 0xff), (uint8_t)(length >> 8) // len
};

result = write_cmd(addr, data, true);
if (result != ENDTRANS_SUCCESS) {
return result;
}
Expand All @@ -97,43 +173,45 @@ uint8_t read_crc16(byte addr, byte *version, uint16_t *crc16, uint16_t offset, u


void get_version (byte addr) {

uint8_t retries = 0;
byte result = ENDTRANS_ADDR_NACK;

while (result != ENDTRANS_SUCCESS) {
debug_print(F("Reading CRC16: "));

byte version;
uint16_t crc16;
result = read_crc16(addr, &version, &crc16, 0, firmware_length);
debug_print_result(result);
debug_print(F("\r\n"));
debug_println_result(result);

if (result != ENDTRANS_SUCCESS) {
_delay_ms(100);
delay(RESET_DELAY);
if (++retries < 4) {
continue;
}
retries = 0;
prompt_trigger(addr);
continue;
}
debug_print(F("Version: "));
debug_print(version);
debug_print(F("\r\nExisting CRC16 of 0000-1FFF: "));
debug_print(crc16, HEX);
debug_print(F("\r\n"));
debug_println(version);
debug_print(F("Existing CRC16 of 0000-1FFF: "));
debug_println(crc16, HEX);
}

}



int erase_program(uint8_t addr) {
// erase user space
uint8_t data[] = { 0x04 };
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), true, true);
uint8_t result = write_cmd(addr, data, true);

debug_print(F("Erasing: "));
debug_print_result(result);
debug_print(F("\r\n"));
debug_println_result(result);
if (result != ENDTRANS_SUCCESS) {
_delay_ms(1000);
debug_print(F("failed.\r\n"));
debug_println(F("failed."));
return -1;
}
return 0;
Expand All @@ -153,14 +231,15 @@ int write_firmware(uint8_t addr ) {

// write page addr
uint8_t data[] = { 0x01, (uint8_t)(offsets[o] & 0xff), (uint8_t)(offsets[o] >> 8)};
result = twi_writeTo(addr, data, ELEMENTS(data), true, true);
result = write_cmd(addr, data, true);
debug_print_result(result);
debug_print(F(" - "));

_delay_ms(DELAY);
// got something other than ACK. Start over.
if (result != ENDTRANS_SUCCESS) {
debug_print(F("\r\nFailed\r\n"));
debug_println();
debug_println(F("Failed"));
return -1;
}

Expand All @@ -187,19 +266,20 @@ int write_firmware(uint8_t addr ) {
data[data_counter++] = (uint8_t)(crc16 >> 8);
data[data_counter++] = (0x00); // dummy end uint8_t

result = twi_writeTo(addr, data, ELEMENTS(data), true, true);
result = write_cmd(addr, data, true);
debug_print(F(" "));
debug_print(frame);
debug_print(F(" of 4: "));
// got something other than NACK. Start over.
if (result != ENDTRANS_DATA_NACK) {
debug_print(F("\nERROR: Got something other than NACK\n") );
debug_println();
debug_println(F("ERROR: Got something other than NACK"));
return -1;
}
debug_print(F("OK;"));
delay(DELAY);
}
debug_print(F("\r\n"));
debug_println();
o++;
}
return 0;
Expand All @@ -209,7 +289,7 @@ int write_firmware(uint8_t addr ) {
int verify_firmware(byte addr) {
byte result = ENDTRANS_DATA_NACK;
// verify firmware
debug_print(F("Verifying install\r\n"));
debug_println(F("Verifying install"));
while (result != ENDTRANS_SUCCESS) {
debug_print(F("CRC16 "));

Expand All @@ -218,22 +298,20 @@ int verify_firmware(byte addr) {
// skip the first 4 bytes, are they were probably overwritten by the reset vector preservation
result = read_crc16(addr, &version, &crc16, offsets[0] + 4, firmware_length - 4);

debug_print_result(result);
debug_print(F("\r\n"));
debug_println_result(result);

if (result != ENDTRANS_SUCCESS) {
_delay_ms(100);
continue;
}
debug_print(F("Version: "));
debug_print(version);
debug_print(F("\r\nCRC16 of "));
debug_println(version);
debug_print(F("CRC16 of "));
debug_print(offsets[0] + 4, HEX);
debug_print(F("-"));
debug_print(offsets[0] + firmware_length, HEX);
debug_print(F(": "));
debug_print(crc16, HEX);
debug_print(F("\r\n"));
debug_println(crc16, HEX);
// calculate our own CRC16
uint16_t check_crc16 = 0xffff;
for (uint16_t i = 4; i < firmware_length; i++) {
Expand All @@ -244,43 +322,40 @@ int verify_firmware(byte addr) {
debug_print(check_crc16, HEX);
return -1;
}
debug_print(F("OK\r\n"));
debug_println(F("OK"));
}
return 0;
}

byte update_attiny(byte addr) {
debug_print(F("Communicating\r\n"));
debug_println(F("Communicating"));

get_version(addr);

int erased = erase_program(addr);

if (erased == -1) {

return 0;
}

int firmware_written = write_firmware(addr);
if(firmware_written == -1) {
debug_print(F("Write failed.\r\n"));
if (firmware_written == -1) {
debug_println(F("Write failed."));
return 0;
}

int firmware_verified = verify_firmware(addr);
if(firmware_verified == -1) {
debug_print(F("Verify failed.\r\n"));
if (firmware_verified == -1) {
debug_println(F("Verify failed."));
return 0;
}

debug_print(F("Resetting ATTiny "));

// execute app
uint8_t data[] = {0x03, 0x00};
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), true, true);
debug_print_result(result);
debug_print(F("\r\n"));
debug_print(F("Done!\r\n"));
uint8_t result = write_cmd(addr, data, true);
debug_println_result(result);
debug_println(F("Done!"));

return 1;
}
Expand All @@ -291,32 +366,36 @@ int right_written = 0;
void loop() {
delay(2000);

if (left_written > 0) {
debug_print(F("Done with left side.\r\n"));
// Update right side first, because resetting it will power-cycle
// LEDs on both sides.
if (right_written > 0) {
debug_println(F("Done with right side."));
leds_success(RIGHT_LEDS);
// we're done
} else {
debug_print(F("Updating left side\r\n"));
reset_left_attiny();
left_written = update_attiny(LEFT_ADDRESS);

debug_println(F("Updating right side"));
reset_right_attiny();
right_written = update_attiny(RIGHT_ADDRESS);
delay(3 * RESET_DELAY);
leds_success(RIGHT_LEDS);
}

if (right_written > 0) {
debug_print(F("Done with right side.\r\n"));
if (left_written > 0) {
debug_println(F("Done with left side."));
leds_success(LEFT_LEDS);
// we're done
} else {
debug_print(F("Updating right side\r\n"));
reset_right_attiny();
right_written = update_attiny(RIGHT_ADDRESS);
debug_println(F("Updating left side"));
reset_left_attiny();
left_written = update_attiny(LEFT_ADDRESS);
delay(3 * RESET_DELAY);
leds_success(LEFT_LEDS);
}

if (left_written && right_written ) {
debug_print (F("Both ATTiny MCUs have been flashed\r\nIt is now safe to reload the regular firmware\r\n"));
debug_println(F("Both ATTiny MCUs have been flashed"));
debug_println(F("It is now safe to reload the regular firmware"));
delay(5000);
return;
}


}


6 changes: 4 additions & 2 deletions firmware/twi-slave.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,14 @@ ISR(TWI_vect) {
break;

case TW_SR_STOP: // A STOP condition or repeated START condition has been received while still addressed as Slave
// Clear the condition immediately, so we're less likely to miss
// a START that immediately follows a STOP, and can still match
// our address
TWI_Start_Transceiver(1);
// Either way, treat it as end of transmission
if (TWI_Rx_Data_Callback) {
TWI_Rx_Data_Callback(TWI_buf, TWI_bufPtr);
}
// Release the bus and re-enable reception in un-addressed mode
TWI_Start_Transceiver(1);
break;

case TW_SR_DATA_NACK: // Previously addressed with own SLA+W; data has been received; NOT ACK has been returned
Expand Down