add programmer
This commit is contained in:
parent
65515c7eca
commit
b9052ca694
27 changed files with 2102 additions and 0 deletions
53
V203F6P6/programmer/Makefile
Normal file
53
V203F6P6/programmer/Makefile
Normal file
|
@ -0,0 +1,53 @@
|
|||
TARGET?= ch32v203
|
||||
|
||||
TOOL ?= gcc
|
||||
#TOOL ?= clang
|
||||
|
||||
PRJ = example
|
||||
|
||||
VPATH = . ./$(TARGET) ./common
|
||||
BLD = ./build/
|
||||
DFLAGS = -d
|
||||
LFLAGS = -g
|
||||
LDLIBS = -L./$(TARGET)/usbd -lusbd
|
||||
BFLAGS = --strip-unneeded
|
||||
|
||||
CFLAGS = -MMD -Wall -Wno-parentheses -ggdb -fno-exceptions -ffunction-sections -fdata-sections
|
||||
CFLAGS+= -I. -I./$(TARGET) -I./$(TARGET)/usbd -I./common
|
||||
DEL = rm -f
|
||||
|
||||
# zdrojaky
|
||||
OBJS = main.o usb_desc.o hack.o
|
||||
OBJS += intelhex.o linkprotocol.o norflash.o spiblocked.o
|
||||
|
||||
include $(TARGET)/$(TOOL).mk
|
||||
BOBJS = $(addprefix $(BLD),$(OBJS))
|
||||
|
||||
all: $(BLD) $(PRJ).elf
|
||||
# ... atd.
|
||||
-include $(BLD)*.d
|
||||
# linker
|
||||
$(PRJ).elf: $(BOBJS)
|
||||
-@echo [LD $(TOOL),$(TARGET)] $@
|
||||
@$(LD) $(LFLAGS) -o $(PRJ).elf $(BOBJS) $(LDLIBS)
|
||||
-@echo "size:"
|
||||
@$(SIZE) $(PRJ).elf
|
||||
-@echo "listing:"
|
||||
$(DUMP) $(DFLAGS) $(PRJ).elf > $(PRJ).lst
|
||||
-@echo "OK."
|
||||
$(COPY) $(BFLAGS) -O binary $(PRJ).elf $(PRJ).bin
|
||||
# preloz co je potreba
|
||||
$(BLD)%.o: %.c
|
||||
-@echo [CC $(TOOL),$(TARGET)] $@
|
||||
@$(CC) -std=gnu99 -c $(CFLAGS) $< -o $@
|
||||
$(BLD)%.o: %.cpp
|
||||
-@echo [CX $(TOOL),$(TARGET)] $@
|
||||
@$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@
|
||||
$(BLD):
|
||||
mkdir $(BLD)
|
||||
flash: $(PRJ).elf
|
||||
minichlink -w $(PRJ).bin flash -b
|
||||
# vycisti
|
||||
clean:
|
||||
$(DEL) $(BLD)* *.lst *.bin *.elf *.map *~
|
||||
.PHONY: all clean flash run
|
1
V203F6P6/programmer/ch32v203
Symbolic link
1
V203F6P6/programmer/ch32v203
Symbolic link
|
@ -0,0 +1 @@
|
|||
../ch32v203/
|
1
V203F6P6/programmer/common
Symbolic link
1
V203F6P6/programmer/common
Symbolic link
|
@ -0,0 +1 @@
|
|||
../common/
|
112
V203F6P6/programmer/endien.h
Normal file
112
V203F6P6/programmer/endien.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
#ifndef ENDIEN_H
|
||||
#define ENDIEN_H
|
||||
#include <stdint.h>
|
||||
/**
|
||||
@file
|
||||
@brief Pořadí bytů ve slově.
|
||||
|
||||
*/
|
||||
#if __GNUC__ < 4 // Compiler test
|
||||
#error "Zkontroluj nastaveni"
|
||||
#endif
|
||||
// Pro 16.bit musíme struktury pakovat
|
||||
#define PACKSTRUCT __attribute__((packed))
|
||||
|
||||
#if __thumb__
|
||||
// Specificky a krátce pro danou architektutu a pokud chceme mít úplnou kontrolu.
|
||||
static inline uint32_t swab32 (uint32_t p) {
|
||||
register uint32_t t;
|
||||
asm volatile ("rev %0, %1":"=r"(t):"0"(p));
|
||||
return t;
|
||||
}
|
||||
#else //__thumb__
|
||||
// Obecně
|
||||
static inline uint32_t swab32 (uint32_t p) {
|
||||
// return __builtin_bswap32 (p); // I takto to jde, ale pro názornost:
|
||||
return ((p & 0x000000ff) << 24)
|
||||
| ((p & 0x0000ff00) << 8)
|
||||
| ((p & 0x00ff0000) >> 8)
|
||||
| ((p & 0xff000000) >> 24);
|
||||
}
|
||||
#endif //__thumb__
|
||||
// Tohle nebudeme ani specifikovat pro ARM
|
||||
static inline uint16_t swab16 (uint16_t p) {
|
||||
return (p << 8) | (p >> 8);
|
||||
}
|
||||
// Nedělá nic a ani nijak neotravuje - pouze pro kompatibilitu
|
||||
static inline uint32_t nosw32 (uint32_t p) {
|
||||
return p;
|
||||
}
|
||||
static inline uint16_t nosw16 (uint16_t p) {
|
||||
return p;
|
||||
}
|
||||
// Specificky podle pořadí bytů - LE je pro ARM i X86 normální.
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define cpu_to_be32 swab32
|
||||
#define be32_to_cpu swab32
|
||||
#define cpu_to_le32 nosw32
|
||||
#define le32_to_cpu nosw32
|
||||
|
||||
#define cpu_to_be16 swab16
|
||||
#define be16_to_cpu swab16
|
||||
#define cpu_to_le16 nosw16
|
||||
#define le16_to_cpu nosw16
|
||||
#else
|
||||
#define cpu_to_be32 nosw32
|
||||
#define be32_to_cpu nosw32
|
||||
#define cpu_to_le32 swab32
|
||||
#define le32_to_cpu swab32
|
||||
|
||||
#define cpu_to_be16 nosw16
|
||||
#define be16_to_cpu nosw16
|
||||
#define cpu_to_le16 swab16
|
||||
#define le16_to_cpu swab16
|
||||
#endif
|
||||
/**
|
||||
@class ui32be
|
||||
@brief C++ 32.bit data v BIG Endien.
|
||||
@class ui32le
|
||||
@brief C++ 32.bit data v LITTLE Endien.
|
||||
@class ui16be
|
||||
@brief C++ 16.bit data v BIG Endien.
|
||||
@class ui16le
|
||||
@brief C++ 16.bit data v LITTLE Endien.
|
||||
*/
|
||||
class ui32be {
|
||||
public:
|
||||
/// Nastavení hodnoty
|
||||
/// @par p uint32_t hodnota
|
||||
void set (uint32_t p) {num = cpu_to_be32 (p); };
|
||||
/// Zjištění hodnoty
|
||||
/// @return uint32_t hodnota
|
||||
uint32_t get (void) {return be32_to_cpu (num); };
|
||||
private:
|
||||
/// Vlastní data uint32_t - k nim se přímo nedostaneme, jsou privátní
|
||||
uint32_t num;
|
||||
}PACKSTRUCT;
|
||||
|
||||
class ui32le {
|
||||
public:
|
||||
void set (uint32_t p) {num = cpu_to_le32 (p); };
|
||||
uint32_t get (void) {return le32_to_cpu (num); };
|
||||
private:
|
||||
uint32_t num;
|
||||
}PACKSTRUCT;
|
||||
/* *******************************************************/
|
||||
class ui16be {
|
||||
public:
|
||||
void set (uint16_t p) {num = cpu_to_be16 (p); };
|
||||
uint16_t get (void) {return be16_to_cpu (num); };
|
||||
private:
|
||||
uint16_t num;
|
||||
}PACKSTRUCT;
|
||||
|
||||
class ui16le {
|
||||
public:
|
||||
void set (uint16_t p) {num = cpu_to_le16 (p); };
|
||||
uint16_t get (void) {return le16_to_cpu (num); };
|
||||
private:
|
||||
uint16_t num;
|
||||
}PACKSTRUCT;
|
||||
|
||||
#endif // ENDIEN_H
|
21
V203F6P6/programmer/hack.c
Normal file
21
V203F6P6/programmer/hack.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
size_t strlen (const char *s) {
|
||||
size_t l = 0;
|
||||
while (*s++) l++;
|
||||
return l;
|
||||
}
|
||||
void *memcpy (void *dest, const void *src, size_t n) {
|
||||
const char *s = (const char *) src;
|
||||
char *d = (char *) dest;
|
||||
int i;
|
||||
for (i=0; i<n; i++) d[i] = s[i];
|
||||
return dest;
|
||||
}
|
||||
void *memset (void *s, int c, size_t n) {
|
||||
char *p = (char *) s;
|
||||
int i;
|
||||
for (i=0; i<n; i++) p[i] = c;
|
||||
return s;
|
||||
}
|
57
V203F6P6/programmer/helpers.h
Normal file
57
V203F6P6/programmer/helpers.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#ifndef HELPERS_H
|
||||
#define HELPERS_H
|
||||
#include <stdint.h>
|
||||
#include "norflash.h"
|
||||
#include "fifo.h"
|
||||
#include "baselayer.h"
|
||||
|
||||
class MemoryBase {
|
||||
NorFlash flash;
|
||||
public:
|
||||
explicit MemoryBase () noexcept : flash() {}
|
||||
uint32_t Write (const uint32_t addr, const void * ptr, const uint32_t len) {
|
||||
return flash.WriteBlock(addr, reinterpret_cast<const uint8_t*>(ptr), len);
|
||||
}
|
||||
uint32_t Read (const uint32_t addr, void * ptr, const uint32_t len) {
|
||||
return flash.ReadBlock(addr, reinterpret_cast<uint8_t*> (ptr), len);
|
||||
}
|
||||
void Erase (const uint32_t blok) {
|
||||
flash.EraseSector(blok);
|
||||
}
|
||||
};
|
||||
|
||||
class CdcCmd : public BaseLayer {
|
||||
static constexpr int maxlen = 0x80;
|
||||
FIFO<char, maxlen> ring;
|
||||
char line [maxlen];
|
||||
unsigned index;
|
||||
public:
|
||||
explicit CdcCmd () noexcept : BaseLayer(), ring(), index(0u) {}
|
||||
uint32_t Up(const char * data, const uint32_t len) override {
|
||||
for (unsigned n=0u; n<len; n++) ring.Write (data [n]);
|
||||
return len;
|
||||
}
|
||||
void SendString (const char * ptr, const uint32_t len) {
|
||||
unsigned ofs = 0u, res = len;
|
||||
while (res) {
|
||||
const unsigned chunk = res > 0x20u ? 0x20u : res;
|
||||
const unsigned n = Down (ptr + ofs, chunk);
|
||||
ofs += n;
|
||||
res -= n;
|
||||
}
|
||||
}
|
||||
char * GetLine (unsigned & len) {
|
||||
char c;
|
||||
while (ring.Read(c)) {
|
||||
line [index++] = c;
|
||||
if (c == '\n') {
|
||||
len = index;
|
||||
index = 0u;
|
||||
return line;
|
||||
}
|
||||
}
|
||||
len = 0u;
|
||||
return line;
|
||||
}
|
||||
};
|
||||
#endif // HELPERS_H
|
249
V203F6P6/programmer/intelhex.cpp
Normal file
249
V203F6P6/programmer/intelhex.cpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
#include <stdio.h>
|
||||
#include "string.h"
|
||||
#include "endien.h"
|
||||
#include "intelhex.h"
|
||||
|
||||
static uint8_t hexval (const char c) {
|
||||
if ('0' <= c && c <= '9')
|
||||
return (uint8_t) c - '0';
|
||||
else if ('a' <= c && c <= 'f')
|
||||
return (uint8_t) c - 'a' + 10;
|
||||
else return 0;
|
||||
}
|
||||
static unsigned to_hex_str (char * str, const uint8_t * data, const unsigned len) {
|
||||
unsigned result = 0;
|
||||
const char * hexstr = "0123456789ABCDEF";
|
||||
for (unsigned n=0; n<len; n++) {
|
||||
const uint8_t c = data [n];
|
||||
str [result++] = hexstr [c >> 4];
|
||||
str [result++] = hexstr [c & 0xF];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static uint8_t chk_sum (const uint8_t * data, const unsigned len) {
|
||||
uint8_t sum = 0;
|
||||
for (unsigned n=0; n<len; n++) sum += data [n];
|
||||
return sum;
|
||||
}
|
||||
|
||||
union IhxHeader {
|
||||
struct {
|
||||
uint8_t reclen;
|
||||
ui16be load;
|
||||
RowTypes rectyp;
|
||||
}__attribute__((packed));
|
||||
uint8_t bytes [4];
|
||||
};
|
||||
union UlbaAddr {
|
||||
ui16be addr;
|
||||
uint8_t bytes [2];
|
||||
};
|
||||
union EipAddr {
|
||||
ui32be addr;
|
||||
uint8_t bytes [4];
|
||||
};
|
||||
|
||||
IntelHex::IntelHex() noexcept : address(0), offset(0), lenght(0), total_lenght(0), ackofs(0) {
|
||||
}
|
||||
|
||||
RowTypes IntelHex::parseRecord (const char * data, const unsigned int len) {
|
||||
//printf ("parseRecord: %d %s\r\n", len, data);
|
||||
if (data[0] != (uint8_t) ':') return badRecord;
|
||||
uint8_t tmp [(len >> 1) + 1];
|
||||
uint8_t val = 0;
|
||||
unsigned k = 0;
|
||||
for (unsigned n=1; n<len; n++) {
|
||||
const uint8_t c = data [n];
|
||||
if (c == '\r') break;
|
||||
if (c == '\n') break;
|
||||
val *= 0x10;
|
||||
val += hexval (c | 0x20);
|
||||
if ((n & 1) == 0) {
|
||||
tmp [k++] = val;
|
||||
val = 0;
|
||||
}
|
||||
}
|
||||
k -= 1;
|
||||
uint8_t chk = 0;
|
||||
for (unsigned n=0; n<k; n++) chk += tmp [n];
|
||||
chk = 0 - chk;
|
||||
// kontrolní součet
|
||||
if (chk != tmp[k]) return badRecord;
|
||||
// chybná délka
|
||||
if (k < sizeof (IhxHeader)) return badRecord;
|
||||
IhxHeader hdr;
|
||||
memcpy (hdr.bytes, tmp, sizeof(IhxHeader));
|
||||
// chybná délka
|
||||
if (k != (hdr.reclen + sizeof(IhxHeader))) return badRecord;
|
||||
//printf ("%d: %d=%d, %d, %02X=%02X\n", len, k, hdr.reclen+4, hdr.rectyp, chk, tmp[k]);
|
||||
UlbaAddr a1; EipAddr a2;
|
||||
RowTypes t = hdr.rectyp;
|
||||
lenght = hdr.reclen;
|
||||
const uint8_t * start = tmp + sizeof(IhxHeader);
|
||||
uint32_t tmpadr;
|
||||
// uint16_t old_ofs = offset;
|
||||
switch (t) {
|
||||
case dataRecord:
|
||||
memcpy (chunk, start, lenght);
|
||||
offset = hdr.load.get();
|
||||
// if (old_ofs != offset) printf ("Posun 0x%04X-0x%04X\n", old_ofs, offset);
|
||||
total_lenght = offset + lenght;
|
||||
break;
|
||||
case elaRecord:
|
||||
total_lenght = 0;
|
||||
memcpy (a1.bytes, start, lenght);
|
||||
address = (uint32_t) a1.addr.get() << 16;
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
case slaRecord:
|
||||
memcpy (a2.bytes, start, lenght);
|
||||
address = a2.addr.get();
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
case ackRecord:
|
||||
case nakRecord:
|
||||
ackofs = hdr.load.get();
|
||||
break;
|
||||
case eofRecord:
|
||||
case reqRecord:
|
||||
case ersRecord:
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
// Tyhle 2 rekordy nějak fungují, ale je to divné. Bohužel třeba objcopy
|
||||
// je na nízkých adresách z nějakého důvodu používá. Možná je v tom chyba.
|
||||
case esaRecord:
|
||||
total_lenght = 0;
|
||||
memcpy (a1.bytes, start, lenght);
|
||||
address = (uint32_t) a1.addr.get() << 4;
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
case ssaRecord:
|
||||
memcpy (a2.bytes, start, lenght);
|
||||
tmpadr = a2.addr.get();
|
||||
address = (tmpadr >> 12); // CS:IP ? čerti vědí, jak je to myšleno
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
bool IntelHex::CompareAckOffsset() {
|
||||
//printf ("ofs: %04X=%04X\n", ackofs, offset);
|
||||
if (ackofs == offset) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void IntelHex::getAddress (uint32_t & adr) {
|
||||
adr = address;
|
||||
}
|
||||
void IntelHex::getLenght (uint32_t & len) {
|
||||
len = total_lenght;
|
||||
}
|
||||
void IntelHex::getOffset (uint32_t & ofs) {
|
||||
ofs = offset;
|
||||
}
|
||||
void IntelHex::WriteData (uint8_t * data) {
|
||||
memcpy (data + offset, chunk, lenght);
|
||||
offset += lenght;
|
||||
}
|
||||
uint8_t * IntelHex::getDataRow (uint32_t & ofs, uint32_t & len) {
|
||||
ofs = offset;
|
||||
len = lenght;
|
||||
return chunk;
|
||||
}
|
||||
void IntelHex::AddOffset (uint32_t len) {
|
||||
offset += len;
|
||||
}
|
||||
|
||||
uint32_t IntelHex::ElaRecord (char * string, const uint32_t adr) {
|
||||
offset = 0;
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
UlbaAddr lba;
|
||||
IhxHeader hdr;
|
||||
hdr.load.set(0);
|
||||
hdr.reclen = sizeof (lba);
|
||||
hdr.rectyp = elaRecord;
|
||||
lba.addr.set (adr >> 16);
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk += chk_sum (lba.bytes, sizeof(UlbaAddr ));
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
result += to_hex_str (string + result, lba.bytes, sizeof(UlbaAddr ));
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
uint32_t IntelHex::SlaRecord (char* string, const uint32_t adr) {
|
||||
offset = 0;
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
EipAddr lba;
|
||||
IhxHeader hdr;
|
||||
hdr.load.set(0);
|
||||
hdr.reclen = sizeof (lba);
|
||||
hdr.rectyp = slaRecord;
|
||||
lba.addr.set (adr);
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk += chk_sum (lba.bytes, sizeof(EipAddr ));
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
result += to_hex_str (string + result, lba.bytes, sizeof(EipAddr ));
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t IntelHex::DataRecord (char * string, const uint8_t * data, const uint32_t len) {
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
IhxHeader hdr;
|
||||
hdr.load.set (offset);
|
||||
hdr.reclen = len;
|
||||
hdr.rectyp = dataRecord;
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk += chk_sum (data, len);
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
if (len) result += to_hex_str (string + result, data, len);
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t IntelHex::EofRecord (char * string) {
|
||||
offset = 0;
|
||||
const char * eof = ":00000001FF\r\n";
|
||||
const uint32_t result = strlen (eof);
|
||||
memcpy (string, eof, result);
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
uint32_t IntelHex::BTxRecord (char * string, RowTypes t) {
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
IhxHeader hdr;
|
||||
hdr.load.set (offset);
|
||||
hdr.reclen = 0;
|
||||
hdr.rectyp = t;
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
//offset += len;
|
||||
return result;
|
||||
}
|
138
V203F6P6/programmer/intelhex.h
Normal file
138
V203F6P6/programmer/intelhex.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
#ifndef INTELHEX_H
|
||||
#define INTELHEX_H
|
||||
#include <stdint.h>
|
||||
|
||||
/// Maximální délka rekordu
|
||||
static const unsigned MaxHexRecord = 0x80u;
|
||||
|
||||
enum RowTypes : uint8_t {
|
||||
// defined by Intel
|
||||
dataRecord = 0, //!< datový paket
|
||||
eofRecord, //!< konec dat
|
||||
esaRecord, //!< X86/16.bit CS/IP počáteční adresa
|
||||
ssaRecord, //!< X86/16.bit CS/IP startovací adresa
|
||||
elaRecord, //!< počáteční adresa 32-bit (horních 16.bit)
|
||||
slaRecord, //!< startovací adresa 32-bit
|
||||
// ... defined by BT protocol
|
||||
// všechny budou používat jen offset pro přenos dat.
|
||||
ackRecord, //!< offset - představuje offset předchozích dat
|
||||
nakRecord, //!< stejně jako předchozí
|
||||
reqRecord, //!< Request for data - offset značí odkud vzhledem k počáteční adrese
|
||||
ersRecord, //!< Erase - po blocích, offset je číslo bloku (závisí na typu flash)
|
||||
badRecord, //!< použito pro detekci chyb
|
||||
};
|
||||
/// @enum RowTypes
|
||||
/// Enumerace datových typů Intel-hex formátu
|
||||
|
||||
/**
|
||||
* @class IntelHex
|
||||
* @brief Hepler pro parsování a vytváření paketů Intel HEX formátu.
|
||||
*
|
||||
* Prvních 6 typů (#RowTypes) je definováno ve specifikaci např.
|
||||
* <a href="https://cs.wikipedia.org/wiki/Intel_HEX">zde</a>. A protože je tam dost volného
|
||||
* místa pro rozšíření, jsou zde dodefinovány další typy použité pro komunikaci. Je to tak
|
||||
* jednodušší než vymýšlet nějaký vlastní formát pro potvrzování a jiné. Protokol je znakový,
|
||||
* poměrně robustní, na druhou stranu má dost velku režii.
|
||||
*
|
||||
* Ukázalo sem že úzkým hrdlem komunikace je čekání na potvrzování, takže se režie poněkud zastírá,
|
||||
* ale potvrzování být musí pokud to má trochu spolehlivě fungovat.
|
||||
* */
|
||||
|
||||
class IntelHex {
|
||||
public:
|
||||
explicit IntelHex() noexcept;
|
||||
/**
|
||||
* @brief Základní funkce parseru
|
||||
*
|
||||
* @param data vstupní řádek (paket)
|
||||
* @param len jeho délka (např. strlen(data))
|
||||
* @return RowTypes - to se pak zpracovává dál v nadřazené části např. LinkProtocol
|
||||
*/
|
||||
RowTypes parseRecord (const char * data, const unsigned len);
|
||||
/**
|
||||
* @brief Nastavení adresy
|
||||
*
|
||||
* Buď počáteční nebo starovací.
|
||||
*
|
||||
* @param adr odkaz na proměnnou, kam bude adresa nastavena
|
||||
*/
|
||||
void getAddress (uint32_t & adr);
|
||||
/**
|
||||
* @brief Nastavení délky
|
||||
* @param len odkaz na proměnnou, kam bude délka nastavena
|
||||
*/
|
||||
void getLenght (uint32_t & len);
|
||||
/**
|
||||
* @brief Nastavení ofsetu
|
||||
* @param ofs odkaz na proměnnou, kam bude ofset nastaven
|
||||
*/
|
||||
void getOffset (uint32_t & ofs);
|
||||
/**
|
||||
* @brief Zápis dat z příchozího dataRecord
|
||||
*
|
||||
* @param data kam se zapíší
|
||||
*/
|
||||
void WriteData (uint8_t * data);
|
||||
/**
|
||||
* @brief Posun ofsetu kvůli udržení konzistence
|
||||
*
|
||||
* @param len o kolik se má posunout
|
||||
*/
|
||||
void AddOffset (uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Vytvoření dataRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param data pure data rekordu
|
||||
* @param len a jejich délka
|
||||
* @return uint32_t kolik bylo zapsáno do string
|
||||
*/
|
||||
uint32_t DataRecord (char * string, const uint8_t * data, const uint32_t len);
|
||||
/**
|
||||
* @brief Vytvoření elaRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param adr počáteční adresa
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t ElaRecord (char * string, const uint32_t adr);
|
||||
/**
|
||||
* @brief Vytvoření slaRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param adr startovací adresa
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t SlaRecord (char * string, const uint32_t adr);
|
||||
/**
|
||||
* @brief Vytvoření eofRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t EofRecord (char * string);
|
||||
|
||||
/**
|
||||
* @brief Vytvoření BT rekordů ackRecord, nakRecord, reqRecord, ersRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param t definuj typ rekordu
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t BTxRecord (char * string, RowTypes t = ackRecord);
|
||||
/// pomocná funkce
|
||||
bool CompareAckOffsset ();
|
||||
/// pomocná funkce
|
||||
uint8_t * getDataRow (uint32_t & ofs, uint32_t & len);
|
||||
protected:
|
||||
private:
|
||||
uint32_t address;
|
||||
uint32_t offset;
|
||||
uint32_t lenght;
|
||||
uint32_t total_lenght;
|
||||
uint32_t ackofs;
|
||||
uint8_t chunk [MaxHexRecord];
|
||||
};
|
||||
|
||||
#endif // INTELHEX_H
|
75
V203F6P6/programmer/linkprotocol.cpp
Normal file
75
V203F6P6/programmer/linkprotocol.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include <string.h>
|
||||
#include "helpers.h"
|
||||
#include "linkprotocol.h"
|
||||
|
||||
#define trace(...)
|
||||
|
||||
LinkProtocol::LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept : cdccmd(g), ihx(), driver(b),
|
||||
begin_addr(0), start_addr(0), lenght (0) {
|
||||
}
|
||||
void LinkProtocol::ParseLine (const char * line) {
|
||||
const unsigned readen = strlen (line);
|
||||
RowTypes t = ihx.parseRecord (line, readen);
|
||||
switch (t) {
|
||||
case dataRecord: AcquireDataRecord (); break;
|
||||
case elaRecord: AcquireElaRecord (); break;
|
||||
case slaRecord: AcquireSlaRecord (); break;
|
||||
case eofRecord: AcquireEofRecord (); break;
|
||||
case reqRecord: AcquireReqRecord (); break;
|
||||
case ersRecord: AcquireErsRecord (); break;
|
||||
default:
|
||||
trace ("BAD record 0x%02X\r\n", t);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
void LinkProtocol::AcquireDataRecord() {
|
||||
uint32_t ofs, len;
|
||||
uint8_t * ptr = ihx.getDataRow (ofs, len);
|
||||
uint32_t res = driver.Write (begin_addr + ofs, ptr, len);
|
||||
if (res) {
|
||||
SendAck();
|
||||
ihx.AddOffset (res); // posun offsetu az po ack
|
||||
return;
|
||||
}
|
||||
SendAck (false);
|
||||
}
|
||||
// První paket, který musí přijít při každé akci.
|
||||
void LinkProtocol::AcquireElaRecord() {
|
||||
ihx.getAddress (begin_addr);
|
||||
trace("Begin = 0x%08X\r\n", begin_addr);
|
||||
SendAck();
|
||||
}
|
||||
typedef void (*pHandler) (void);
|
||||
void LinkProtocol::AcquireSlaRecord() {
|
||||
ihx.getAddress (start_addr);
|
||||
trace("Start = 0x%08X\r\n", start_addr);
|
||||
}
|
||||
void LinkProtocol::AcquireEofRecord() {
|
||||
ihx.getLenght (lenght);
|
||||
trace ("Lenght = %d\r\n", lenght);
|
||||
SendAck();
|
||||
}
|
||||
void LinkProtocol::AcquireReqRecord() {
|
||||
static constexpr unsigned chunk_size = 0x20;
|
||||
uint8_t data [chunk_size];
|
||||
uint32_t res, len, ofs;
|
||||
len = chunk_size;
|
||||
ihx.getOffset (ofs);
|
||||
//trace ("AcquireReqRecord: %04X, len=%d\r\n", ofs, len);
|
||||
res = driver.Read (begin_addr + ofs, data, len);
|
||||
res = ihx.DataRecord(strbuf, data, res);
|
||||
cdccmd.SendString (strbuf, res);
|
||||
}
|
||||
void LinkProtocol::AcquireErsRecord() {
|
||||
uint32_t block;
|
||||
ihx.getOffset (block);
|
||||
driver.Erase (block);
|
||||
SendAck();
|
||||
}
|
||||
|
||||
void LinkProtocol::SendAck (bool ok) {
|
||||
RowTypes t = ok ? ackRecord : nakRecord;
|
||||
const uint32_t r = ihx.BTxRecord (strbuf, t);
|
||||
cdccmd.SendString (strbuf, r);
|
||||
}
|
55
V203F6P6/programmer/linkprotocol.h
Normal file
55
V203F6P6/programmer/linkprotocol.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef LINKPROTOCOL_H
|
||||
#define LINKPROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "intelhex.h"
|
||||
|
||||
class CdcCmd;
|
||||
class MemoryBase;
|
||||
/**
|
||||
* @class LinkProtocol
|
||||
* @brief Protokol po CDC.
|
||||
*
|
||||
* Vychází z formátu IntelHex. komunikaci začíná PC, modul odpovídá. První co musí PC (vždy na začátku konunikace !) poslat
|
||||
* je paket ElaRecord (4), který určuje počáteční adresu - podle ní modul vybere potřebný ovladač paměti. Formy komunikace :
|
||||
* -# Mazání obsahu flash. Probíhá po jednotlivých blocích.
|
||||
* - PC -> MODUL : ersRecord (v poli offset číslo bloku relativně k počátku typu paměti)
|
||||
* - MODUL -> PC : ackRecord (v poli offset číslo bloku relativně k počátku typu paměti)
|
||||
* -# Zápis dat do paměti.
|
||||
* - PC -> MODUL : dataRecord (max. 32 bytů)
|
||||
* - MODUL -> PC : ackRecord nebo nakRecord pokud je cosi špatně
|
||||
* -# Čtení dat z paměti.
|
||||
* - PC -> MODUL : reqRecord (v poli offset odkud chci číst)
|
||||
* - MODUL -> PC : dataRecord - pokud má nulovou délku, další data nejsou. Posílána je délka 32 bytů.
|
||||
* -# Spuštění uživatelského programu.
|
||||
* - PC -> MODUL : slaRecord
|
||||
* - bez odpovědi - je to jen pro uživatelské testy a může to špatně skončit. Vlastně je to přidáno
|
||||
* jen proto, že to jde.
|
||||
* */
|
||||
class LinkProtocol {
|
||||
static constexpr unsigned strbuflen = 0x80u;
|
||||
public:
|
||||
explicit LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept;
|
||||
void ParseLine (const char * line);
|
||||
protected:
|
||||
void AcquireDataRecord ();
|
||||
void AcquireElaRecord ();
|
||||
void AcquireSlaRecord ();
|
||||
void AcquireEofRecord ();
|
||||
void AcquireReqRecord ();
|
||||
void AcquireErsRecord ();
|
||||
|
||||
void SendAck (bool ok=true);
|
||||
private:
|
||||
CdcCmd & cdccmd;
|
||||
IntelHex ihx;
|
||||
MemoryBase & driver;
|
||||
|
||||
uint32_t begin_addr;
|
||||
uint32_t start_addr;
|
||||
uint32_t lenght;
|
||||
|
||||
char strbuf [strbuflen];
|
||||
};
|
||||
|
||||
#endif // LINKPROTOCOL_H
|
32
V203F6P6/programmer/main.cpp
Normal file
32
V203F6P6/programmer/main.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include "system.h"
|
||||
#include "gpio.h"
|
||||
#include "cdc_class.h"
|
||||
#include "linkprotocol.h"
|
||||
#include "helpers.h"
|
||||
/*********************************************************************************
|
||||
* Do mluvícího teploměru bylo potřeba nějak dostat data do externí flash.
|
||||
* Zde je základ programátoru, je to recyklovaný kód, takže zatím to umí jen
|
||||
* binární soubory do 64KiB, ale patrně nebude problém dopsat to na větší délku.
|
||||
* Software je dost surový, bez kontrol, prostě jen aby to trochu fungovalo.
|
||||
**********************************************************************************/
|
||||
static GpioClass led (GPIOB, 8);
|
||||
static cdc_class cdc;
|
||||
static MemoryBase mem;
|
||||
static CdcCmd cmd;
|
||||
static LinkProtocol lnk (cmd, mem);
|
||||
|
||||
int main () {
|
||||
led << true;
|
||||
cdc.init ();
|
||||
cmd += cdc;
|
||||
unsigned pass = 0u, command;
|
||||
for (;;) {
|
||||
char * res = cmd.GetLine(command);
|
||||
if (command == 0u) continue;
|
||||
lnk.ParseLine(res);
|
||||
pass += 1u;
|
||||
const bool b = pass & 1u;
|
||||
led << b;
|
||||
}
|
||||
return 0;
|
||||
}
|
82
V203F6P6/programmer/norflash.cpp
Normal file
82
V203F6P6/programmer/norflash.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#include "string.h"
|
||||
#include "norflash.h"
|
||||
extern "C" {
|
||||
extern void delay_init ();
|
||||
extern void Delay_Ms (const unsigned dly);
|
||||
};
|
||||
union FlashCommandHeader {
|
||||
struct {
|
||||
FLASH_COMMANDS cmd : 8;
|
||||
uint32_t adr : 24; // adresa je BIG ENDIEN - vyšší byte vystupuje po SPI dříve (MSB FIRST).
|
||||
}__attribute__((packed));
|
||||
uint8_t bytes [4];
|
||||
}__attribute__((packed));
|
||||
static inline uint32_t be_set_24 (const uint32_t p) {
|
||||
return ((p & 0x000000ff) << 16)
|
||||
| ((p & 0x0000ff00) << 0)
|
||||
| ((p & 0x00ff0000) >> 16);
|
||||
}
|
||||
static constexpr unsigned FlashBlockLen = 0x1000u; // 4KiB = 4096 B
|
||||
|
||||
NorFlash::NorFlash() noexcept : spi() {
|
||||
delay_init();
|
||||
}
|
||||
unsigned int NorFlash::ReadBlock(const unsigned int addr, uint8_t * data, const unsigned int len) {
|
||||
FlashCommandHeader header;
|
||||
header.cmd = FLASH_4READ;
|
||||
header.adr = be_set_24(addr);
|
||||
spi.ChipSelect(true);
|
||||
for (unsigned n=0u; n<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
|
||||
for (unsigned n=0u; n<len; n++) data [n] = spi.ReadWriteByte(0xff);
|
||||
spi.ChipSelect(false);
|
||||
return len;
|
||||
}
|
||||
unsigned int NorFlash::WriteBlock(const unsigned int addr, const uint8_t * data, const unsigned int len) {
|
||||
FlashCommandHeader header;
|
||||
header.cmd = FLASH_4PP;
|
||||
header.adr = be_set_24(addr);
|
||||
SingleCommand(FLASH_WREN);
|
||||
spi.ChipSelect(true);
|
||||
for (unsigned n=0u; n<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
|
||||
for (unsigned n=0u; n<len; n++) spi.ReadWriteByte(data [n]);
|
||||
spi.ChipSelect(false);
|
||||
if (!WaitForReady()) return 0;
|
||||
return len;
|
||||
}
|
||||
bool NorFlash::EraseSector(const unsigned int addr) {
|
||||
FlashCommandHeader header;
|
||||
header.cmd = FLASH_4SE;
|
||||
header.adr = be_set_24(addr);
|
||||
SingleCommand(FLASH_WREN);
|
||||
spi.ChipSelect(true);
|
||||
for (unsigned n=0u; n<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
|
||||
spi.ChipSelect(false);
|
||||
if (!WaitForReady(400u)) return false;
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
inline bool NorFlash::SingleCommand(const FLASH_COMMANDS cmd) const {
|
||||
uint8_t tmp = cmd;
|
||||
spi.ChipSelect(true);
|
||||
spi.ReadWriteByte (tmp);
|
||||
spi.ChipSelect(false);
|
||||
return true;
|
||||
}
|
||||
inline uint8_t NorFlash::ReadStatus(const FLASH_COMMANDS cmd) const {
|
||||
uint8_t buf [2] = {cmd, 0};
|
||||
spi.ChipSelect(true);
|
||||
for (unsigned n=0u; n<2u; n++) buf[n] = spi.ReadWriteByte(buf[n]);
|
||||
spi.ChipSelect(false);
|
||||
return buf[1];
|
||||
}
|
||||
bool NorFlash::WaitForReady(const unsigned int ms) const {
|
||||
uint8_t sr;
|
||||
for (unsigned n=0; n<ms; n++) {
|
||||
Delay_Ms (1u);
|
||||
sr = ReadStatus(FLASH_RDSR1);
|
||||
if ((sr & 1u) == 0u) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
40
V203F6P6/programmer/norflash.h
Normal file
40
V203F6P6/programmer/norflash.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef NORFLASH_H
|
||||
#define NORFLASH_H
|
||||
#include <stdint.h>
|
||||
#include "spiblocked.h"
|
||||
/// Enumerace povelů pro SPI FLASH.
|
||||
enum FLASH_COMMANDS : uint8_t {
|
||||
/* single byte commands */
|
||||
FLASH_WRDI = 0x04, // nepoužito
|
||||
FLASH_WREN = 0x06,
|
||||
FLASH_RDID = 0x9F,
|
||||
FLASHRSTEN = 0x66, // nepoužito
|
||||
FLASH__RST = 0x99, // nepoužito
|
||||
FLASH__RES = 0xAB, // release from power down, nepoužito
|
||||
FLASH__DPD = 0xB9, // power down, nepoužito
|
||||
// multi - byte
|
||||
FLASH_RDSR1 = 0x05,
|
||||
// dále se mohou povely lišit
|
||||
FLASH_RDSR2 = 0x35, // nepoužito
|
||||
FLASH_4READ = 0x03,
|
||||
FLASH_4PP = 0x02,
|
||||
FLASH_4SE = 0x20,
|
||||
};
|
||||
|
||||
class NorFlash {
|
||||
SpiBlocked spi;
|
||||
public:
|
||||
explicit NorFlash () noexcept;
|
||||
unsigned ReadBlock (const unsigned addr, uint8_t * data, const unsigned len);
|
||||
unsigned WriteBlock (const unsigned addr, const uint8_t * data, const unsigned len);
|
||||
bool EraseSector (const unsigned addr);
|
||||
protected:
|
||||
/// Jednoduchý příkaz
|
||||
bool SingleCommand (const FLASH_COMMANDS cmd) const;
|
||||
/// Načtení registru STATUS1 nebo STATUS2 pomocí FLASH_RDSRx
|
||||
uint8_t ReadStatus (const FLASH_COMMANDS cmd) const;
|
||||
/// Čekání na dokončení operace zápisu / mazání
|
||||
bool WaitForReady (const unsigned ms = 5u) const;
|
||||
};
|
||||
|
||||
#endif // NORFLASH_H
|
20
V203F6P6/programmer/software/Makefile
Normal file
20
V203F6P6/programmer/software/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PR = programmer
|
||||
CC = gcc
|
||||
CX = g++
|
||||
VPATH = .
|
||||
CFLAGS = -Os -Wall -I. -I../common
|
||||
MFLAGS = -o $(PR)
|
||||
|
||||
all: $(PR)
|
||||
|
||||
OBJECTS = main.o usart.o baud.o linkprotocol.o intelhex.o
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
%.o: %.cpp
|
||||
$(CX) -std=c++17 -c $(CFLAGS) $< -o $@
|
||||
$(PR): $(OBJECTS)
|
||||
$(CX) $(MFLAGS) $(OBJECTS) -lpthread
|
||||
clean:
|
||||
rm -f *.o *~
|
||||
|
14
V203F6P6/programmer/software/baud.cpp
Normal file
14
V203F6P6/programmer/software/baud.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <asm/termbits.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "usart.h"
|
||||
|
||||
void UsartClass::setBaud (int baud) {
|
||||
struct termios2 tio;
|
||||
|
||||
ioctl(fd, TCGETS2, &tio);
|
||||
tio.c_cflag &= ~CBAUD;
|
||||
tio.c_cflag |= BOTHER;
|
||||
tio.c_ispeed = baud;
|
||||
tio.c_ospeed = baud;
|
||||
ioctl(fd, TCSETS2, &tio);
|
||||
}
|
112
V203F6P6/programmer/software/endien.h
Normal file
112
V203F6P6/programmer/software/endien.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
#ifndef ENDIEN_H
|
||||
#define ENDIEN_H
|
||||
#include <stdint.h>
|
||||
/**
|
||||
@file
|
||||
@brief Pořadí bytů ve slově.
|
||||
|
||||
*/
|
||||
#if __GNUC__ < 4 // Compiler test
|
||||
#error "Zkontroluj nastaveni"
|
||||
#endif
|
||||
// Pro 16.bit musíme struktury pakovat
|
||||
#define PACKSTRUCT __attribute__((packed))
|
||||
|
||||
#if __thumb__
|
||||
// Specificky a krátce pro danou architektutu a pokud chceme mít úplnou kontrolu.
|
||||
static inline uint32_t swab32 (uint32_t p) {
|
||||
register uint32_t t;
|
||||
asm volatile ("rev %0, %1":"=r"(t):"0"(p));
|
||||
return t;
|
||||
}
|
||||
#else //__thumb__
|
||||
// Obecně
|
||||
static inline uint32_t swab32 (uint32_t p) {
|
||||
// return __builtin_bswap32 (p); // I takto to jde, ale pro názornost:
|
||||
return ((p & 0x000000ff) << 24)
|
||||
| ((p & 0x0000ff00) << 8)
|
||||
| ((p & 0x00ff0000) >> 8)
|
||||
| ((p & 0xff000000) >> 24);
|
||||
}
|
||||
#endif //__thumb__
|
||||
// Tohle nebudeme ani specifikovat pro ARM
|
||||
static inline uint16_t swab16 (uint16_t p) {
|
||||
return (p << 8) | (p >> 8);
|
||||
}
|
||||
// Nedělá nic a ani nijak neotravuje - pouze pro kompatibilitu
|
||||
static inline uint32_t nosw32 (uint32_t p) {
|
||||
return p;
|
||||
}
|
||||
static inline uint16_t nosw16 (uint16_t p) {
|
||||
return p;
|
||||
}
|
||||
// Specificky podle pořadí bytů - LE je pro ARM i X86 normální.
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define cpu_to_be32 swab32
|
||||
#define be32_to_cpu swab32
|
||||
#define cpu_to_le32 nosw32
|
||||
#define le32_to_cpu nosw32
|
||||
|
||||
#define cpu_to_be16 swab16
|
||||
#define be16_to_cpu swab16
|
||||
#define cpu_to_le16 nosw16
|
||||
#define le16_to_cpu nosw16
|
||||
#else
|
||||
#define cpu_to_be32 nosw32
|
||||
#define be32_to_cpu nosw32
|
||||
#define cpu_to_le32 swab32
|
||||
#define le32_to_cpu swab32
|
||||
|
||||
#define cpu_to_be16 nosw16
|
||||
#define be16_to_cpu nosw16
|
||||
#define cpu_to_le16 swab16
|
||||
#define le16_to_cpu swab16
|
||||
#endif
|
||||
/**
|
||||
@class ui32be
|
||||
@brief C++ 32.bit data v BIG Endien.
|
||||
@class ui32le
|
||||
@brief C++ 32.bit data v LITTLE Endien.
|
||||
@class ui16be
|
||||
@brief C++ 16.bit data v BIG Endien.
|
||||
@class ui16le
|
||||
@brief C++ 16.bit data v LITTLE Endien.
|
||||
*/
|
||||
class ui32be {
|
||||
public:
|
||||
/// Nastavení hodnoty
|
||||
/// @par p uint32_t hodnota
|
||||
void set (uint32_t p) {num = cpu_to_be32 (p); };
|
||||
/// Zjištění hodnoty
|
||||
/// @return uint32_t hodnota
|
||||
uint32_t get (void) {return be32_to_cpu (num); };
|
||||
private:
|
||||
/// Vlastní data uint32_t - k nim se přímo nedostaneme, jsou privátní
|
||||
uint32_t num;
|
||||
}PACKSTRUCT;
|
||||
|
||||
class ui32le {
|
||||
public:
|
||||
void set (uint32_t p) {num = cpu_to_le32 (p); };
|
||||
uint32_t get (void) {return le32_to_cpu (num); };
|
||||
private:
|
||||
uint32_t num;
|
||||
}PACKSTRUCT;
|
||||
/* *******************************************************/
|
||||
class ui16be {
|
||||
public:
|
||||
void set (uint16_t p) {num = cpu_to_be16 (p); };
|
||||
uint16_t get (void) {return be16_to_cpu (num); };
|
||||
private:
|
||||
uint16_t num;
|
||||
}PACKSTRUCT;
|
||||
|
||||
class ui16le {
|
||||
public:
|
||||
void set (uint16_t p) {num = cpu_to_le16 (p); };
|
||||
uint16_t get (void) {return le16_to_cpu (num); };
|
||||
private:
|
||||
uint16_t num;
|
||||
}PACKSTRUCT;
|
||||
|
||||
#endif // ENDIEN_H
|
75
V203F6P6/programmer/software/helpers.h
Normal file
75
V203F6P6/programmer/software/helpers.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef HELPERS_H
|
||||
#define HELPERS_H
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "fifo.h"
|
||||
#include "baselayer.h"
|
||||
|
||||
class MemoryBase {
|
||||
unsigned lenght;
|
||||
unsigned char * data;
|
||||
public:
|
||||
explicit MemoryBase (const unsigned len) noexcept : lenght(len) {
|
||||
data = new unsigned char [lenght];
|
||||
}
|
||||
~MemoryBase () { delete [] data; }
|
||||
uint32_t getLenght () { return lenght; }
|
||||
uint8_t * getData () { return data; }
|
||||
uint32_t Write (const uint32_t addr, const void * ptr, const uint32_t len) {
|
||||
printf("Readen at 0x%04X \r", addr);
|
||||
fflush(stdout);
|
||||
const uint8_t * dptr = reinterpret_cast<const uint8_t*>(ptr);
|
||||
for (unsigned n=0; n<len; n++) data [addr + n] = dptr [n];
|
||||
return len;
|
||||
}
|
||||
uint32_t Read (const uint32_t addr, void * ptr, const uint32_t len) {
|
||||
uint8_t * data = reinterpret_cast<uint8_t*>(ptr);
|
||||
for (unsigned n=0; n<len; n++) data[n] = n;
|
||||
return len; }
|
||||
void Erase (const uint32_t blok) {}
|
||||
};
|
||||
|
||||
class CdcCmd : public BaseLayer {
|
||||
static constexpr int maxlen = 0x80;
|
||||
FIFO<char, maxlen> ring;
|
||||
char line [maxlen];
|
||||
unsigned index;
|
||||
public:
|
||||
explicit CdcCmd () noexcept : BaseLayer(), ring(), index(0u) {}
|
||||
uint32_t Up(const char * data, const uint32_t len) override {
|
||||
for (unsigned n=0u; n<len; n++) ring.Write (data [n]);
|
||||
return len;
|
||||
}
|
||||
void SendString (const char * ptr, const uint32_t len) {
|
||||
/*
|
||||
fprintf(stdout, "TX");
|
||||
int r = fwrite (ptr, 1, len, stdout); (void) r;
|
||||
fflush (stdout);
|
||||
*/
|
||||
unsigned ofs = 0u, res = len;
|
||||
while (res) {
|
||||
const unsigned n = Down (ptr + ofs, res);
|
||||
ofs += n;
|
||||
res -= n;
|
||||
}
|
||||
}
|
||||
char * GetLine (unsigned & len) {
|
||||
char c;
|
||||
while (ring.Read(c)) {
|
||||
line [index++] = c;
|
||||
if (c == '\n') {
|
||||
len = index;
|
||||
/*
|
||||
fprintf(stdout, "RX");
|
||||
int r = fwrite (line, 1, index, stdout); (void) r;
|
||||
fflush (stdout);
|
||||
*/
|
||||
index = 0u;
|
||||
return line;
|
||||
}
|
||||
}
|
||||
len = 0u;
|
||||
return line;
|
||||
}
|
||||
};
|
||||
#endif // HELPERS_H
|
249
V203F6P6/programmer/software/intelhex.cpp
Normal file
249
V203F6P6/programmer/software/intelhex.cpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
#include <stdio.h>
|
||||
#include "string.h"
|
||||
#include "endien.h"
|
||||
#include "intelhex.h"
|
||||
|
||||
static uint8_t hexval (const char c) {
|
||||
if ('0' <= c && c <= '9')
|
||||
return (uint8_t) c - '0';
|
||||
else if ('a' <= c && c <= 'f')
|
||||
return (uint8_t) c - 'a' + 10;
|
||||
else return 0;
|
||||
}
|
||||
static unsigned to_hex_str (char * str, const uint8_t * data, const unsigned len) {
|
||||
unsigned result = 0;
|
||||
const char * hexstr = "0123456789ABCDEF";
|
||||
for (unsigned n=0; n<len; n++) {
|
||||
const uint8_t c = data [n];
|
||||
str [result++] = hexstr [c >> 4];
|
||||
str [result++] = hexstr [c & 0xF];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static uint8_t chk_sum (const uint8_t * data, const unsigned len) {
|
||||
uint8_t sum = 0;
|
||||
for (unsigned n=0; n<len; n++) sum += data [n];
|
||||
return sum;
|
||||
}
|
||||
|
||||
union IhxHeader {
|
||||
struct {
|
||||
uint8_t reclen;
|
||||
ui16be load;
|
||||
RowTypes rectyp;
|
||||
}__attribute__((packed));
|
||||
uint8_t bytes [4];
|
||||
};
|
||||
union UlbaAddr {
|
||||
ui16be addr;
|
||||
uint8_t bytes [2];
|
||||
};
|
||||
union EipAddr {
|
||||
ui32be addr;
|
||||
uint8_t bytes [4];
|
||||
};
|
||||
|
||||
IntelHex::IntelHex() noexcept : address(0), offset(0), lenght(0), total_lenght(0), ackofs(0) {
|
||||
}
|
||||
|
||||
RowTypes IntelHex::parseRecord (const char * data, const unsigned int len) {
|
||||
//printf ("parseRecord: %d %s\r\n", len, data);
|
||||
if (data[0] != (uint8_t) ':') return badRecord;
|
||||
uint8_t tmp [(len >> 1) + 1];
|
||||
uint8_t val = 0;
|
||||
unsigned k = 0;
|
||||
for (unsigned n=1; n<len; n++) {
|
||||
const uint8_t c = data [n];
|
||||
if (c == '\r') break;
|
||||
if (c == '\n') break;
|
||||
val *= 0x10;
|
||||
val += hexval (c | 0x20);
|
||||
if ((n & 1) == 0) {
|
||||
tmp [k++] = val;
|
||||
val = 0;
|
||||
}
|
||||
}
|
||||
k -= 1;
|
||||
uint8_t chk = 0;
|
||||
for (unsigned n=0; n<k; n++) chk += tmp [n];
|
||||
chk = 0 - chk;
|
||||
// kontrolní součet
|
||||
if (chk != tmp[k]) return badRecord;
|
||||
// chybná délka
|
||||
if (k < sizeof (IhxHeader)) return badRecord;
|
||||
IhxHeader hdr;
|
||||
memcpy (hdr.bytes, tmp, sizeof(IhxHeader));
|
||||
// chybná délka
|
||||
if (k != (hdr.reclen + sizeof(IhxHeader))) return badRecord;
|
||||
//printf ("%d: %d=%d, %d, %02X=%02X\n", len, k, hdr.reclen+4, hdr.rectyp, chk, tmp[k]);
|
||||
UlbaAddr a1; EipAddr a2;
|
||||
RowTypes t = hdr.rectyp;
|
||||
lenght = hdr.reclen;
|
||||
const uint8_t * start = tmp + sizeof(IhxHeader);
|
||||
uint32_t tmpadr;
|
||||
// uint16_t old_ofs = offset;
|
||||
switch (t) {
|
||||
case dataRecord:
|
||||
memcpy (chunk, start, lenght);
|
||||
offset = hdr.load.get();
|
||||
// if (old_ofs != offset) printf ("Posun 0x%04X-0x%04X\n", old_ofs, offset);
|
||||
total_lenght = offset + lenght;
|
||||
break;
|
||||
case elaRecord:
|
||||
total_lenght = 0;
|
||||
memcpy (a1.bytes, start, lenght);
|
||||
address = (uint32_t) a1.addr.get() << 16;
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
case slaRecord:
|
||||
memcpy (a2.bytes, start, lenght);
|
||||
address = a2.addr.get();
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
case ackRecord:
|
||||
case nakRecord:
|
||||
ackofs = hdr.load.get();
|
||||
break;
|
||||
case eofRecord:
|
||||
case reqRecord:
|
||||
case ersRecord:
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
// Tyhle 2 rekordy nějak fungují, ale je to divné. Bohužel třeba objcopy
|
||||
// je na nízkých adresách z nějakého důvodu používá. Možná je v tom chyba.
|
||||
case esaRecord:
|
||||
total_lenght = 0;
|
||||
memcpy (a1.bytes, start, lenght);
|
||||
address = (uint32_t) a1.addr.get() << 4;
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
case ssaRecord:
|
||||
memcpy (a2.bytes, start, lenght);
|
||||
tmpadr = a2.addr.get();
|
||||
address = (tmpadr >> 12); // CS:IP ? čerti vědí, jak je to myšleno
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
bool IntelHex::CompareAckOffsset() {
|
||||
//printf ("ofs: %04X=%04X\n", ackofs, offset);
|
||||
if (ackofs == offset) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void IntelHex::getAddress (uint32_t & adr) {
|
||||
adr = address;
|
||||
}
|
||||
void IntelHex::getLenght (uint32_t & len) {
|
||||
len = total_lenght;
|
||||
}
|
||||
void IntelHex::getOffset (uint32_t & ofs) {
|
||||
ofs = offset;
|
||||
}
|
||||
void IntelHex::WriteData (uint8_t * data) {
|
||||
memcpy (data + offset, chunk, lenght);
|
||||
offset += lenght;
|
||||
}
|
||||
uint8_t * IntelHex::getDataRow (uint32_t & ofs, uint32_t & len) {
|
||||
ofs = offset;
|
||||
len = lenght;
|
||||
return chunk;
|
||||
}
|
||||
void IntelHex::AddOffset (uint32_t len) {
|
||||
offset += len;
|
||||
}
|
||||
|
||||
uint32_t IntelHex::ElaRecord (char * string, const uint32_t adr) {
|
||||
offset = 0;
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
UlbaAddr lba;
|
||||
IhxHeader hdr;
|
||||
hdr.load.set(0);
|
||||
hdr.reclen = sizeof (lba);
|
||||
hdr.rectyp = elaRecord;
|
||||
lba.addr.set (adr >> 16);
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk += chk_sum (lba.bytes, sizeof(UlbaAddr ));
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
result += to_hex_str (string + result, lba.bytes, sizeof(UlbaAddr ));
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
uint32_t IntelHex::SlaRecord (char* string, const uint32_t adr) {
|
||||
offset = 0;
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
EipAddr lba;
|
||||
IhxHeader hdr;
|
||||
hdr.load.set(0);
|
||||
hdr.reclen = sizeof (lba);
|
||||
hdr.rectyp = slaRecord;
|
||||
lba.addr.set (adr);
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk += chk_sum (lba.bytes, sizeof(EipAddr ));
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
result += to_hex_str (string + result, lba.bytes, sizeof(EipAddr ));
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t IntelHex::DataRecord (char * string, const uint8_t * data, const uint32_t len) {
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
IhxHeader hdr;
|
||||
hdr.load.set (offset);
|
||||
hdr.reclen = len;
|
||||
hdr.rectyp = dataRecord;
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk += chk_sum (data, len);
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
if (len) result += to_hex_str (string + result, data, len);
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t IntelHex::EofRecord (char * string) {
|
||||
offset = 0;
|
||||
const char * eof = ":00000001FF\r\n";
|
||||
const uint32_t result = strlen (eof);
|
||||
memcpy (string, eof, result);
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
uint32_t IntelHex::BTxRecord (char * string, RowTypes t) {
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
IhxHeader hdr;
|
||||
hdr.load.set (offset);
|
||||
hdr.reclen = 0;
|
||||
hdr.rectyp = t;
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
//offset += len;
|
||||
return result;
|
||||
}
|
138
V203F6P6/programmer/software/intelhex.h
Normal file
138
V203F6P6/programmer/software/intelhex.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
#ifndef INTELHEX_H
|
||||
#define INTELHEX_H
|
||||
#include <stdint.h>
|
||||
|
||||
/// Maximální délka rekordu
|
||||
static const unsigned MaxHexRecord = 0x80;
|
||||
|
||||
enum RowTypes : uint8_t {
|
||||
// defined by Intel
|
||||
dataRecord = 0, //!< datový paket
|
||||
eofRecord, //!< konec dat
|
||||
esaRecord, //!< X86/16.bit CS/IP počáteční adresa
|
||||
ssaRecord, //!< X86/16.bit CS/IP startovací adresa
|
||||
elaRecord, //!< počáteční adresa 32-bit (horních 16.bit)
|
||||
slaRecord, //!< startovací adresa 32-bit
|
||||
// ... defined by BT protocol
|
||||
// všechny budou používat jen offset pro přenos dat.
|
||||
ackRecord, //!< offset - představuje offset předchozích dat
|
||||
nakRecord, //!< stejně jako předchozí
|
||||
reqRecord, //!< Request for data - offset značí odkud vzhledem k počáteční adrese
|
||||
ersRecord, //!< Erase - po blocích, offset je číslo bloku (závisí na typu flash)
|
||||
badRecord, //!< použito pro detekci chyb
|
||||
};
|
||||
/// @enum RowTypes
|
||||
/// Enumerace datových typů Intel-hex formátu
|
||||
|
||||
/**
|
||||
* @class IntelHex
|
||||
* @brief Hepler pro parsování a vytváření paketů Intel HEX formátu.
|
||||
*
|
||||
* Prvních 6 typů (#RowTypes) je definováno ve specifikaci např.
|
||||
* <a href="https://cs.wikipedia.org/wiki/Intel_HEX">zde</a>. A protože je tam dost volného
|
||||
* místa pro rozšíření, jsou zde dodefinovány další typy použité pro komunikaci. Je to tak
|
||||
* jednodušší než vymýšlet nějaký vlastní formát pro potvrzování a jiné. Protokol je znakový,
|
||||
* poměrně robustní, na druhou stranu má dost velku režii.
|
||||
*
|
||||
* Ukázalo sem že úzkým hrdlem komunikace je čekání na potvrzování, takže se režie poněkud zastírá,
|
||||
* ale potvrzování být musí pokud to má trochu spolehlivě fungovat.
|
||||
* */
|
||||
|
||||
class IntelHex {
|
||||
public:
|
||||
explicit IntelHex() noexcept;
|
||||
/**
|
||||
* @brief Základní funkce parseru
|
||||
*
|
||||
* @param data vstupní řádek (paket)
|
||||
* @param len jeho délka (např. strlen(data))
|
||||
* @return RowTypes - to se pak zpracovává dál v nadřazené části např. LinkProtocol
|
||||
*/
|
||||
RowTypes parseRecord (const char * data, const unsigned len);
|
||||
/**
|
||||
* @brief Nastavení adresy
|
||||
*
|
||||
* Buď počáteční nebo starovací.
|
||||
*
|
||||
* @param adr odkaz na proměnnou, kam bude adresa nastavena
|
||||
*/
|
||||
void getAddress (uint32_t & adr);
|
||||
/**
|
||||
* @brief Nastavení délky
|
||||
* @param len odkaz na proměnnou, kam bude délka nastavena
|
||||
*/
|
||||
void getLenght (uint32_t & len);
|
||||
/**
|
||||
* @brief Nastavení ofsetu
|
||||
* @param ofs odkaz na proměnnou, kam bude ofset nastaven
|
||||
*/
|
||||
void getOffset (uint32_t & ofs);
|
||||
/**
|
||||
* @brief Zápis dat z příchozího dataRecord
|
||||
*
|
||||
* @param data kam se zapíší
|
||||
*/
|
||||
void WriteData (uint8_t * data);
|
||||
/**
|
||||
* @brief Posun ofsetu kvůli udržení konzistence
|
||||
*
|
||||
* @param len o kolik se má posunout
|
||||
*/
|
||||
void AddOffset (uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Vytvoření dataRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param data pure data rekordu
|
||||
* @param len a jejich délka
|
||||
* @return uint32_t kolik bylo zapsáno do string
|
||||
*/
|
||||
uint32_t DataRecord (char * string, const uint8_t * data, const uint32_t len);
|
||||
/**
|
||||
* @brief Vytvoření elaRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param adr počáteční adresa
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t ElaRecord (char * string, const uint32_t adr);
|
||||
/**
|
||||
* @brief Vytvoření slaRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param adr startovací adresa
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t SlaRecord (char * string, const uint32_t adr);
|
||||
/**
|
||||
* @brief Vytvoření eofRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t EofRecord (char * string);
|
||||
|
||||
/**
|
||||
* @brief Vytvoření BT rekordů ackRecord, nakRecord, reqRecord, ersRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param t definuj typ rekordu
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t BTxRecord (char * string, RowTypes t = ackRecord);
|
||||
/// pomocná funkce
|
||||
bool CompareAckOffsset ();
|
||||
/// pomocná funkce
|
||||
uint8_t * getDataRow (uint32_t & ofs, uint32_t & len);
|
||||
protected:
|
||||
private:
|
||||
uint32_t address;
|
||||
uint32_t offset;
|
||||
uint32_t lenght;
|
||||
uint32_t total_lenght;
|
||||
uint32_t ackofs;
|
||||
uint8_t chunk [MaxHexRecord];
|
||||
};
|
||||
|
||||
#endif // INTELHEX_H
|
122
V203F6P6/programmer/software/linkprotocol.cpp
Normal file
122
V203F6P6/programmer/software/linkprotocol.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
#include <string.h>
|
||||
#include "helpers.h"
|
||||
#include "linkprotocol.h"
|
||||
|
||||
#define trace(...)
|
||||
|
||||
LinkProtocol::LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept : cdccmd(g), ihx(), driver(b),
|
||||
begin_addr(0), start_addr(0), lenght (0) {
|
||||
}
|
||||
void LinkProtocol::ParseLine (const char * line) {
|
||||
const unsigned readen = strlen (line);
|
||||
RowTypes t = ihx.parseRecord (line, readen);
|
||||
switch (t) {
|
||||
case dataRecord: AcquireDataRecord (); break;
|
||||
case elaRecord: AcquireElaRecord (); break;
|
||||
case slaRecord: AcquireSlaRecord (); break;
|
||||
case eofRecord: AcquireEofRecord (); break;
|
||||
case reqRecord: AcquireReqRecord (); break;
|
||||
case ersRecord: AcquireErsRecord (); break;
|
||||
default:
|
||||
trace ("BAD record 0x%02X\r\n", t);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
void LinkProtocol::AcquireDataRecord() {
|
||||
uint32_t ofs, len;
|
||||
uint8_t * ptr = ihx.getDataRow (ofs, len);
|
||||
uint32_t res = driver.Write (begin_addr + ofs, ptr, len);
|
||||
if (res) {
|
||||
//SendAck();
|
||||
ihx.AddOffset (res); // posun offsetu az po ack
|
||||
return;
|
||||
}
|
||||
//SendAck (false);
|
||||
}
|
||||
// První paket, který musí přijít při každé akci.
|
||||
void LinkProtocol::AcquireElaRecord() {
|
||||
ihx.getAddress (begin_addr);
|
||||
trace("Begin = 0x%08X\r\n", begin_addr);
|
||||
//driver = GetDriverByAddress (begin_addr);
|
||||
//if (driver) driver->On (begin_addr);
|
||||
SendAck();
|
||||
}
|
||||
typedef void (*pHandler) (void);
|
||||
void LinkProtocol::AcquireSlaRecord() {
|
||||
ihx.getAddress (start_addr);
|
||||
trace("Start = 0x%08X\r\n", start_addr);
|
||||
}
|
||||
void LinkProtocol::AcquireEofRecord() {
|
||||
ihx.getLenght (lenght);
|
||||
trace ("Lenght = %d\r\n", lenght);
|
||||
SendAck();
|
||||
}
|
||||
void LinkProtocol::AcquireReqRecord() {
|
||||
static constexpr unsigned chunk_size = 0x10;
|
||||
uint8_t data [chunk_size];
|
||||
uint32_t res = 0x10, len, ofs;
|
||||
len = chunk_size;
|
||||
ihx.getOffset (ofs);
|
||||
//trace ("AcquireReqRecord: %04X, len=%d\r\n", ofs, len);
|
||||
res = driver.Read (begin_addr + ofs, data, len);
|
||||
res = ihx.DataRecord (strbuf, data, res);
|
||||
cdccmd.SendString (strbuf, res);
|
||||
}
|
||||
void LinkProtocol::AcquireErsRecord() {
|
||||
uint32_t block;
|
||||
ihx.getOffset (block);
|
||||
driver.Erase (block);
|
||||
SendAck();
|
||||
}
|
||||
|
||||
void LinkProtocol::SendAck (bool ok) {
|
||||
RowTypes t = ok ? ackRecord : nakRecord;
|
||||
const uint32_t r = ihx.BTxRecord (strbuf, t);
|
||||
cdccmd.SendString (strbuf, r);
|
||||
}
|
||||
////////////////////////////////////////////////////////
|
||||
void LinkProtocol::ReadMem() {
|
||||
uint32_t res = ihx.BTxRecord (strbuf, reqRecord);
|
||||
cdccmd.SendString (strbuf, res);
|
||||
}
|
||||
bool LinkProtocol::isEOF(const unsigned len) {
|
||||
uint32_t ofs;
|
||||
ihx.getOffset(ofs);
|
||||
if (ofs < len) return false;
|
||||
return true;
|
||||
}
|
||||
bool LinkProtocol::StartOperation() {
|
||||
uint32_t res = ihx.ElaRecord(strbuf, 0u);
|
||||
cdccmd.SendString (strbuf, res);
|
||||
return true;
|
||||
}
|
||||
bool LinkProtocol::Erase(const unsigned int n) {
|
||||
uint32_t ofs, addr = n * 0x1000u;
|
||||
ihx.getOffset(ofs);
|
||||
ihx.AddOffset(addr - ofs);
|
||||
uint32_t res = ihx.BTxRecord (strbuf, ersRecord);
|
||||
cdccmd.SendString (strbuf, res);
|
||||
return true;
|
||||
}
|
||||
bool LinkProtocol::WriteMem(const uint8_t * data, const unsigned int len) {
|
||||
unsigned ofs = 0u, rem = len, chunk;
|
||||
for (;;) {
|
||||
unsigned value;
|
||||
char * res = cdccmd.GetLine(value);
|
||||
if (value == 0u) continue;
|
||||
RowTypes t = ihx.parseRecord(res, value);
|
||||
if (t != ackRecord) return false;
|
||||
if (rem == 0u) break;
|
||||
|
||||
chunk = rem > 0x20 ? 0x20 : rem;
|
||||
unsigned rl = ihx.DataRecord(strbuf, data + ofs, chunk);
|
||||
printf("Write at 0x%04X \r", ofs);
|
||||
fflush(stdout);
|
||||
ihx.AddOffset(chunk);
|
||||
cdccmd.SendString(strbuf, rl);
|
||||
ofs += chunk;
|
||||
rem -= chunk;
|
||||
}
|
||||
return true;
|
||||
}
|
60
V203F6P6/programmer/software/linkprotocol.h
Normal file
60
V203F6P6/programmer/software/linkprotocol.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef LINKPROTOCOL_H
|
||||
#define LINKPROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "intelhex.h"
|
||||
|
||||
class CdcCmd;
|
||||
class MemoryBase;
|
||||
/**
|
||||
* @class LinkProtocol
|
||||
* @brief Protokol po CDC.
|
||||
*
|
||||
* Vychází z formátu IntelHex. komunikaci začíná PC, modul odpovídá. První co musí PC (vždy na začátku konunikace !) poslat
|
||||
* je paket ElaRecord (4), který určuje počáteční adresu - podle ní modul vybere potřebný ovladač paměti. Formy komunikace :
|
||||
* -# Mazání obsahu flash. Probíhá po jednotlivých blocích.
|
||||
* - PC -> MODUL : ersRecord (v poli offset číslo bloku relativně k počátku typu paměti)
|
||||
* - MODUL -> PC : ackRecord (v poli offset číslo bloku relativně k počátku typu paměti)
|
||||
* -# Zápis dat do paměti.
|
||||
* - PC -> MODUL : dataRecord (max. 32 bytů)
|
||||
* - MODUL -> PC : ackRecord nebo nakRecord pokud je cosi špatně
|
||||
* -# Čtení dat z paměti.
|
||||
* - PC -> MODUL : reqRecord (v poli offset odkud chci číst)
|
||||
* - MODUL -> PC : dataRecord - pokud má nulovou délku, další data nejsou. Posílána je délka 32 bytů.
|
||||
* -# Spuštění uživatelského programu.
|
||||
* - PC -> MODUL : slaRecord
|
||||
* - bez odpovědi - je to jen pro uživatelské testy a může to špatně skončit. Vlastně je to přidáno
|
||||
* jen proto, že to jde.
|
||||
* */
|
||||
class LinkProtocol {
|
||||
static constexpr unsigned strbuflen = 0x80u;
|
||||
public:
|
||||
explicit LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept;
|
||||
void ParseLine (const char * line);
|
||||
bool StartOperation ();
|
||||
void ReadMem ();
|
||||
bool WriteMem (const uint8_t * data, const unsigned len);
|
||||
bool Erase (const unsigned n);
|
||||
bool isEOF (const unsigned len);
|
||||
protected:
|
||||
void AcquireDataRecord ();
|
||||
void AcquireElaRecord ();
|
||||
void AcquireSlaRecord ();
|
||||
void AcquireEofRecord ();
|
||||
void AcquireReqRecord ();
|
||||
void AcquireErsRecord ();
|
||||
|
||||
void SendAck (bool ok=true);
|
||||
private:
|
||||
CdcCmd & cdccmd;
|
||||
IntelHex ihx;
|
||||
MemoryBase & driver;
|
||||
|
||||
uint32_t begin_addr;
|
||||
uint32_t start_addr;
|
||||
uint32_t lenght;
|
||||
|
||||
char strbuf [strbuflen];
|
||||
};
|
||||
|
||||
#endif // LINKPROTOCOL_H
|
121
V203F6P6/programmer/software/main.cpp
Normal file
121
V203F6P6/programmer/software/main.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
#include <signal.h>
|
||||
#include <cstdio>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include "usart.h"
|
||||
#include "fifo.h"
|
||||
#include "linkprotocol.h"
|
||||
#include "helpers.h"
|
||||
|
||||
static constexpr unsigned FlashBlockSize = 0x1000u;
|
||||
volatile bool loop = false;
|
||||
void sig_handler (int) {
|
||||
loop = false;
|
||||
}
|
||||
static UsartClass usart ("/dev/serial/by-id/usb-Kizarm_Labs._Flash_Programmer_0001-if00", 9600);
|
||||
static MemoryBase mem (0x400u * FlashBlockSize); // 4MiB flash
|
||||
static CdcCmd cmd;
|
||||
static LinkProtocol lnk (cmd, mem);
|
||||
|
||||
static void read_flash (const unsigned len) {
|
||||
lnk.StartOperation();
|
||||
while (loop) {
|
||||
unsigned value;
|
||||
char * res = cmd.GetLine(value);
|
||||
if (value == 0u) continue;
|
||||
lnk.ParseLine(res);
|
||||
if (lnk.isEOF(len)) break;
|
||||
lnk.ReadMem();
|
||||
}
|
||||
}
|
||||
static void erase_blocks (const unsigned num = 1) {
|
||||
unsigned block = 0u;
|
||||
lnk.StartOperation();
|
||||
while (loop) {
|
||||
unsigned value;
|
||||
char * res = cmd.GetLine(value);
|
||||
if (value == 0u) continue;
|
||||
if (block >= num) break;
|
||||
lnk.ParseLine(res);
|
||||
lnk.Erase (block);
|
||||
printf("Erasing blok %d \r", block);
|
||||
fflush(stdout);
|
||||
block += 1u;
|
||||
}
|
||||
}
|
||||
static uint8_t * open_file_for_read (const char * name, unsigned & len) {
|
||||
struct stat prop;
|
||||
const int r = stat (name, & prop);
|
||||
if (r) return nullptr;
|
||||
const unsigned flen = prop.st_size;
|
||||
FILE * in = fopen (name, "r");
|
||||
uint8_t * data = new uint8_t [flen];
|
||||
const int l = fread (data, 1, flen, in);
|
||||
printf("readen = %d bytes\n", l);
|
||||
fclose(in);
|
||||
len = flen;
|
||||
return data;
|
||||
}
|
||||
static void read_flash_binary (const char * name) {
|
||||
const unsigned flen = 0x10000u;
|
||||
printf("Read data\n");
|
||||
read_flash (flen);
|
||||
FILE * out = fopen(name,"w");
|
||||
int r = fwrite(mem.getData(), 1, flen, out);
|
||||
(void) r;
|
||||
fclose(out);
|
||||
}
|
||||
static void write_flash_binary (const char * name) {
|
||||
unsigned flen = 0u;
|
||||
uint8_t * data = open_file_for_read(name, flen);
|
||||
if (!data) return;
|
||||
printf("Write data\n");
|
||||
uint32_t blocks = flen / FlashBlockSize;
|
||||
if (flen % FlashBlockSize) blocks += 1u;
|
||||
erase_blocks (blocks);
|
||||
|
||||
lnk.StartOperation();
|
||||
lnk.WriteMem (data, flen);
|
||||
delete [] data;
|
||||
}
|
||||
static void verify_flash_binary (const char * name) {
|
||||
unsigned flen = 0u;
|
||||
uint8_t * data = open_file_for_read(name, flen);
|
||||
if (!data) return;
|
||||
printf("Verify data\n");
|
||||
read_flash (flen);
|
||||
uint8_t * fdata = mem.getData();
|
||||
unsigned ok = 0;
|
||||
for (unsigned n=0u; n<flen; n++) {
|
||||
if (fdata[n] != data[n]) {
|
||||
printf("err at %04X - %02X != %02X\n", n, fdata[n], data[n]);
|
||||
ok += 1u;
|
||||
}
|
||||
}
|
||||
if (ok == 0u) printf("\nFlash OK\n");
|
||||
else printf("\nTotal errors = %d\n", ok);
|
||||
|
||||
delete [] data;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 3) {
|
||||
printf("Usage:\r\t$ %s r|w|v file.bin\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
loop = true;
|
||||
signal(SIGINT, sig_handler);
|
||||
cmd += usart;
|
||||
if (argv[1][0] == 'r') {
|
||||
read_flash_binary (argv[2]);
|
||||
} else if (argv[1][0] == 'w') {
|
||||
write_flash_binary(argv[2]);
|
||||
} else if (argv[1][0] == 'v') {
|
||||
verify_flash_binary(argv[2]);
|
||||
} else {
|
||||
printf("bad parameter %s\n", argv[1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
85
V203F6P6/programmer/software/usart.cpp
Normal file
85
V203F6P6/programmer/software/usart.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "usart.h"
|
||||
|
||||
UsartClass::UsartClass (const char * name, const int baudrate) : BaseLayer() {
|
||||
id = name;
|
||||
running = false;
|
||||
fd = ::open (id, O_RDWR);
|
||||
if (fd < 0) return;
|
||||
|
||||
timeout = 20'000'000 / baudrate; // cca 2 násobek pro 1 byte
|
||||
|
||||
struct termios LineFlags;
|
||||
int attr = tcgetattr (fd, &LineFlags);
|
||||
if (attr) {
|
||||
printf ("Nelze zjistit parametry portu %s\r\n", name);
|
||||
::close (fd);
|
||||
return;
|
||||
}
|
||||
// nastaveni komunikacnich parametru do struct termios
|
||||
LineFlags.c_iflag = IGNPAR /* | IXON | IXOFF*/; // ignoruj chyby parity
|
||||
LineFlags.c_oflag = 0; // normalni nastaveni
|
||||
LineFlags.c_cflag = CS8 | CREAD | CLOCAL; // 8-bit, povol prijem
|
||||
LineFlags.c_lflag = 0; // Raw input bez echa
|
||||
LineFlags.c_cc [VMIN] = 1; // minimalni pocet znaku pro cteni
|
||||
LineFlags.c_cc [VTIME] = 1; // read timeout 0.1 s
|
||||
|
||||
tcsetattr (fd, TCSANOW, &LineFlags);
|
||||
fcntl (fd, F_SETFL, 0);
|
||||
|
||||
int flag = TIOCM_DTR;
|
||||
ioctl(fd, TIOCMBIS, & flag);
|
||||
|
||||
setBaud(baudrate);
|
||||
|
||||
printf ("Port %s opened (%d) at %d Bd\r\n", id, fd, baudrate);
|
||||
usleep (10000);
|
||||
running = true;
|
||||
pthread_create (&rc, NULL, UsartClass::UsartHandler, this);
|
||||
}
|
||||
|
||||
UsartClass::~UsartClass() {
|
||||
running = false;
|
||||
pthread_cancel (rc);
|
||||
|
||||
int flag = TIOCM_DTR;
|
||||
ioctl(fd, TIOCMBIC, & flag);
|
||||
|
||||
::close (fd);
|
||||
printf ("\nPort %s closed\r\n", id);
|
||||
}
|
||||
uint32_t UsartClass::Up (const char * data, uint32_t len) {
|
||||
return BaseLayer::Up (data, len);
|
||||
}
|
||||
|
||||
uint32_t UsartClass::Down (const char * data, uint32_t len) {
|
||||
if (!running) return 0;
|
||||
const unsigned maxchunk = 32; // USB bulk len (64) je moc
|
||||
unsigned offset = 0, remain = len;
|
||||
while (remain) {
|
||||
const unsigned chunk = remain > maxchunk ? maxchunk : remain;
|
||||
const unsigned writn = ::write (fd, data + offset, chunk);
|
||||
// Počkat je nejjednodušší prevence zahlcení USB (USART má omezenou rychlost)
|
||||
// usleep (timeout * writn); // závisí na baud rate
|
||||
offset += writn;
|
||||
remain -= writn;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
void UsartClass::ReadLoop (void) {
|
||||
int n;
|
||||
while (running) {
|
||||
// Nutno číst po jednom nebo použít timer, jinak to po prvním čtení zdechne.
|
||||
n = ::read (fd, rxbuf, 1);
|
||||
if (!n) continue;
|
||||
Up (rxbuf, n);
|
||||
}
|
||||
}
|
32
V203F6P6/programmer/software/usart.h
Normal file
32
V203F6P6/programmer/software/usart.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef USARTCLASS_H
|
||||
#define USARTCLASS_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include "../common/baselayer.h"
|
||||
|
||||
static constexpr unsigned BUFLEN = 1024u;
|
||||
// Bottom
|
||||
class UsartClass : public BaseLayer {
|
||||
public:
|
||||
UsartClass (const char* name, const int baudrate);
|
||||
uint32_t Up (const char* data, uint32_t len);
|
||||
uint32_t Down(const char* data, uint32_t len);
|
||||
virtual ~UsartClass ();
|
||||
protected:
|
||||
void ReadLoop (void);
|
||||
static void* UsartHandler (void* p) {
|
||||
UsartClass* inst = (UsartClass*) p;
|
||||
inst->ReadLoop();
|
||||
return nullptr;
|
||||
};
|
||||
void setBaud (int baud);
|
||||
public:
|
||||
bool running;
|
||||
private:
|
||||
const char * id; //!< Identifikátor třídy pro ladění
|
||||
char rxbuf[BUFLEN];
|
||||
int fd;
|
||||
int timeout;
|
||||
pthread_t rc;
|
||||
};
|
||||
#endif // USARTCLASS_H
|
66
V203F6P6/programmer/spiblocked.cpp
Normal file
66
V203F6P6/programmer/spiblocked.cpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#include "system.h"
|
||||
#include "spiblocked.h"
|
||||
enum SPICLK : uint32_t {
|
||||
FPCLK_2 = 0u, // 72 MHz
|
||||
FPCLK_4, // 36 MHz
|
||||
FPCLK_8, // 18 MHz
|
||||
FPCLK_16, // 9 MHz
|
||||
FPCLK_32, // 4.5 MHz
|
||||
FPCLK_64, // 2.25 MHz
|
||||
FPCLK_128, // 1.125 MHz
|
||||
FPCLK_256, // 0.5625 MHz
|
||||
};
|
||||
static constexpr unsigned FM = 3u; // 50 MHz
|
||||
static void InitPins () noexcept {
|
||||
// PA4 - NSS, PA5 - SCK, PA6 - MISO, PA7 - MOSI
|
||||
GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> uint32_t {
|
||||
r.B.MODE4 = FM;
|
||||
r.B.CNF4 = 0u; // gen push - pull
|
||||
r.B.MODE5 = FM;
|
||||
r.B.CNF5 = 2u; // alt push - pull
|
||||
r.B.MODE6 = 0u; // input mode
|
||||
r.B.CNF6 = 1u; // floating
|
||||
r.B.MODE7 = FM;
|
||||
r.B.CNF7 = 2u; // alt push - pull
|
||||
return r.R;
|
||||
});
|
||||
// AFIO - default
|
||||
GPIOA.BSHR.B.BS4 = SET;
|
||||
}
|
||||
|
||||
SpiBlocked::SpiBlocked() noexcept {
|
||||
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> uint32_t {
|
||||
r.B.SPI1EN = SET;
|
||||
r.B.IOPAEN = SET;
|
||||
//r.B.AFIOEN = SET;
|
||||
return r.R;
|
||||
});
|
||||
InitPins();
|
||||
RCC.APB2PRSTR.B.SPI1RST = SET;
|
||||
RCC.APB2PRSTR.B.SPI1RST = RESET;
|
||||
SPI1.CTLR1.modify([](SPI1_Type::CTLR1_DEF & r) -> uint32_t {
|
||||
r.B.CPHA = RESET;
|
||||
r.B.CPOL = RESET;
|
||||
r.B.MSTR = SET; // master
|
||||
r.B.DFF = RESET; // 8 bit
|
||||
r.B.SSM = SET; // software
|
||||
r.B.SSI = SET; // !!! netuším proč, ale jinak se nenastaví MSTR a SPE
|
||||
r.B.LSBFIRST = RESET;
|
||||
r.B.BR = FPCLK_64;
|
||||
return r.R;
|
||||
});
|
||||
SPI1.CRCR.R = 7u;
|
||||
SPI1.CTLR1.B.SPE = SET;
|
||||
}
|
||||
uint8_t SpiBlocked::ReadWriteByte(const uint8_t data) const {
|
||||
while (SPI1.STATR.B.TXE == RESET);
|
||||
SPI1.DATAR.B.DATAR = data;
|
||||
while (SPI1.STATR.B.RXNE == RESET);
|
||||
return SPI1.DATAR.B.DATAR;
|
||||
}
|
||||
void SpiBlocked::ChipSelect(const bool on) const {
|
||||
if (on) GPIOA.BSHR.B.BR4 = SET;
|
||||
else GPIOA.BSHR.B.BS4 = SET;
|
||||
}
|
||||
|
||||
|
12
V203F6P6/programmer/spiblocked.h
Normal file
12
V203F6P6/programmer/spiblocked.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef SPIBLOCKED_H
|
||||
#define SPIBLOCKED_H
|
||||
#include <stdint.h>
|
||||
|
||||
class SpiBlocked {
|
||||
public:
|
||||
explicit SpiBlocked () noexcept;
|
||||
uint8_t ReadWriteByte (const uint8_t data) const;
|
||||
void ChipSelect (const bool on) const;
|
||||
};
|
||||
|
||||
#endif // SPIBLOCKED_H
|
80
V203F6P6/programmer/usb_desc.cpp
Normal file
80
V203F6P6/programmer/usb_desc.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : usb_desc.c
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2022/08/20
|
||||
* Description : usb device descriptor,configuration descriptor,
|
||||
* string descriptors and other descriptors.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "usb_desc.h"
|
||||
|
||||
/* Device Descriptor */
|
||||
const uint8_t USBD_DeviceDescriptor[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x10, 0x01, // bcdUSB 1.10
|
||||
0x02, // bDeviceClass
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
DEF_USBD_UEP0_SIZE, // bMaxPacketSize0 64
|
||||
(uint8_t)DEF_USB_VID, (uint8_t)(DEF_USB_VID >> 8), // idVendor 0x1A86
|
||||
(uint8_t)DEF_USB_PID, (uint8_t)(DEF_USB_PID >> 8), // idProduct 0x5537
|
||||
DEF_IC_PRG_VER, 0x00, // bcdDevice 0.01
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x03, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
/* Configuration Descriptor */
|
||||
const uint8_t USBD_ConfigDescriptor[] =
|
||||
{
|
||||
/* Configure descriptor */
|
||||
0x09, 0x02, 0x43, 0x00, 0x02, 0x01, 0x00, 0x80, 0x32,
|
||||
|
||||
/* Interface 0 (CDC) descriptor */
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00,
|
||||
|
||||
/* Functional Descriptors */
|
||||
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||
|
||||
/* Length/management descriptor (data class interface 1) */
|
||||
0x05, 0x24, 0x01, 0x00, 0x01,
|
||||
0x04, 0x24, 0x02, 0x02,
|
||||
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||
|
||||
/* Interrupt upload endpoint descriptor */
|
||||
0x07, 0x05, 0x81, 0x03, (uint8_t)DEF_USBD_ENDP1_SIZE, (uint8_t)( DEF_USBD_ENDP1_SIZE >> 8 ), 0x01,
|
||||
|
||||
/* Interface 1 (data interface) descriptor */
|
||||
0x09, 0x04, 0x01, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x00,
|
||||
|
||||
/* Endpoint descriptor */
|
||||
0x07, 0x05, 0x02, 0x02, (uint8_t)DEF_USBD_ENDP2_SIZE, (uint8_t)( DEF_USBD_ENDP2_SIZE >> 8 ), 0x00,
|
||||
|
||||
/* Endpoint descriptor */
|
||||
0x07, 0x05, 0x83, 0x02, (uint8_t)DEF_USBD_ENDP3_SIZE, (uint8_t)( DEF_USBD_ENDP3_SIZE >> 8 ), 0x00,
|
||||
};
|
||||
#define DEF_STRDESC(p,n) w_text<(sizeof(p)>>1)>n={sizeof(n)-2u,3u,{p}}
|
||||
template<const unsigned N> struct w_text {
|
||||
uint8_t len, typ;
|
||||
const char16_t str [N];
|
||||
};
|
||||
static const DEF_STRDESC((u"Kizarm Labs."), str_1);
|
||||
static const DEF_STRDESC((u"Flash Programmer"),str_2);
|
||||
static const DEF_STRDESC((u"0001"), str_3);
|
||||
/* Language Descriptor */
|
||||
static const uint8_t LangDescr[] = {
|
||||
0x04, 0x03, 0x09, 0x04
|
||||
};
|
||||
const uint8_t * USBD_StringLangID = reinterpret_cast<const uint8_t *>(LangDescr);
|
||||
const uint8_t * USBD_StringVendor = reinterpret_cast<const uint8_t *>(&str_1);
|
||||
const uint8_t * USBD_StringProduct = reinterpret_cast<const uint8_t *>(&str_2);
|
||||
const uint8_t * USBD_StringSerial = reinterpret_cast<const uint8_t *>(&str_3);
|
||||
|
Loading…
Add table
Reference in a new issue