Acceso a registros stm32 a través de estructuras en GCC

Estoy tratando de hacer rodar mi propio dispositivo USB usando una placa de pastilla azul STM32F103 en C con el compilador arm-none-eabi de GCC, pero me encontré con un comportamiento extraño que estoy tratando de entender.

Creé una estructura que modela las tablas de descriptores de búfer de punto final asociadas con el periférico USB:

typedef struct _EP_BUF_DSCR {
    uint32_t ADDR_TX;
    uint32_t COUNT_TX;
    uint32_t ADDR_RX;
    uint32_t COUNT_RX;
} EP_BUF_DSCR;

Y creé un puntero a una de estas estructuras y lo configuré para abordar el comienzo del área de memoria de paquetes USB de la MCU:

EP_BUF_DSCR *EP0_DSCR = (EP_BUF_DSCR *) 0x40006000;

Así que ahora puedo configurar los campos ADDR_TX y ADDR_RX simplemente eliminando la referencia de los campos apropiados y asignándoles valores. Sin embargo, cuando trato de hacer esto con el campo COUNT_RX, parece no tener efecto:

// debugger memory view shows register as set to 0 after running this:
EP0_DSCR->COUNT_RX = 0x8400;  

Pero puedo cambiar el valor en este registro creando un puntero directamente a él y desreferenciandolo:

// debugger memory view shows register set to correct value after running:
*((uint32_t *)(0x40006000 + 12)) = 0x8400;  

¿Alguien podría proporcionar alguna idea de por qué sucede esto? ¿He hecho algo tonto o estoy confiando involuntariamente en el comportamiento del compilador no documentado?

Respuestas (2)

Por lo general, los registros de funciones especiales de hardware asignados a la memoria deben declararse volátiles para que el hardware sepa que algo puede necesitar o alterar su estado más allá del hilo local de ejecución; de lo contrario, es libre de almacenar en caché los valores en los registros o incluso olvidarse de calcular los aparentemente no utilizados. .

En el STM32, ciertos SFR también pueden estar restringidos a un ancho de acceso particular.

Si lo que intenta hacer es legítimo, es probable que ya exista una definición adecuada en el controlador del proveedor (o alternativo) o en los archivos HAL. E incluso si no lo hay, puede aprender mucho al leerlos para ver cómo se declara algo simple como un bloque GPIO.

Por supuesto, también es posible que haya algo específicamente incorrecto con la operación particular que está tratando de realizar; por ejemplo, es posible que no se pueda escribir, o que solo se pueda escribir de manera significativa cuando el hardware está en un estado particular, en el que el depurador tiene suerte. logrando mientras que su programa por lo general no lo hace.

¡Ay! Declararlo volátil parece haber hecho el truco. ¡Gracias!
@ user3513491 ¿por qué no usar CMSIS? Ya lo ha declarado todo. No hay necesidad de reinventar la rueda. Ninguno CMSIS no es HAL, por lo que puede hacer su programación de registro simple.

Hay un par de cosas que podrían estar pasando aquí:

  1. Como señaló Chris Stratton, debe declarar EP0_DSCR(y cualquier otro puntero a los registros de hardware) como volatile.

  2. ¿Tiene APB1 y el periférico USB encendidos y sincronizados correctamente? Es posible que los registros no respondan correctamente si los periféricos no están funcionando. En particular, el periférico USB debe sincronizarse a 48 MHz y APB1 debe sincronizarse a ≥8 MHz.

  3. ¿Tienes habilitado el periférico CAN? Tanto CAN como USB comparten un búfer de memoria, por lo que no puede usar ambos al mismo tiempo.