Creando una tabla de búsqueda programable en STM32

7

Estoy intentando definir una tabla de consulta que no es una constante en un STM32F103. Básicamente, quiero tener una página de flash que actúe como una tabla de búsqueda constante en el funcionamiento normal, pero de vez en cuando (pienso en días diferentes) quiero poder borrar esa tabla y escribir una nueva en la página de flash . Creo que entiendo cómo usar la HAL para las funciones para hacer lo que necesito en mi programa, pero parece que necesitaría declarar este bloque de memoria en el archivo del vinculador y estoy teniendo muchos problemas para encontrar un ejemplo que cubre eso. El mem.ld predeterminado de CubeMX se ve así:

MEMORY
{
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
  RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
}

Parece que necesito un bloque de secciones de salida después de eso, donde confío una palabra clave a > FLASH, pero no sé qué palabra clave sería o cómo declararía la tabla en el código para darle la dirección correcta .

He visto la nota de la aplicación EEPROM emulada, pero parece una sobrecarga adicional para la memoria que no verá suficientes ciclos de borrado / escritura para preocuparse por la vida útil de la memoria. ¡Ayuda!

    
pregunta Brett K Smith

4 respuestas

7

Nota: la vinculación no es una parte de los estándares del lenguaje C, por lo que cada compilador implementa los archivos del enlazador de manera diferente. Parece que estás usando GCC, así que compartiré un código que funciona con él.

linker.ld:

MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 8K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 31K
/*(1)*/ FIXED_DATA(rw)  : ORIGIN = 0x8007C00, LENGTH = 1K /* your LUT here*/
}
............
.FIXED_DATA (NOLOAD):/*(2)*/
  {
    *(STATIC_DATA);/*(3)*/
  } >FIXED_DATA /*(4)*/

En C:

__attribute__((section("STATIC_DATA"))) 
             const static volatile statistic_static_data_t staticData;

Lo que esto hace:

(1) crea un área llamada FIXED_DATA de la ubicación y el tamaño dados. Tenga en cuenta que mi código es de un dispositivo diferente. Verifique su hoja de datos para ver qué tamaño tienen sus bloques (¡es posible que ni siquiera tengan el mismo tamaño dentro de un dispositivo!). Asegúrate de reducir el tamaño de FLASH en consecuencia, de lo contrario obtendrás un error acerca de que no encajan en la memoria.

(2) crea una sección llamada FIXED_DATA. En realidad, no tiene que llamarse FIXED_DATA, pero ayuda a realizar un seguimiento. El atributo NOLOAD le dice al enlazador que no llene el área (ver más abajo).

(3) ponga todas las variables marcadas DATOS ESTÁTICOS en esta sección de memoria

(4) coloque todo lo que está en esta sección en el área denominada FIXED_DATA que creamos muy arriba

En el archivo C, solo tiene que etiquetar las variables que desea colocar en el área. Recomiendo llamarlos const porque generalmente no quieres escribir en flash directamente. Volatile ayuda con las optimizaciones del compilador que asumen que los datos const nunca cambian.

¿Por qué pasar por todo este problema, en lugar de usar las soluciones mucho más simples en otras respuestas? Porque es actualizable . Si, en el futuro, desea hacer una actualización de FW, es posible que desee mantener los datos almacenados en la memoria. La instrucción NOLOAD en el archivo del vinculador hace exactamente eso: el vinculador no llena los datos con 0, como haría normalmente si tuviera una variable global viviendo allí.

Puede encontrar más información sobre las formas arcanas del enlazador si busca "sintaxis de ld"

    
respondido por el Makotanist
4

Esto no es tan complicado de hacer.

Básicamente, lo que necesitas es una variable global constante que se colocará en flash y definirá la ubicación utilizando un pragma.

En C ++, el encabezado podría verse así:

class FlashLookUpTable
{
    public:
    struct LookUpTable_t
    {
        uint32_t table[100];
    };

    public:
    static LookUpTable_t const * GetLookUpTablePointer();

    private:
    static const uint32_t FLASH_PAGE_SIZE = 1024U; // or whatever the flash smallest deletable size is
    // This variable contains the number of bytes needed to store the structure in complete flash pages
    static const uint32_t ARRAY_SIZE = (sizeof(LookUpTable_t)/FLASH_PAGE_SIZE) + FLASH_PAGE_SIZE;

    union FlashPageSizedStructure
    {
        LookUpTable_t t;
        uint8_t flashpage[ARRAY_SIZE];
    }

    static const FlashPageSizedStructure tableInFlash;

};

Y así es como se ve la implementación:

// the exact pragma depends on the compiler used, this one works for IAR
// the location should be at the start of a page boundary, especially when using this union approach
#pragma location=0x800FC00U
const FlashLookUpTable::FlashPageSizedStructure FlashLookUpTable::tableInFlash = 
{
    // initialize values here
}

FlashLookUpTable::LookUpTable_t const * FlashLookUpTable::GetLookUpTablePointer(void) const 
{
    return &tableInFlash.t;
}

Para escribir esa página en flash, necesita un búfer (ya sea en RAM o en flash) con el mismo tamaño de una página de flash porque tiene que borrar la página antes de volver a escribirla, por lo que puede cambiar la ubicación de una página. un solo valor no es posible.

Dependiendo de cómo lo use exactamente, es posible que deba declarar la estructura como volatile . Esto sucede especialmente si accede directamente a la tabla (no con un puntero como en este caso).

Algunos compiladores optimizan el código de tal manera, que eliminan la constante de la tabla directamente en el código. El resultado es que, si cambia el valor en su tabla, el valor no se tiene en cuenta en el código.

El compilador IAR tuvo algunos problemas (está arreglado en la versión actual) al manejar static volatile const , así que cambié a usar un puntero.

Si desea cambiar los valores, necesita algún tipo de algoritmo flash.

El flash de escritura siempre consiste en:

  1. contenido de la página de copia de seguridad
  2. Actualizar copia de seguridad con valores modificados
  3. Borrar página
  4. escribir página
  5. por seguridad: compare la página escrita con la copia de seguridad

Nota para los avanzados: En algunos casos, puede explotar que puede escribir ceros en las posiciones donde se encontraba uno, por lo que podría cambiar un 0x7F a 0x3F, pero no al revés. En ese caso no es necesario hacer un borrado de página. Algunos controladores podrían no admitir esto.

    
respondido por el Arsenal
3

Sí, sería más limpio declarar una sección para su tabla.

Pero la forma más sencilla es reducir la sección de FLASH según el tamaño de la página y luego:

int *table = (int *) (0x0800000 +0x20000 - PAGE_SIZE)

Aquí el "0x20000" es el tamaño de la memoria flash. En / mi / código utilizo un define MY_FLASH_SIZE definido como:     #define MY_FLASH_SIZE ((* (short *) 0x1ffff7cc) < < 10) que funciona para la serie F0, quizás la tuya también. He pasado de dispositivos de 128k en desarrollo (solo unos cuantos más caros que los de 64k) a dispositivos de 64k en producción sin darme cuenta que esto cambió la ubicación del área de flash ... :-)

    
respondido por el rew
1

Si tiene flash de sobra, puede declarar una variable constante global el doble del tamaño de una página de flash y redondear su dirección de inicio hasta un múltiplo del tamaño de la página de flash. Un poco inútil, pero no hay necesidad de hacer magia de código de enlace.

    
respondido por el Wouter van Ooijen

Lea otras preguntas en las etiquetas