Created
November 29, 2018 07:08
-
-
Save yurapyon/4b5915822763284fa6321dd8018b3bfd to your computer and use it in GitHub Desktop.
arduino uno uploader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include <SPI.h> | |
#define P_MOSI 11 | |
#define P_MISO 12 | |
#define P_SCK 13 | |
#define P_RESET 8 | |
#define P_ERR 9 | |
// spi has to be 2x slower than slowest device clock | |
uint8_t hr_nibble_to_uint(char nibble) { | |
if (nibble >= '0' && nibble <= '9') { | |
return nibble - 0x30; | |
} else if (nibble >= 'A' && nibble <= 'F') { | |
return 0xA + (nibble - 0x41); | |
} else if (nibble >= 'a' && nibble <= 'f') { | |
return 0xA + (nibble - 0x61); | |
} else { | |
return 0; | |
} | |
} | |
// converts 2 humanreadable hexchars from str to a uint8 | |
uint8_t hr_byte_to_uint(const char *byte) { | |
uint8_t buf[2]; | |
buf[0] = hr_nibble_to_uint(byte[0]); | |
buf[1] = hr_nibble_to_uint(byte[1]); | |
return buf[0] * 16 + buf[1]; | |
} | |
// | |
#define RECV_BUF_SZ 256 | |
uint8_t recv_buf[RECV_BUF_SZ + 1]; | |
uint8_t recv_buf_len = 0; | |
int receive_from_pc() { | |
uint8_t msg_sz; | |
for (;;) { | |
if (Serial.available() > 0) { | |
msg_sz = Serial.read(); | |
break; | |
} | |
delay(5); | |
} | |
recv_buf_len = 0; | |
for (;;) { | |
if (Serial.available() > 0) { | |
recv_buf[recv_buf_len] = Serial.read(); | |
recv_buf_len++; | |
if (recv_buf_len >= msg_sz) { | |
break; | |
} | |
} | |
delay(5); | |
} | |
recv_buf[recv_buf_len] = '\0'; | |
return msg_sz; | |
} | |
void transmit_to_pc(const char *buf, unsigned int sz) { | |
Serial.write(sz); | |
Serial.flush(); | |
Serial.write(buf, sz); | |
Serial.flush(); | |
} | |
const uint8_t msg_ok = 0; | |
const uint8_t msg_begin = 1; | |
// | |
uint8_t spi_transfer(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4) { | |
SPI.transfer(a1); | |
SPI.transfer(a2); | |
SPI.transfer(a3); | |
return SPI.transfer(a4); | |
} | |
void begin_programming() { | |
digitalWrite(P_RESET, HIGH); | |
pinMode(P_MOSI, OUTPUT); | |
pinMode(P_MISO, INPUT); | |
pinMode(P_SCK, OUTPUT); | |
pinMode(P_RESET, OUTPUT); | |
digitalWrite(P_SCK, LOW); | |
delay(20); | |
digitalWrite(P_RESET, HIGH); | |
delayMicroseconds(200); | |
digitalWrite(P_RESET, LOW); | |
delay(40); | |
SPI.begin(); | |
SPI.beginTransaction(SPISettings(1000000/6, MSBFIRST, SPI_MODE0)); | |
SPI.transfer(0xAC); | |
SPI.transfer(0x53); | |
uint8_t sync = SPI.transfer(0x00); | |
SPI.transfer(0x00); | |
uint8_t sig_bytes[4]; | |
sig_bytes[0] = spi_transfer(0x30, 0x00, 0x00, 0x00); | |
sig_bytes[1] = spi_transfer(0x30, 0x00, 0x01, 0x00); | |
sig_bytes[2] = spi_transfer(0x30, 0x00, 0x02, 0x00); | |
sig_bytes[3] = spi_transfer(0x30, 0x00, 0x03, 0x00); | |
transmit_to_pc(sig_bytes, 4); | |
uint8_t fuses[3]; | |
fuses[0] = spi_transfer(0x50, 0x00, 0x00, 0x00); | |
fuses[1] = spi_transfer(0x58, 0x08, 0x00, 0x00); | |
fuses[2] = spi_transfer(0x50, 0x08, 0x00, 0x00); | |
transmit_to_pc(fuses, 3); | |
spi_transfer(0xAC, 0x80, 0x00, 0x00); | |
delay(25); | |
} | |
void end_programming() { | |
SPI.endTransaction(); | |
SPI.end(); | |
digitalWrite(P_RESET, HIGH); | |
} | |
void load_flash_word(uint16_t addr, uint16_t word) { | |
// write low first | |
spi_transfer(0x40, addr >> 8, addr, word); | |
// write high | |
spi_transfer(0x48, addr >> 8, addr, word >> 8); | |
} | |
void write_flash_page(uint16_t addr) { | |
spi_transfer(0x4c, addr >> 8, addr, 0x00); | |
// wait 2.6 ms | |
delay(10); | |
} | |
uint16_t read_flash_word(uint16_t addr) { | |
uint8_t low = spi_transfer(0x20, addr >> 8, addr, 0x00); | |
uint16_t high = spi_transfer(0x28, addr >> 8, addr, 0x00); | |
return (high << 8) | low; | |
} | |
// adresses are 16 or less bits long | |
#define PAGE_SZ 64 | |
uint16_t page_of(uint16_t addr) { | |
if (PAGE_SZ == 64) { | |
return addr & 0xffc0; | |
} | |
return addr; | |
} | |
uint16_t curr_addr; | |
uint16_t curr_page; | |
// | |
// when writing to 8bit location from 16bit hex file | |
// msB is ignored | |
// adresses >= x8000 are eeprom // 32kW flash | |
// adresses >= x9000 are fuse // 4kB eeprom | |
// x9000 high low fuses | |
// x9001 extended fuses | |
// todo check checksum | |
unsigned int write_hex_line(const char *line) { | |
// assume lines look like this | |
// :szaddrtpdatadatadatacs | |
uint8_t checksum_acc = 0; | |
const char *temp = line; | |
// TODO error check starts with ':' | |
temp++; | |
uint8_t sz = hr_byte_to_uint(temp) / 2; | |
temp += 2; | |
uint16_t addr_high = hr_byte_to_uint(temp); | |
temp += 2; | |
uint8_t addr_low = hr_byte_to_uint(temp); | |
temp += 2; | |
uint8_t type = hr_byte_to_uint(temp); | |
temp += 2; | |
if (type == 1) { | |
return 1; | |
} | |
uint16_t addr = (addr_high << 8) | addr_low; | |
// todo handle if sz is 0 | |
curr_addr = addr; | |
curr_page = page_of(curr_addr); | |
if (curr_addr < 0x8000) { | |
// TODO clean up | |
// use a buffer to hold data thats been converted to u16 | |
for (int i = 0; i < sz; ++i) { | |
uint16_t data_high = hr_byte_to_uint(temp); | |
temp += 2; | |
uint8_t data_low = hr_byte_to_uint(temp); | |
temp += 2; | |
load_flash_word(curr_addr, (data_high << 8) | data_low); | |
curr_addr++; | |
if(curr_page != page_of(curr_addr)) { | |
write_flash_page(curr_addr - 1); | |
curr_page = page_of(curr_addr); | |
} | |
} | |
write_flash_page(curr_addr - 1); | |
temp = line + 9; | |
curr_addr = addr; | |
for (int i = 0; i < sz; ++i) { | |
uint16_t data_high = hr_byte_to_uint(temp); | |
temp += 2; | |
uint8_t data_low = hr_byte_to_uint(temp); | |
temp += 2; | |
data_high = (data_high << 8) | data_low; | |
uint16_t from_chip = read_flash_word(curr_addr); | |
if (data_high != from_chip) { | |
digitalWrite(P_ERR, HIGH); | |
} | |
// transmit_to_pc((char *)&data_high, 2); | |
// transmit_to_pc((char *)&from_chip, 2); | |
curr_addr++; | |
} | |
} else if (curr_addr == 0x9000) { | |
if (sz > 0) { | |
uint8_t high = hr_byte_to_uint(temp); | |
temp += 2; | |
uint8_t low = hr_byte_to_uint(temp); | |
temp += 2; | |
spi_transfer(0xAC, 0xA0, 0x00, low); | |
delay(10); | |
spi_transfer(0xAC, 0xA8, 0x00, high); | |
delay(10); | |
} | |
} | |
return 0; | |
} | |
#define HEX_BUF_SZ 256 | |
char hex_buf[HEX_BUF_SZ]; | |
uint8_t hex_buf_at = 0; | |
bool programming = false; | |
void setup() { | |
pinMode(P_ERR, OUTPUT); | |
digitalWrite(P_ERR, LOW); | |
Serial.begin(19200); | |
transmit_to_pc(&msg_ok, 1); | |
} | |
void loop() { | |
int sz = receive_from_pc(); | |
if (!programming) { | |
if (sz == 1 && recv_buf[0] == msg_begin) { | |
begin_programming(); | |
programming = true; | |
hex_buf_at = 0; | |
transmit_to_pc(&msg_ok, 1); | |
} | |
} else { | |
// every transmission is part of a hex file | |
memcpy(hex_buf + hex_buf_at, recv_buf, sz); | |
hex_buf_at += sz; | |
for (int i = 0; i < hex_buf_at; ++i) { | |
if (hex_buf[i] == '\n') { | |
if (write_hex_line(hex_buf)) { | |
end_programming(); | |
programming = false; | |
break; | |
} | |
memmove(hex_buf, hex_buf + i + 1, hex_buf_at - i - 1); | |
hex_buf_at = hex_buf_at - i - 1; | |
i = 0; | |
} | |
} | |
transmit_to_pc(&msg_ok, 1); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment