AVR- Anwendungsprogramm an Bootloader senden

Jetzt ist es an der Zeit, den Anwendungscode an den Bootloader zu senden, der in einen ATmega32A-Controller programmiert wurde. Es muss im Hex-Format gesendet werden, aber ich habe keine Ahnung von "Wie soll ich es senden?"

Die für einen Anwendungscode von Atmel Studio kompilierte Hex-Datei lautet wie folgt:

    :100000000C942A000C943F000C943F000C943F0089
    :100010000C943F000C943F000C943F000C943F0064
    :100020000C943F000C943F000C943F000C943F0054
    :100030000C943F000C943F000C943F000C943F0044
    :100040000C943F000C943F000C943F000C943F0034
    :100050000C943F0011241FBECFE5D8E0DEBFCDBF1A
    :1000600010E0A0E6B0E0E0E2F2E002C005900D9200
    :10007000A23EB107D9F70E94BD000C940E010C946A
    :100080000000D09A899A88988AB188618AB980B527
    :100090008F7B80BD529880B5866080BD80B5877F9C
    :1000A00080BD80B58F7C80BD89E189B910BC089581
    :1000B0005D9BFECF8CB908950F931F93CF93DF9371
    :1000C000FC0101900020E9F73197E81BF90B51F092
    :1000D000EC018C010E0F1F1F89910E945800C01760
    :1000E000D107D1F7DF91CF911F910F9108955F9BB9
    :1000F000FECF8CB190E008950E9477008335E1F740
    :100100000E9477008935C1F70E9477008335A1F7F7
    :100110000E947700833581F70E947700843561F76C
    :100120000E947700813441F70E947700823521F7E1
    :100130000E947700843501F70895CF93DF93EC0197
    :100140000E945C000E9477008F3421F00E947700AB
    :100150008F34E1F70E9477008B3461F02FE78AE15A
    :1001600096E0215080409040E1F700C00000CE01B1
    :100170000E949D00DF91CF9108950E9441002FE7DA
    :100180008AE196E0215080409040E1F700C00000F5
    :100190000E947C002FE382E49FE021508040904049
    :1001A000E1F700C0000080E690E00E949D008DE62F
    :1001B00090E00E949D002FE78AE196E02150804068
    :1001C0009040E1F700C0000087E790E00E945C00EB
    :1001D0002FE78AE196E0215080409040E1F700C08F
    :1001E000000084E990E00E945C002FE78AE196E03D
    :1001F000215080409040E1F700C000008EEB90E07D
    :100200000E945C002FE78AE196E0215080409040F8
    :10021000E1F700C0000080E090E00895F894FFCF7F
    :1002200041545E4E5754494D453F0D0A0041542BF1
    :1002300043474D490D0A0041545E4950494E4954C7
    :100240003D2261697274656C677072732E636F6DA5
    :10025000220D0A0041545E49504F50454E3D312C0D
    :1002600022544350222C223132322E3136352E3256
    :1002700033302E3137222C373836390D0A004154AD
    :100280005E495053454E443D312C22646576696386
    :10029000652D69642C6770735F64617461220D0A57
    :0202A00000005C
    :00000001FF

Standardmäßig enthält die erste Zeile Daten 0C942A000C943F000C943F000C943F00und Prüfsumme 89. Muss ich nur die 16 Datenbytes gefolgt von der Prüfsumme senden? In manchen Programmen haben einige Zeilen nicht einmal 16 Byte Daten. Es hat nur 10 Bytes mit Prüfsumme am Ende, zB: :0A0B4000CDBFED010895F894FFCF3A.

Es gibt nirgendwo klare Beispiele. Ich nehme an, jemand da draußen muss etwas Erfahrung darin haben, um mir zu helfen.

Möglicherweise möchten Sie dies auf embedded.SE umstellen
Hallo Dzarda, könntest du das bitte näher erläutern
Ich habe im Moment keine Zeit, eine vollständige Antwort zu schreiben, aber es ist ein Intel-Hex- Format, und normalerweise ist es am einfachsten, die gesamte Menge zu senden und im Bootloader zu dekodieren. Dann können Sie einfach ein normales Terminalprogramm verwenden, um es zu senden.
Hallo PeterJ, ich bin mir sicher, dass ich es entschlüsseln kann. Aber muss ich die Prüfsumme an jedem Zeilenende einfügen? Und wie man auffüllt, wenn eine Zeile weniger Daten hat

Antworten (3)

Dies erfordert einige weitere Arbeit Ihrerseits, um es in Ihren Code zu integrieren, sollte Ihnen aber einige Ideen geben. Der erste Schritt im Bootloader besteht darin, Header in Bezug auf die Programmierung des FLASH einzufügen:

#include <avr/boot.h>
#include <avr/pgmspace.h>

Ein paar andere Definitionen, die helfen, Dinge später zu definieren und einige Antwortcodes am Ende der Verarbeitung jeder Zeile zu definieren, sind:

#define HEX2DEC(x)  (((x < 'A') ? ((x) - 48) : ((x) - 55)))
#define SPM_PAGEMASK ((uint32_t) ~(SPM_PAGESIZE - 1))
enum response_t {RSP_OK, RSP_CHECKSUM_FAIL, RSP_INVALID, RSP_FINISHED};

Dann kann eine Routine wie die folgende verwendet werden, um das Intel-Hex-Format zu verarbeiten und in FLASH zu schreiben. Es ist für ein größeres Gerät geschrieben, behandelt also erweiterte Adressen in den Hex-Dateien, sodass Sie diese für Ihr kleineres Gerät entfernen können, um die Codegröße zu reduzieren, aber es schadet nicht, es an Ort und Stelle zu lassen. Sie müssen auch Ihren eigenen UART-Empfangs-/Init-Code hinzufügen und bestimmen, was zu tun ist, wenn die Dinge ablaufen, ich habe in diesem Fall einen Watchdog-Timer verwendet.

enum response_t process_line()
{
    char c, line_buffer[128], data_buffer[64];
    uint8_t line_len = 0, data_len = 0, data_count, line_type, line_pos, data;
    uint8_t addrh, addrl, checksum, recv_checksum;
    uint16_t addr, extended_addr = 0, i;
    static uint32_t full_addr, last_addr = 0xFFFFFFFF;

    c = uart_getc();
    while (c != '\r')
    {
        if (c == ':')
            line_len = 0;
        else if (c == '\n')
            ;
        else if (c == '\0')
            ;
        else if (line_len < sizeof(line_buffer))
            line_buffer[line_len++] = c;
        c = uart_getc();
    }
    if (line_len < 2)
        return RSP_INVALID;
    data_count = (HEX2DEC(line_buffer[0]) << 4) + HEX2DEC(line_buffer[1]);
    if (line_len != data_count * 2 + 10)
        return RSP_INVALID;
    addrh =  (HEX2DEC(line_buffer[2]) << 4) + HEX2DEC(line_buffer[3]);
    addrl =  (HEX2DEC(line_buffer[4]) << 4) + HEX2DEC(line_buffer[5]);
    addr = (addrh << 8) + addrl;
    line_type = (HEX2DEC(line_buffer[6]) << 4) + HEX2DEC(line_buffer[7]);
    line_pos = 8;
    checksum = data_count + addrh + addrl + line_type;
    for (i=0; i < data_count; i++)
    {
        data = (HEX2DEC(line_buffer[line_pos]) << 4) + HEX2DEC(line_buffer[line_pos + 1]);
        line_pos += 2;
        data_buffer[data_len++] = data;
        checksum += data;
    }
    checksum = 0xFF - checksum + 1;
    recv_checksum = (HEX2DEC(line_buffer[line_pos]) << 4) + HEX2DEC(line_buffer[line_pos + 1]);
    if (checksum != recv_checksum)
        return RSP_CHECKSUM_FAIL;
    if (line_type == 1)
    {
        if (last_addr != 0xFFFFFFFF)
        {
            boot_page_write (last_addr & SPM_PAGEMASK);
            boot_spm_busy_wait();
        }
        return RSP_FINISHED;
    }
    else if ((line_type == 2) || (line_type == 4))
        extended_addr = (data_buffer[0] << 8) + data_buffer[1];
    else if (line_type == 0)
    {
        full_addr = ((uint32_t) extended_addr << 16) + addr;
        if ((full_addr & SPM_PAGEMASK) != (last_addr & SPM_PAGEMASK))
        {
            if (last_addr != 0xFFFFFFFF)
            {
                boot_page_write (last_addr & SPM_PAGEMASK);
                boot_spm_busy_wait();
            }
            boot_page_erase (full_addr);
            boot_spm_busy_wait ();
        }
        for (i=0; i < data_len; i+=2)
        {
            uint16_t w = data_buffer[i] + ((uint16_t) data_buffer[i + 1] << 8);
            boot_page_fill (full_addr + i, w);
        }
        last_addr = full_addr;
    }
    return RSP_OK;
}

Es liest eine ganze Hex-Zeile und verifiziert die Prüfsumme, also vergleichen Sie es mit dem Intel-Hex-Format, um zu sehen, wie es im Detail funktioniert, bevor Sie es verwenden. Von dort ist die allgemeine Idee, dass, wenn es auf eine neue FLASH-Seitengrenze trifft, es löscht, aber ansonsten fortfährt und die Daten schreibt.

Wenn der Bootloader eingegeben wird, ist dies der Hauptcode, den ich verwendet habe, um ihn aufzurufen, aber noch einmal müssen Sie herausfinden, was zu tun ist, wenn ein Fehler auftritt. In meinem Fall habe ich eine benutzerdefinierte PC-Software verwendet, also einige Zeichenfolgen zurückgesendet, um den Status anzuzeigen, aber für einen Terminal-Upload möchten Sie möglicherweise nur ein EEPROM-Flag setzen, je nachdem, ob es erfolgreich war und Sie damit booten oder anzeigen sollten eine benutzerfreundliche Nachricht und rufen Sie den Bootloader erneut auf:

void enter_loader()
{
    enum response_t response = RSP_OK;

    uart_puts("+OK/\r\n");
    while (response != RSP_FINISHED)
    {
        response = process_line();
        if (response == RSP_OK)
            uart_puts("+OK/");
        else if (response != RSP_FINISHED)
            uart_puts("+FAIL/");
        wdt_reset();
    }   
    boot_rww_enable ();
    while (1) // Use watchdog to reboot
        ;
}

Wie auch immer, das sollte Ihnen einen guten Start geben und der obige Code wurde ziemlich gut mit einiger ziemlich komplexer Firmware getestet. Sie brauchen nur ein wenig Arbeit, um den fehlenden UART und anderen Initialisierungscode zu schreiben, um zu bestimmen, wann der Bootloader für Ihre Anwendung aufgerufen werden soll. Für einen ATmega32A sind möglicherweise auch einige Anpassungen erforderlich, aber ich denke, der Ansatz sollte auf allen AVR-Geräten ziemlich gut funktionieren.

Hallo PeterJ, wenn einige Seiten außer der letzten Seite einen kurzen Code wie in ":02072000FFCF09:1007220041545E4950494E49543D220000000000A8" haben, bekomme ich diese 2 Bytes, die sich auf jeder Seite bis zum Ende verschieben
@ user2943851, ich werde heute wahrscheinlich keine Chance haben, aber wollten Sie die Datei in sagen wir Pastebin posten und hier mit einem Link antworten, und ich werde es mir ansehen und sehen, was ich entdecken kann.
Im Moment verwende ich SRecord, um Intel Hex mit variabler Länge in Intel Hex mit fester Länge zu konvertieren. Es wäre jedoch schön, das Parsen des ursprünglichen Intel-Hex-Formats zu lernen. Also bemüht

