Hab mir da mal was zusammen gesucht... vielleicht hilfts ja dem einen oder anderen.
Im Prinzip stammt der entrümpelte ihex Loader aus der Anleitung für ein Bootloader auf http://www.mikrocontroller.net/artic...ache_Anleitung
Sinngemäß soll das ihexfile per eeprom_update_block() auf die eepromzellen geschrieben werden.
Ich hab irgendwo gelesen, das die ersten Bytes (1? 2?) des EEPROM vom nativen Bootloader des RP6/M32 zum steuern von LEDs verwendet werden.
Irgendwie sollte man verhindern können, das genau diese Adressen von einem epp file überschrieben werden bzw. müsste dafür eine Art "Align" der eeprom-Daten erfolgen. Ich hab aber noch nicht rausgefunden wie das genau geht. Man könnte den Loader vielleicht so umbauen das er die ersten 2 byte im ihex nicht schreibt und eine erste int (nicht zu schreibende) Dummyvariable fürs Alignment vor allen anderen in den Code setzen. Allerdings ist das übelstes Gefrickel, vielleicht hat jemand noch eine andere Idee.* Ich dachte an sowas wie den Section-Eintrag für den Linker bezüglich .eeprom hoch zu drehen oder so.
*das geht nicht, da der Linker die Variablen nach eigener Vorstellung und nicht chronologisch anordnet.
Aufgerufen wird das mit:
Code:
UART_Mode(UART_ASCII);
puts_P(PSTR("laedt epp..."));
if (import_epp()) puts_P(PSTR("...fertig")); else puts_P(PSTR("...FEHLER"));
Also zumindest für den Fall das ihr meine UART Lib verwendet 
Die Einstellungen für die UART LIB sind:
#define WENEEDASCII 1
#define WENEEDTXXONOFF 1
#define WENEEDRXXONOFF 1
#define WENEED7BIT 1
#define WENEEDCRLF 1
Man kann dann im Realterm dank xon/xoff über "Send File" das epp File in einem Rutsch hoch laden.
Mit der alten Lib hab ich es nicht probiert aber wenn man sicher stellen kann das keine Zeichen flöten gehen, gehts auch damit. Wenn...
Man muss dann nur die getchar() durch readChar mit dieser komischen while not null ersetzen und im Original ist übrigends auch eine xon/xoff Steuerung enthalten.
Mit der neuen Lib kann man die Zeilen sogar über copy&paste per Robotloaderterminal einfügen... mit der alten weis ich nicht, ich glaube da müsste man noch ein paar \r und \f aus dem Datenstrom fischen. Der Parser kann keine extended Adresses aber er ist nun problemlos nachrüstbar.
Code:
#include <avr/eeprom.h>
uint16_t hex2num (const uint8_t * ascii, uint8_t num)
{
uint8_t i;
uint16_t val = 0;
for (i=0; i<num; i++)
{
uint8_t c = ascii[i];
if (c >= '0' && c <= '9') c -= '0';
else if (c >= 'A' && c <= 'F') c -= 'A' - 10;
else if (c >= 'a' && c <= 'f') c -= 'a' - 10;
val = 16 * val + c;
}
return val;
}
/* Zustände des Hex-File-Parsers */
#define PARSER_STATE_START 0
#define PARSER_STATE_SIZE 1
#define PARSER_STATE_ADDRESS 2
#define PARSER_STATE_TYPE 3
#define PARSER_STATE_DATA 4
#define PARSER_STATE_CHECKSUM 5
#define PARSER_STATE_ERROR 6
#define PARSER_STATE_EXT_SEGMENTADRESS 7
#define PARSER_STATE_SEGMENTADRESS 8
#define PARSER_STATE_EXT_LINEARADRESS 9
#define PARSER_STATE_LINEARADRESS 10
#define START_SIGN ':' /* Hex-Datei Zeilenstartzeichen */
#define IHEXSIZE 255 /* maxbytes per Line, kann kleiner als 255 sein, beim GCC reichen üblicher weise 16 bytes */
/* Zustände des Bootloader-Programms */
uint8_t import_epp ( void ){
/* Empfangenes Zeichen */
uint16_t c = 0,
/* Intel-HEX Zieladresse */
hex_addr = 0,
/* Intel-HEX Checksumme zum Überprüfen des Daten */
hex_check = 0;
/* Empfangszustandssteuerung */
uint8_t parser_state = PARSER_STATE_START,
/* Datenpuffer für die Hexdaten*/
flash_data[IHEXSIZE],
/* Positions zum Schreiben in der Datenpuffer */
flash_cnt = 0,
/* Position zum Schreiben in den HEX-Puffer */
hex_cnt = 0,
/* Puffer für die Umwandlung der ASCII in Binärdaten */
hex_buffer[5],
/* Intel-HEX Datenlänge */
hex_size = 0,
/* Intel-HEX Recordtype */
hex_type = 0,
/* empfangene HEX-Checksumme */
hex_checksum=0;
while (true) {
c = getchar();
switch(parser_state) {
/* Warte auf Zeilen-Startzeichen */
case PARSER_STATE_START:
if((uint8_t)c == START_SIGN) {
parser_state = PARSER_STATE_SIZE;
hex_cnt = 0;
hex_check = 0;
flash_cnt = 0;
}
break;
/* Parse Datengröße und prüfen auf Bufferüberlauf */
case PARSER_STATE_SIZE:
hex_buffer[hex_cnt++] = (uint8_t)c;
if(hex_cnt == 2) {
parser_state = PARSER_STATE_ADDRESS;
hex_cnt = 0;
hex_size = (uint8_t)hex2num(hex_buffer, 2);
hex_check += hex_size;
}
break;
/* Parse Zieladresse */
case PARSER_STATE_ADDRESS:
hex_buffer[hex_cnt++] = (uint8_t)c;
if(hex_cnt == 4) {
parser_state = PARSER_STATE_TYPE;
hex_cnt = 0;
hex_addr = hex2num(hex_buffer, 4);
hex_check += (uint8_t) hex_addr;
hex_check += (uint8_t) (hex_addr >> 8);
}
break;
/* Parse Zeilentyp */
case PARSER_STATE_TYPE:
hex_buffer[hex_cnt++] = (uint8_t)c;
if(hex_cnt == 2) {
hex_cnt = 0;
hex_type = (uint8_t)hex2num(hex_buffer, 2);
hex_check += hex_type;
switch(hex_type) {
case 0: parser_state = PARSER_STATE_DATA; break;
case 1: parser_state = PARSER_STATE_CHECKSUM; break;
case 2: parser_state = PARSER_STATE_EXT_SEGMENTADRESS; break;
case 3: parser_state = PARSER_STATE_SEGMENTADRESS; break;
case 4: parser_state = PARSER_STATE_EXT_LINEARADRESS; break;
case 5: parser_state = PARSER_STATE_LINEARADRESS; break;
default: parser_state = PARSER_STATE_ERROR; break; //reading bullshit
}
}
break;
case PARSER_STATE_EXT_SEGMENTADRESS:
// Tag für RP6 überlesen
parser_state = PARSER_STATE_START;
break;
case PARSER_STATE_SEGMENTADRESS:
// Tag für RP6 überlesen
parser_state = PARSER_STATE_START;
break;
case PARSER_STATE_EXT_LINEARADRESS:
// Tag für RP6 überlesen
parser_state = PARSER_STATE_START;
break;
case PARSER_STATE_LINEARADRESS:
// Tag für RP6 überlesen
parser_state = PARSER_STATE_START;
break;
/* Parse Flash-Daten */
case PARSER_STATE_DATA:
hex_buffer[hex_cnt++] = (uint8_t)c;
if(hex_cnt == 2) {
hex_cnt = 0;
flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);
hex_check += flash_data[flash_cnt++];
if (hex_size==flash_cnt) parser_state = PARSER_STATE_CHECKSUM;
}
break;
/* Parse Checksumme */
case PARSER_STATE_CHECKSUM:
hex_buffer[hex_cnt++] = (uint8_t)c;
if(hex_cnt == 2) {
hex_checksum = (uint8_t)hex2num(hex_buffer, 2);
hex_check += hex_checksum;
hex_check &= 0x00FF;
/* Überprüfe Checksumme -> muss '0' und die angegebene Datenmenge vorhanden sein */
if(hex_check == 0) {
if(hex_type == 1 ) return true;
eeprom_update_block(&flash_data, (void *)hex_addr, hex_size);
parser_state = PARSER_STATE_START;
} else parser_state = PARSER_STATE_ERROR;
}
break;
/* Parserfehler (falscher Zeilentyp oder Datenfehler) */
case PARSER_STATE_ERROR:
return false;
break;
default:
break;
}
}
}
Ich möchte übrigends die Geschichte benutzen um ab und zu Zeichentabellen fürs LCD Display im EEPROM abzulegen die bei Displayinit() auf das Display geladen werden. Solche Zeichen ändern sich ab und an aber nicht bei jedem Programmstart... und da es 8x8 Byte sind, die im Programmspeicher oder RAM nix verloren haben, sind sie im internen EEPROM auch bestens aufgehoben. Die Nutzung vom EEPROM als nichtflüchtigen Speicher hat erst mal nichts mit Konstanten oder Vars zu tun, da lasse ich mich auch nicht auf ein Definitionsgerangel um Haarspaltereien ein. Bekanntlich sind Daten, die ich zur Compile Zeit im RAM oder auf PGM anlege und dann aufs eprom schreibe zunächst 2 mal im System vorhanden und ich muss mich zusätzlich um deren Adressmanagement incl. copieren kümmern. Alles Dinge die mir der Compiler bei Nutzung der entsprechenden Direktiven abnehmen würde. Das man das auch "zu Fuß" machen kann... klar... aber warum soll ich Kompromisse eingehen und Fehlerquellen akzeptieren, wenn ich nicht zwingend muss?
@Slyd hmm.. also grade bei der M256 wäre es dann besonders interessant, wie man die ersten Bytes blockt/reserviert damit GCC nicht die Daten auf EEPROM Adresse 0x0000 legt. Hatte den Post erst gesehen nachdem ich abgeschickt hab. Aber Du bist schon auf das Problem am Rande eingegangen. Es wäre aber besser gewesen, die Bootloaderdaten am Schluß des EEPROMS anzulegen so das die Compilerdaten und Bootloaderdaten wie bei Stack und Vars im Ram entgegen wachsen können.
Naja da is nu nichts mehr dran zu ändern... aber falls du mal ein RP7 auflegen solltest... *schmunzels
OK Auf Grund der Überlegungen und Infos noch mal der Hinweis: Man sollte überlegen ob man den Code in der aktuellen Fassung bzw. die Vorgehensweise mit EEMEM so benutzt. Auf der M256 zerschiesst man sich vermutlich damit die (Wlan-Netzwerk?)-Einstellungen, auf den kleineren Boards die LED Steuerung des Bootloaders. Bitte die Vorgehensweise aus dem NACHTRAG mit EEPMEM anwenden.
NACHTRAG:
Ich hab eine Möglichkeit gefunden bezüglich der ersten Bytes im EEPROM.
Man legt eine neue Section an indem man dem Linker im AVR Studio Projekt bei Memory Settings unter EEPROM Segment ohne " einträgt: ".realeep=0x810020"
Das ist vergleichbar mit -Wl,-section-start=.realeep=0x810020 im Makefile.
Dann definiert man im .h File mit #define EEPMEM __attribute__((section(".realeep")))
ein neues Segmentarttribut und gibt das z,.B. als const uint8_t chrdata0[8] EEPMEM = {data...}; an.
Const weil es vom normalen Code aus nicht beschreibbar ist. Man kann selbstverständlich mit den eeprom Funktionen aus eprom.h die Daten auch händisch lesen und beschreiben. Aber z.B. eine weitere Wertezuweisung ausserhalb von epp Dateien sollte der Compiler schon durch die Type Prüfung nachhaltig verhindern da man zum beschreiben der EPROMZellen eben mehr als nur eine Zusweisung braucht!
Das resultierende epp file sieht dann so aus:
Code:
:02000004008179
:100020002E2C6F4F302A304F6F2C2E2000000006F0
:100030000906000000000000000A1F0E0400000076
:10004000000A1F0E04000000000A1F0E040000003A
:10005000000A1F0E04000000000A1F0E0400000426
:100060000404040404000000000A1F0E0400000041
:100070000000000000000000000000000000000080
:0F00800000000000000000000000000000000071
:00000001FF
Man sieht, das in der ersten Zeile nun der Befehlscode 04 (Lienaradressextension) verwendet wird was in in dem import_epp(); durch case 4: parser_state = PARSER_STATE_ERROR; break; //PARSER_STATE_EXT_LINEARADRESS noch zu einem Error führt weil das setzen der virtuellen Adresse 0x810020 nicht umgesetzt ist. Das werde ich dann noch korrigieren. <-[erledigt, Tags mit Adressfeldern werden nun überlesen]
Damit sind aber im Programm selbst alle Adressen im Eeprom ab 0x810020 alloziiert und das import_epp(); wird das dann auch beim Schreiben der Daten berücksichtigen. Dann besteht auch die Gefahr des Überschreibens der eeprom Daten des Bootloaders nicht mehr. Zumindest so lange man die Finger von EEMEM lässt und stattdessen dann EEPMEM nutzt.
So kommt man nun an die Vars einfach und problemlos dran...
Code:
const uint16_t reboot_counter_ee EEPMEM;
uint16_t reboots = eeprom_read_word(&reboot_counter_ee); //das konnte man bisher auch von Hand als Hex... oder #define
reboots++;
eeprom_write_word(&reboot_counter_ee, reboots); //das geht auch trotz const
printf("Wir haben %d mal gebootet.\n",reboot_counter_ee); //das geht nun auch und spart Code&Platz weil ich die Daten nicht mehr zwischenlagern muss!!!
reboot_counter_ee=0; //das muss der compiler natürlich verhindern...
Gruß
Lesezeichen