First commit
This commit is contained in:
@@ -0,0 +1,566 @@
|
||||
/* Routines for reading/writing Intel INHX8M and INHX32 files
|
||||
|
||||
Copyright 2002 Brandon Fosdick (BSD License)
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
//#include <unistd.h>
|
||||
#include "intelhex.h"
|
||||
|
||||
namespace intelhex
|
||||
{
|
||||
|
||||
#define INH32M_HEADER ":020000040000FA"
|
||||
|
||||
// Array access operator
|
||||
value_type& hex_data::operator[](address_type address)
|
||||
{
|
||||
// Start at the end of the list and find the first (last) block with an address
|
||||
// less than addr
|
||||
reverse_iterator i = blocks.rbegin();
|
||||
while( i != blocks.rend() )
|
||||
{
|
||||
if( i->first <= address )
|
||||
{
|
||||
// Use the block if address is interior or adjacent to the block
|
||||
if( (address - i->first) <= i->second.size() )
|
||||
return i->second[address - i->first];
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return blocks[address][0];
|
||||
}
|
||||
|
||||
// Return the value at address, or _fill if not set
|
||||
value_type hex_data::get(address_type address)
|
||||
{
|
||||
// Start at the end of the list and find the first (last) block with an address
|
||||
// less than addr
|
||||
reverse_iterator i = blocks.rbegin();
|
||||
while( i != blocks.rend() )
|
||||
{
|
||||
if( i->first <= address )
|
||||
{
|
||||
// Use the block if address is interior to the block
|
||||
if( (address - i->first) < i->second.size() )
|
||||
return i->second[address - i->first];
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return _fill;
|
||||
}
|
||||
|
||||
// Set the value at address or create a new element using value
|
||||
void hex_data::set(address_type address, value_type value)
|
||||
{
|
||||
if( value == fill() ) // Handle fill values
|
||||
{
|
||||
erase(address); // If the address is already set, erase it
|
||||
return;
|
||||
}
|
||||
|
||||
// Start at the end of the list and find the first (last) block with an address
|
||||
// less than addr
|
||||
reverse_iterator i = blocks.rbegin();
|
||||
while( i != blocks.rend() )
|
||||
{
|
||||
if( i->first <= address )
|
||||
{
|
||||
// Use the block if address is interior or adjacent to the block
|
||||
const address_type index = address - i->first;
|
||||
if( index < i->second.size() )
|
||||
{
|
||||
i->second[index] = value;
|
||||
return;
|
||||
}
|
||||
else if( index == i->second.size() )
|
||||
{
|
||||
i->second.push_back(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
blocks[address].push_back(value); // Otherwise create a new block
|
||||
}
|
||||
|
||||
// Merge adjacent blocks
|
||||
void hex_data::compact()
|
||||
{
|
||||
iterator previous = blocks.begin();
|
||||
iterator i = previous;
|
||||
|
||||
for(++i; i != blocks.end(); ++i)
|
||||
{
|
||||
if( (previous->first + previous->second.size()) == i->first )
|
||||
{
|
||||
previous->second.insert(previous->second.end(), i->second.begin(), i->second.end());
|
||||
blocks.erase(i);
|
||||
i = previous;
|
||||
}
|
||||
previous = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all allocated memory
|
||||
void hex_data::clear()
|
||||
{
|
||||
_fill = 0;
|
||||
format = HEX_FORMAT_INHX8M;
|
||||
linear_addr_rec = false;
|
||||
segment_addr_rec = false;
|
||||
blocks.clear();
|
||||
}
|
||||
|
||||
// Erase a single element at the given address
|
||||
void hex_data::erase(address_type address)
|
||||
{
|
||||
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
|
||||
{
|
||||
// The blocks are sorted, so if the byte to be deleted is
|
||||
// before the block it must be a blank address that's either
|
||||
// before the first block or after any previous blocks.
|
||||
if( address < i->first )
|
||||
break;
|
||||
// Ignore the block if address is past the end of the block
|
||||
const address_type ope = i->first + i->second.size();
|
||||
if( address >= ope )
|
||||
continue;
|
||||
// address is now guaranteed to be >= i->first and < ope
|
||||
// Copy trailing portion of the old block to a new block
|
||||
if( (ope - address) > 1 )
|
||||
{
|
||||
const address_type index = address-i->first+1;
|
||||
blocks[address+1].assign(i->second.begin()+index, i->second.end());
|
||||
}
|
||||
// Truncate or delete old block
|
||||
const address_type size = address - i->first;
|
||||
if( size )
|
||||
i->second.resize(size);
|
||||
else
|
||||
blocks.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Erase [first, last]
|
||||
void hex_data::erase(address_type first, address_type last)
|
||||
{
|
||||
if( first > last )
|
||||
std::swap(first, last);
|
||||
|
||||
for(iterator i=blocks.begin(); (i!=blocks.end()) && (first<=last); ++i)
|
||||
{
|
||||
const address_type ope = i->first + i->second.size();
|
||||
if( first >= ope ) // Ignore all blocks with addresses < first
|
||||
continue;
|
||||
// The blocks are sorted, so if the first byte to be deleted is
|
||||
// before the block it must be a blank address that's either
|
||||
// before the first block or after any previous blocks.
|
||||
if( first < i->first )
|
||||
{
|
||||
if( last < i->first ) // If the entire range is before the
|
||||
return; // block there's nothing left to do
|
||||
first = i->first; // Advance to the next non-blank address
|
||||
}
|
||||
// first is now guaranteed to be >= i->first and < ope
|
||||
if( last < ope ) // Entire range is interior
|
||||
{
|
||||
// Copy trailing portion of the old block to a new block
|
||||
if( (ope - last) > 1 )
|
||||
{
|
||||
const address_type index = last-i->first+1;
|
||||
blocks[last+1].assign(i->second.begin()+index, i->second.end());
|
||||
}
|
||||
// Truncate or delete old block
|
||||
const address_type size = first - i->first;
|
||||
if( size )
|
||||
i->second.resize(size);
|
||||
else
|
||||
blocks.erase(i);
|
||||
return;
|
||||
}
|
||||
else // Truncate block
|
||||
{
|
||||
const address_type size = first - i->first;
|
||||
if( size )
|
||||
i->second.resize(size);
|
||||
else
|
||||
blocks.erase(i--);
|
||||
first = ope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hex_data::size_type hex_data::size()
|
||||
{
|
||||
size_type s=0;
|
||||
|
||||
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
|
||||
s += i->second.size();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// Returns the number of populated elements with addresses less than addr
|
||||
hex_data::size_type hex_data::size_below_addr(address_type addr)
|
||||
{
|
||||
size_type s=0;
|
||||
|
||||
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
|
||||
{
|
||||
if( (i->first + i->second.size()) < addr)
|
||||
s += i->second.size();
|
||||
else if( i->first < addr )
|
||||
s += addr - i->first;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// number of words in [lo, hi)
|
||||
hex_data::size_type hex_data::size_in_range(address_type lo, address_type hi)
|
||||
{
|
||||
size_type s=0;
|
||||
|
||||
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
|
||||
{
|
||||
if( i->first < lo )
|
||||
{
|
||||
const size_type a = i->first + i->second.size();
|
||||
if( a >= lo )
|
||||
s += a - lo;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (i->first + i->second.size()) < hi)
|
||||
s += i->second.size();
|
||||
else if( i->first < hi )
|
||||
s += hi - i->first;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// Return the max address of all of the set words with addresses less than or equal to hi
|
||||
address_type hex_data::max_addr_below(address_type hi)
|
||||
{
|
||||
address_type s=0;
|
||||
|
||||
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
|
||||
{
|
||||
if( i->first <= hi)
|
||||
{
|
||||
const address_type a = i->first + i->second.size() - 1; //Max address of this block
|
||||
if( a > s )
|
||||
s = a;
|
||||
}
|
||||
}
|
||||
if( s > hi )
|
||||
return hi;
|
||||
else
|
||||
return s;
|
||||
}
|
||||
|
||||
// Lowest address
|
||||
address_type hex_data::min_address() const
|
||||
{
|
||||
return blocks.begin()->first;
|
||||
}
|
||||
|
||||
// Highest address
|
||||
address_type hex_data::max_address() const
|
||||
{
|
||||
return blocks.rbegin()->first + blocks.rbegin()->second.size() - 1;
|
||||
}
|
||||
|
||||
//Return true if an element exists at addr
|
||||
bool hex_data::is_set(address_type addr)
|
||||
{
|
||||
// Start at the end of the list and find the first (last) block with an address
|
||||
// less than addr
|
||||
reverse_iterator i = blocks.rbegin();
|
||||
while( (i!=blocks.rend()) && (i->first > addr))
|
||||
++i;
|
||||
|
||||
if( (addr - i->first) > i->second.size() )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load from a file
|
||||
void hex_data::load(const std::string &path)
|
||||
{
|
||||
std::ifstream f(path.c_str());
|
||||
read(f);
|
||||
}
|
||||
|
||||
// Convert a string from hex to binary and append it to a block
|
||||
uint8_t hex2binary(hex_data::data_container& to, std::string& from)
|
||||
{
|
||||
value_type sum = 0, value;
|
||||
uint8_t character;
|
||||
bool first = true;
|
||||
std::string::iterator i = from.begin();
|
||||
|
||||
while( i != from.end() )
|
||||
{
|
||||
character = *i;
|
||||
|
||||
if( (character >= '0') && (character <= '9') )
|
||||
character -= '0';
|
||||
else if( (character >= 'A') && (character <= 'Z') )
|
||||
character -= 'A' - 10;
|
||||
else if( (character >= 'a') && (character <= 'z') )
|
||||
character -= 'a' - 10;
|
||||
else
|
||||
break; // Bad character
|
||||
|
||||
if( first )
|
||||
value = character << 4;
|
||||
else
|
||||
{
|
||||
value |= character;
|
||||
to.push_back(value);
|
||||
sum += value;
|
||||
}
|
||||
|
||||
first = !first;
|
||||
++i;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Read data from an input stream
|
||||
void hex_data::read(std::istream &s)
|
||||
{
|
||||
address_type address;
|
||||
address_type extended_address(0);
|
||||
std::string line;
|
||||
data_container buffer;
|
||||
|
||||
while( (s.get() == ':') && s.good() )
|
||||
{
|
||||
std::getline(s, line); // Read the whole line
|
||||
if( line.size() <= 10 ) // Ignore truncated lines
|
||||
break;
|
||||
buffer.clear();
|
||||
buffer.reserve(line.size()/2); // Pre-allocate
|
||||
if( hex2binary(buffer, line) ) // Ignore lines with bad checksums
|
||||
break;
|
||||
|
||||
address = buffer[1];
|
||||
address = (address << 8) | buffer[2];
|
||||
unsigned length = buffer[0];
|
||||
const unsigned type = buffer[3];
|
||||
value_type* data = &buffer[4];
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case 0: //Data block
|
||||
{
|
||||
address += extended_address;
|
||||
iterator i = blocks.begin();
|
||||
for(; i != blocks.end(); ++i ) // Find a block that includes address
|
||||
{
|
||||
address_type num = 0;
|
||||
// If the start of the new block is interior to an existing block...
|
||||
if( (i->first <= address) && ( (i->first + i->second.size()) > address) )
|
||||
{
|
||||
// Store the portion of the new block that overlaps the existing block
|
||||
const size_type index = address - i->first;
|
||||
num = i->second.size() - index;
|
||||
if( num > length )
|
||||
num = length;
|
||||
std::copy(data, data+num, &(i->second[index]));
|
||||
}
|
||||
// If the end of the new block is interior to an existing block...
|
||||
if( (address < i->first) && ((address + length) > i->first) )
|
||||
{
|
||||
// Create a new block for the non-overlapping portion
|
||||
num = i->first - address;
|
||||
if( num > length )
|
||||
num = length;
|
||||
blocks[address].assign(data, data+num);
|
||||
}
|
||||
length -= num;
|
||||
address += num;
|
||||
data += num;
|
||||
// Bail out early if there's nothing left to do
|
||||
if( 0 == length )
|
||||
break;
|
||||
}
|
||||
// Handle any leftover bytes
|
||||
if( length )
|
||||
blocks[address].assign(data, data+length);
|
||||
break;
|
||||
}
|
||||
case 1: break; // Ignore EOF record
|
||||
case 2: // Segment address record (INHX32)
|
||||
if( (0 == address) && (2 == length) )
|
||||
{
|
||||
extended_address = buffer[4];
|
||||
extended_address = (extended_address << 8) | buffer[5];
|
||||
extended_address <<= 4;
|
||||
segment_addr_rec = true;
|
||||
}
|
||||
break;
|
||||
case 4: // Linear address record (INHX32)
|
||||
if( (0 == address) && (2 == length) )
|
||||
{
|
||||
extended_address = buffer[4];
|
||||
extended_address = (extended_address << 8) | buffer[5];
|
||||
extended_address <<= 16;
|
||||
linear_addr_rec = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write all data to a file
|
||||
void hex_data::write(const char *path)
|
||||
{
|
||||
std::ofstream ofs(path);
|
||||
if( !ofs ) // Bail out on bad files
|
||||
return;
|
||||
write(ofs);
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
// Write all data to an output stream
|
||||
void hex_data::write(std::ostream &os)
|
||||
{
|
||||
uint8_t checksum;
|
||||
uint16_t linear_address(0);
|
||||
|
||||
if( !os ) // Bail out on bad streams
|
||||
return;
|
||||
|
||||
os.setf(std::ios::hex, std::ios::basefield); //Set the stream to ouput hex instead of decimal
|
||||
os.setf(std::ios::uppercase); //Use uppercase hex notation
|
||||
os.fill('0'); //Pad with zeroes
|
||||
|
||||
//If we already know that this is an INHX32M file, start with a segment address record
|
||||
// otherwise check all of the blocks just to make sure
|
||||
if( linear_addr_rec )
|
||||
{
|
||||
os << INH32M_HEADER << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(iterator i=blocks.begin(); i!=blocks.end(); i++)
|
||||
{
|
||||
if(i->first > 0xFFFF) //Check the upper 16 bits
|
||||
{
|
||||
linear_addr_rec = true;
|
||||
os << INH32M_HEADER << std::endl;
|
||||
break; //Only need to find one
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(iterator i=blocks.begin(); i!=blocks.end(); i++)
|
||||
{
|
||||
// Check upper 16 bits of the block address for non-zero,
|
||||
// which indicates that a segment address record is needed
|
||||
if( i->first > 0xFFFF )
|
||||
{
|
||||
const uint16_t addr(i->first >> 16);
|
||||
//Has a record for this segment already been emitted?
|
||||
if( addr != linear_address )
|
||||
{
|
||||
//Emit a new segment address record
|
||||
os << ":02000004";
|
||||
os.width(4);
|
||||
os << addr; //Address
|
||||
// Create a checksum for the linear address record
|
||||
checksum = 0x06 + addr + (addr >> 8);
|
||||
checksum = 0x01 + ~checksum;
|
||||
os.width(2);
|
||||
// OSX (or maybe GCC), seems unable to handle uint8_t
|
||||
// arguments to a stream
|
||||
os << static_cast<uint16_t>(checksum); // Checksum byte
|
||||
os << std::endl;
|
||||
linear_address = addr;
|
||||
}
|
||||
}
|
||||
checksum = 0;
|
||||
os << ':'; //Every line begins with ':'
|
||||
os.width(2);
|
||||
os << i->second.size(); //Length
|
||||
checksum += i->second.size();
|
||||
os.width(4);
|
||||
os << static_cast<uint16_t>(i->first); //Address
|
||||
checksum += static_cast<uint8_t>(i->first); // Low byte
|
||||
checksum += static_cast<uint8_t>(i->first >> 8); // High byte
|
||||
os << "00"; //Record type
|
||||
for(unsigned j=0; j<i->second.size(); ++j) //Store the data bytes, LSB first, ASCII HEX
|
||||
{
|
||||
os.width(2);
|
||||
// OSX (or maybe GCC), seems unable to handle uint8_t
|
||||
// arguments to a stream
|
||||
os << static_cast<uint16_t>(i->second[j]);
|
||||
checksum += i->second[j];
|
||||
}
|
||||
checksum = 0x01 + ~checksum;
|
||||
os.width(2);
|
||||
// OSX (or maybe GCC), seems unable to handle uint8_t arguments to a stream
|
||||
os << static_cast<uint16_t>(checksum); // Checksum byte
|
||||
os << std::endl;
|
||||
}
|
||||
os << ":00000001FF\n"; //EOF marker
|
||||
}
|
||||
|
||||
// Make things pretty
|
||||
// Truncate blocks to a given length as needed
|
||||
void hex_data::tidy(hex_data::size_type length)
|
||||
{
|
||||
for(iterator i=blocks.begin(); i!=blocks.end(); i++)
|
||||
{
|
||||
if(i->second.size() > length) //If the block is too long...
|
||||
{
|
||||
//Make an interator that points to the first element to copy out of i->second
|
||||
data_container::iterator k(i->second.begin());
|
||||
advance(k, length);
|
||||
|
||||
// Assign the extra elements to a new block and truncate the original
|
||||
blocks[i->first + length].assign(k, i->second.end());
|
||||
i->second.erase(k, i->second.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Compare two sets of hex data
|
||||
// Return true if every word in hex1 has a corresponding, and equivalent, word in hex2
|
||||
bool compare(hex_data& hex1, hex_data& hex2, value_type mask, address_type begin, address_type end)
|
||||
{
|
||||
//Walk block list from hex1
|
||||
for( hex_data::iterator i = hex1.begin(); i != hex1.end(); ++i )
|
||||
{
|
||||
//Walk the block
|
||||
address_type addr(i->first);
|
||||
for( hex_data::data_container::iterator j = i->second.begin(); j != i->second.end(); ++j, ++addr)
|
||||
{
|
||||
if( (addr < begin) || (addr > end) )
|
||||
continue;
|
||||
|
||||
//Compare both sides through the given mask
|
||||
if( ((*j) & mask) != (hex2.get(addr) & mask) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/* Routines for reading/writing Intel INHX8M and INHX32 files
|
||||
|
||||
Copyright 2002 Brandon Fosdick (BSD License)
|
||||
*/
|
||||
|
||||
#ifndef INTELHEXH
|
||||
#define INTELHEXH
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//#include <unistd.h>
|
||||
|
||||
namespace intelhex
|
||||
{
|
||||
|
||||
#define HEX_FORMAT_INHX8M 0x01
|
||||
#define HEX_FORMAT_INHX32 0x02
|
||||
|
||||
class hex_data;
|
||||
typedef hex_data container;
|
||||
typedef uint32_t address_type;
|
||||
typedef uint8_t value_type;
|
||||
|
||||
//The data set that results from parsing a hex file
|
||||
struct hex_data {
|
||||
|
||||
//Each line of the hex file generates a block of memory at a particular address
|
||||
typedef std::vector<value_type> data_container; //Element container
|
||||
typedef std::map<address_type, data_container> container; //List of data blocks
|
||||
|
||||
typedef container::iterator iterator;
|
||||
typedef container::reverse_iterator reverse_iterator;
|
||||
typedef data_container::size_type size_type;
|
||||
|
||||
|
||||
private:
|
||||
value_type _fill; // Value returned for unset addresses
|
||||
char format; //Format of the parsed file (necessary?)
|
||||
bool segment_addr_rec; // Uses/Has a segment address record
|
||||
bool linear_addr_rec; // Uses/Has a linear address record
|
||||
container blocks; // List of data blocks
|
||||
|
||||
public:
|
||||
hex_data() : _fill(0), segment_addr_rec(false), linear_addr_rec(false) {}
|
||||
hex_data(const std::string &s) : _fill(0), segment_addr_rec(false), linear_addr_rec(false)
|
||||
{
|
||||
load(s);
|
||||
}
|
||||
iterator begin() { return blocks.begin(); }
|
||||
iterator end() { return blocks.end(); }
|
||||
|
||||
void compact(); // Merge adjacent blocks
|
||||
void clear(); //Delete everything
|
||||
void erase(address_type); // Erase a single element
|
||||
void erase(address_type first, address_type last); // Erase [first, last]
|
||||
value_type fill() { return _fill; }
|
||||
void fill(value_type f) { _fill = f; }
|
||||
size_type size();
|
||||
size_type size_below_addr(address_type);
|
||||
size_type size_in_range(address_type, address_type); //number of words in [lo, hi)
|
||||
address_type max_addr_below(address_type);
|
||||
|
||||
address_type min_address() const; // Lowest address
|
||||
address_type max_address() const; // Highest address
|
||||
|
||||
bool is_set(address_type);
|
||||
|
||||
value_type& operator[](address_type); //Array access operator
|
||||
value_type get(address_type); // Return the value at address
|
||||
void set(address_type, value_type); // Set the value at address
|
||||
|
||||
void load(const std::string&); // Load from a file
|
||||
void read(std::istream &); // Read data from an input stream
|
||||
void write(const char *); //Save hex data to a hex file
|
||||
void write(std::ostream &); //Write all data to an output stream
|
||||
void tidy(size_type length); // Make things pretty
|
||||
};
|
||||
|
||||
bool compare(hex_data&, hex_data&, value_type, address_type, address_type);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,287 @@
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "intelhex.h"
|
||||
#include "serial/serial.h"
|
||||
|
||||
|
||||
#define ACK 0x79
|
||||
#define NACK 0x1F
|
||||
#define CMD_ERASE 0x43
|
||||
#define CMD_GETID 0x02
|
||||
#define CMD_WRITE 0x2b
|
||||
|
||||
|
||||
using std::cout, std::endl, std::cerr;
|
||||
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
|
||||
#define APPNAME "C:/Users/gritt/Documents/workspace/STM32/Custom-Bootloader/App1/build/App1.hex"
|
||||
#define COMPORT "COM3"
|
||||
|
||||
std::map <uint16_t, std::string> deviceIDs = {
|
||||
{0x410, "STM32F103RB"},
|
||||
{0x415, "STM32L152RG"},
|
||||
{0x417, "STM32L053R8"},
|
||||
{0x421, "STM32F446RE"},
|
||||
{0x431, "STM32F411RE"},
|
||||
{0x433, "STM32F401RE"},
|
||||
{0x437, "STM32L152RE"},
|
||||
{0x439, "STM32F302R8"},
|
||||
{0x438, "STM32F334R8"},
|
||||
{0x440, "STM32F030R8"},
|
||||
{0x442, "STM32F091RC"},
|
||||
{0x446, "STM32F303RE"},
|
||||
{0x447, "STM32L073RZ"},
|
||||
{0x448, "STM32F070RB/STM32F072RB"},
|
||||
{0x458, "STM32F410RB"},
|
||||
};
|
||||
|
||||
|
||||
u32 computeStm32Crc(u32* buffer, u32 len) {
|
||||
u32 crc = 0xffffffff;
|
||||
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
crc ^= buffer[i];
|
||||
|
||||
for (u8 j = 0; j < 32; ++j) {
|
||||
|
||||
if (crc & 0x80000000) {
|
||||
crc = (crc << 1) ^ 0x04C11DB7; // Polynomial used in STM32
|
||||
}
|
||||
else {
|
||||
crc = (crc << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (crc & 0xffffffff);
|
||||
}
|
||||
|
||||
u32 computeStm32Crc_bytes(u8* buffer, u32 len) {
|
||||
u32 crc = 0xffffffff;
|
||||
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
u32 data = buffer[i];
|
||||
crc ^= data;
|
||||
|
||||
for (u8 j = 0; j < 32; ++j) {
|
||||
|
||||
if (crc & 0x80000000) {
|
||||
crc = (crc << 1) ^ 0x04C11DB7; // Polynomial used in STM32
|
||||
}
|
||||
else {
|
||||
crc = (crc << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (crc & 0xffffffff);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Flasher {
|
||||
|
||||
public:
|
||||
|
||||
Flasher(std::string com) :
|
||||
|
||||
timeout (0, 1000, 1, 1000, 1)
|
||||
{
|
||||
|
||||
stm32Serial.setPort(com);
|
||||
stm32Serial.setBaudrate(115200);
|
||||
stm32Serial.setTimeout(timeout);
|
||||
try {
|
||||
stm32Serial.open();
|
||||
|
||||
u32 len = stm32Serial.available();
|
||||
if (len != 0) {
|
||||
stm32Serial.read(len);
|
||||
}
|
||||
}
|
||||
|
||||
catch (...) {
|
||||
cerr << "Unable to open serial port" << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
~Flasher() {
|
||||
stm32Serial.close();
|
||||
}
|
||||
|
||||
u32 getDeviceID() {
|
||||
if (!stm32Serial.isOpen()) {
|
||||
cerr << "Unable to open serial " << COMPORT << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::array <u8, 20> payload;
|
||||
std::fill(std::begin(payload), std::end(payload), 0);
|
||||
|
||||
payload[0] = CMD_GETID;
|
||||
u32 crc = computeStm32Crc((u32*)payload.data(), 1);
|
||||
memcpy(payload.data() + 1, &crc, sizeof(u32));
|
||||
|
||||
stm32Serial.write(payload.data(), 20);
|
||||
|
||||
// read 1 byte (ACK or NACK)
|
||||
u32 len = stm32Serial.read(payload.data(), 1);
|
||||
if (len != 1) {
|
||||
cerr << "Error reading device ID\n" << endl;
|
||||
return 0;
|
||||
}
|
||||
if (payload[0] == ACK) {
|
||||
len = stm32Serial.read(payload.data(), 2);
|
||||
if (len == 2) {
|
||||
|
||||
u32 deviceID = 0;
|
||||
memcpy(&deviceID, payload.data(), 2);
|
||||
return deviceID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
cerr << "Received NACK on device ID request" << endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u8 eraseFlash () {
|
||||
|
||||
if (!stm32Serial.isOpen())
|
||||
return 1;
|
||||
|
||||
std::array <u8, 20> payload;
|
||||
payload[0] = CMD_ERASE;
|
||||
payload[1] = 0xff;
|
||||
u32 crc = computeStm32Crc_bytes(payload.data(), 2);
|
||||
memcpy(payload.data()+2, &crc, sizeof(u32));
|
||||
|
||||
|
||||
|
||||
stm32Serial.write(payload.data(), 20);
|
||||
|
||||
// read 1 byte (ACK or NACK)
|
||||
// since erase operation can take several seconds, increase read timeout to 50s
|
||||
timeout.read_timeout_constant = 50000;
|
||||
stm32Serial.setTimeout(timeout);
|
||||
|
||||
u32 len = stm32Serial.read(payload.data(), 1);
|
||||
|
||||
if (len == 0) {
|
||||
cerr << "read timed out" << endl;
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
if (payload[0] == ACK) {
|
||||
cout << "received ACK" << endl;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
cerr << "received NACK" << endl;
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flashImage (std::string image) {
|
||||
|
||||
intelhex::hex_data program = intelhex::hex_data(APPNAME);
|
||||
std::array <u8, 20> requestPayload;
|
||||
std::array <u8, 20> dataPayload;
|
||||
std::array <u8, 20> readBuffer;
|
||||
|
||||
requestPayload[0] = CMD_WRITE;
|
||||
|
||||
u32 line = 1;
|
||||
for (auto& record : program) {
|
||||
|
||||
cout << std::dec << (int)line++ << ":Write at " << std::hex << (int) record.first << endl;
|
||||
|
||||
// send write request and address
|
||||
memcpy(requestPayload.data()+1, &record.first,sizeof (u32));
|
||||
u32 crc = computeStm32Crc_bytes(requestPayload.data(), 5);
|
||||
memcpy(requestPayload.data()+5, &crc, sizeof(u32));
|
||||
|
||||
stm32Serial.write(requestPayload.data(), 20);
|
||||
|
||||
u8 len = stm32Serial.read(readBuffer.data(), 1);
|
||||
if (len == 0 || len==1 && readBuffer[0] == NACK)
|
||||
return;
|
||||
|
||||
// send data
|
||||
for (u8 i = 0; i < 16; i++) {
|
||||
if (i < record.second.size())
|
||||
dataPayload[i] = record.second[i];
|
||||
else
|
||||
dataPayload[i] = 0xff;
|
||||
}
|
||||
|
||||
crc = computeStm32Crc((u32*)dataPayload.data(), 4);
|
||||
memcpy(dataPayload.data()+16, &crc, sizeof(u32));
|
||||
|
||||
stm32Serial.write(dataPayload.data(), 20);
|
||||
|
||||
len = stm32Serial.read(readBuffer.data(), 1);
|
||||
if (len == 0) {
|
||||
cerr << "TIMEOUT" << endl;
|
||||
return;
|
||||
}
|
||||
if (len == 1 && readBuffer[0] == NACK) {
|
||||
cerr << "NACK" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
cout << "Done" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// open serial port
|
||||
serial::Timeout timeout;
|
||||
serial::Serial stm32Serial;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
|
||||
Flasher flasher(COMPORT);
|
||||
u32 devID = flasher.getDeviceID();
|
||||
cout << "Devide ID: " << devID << endl;
|
||||
|
||||
if (devID == 0)
|
||||
return 1;
|
||||
|
||||
|
||||
|
||||
std::string device = deviceIDs[devID];
|
||||
cout << "found " << device << endl;
|
||||
|
||||
cout << "Erase flash" << endl;
|
||||
if (flasher.eraseFlash() != 0) {
|
||||
cerr << "Error erasing flash" << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
cout << "Done" << endl;
|
||||
|
||||
|
||||
flasher.flashImage(APPNAME);
|
||||
cout << "IMAGE WRITING DONE" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user