Zum HEX-Format:

Lesen Sie, wie im Kommentar vorgeschlagen, das Intel HEX-Format. http://en.wikipedia.org/wiki/Intel_HEX

Was es sagt, ist im Grunde, dass jede Zeile einem Datenblock entspricht. Das Format der Zeile ist wie folgt:

:XYZC

Wo:

  • X ist 1 Byte, das die Nutzdatenlänge angibt
  • Y ist 1 Byte, das den Payload-Typ angibt *
  • Z ist die Nutzlast der Länge X Bytes
  • C ist 1 Byte der Prüfsumme der Zeile

*

'00' Datenaufzeichnung
'01' Dateiendeaufzeichnung
'02' Erweiterte Segmentadresse
'03' Startsegmentadressenaufzeichnung
'04' Erweiterte lineare Adressaufzeichnung
'05' Startlineare Adressaufzeichnung

Siehe http://microsym.com/editor/assets/intelhex.pdf für die gesamte Spezifikation.


Zum Übertragungsweg:

Es wird auch im Kommentar erwähnt - Sie sind viel besser dran, die gesamte Datei so zu senden, wie sie ist. Auf diese Weise benötigen Sie kein spezielles Programm auf der PC-Seite, sondern nur ein Terminal, das mit Ihrem Gerät kommuniziert. Analysieren Sie dann den Text auf dem Chip.


So schreiben Sie es:

Die einfachste Methode ist, die Anwendungshinweise von Atmel zu lesen:

Alle Selbstprogrammierungsoperationen werden unter Verwendung des SPM-Befehls ausgeführt

Diese finden Sie hier: http://www.atmel.com/images/doc1644.pdf

Der AVR109 ist ein berühmter Bootloader für Mega-Geräte. Hier finden Sie viele Informationen.

Ja, ich werde analysieren und ich werde nur die Daten nehmen. Meine Frage ist nun "Wie schreibt man das?", was mit der Prüfsumme zu tun ist
Warten Sie, ich werde die Antwort bearbeiten.

Es gibt Werkzeuge für solche Dinge. Zuallererst: Sie möchten den Controller nicht wirklich herausfinden lassen, wo er was ablegen soll, da dies das Parsen der Datei auf dem Controller erfordern würde.

Konvertieren Sie die Datei stattdessen in eine Binärdatei (die 1:1 in Flash geschrieben werden kann) und lassen Sie sie dann vom Controller auf die Anwendungsadresse flashen (das müssen Sie vorher herausfinden). Ein mögliches Programm, das ich sehe: Ihre Anwendung ist mit der Adresse 0x0 verknüpft - das bedeutet, dass der Bootloader, wenn er sich an dieser bestimmten Adresse befindet, nicht funktioniert (Sie müssen ihn mit einer anderen Adresse verknüpfen). Wenn sich der Bootloader an einer anderen Adresse befindet (vorzugsweise am Ende des Flashs), können Sie loslegen.

Bitte finden Sie hier ein Skript, das in Binärdateien konvertiert und alles andere erledigt: http://1drv.ms/1hj9eU0

Es ist für eine andere Atmel-Familie (AVR32) gedacht, aber Sie sollten die Idee bekommen. Wenn Sie Fragen haben, lassen Sie es mich wissen.

Ich bekomme nicht hin, was du mit 1:1 zum Flashen meinst?
Wenn Sie diese Datei auf Ihr Gerät übertragen, müssen Sie die ASCII-Zeichen „C“ und „9“ nicht auf Ihr Gerät schreiben, aber Sie müssen das Byte 0xc9 an diesen Speicherplatz schreiben. Um eine solche Binärdatei aus einer Hex-Datei zu erstellen, müssen Sie die Hex-Datei in eine "reine" Binärdatei konvertieren (das macht srec_cat aus der Zip-Datei).