Speichern einer Variablen zwischen Neustarts auf einem Mikrocontroller ohne Verwendung von Flash-Speicher

Ich entwickle einen Bootloader und habe einige Probleme, vom Bootloader zur Anwendung zu springen. Soweit ich sehen kann, liegt das Problem daran, dass ich bestimmte Initialisierungen im Bootloader durchführe, die dann auch in der Anwendung durchgeführt werden. Ich habe eine Frage dazu in den TI-Foren ( https://e2e.ti.com/support/microcontrollers/other/f/908/p/894608/3308592#3308592 ) gepostet, aber ich bekomme keine Antworten, also suche ich für eine andere Möglichkeit, es zu beheben.
Meine Idee ist jetzt, den Bootloader zu starten, das Anwendungs-Image herunterzuladen, es im Flash-Speicher zu speichern und dann, anstatt zur Anwendung zu springen, das Gerät neu zu starten und zur Anwendung zu springen, bevor irgendeine Art von Initialisierung durchgeführt wurde. Ich bin mir nicht sicher, ob das funktionieren wird, aber ich stecke schon eine Weile auf dem Sprung vom Bootloader zur Anwendung fest und möchte endlich etwas vorankommen.

Damit dies funktioniert, muss der Bootloader irgendwie wissen, dass er vor jeder Initialisierung zur Anwendung springen soll. Soweit ich weiß, ist es nicht möglich, einfach in ein einzelnes Byte irgendwo im Flash-Speicher zu schreiben, da dazu ein ganzer Block gelöscht werden muss und die Blockgröße auf dem von mir verwendeten Gerät 8 KB groß ist. Es scheint eine Verschwendung zu sein, 8 KB auf einem Gerät zu reservieren, nur damit ich einen einfachen Wert speichern kann, der zwischen Systemrücksetzungen weitergegeben werden kann.

Hat jemand Tipps, wie ich das hinbekommen könnte?

Ich entwickle für den CC2652R1F mit Code Composer Studio zusammen mit dem Compiler TI v18.12.5.LTS.

BEARBEITEN :
Ich habe versucht, kkrambos Situation zu verwenden, aber es scheint nicht richtig zu funktionieren.
Ich habe meine Linker-Befehlsdatei so bearbeitet, dass sie einen .boot_cfg-Abschnitt im RAM mit einer Größe von vier enthält. Ich habe die neue Linker-Befehlsdatei weiter unten eingefügt.
Ich habe dann eine globale Variable uint32_t configin meiner Datei erstellt, um #pragma DATA_SECTION(config, ".boot_cfg");sie mit diesem Abschnitt zu verknüpfen, aber ich bin mir nicht sicher, ob ich das richtig gemacht habe. Danach habe ich versucht, den Wert zu lesen, dann das System zu ändern und dann zurückzusetzen, um zu sehen, ob es funktioniert hat. Dies ist der Code, den ich verwendet habe:

    if (config == 0) {
        GPIO_write(CONFIG_GPIO_LED_RED, CONFIG_GPIO_LED_ON);
        sleep(2);
        config = 2;
        SysCtrlSystemReset();
    } else {
        GPIO_write(CONFIG_GPIO_LED_GREEN, CONFIG_GPIO_LED_ON);
        while(1) {}
    }

Wenn ich nur den Bootloader flashe, damit ich das Gerät nicht mit dem Debugger zurücksetzen muss, weil ich denke, dass es die Stromversorgung zurücksetzen könnte, sehe ich das rote Licht aufleuchten, dann sollte sich der Wert ändern und das System sollte zurückgesetzt werden und dann würde ich hoffentlich sehen Das grüne Licht geht an, aber keines der Lichter geht an. Daher denke ich, dass die SysCtrlSystemReset()Funktion wahrscheinlich nicht funktioniert.
Also habe ich versucht, Schritt für Schritt durch den Bootloader zu gehen, dann config = 2;habe ich nach der Anweisung die CPU-Reset-Taste in der IDE verwendet, um zum Anfang zurückzukehren, aber wenn ich dann zur if-Anweisung zurückkomme, ist der Wert immer noch 0.

Linker-Befehlsdatei:

--stack_size=1024   /* C stack is also used for ISR stack */

--heap_size=256

/* Retain interrupt vector table variable                                    */
--retain=g_pfnVectors
/* Override default entry point.                                             */
--entry_point ResetISR
/* Allow main() to take args                                                 */
--args 0x8
/* Suppress warnings and errors:                                             */
/* - 10063: Warning about entry point not being _c_int00                     */
/* - 16011, 16012: 8-byte alignment errors. Observed when linking in object  */
/*   files compiled using Keil (ARM compiler)                                */
--diag_suppress=10063,16011,16012

#define FLASH_BASE              0x0
#define FLASH_SIZE              0x58000
#define RAM_BASE                0x20000000
#define RAM_CFG_BASE            0x20013FFC
#define RAM_CFG_SIZE            0x4 // reserve 4 bytes of ram for bootloader config
#define RAM_SIZE                0x14000 - RAM_CFG_SIZE
#define GPRAM_BASE              0x11000000
#define GPRAM_SIZE              0x2000

#define FLASH_START             FLASH_BASE

#define PAGE_SIZE               0x2000

#define BOOT_BASE               0x50000
#define BOOT_END                FLASH_CCFG_START - 1
#define BOOT_SIZE               ((BOOT_END) - (BOOT_BASE) + 1)

#define APP_BASE                0x0
#define APP_END                 BOOT_BASE
#define APP_SIZE                BOOT_BASE

#define FLASH_CCFG_START        0x00057FA8
#define FLASH_CCFG_END          (FLASH_START + FLASH_SIZE - 1)
#define FLASH_CCFG_SIZE         ((FLASH_CCFG_END) - (FLASH_CCFG_START) + 1)

/* System memory map */

MEMORY
{

    BOOT (RWX)  : origin = BOOT_BASE, length = BOOT_SIZE

    APP (RWX) : origin = APP_BASE, length = APP_SIZE

    FLASH_CCFG (RX) : origin = FLASH_CCFG_START, length = FLASH_CCFG_SIZE

    /* Application uses internal RAM for data */
    SRAM (RWX) : origin = RAM_BASE, length = RAM_SIZE
    RAM_CFG (RWX) : origin = RAM_CFG_BASE, length = RAM_CFG_SIZE
    /* Application can use GPRAM region as RAM if cache is disabled in the CCFG
    (DEFAULT_CCFG_SIZE_AND_DIS_FLAGS.SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM = 0) */
    GPRAM (RWX): origin = GPRAM_BASE, length = GPRAM_SIZE
}



/* Create global constant that points to top of stack */
/* CCS: Change stack size under Project Properties    */
__STACK_TOP = __stack + __STACK_SIZE;


/* Section allocation in memory */

SECTIONS
{
    .intvecs        :   > BOOT_BASE
    .text           :   > BOOT
    .TI.ramfunc     : {} load=BOOT, run=SRAM, table(BINIT)
    .const          :   > BOOT
    .constdata      :   > BOOT
    .rodata         :   > BOOT
    .binit          :   > BOOT
    .cinit          :   > BOOT
    .pinit          :   > BOOT
    .init_array     :   > BOOT
    .emb_text       :   > BOOT
    .ccfg           :   > FLASH_CCFG (HIGH)

    .boot_cfg       :   > RAM_CFG
    .vtable         :   > SRAM
    .vtable_ram     :   > SRAM
     vtable_ram     :   > SRAM
    .data           :   > SRAM
    .bss            :   > SRAM
    .sysmem         :   > SRAM
    .stack          :   > SRAM (HIGH)
    .nonretenvar    :   > SRAM

    .gpram          :   > GPRAM

}
Verwenden Sie ein separates serielles EEPROM?
@DaveTweed Wir haben ein EEPROM, das über SPI verbunden ist, aber ich bin mir nicht sicher, wie ich damit kommunizieren soll, ohne die Texas Instruments API zu verwenden, die die Initialisierung erfordert, von der ich glaube, dass sie die Sprungprobleme verursacht.
@VincentKen Finde es heraus. Schnellste und einfachste Route.
Die API initialisiert möglicherweise die SPI-Schnittstelle (neu) – was kein Problem sein sollte – aber wenn sie den Inhalt Ihres EEPROM ändert, ist sie ernsthaft kaputt!
Es sei denn, die Variable, die Sie speichern möchten, ist nur, ob Sie einen Softboot durchgeführt haben oder nicht. Es sollte ein Flag geben, das dies überwachen kann, möglicherweise in Ihrem Watchdog.
@DaveTweed Die Verwendung von erfordert zuerst den Aufruf SPI_init()und dann SPI_open()das Öffnen einer bestimmten Schnittstelle. Ich kann den ersten vor jeder anderen Initialisierung aufrufen, aber wenn ich SPI_open()vor dem Aufruf aufrufe Board_init(), lande ich im FaultISR. Ich glaube, dass dies Board_init()all diese Probleme verursacht, also würde ich gerne springen, bevor ich es anrufe.
Ich verstehe nicht. Unter welchen Bedingungen würden Sie nicht anrufen, Board_init()bevor Sie etwas anderes tun? Wenn es etwas tut, was Sie nicht wollen, dann müssen Sie es ändern.
@DaveTweed Board_init()wird automatisch von Code Composer Studio basierend auf Definitionen in einer .syscfg-Datei generiert. Die Anwendung hat auch eine eigene Board_init()Methode, sodass sie zuerst im Bootloader und dann erneut in der Anwendung aufgerufen wird. Durch das Entfernen aus der Anwendung konnte ich nach dem Springen mit dem Debugger einige zusätzliche Schritte ausführen. Es ist natürlich irgendwann kaputt gegangen, weil die Board_init()Funktion aufgerufen werden muss, bevor APIs verwendet werden können.

Antworten (1)

Sie können einen benutzerdefinierten Abschnitt im RAM erstellen, der beim Start nicht initialisiert wird, sodass die Werte vor dem Soft-Reset oder Sprung danach erhalten bleiben. Dies funktioniert nur bei Soft-Resets und Sprüngen. Wenn die Stromversorgung unterbrochen wird (Hard-Reset), werden die Werte in diesem Abschnitt nicht initialisiert.

Ihr System verfügt über Startcode, der die C-Laufzeitumgebung vor dem Aufruf von main einrichtet. Ihr Projekt verwendet möglicherweise den von Ihrer Toolchain bereitgestellten Standard-Startcode. Der standardmäßige Startcode initialisiert wahrscheinlich den .bss-Abschnitt auf Null und initialisiert die Variablen im .data-Abschnitt durch Kopieren aus dem .rodata-Abschnitt. Wenn Ihr Bootloader und Ihre Anwendung als separate Projekte kompiliert werden, haben sie jeweils einen eigenen Startcode, .bss-, .data- und .rodata-Abschnitte.

Ihr Projekt verwendet möglicherweise auch das von Ihrer Toolchain bereitgestellte Standard-Linker-Skript. Das standardmäßige Linker-Skript stellt Speicherabschnitte bereit, einschließlich .bss, .data und .rodata. Sie können das Linker-Skript für Ihre Projekte anpassen und einen benutzerdefinierten Abschnitt mit dem Namen .myspecialsection hinzufügen. Sie können .myspecialsection an einer festen Adresse und Größe finden, sodass sowohl der Bootloader als auch die Anwendung denselben Speicher für diesen Abschnitt verwenden. Sie können es so machen, dass .myspecialsection nicht durch den Startcode initialisiert wird. Und Sie können Variablen statisch zuweisen und sie in .myspecialsection finden. Jetzt kann der Bootloader oder die Anwendung einen Wert in .myspecialsection schreiben und einen Soft-Reset durchführen oder zu dem anderen Programm springen, und die Werte in .myspecialsection behalten ihre Werte bei.

Lesen Sie das Linker-Handbuch, um Einzelheiten darüber zu erfahren, wie Sie einen Abschnitt erstellen, wie Sie ihn finden und seine Größe festlegen, wie Sie ihn nicht initialisieren und wie Sie Variablen in dem Abschnitt finden.

Erwägen Sie auch die Verwendung einer Prüfsumme für die Daten in .myspecialsection, um zu vermeiden, dass die Daten verwendet werden, wenn sie nicht initialisiert sind.

Ich habe meiner Frage eine Bearbeitung hinzugefügt, um die Ergebnisse anzuzeigen, die ich beim Versuch erzielt habe. Ich bin mir nicht sicher, ob ich es richtig gemacht habe
Überprüfen Sie die Adresse der configVariablen im Debugger. Ist die Adresse im Abschnitt .boot_cfg wie erwartet? Überprüfen Sie auch, ob der Startcode den Abschnitt .boot_cfg initialisiert. Benötigen Sie irgendwo im Linker-Skript eine Art "NOINIT"-Modifikator?
Das Hinzufügen TYPE=NOINITzur Abschnittsdefinition scheint es behoben zu haben. Aber als ich nach Möglichkeiten suchte, einen Soft-Reset einzuleiten, stieß ich auf das, HAL_SYSTEM_RESET()das in seinen Kommentaren besagt, dass das CC26xx-System nicht für Soft-Resets ausgelegt ist und Soft-Resets das System instabil machen und stattdessen Hard-Resets verwenden.