Temporizador STM32 SysTick: no ejecuta el evento de alarma

Estoy intentando escribir una interrupción para mantener el tiempo básico medido en tics en un STM32F407VGT (Discovery Board).

Mi interrupción parece ejecutarse una vez (aunque no puedo estar seguro), pero luego falla/bloquea el procesador por completo. Todas las salidas de UART se detienen...

El código es esencialmente el mismo que el de "La guía definitiva del ARM Cortex-M3, 2.ª ed." (Joseph Yiu, Newnes.) ¿Podría ser un problema el hecho de que el STM32F4xx sea un ARM Cortex-M4? Si es así, ¿cómo soluciono esto? Inicialmente intenté usar TMR2, con problemas similares, lo que me hace pensar que es algo que me estoy perdiendo. También intenté usar SysTickConfig, con los mismos problemas.

uint32_t ticks;

void SysTickAlarm(void)
{
    SysTick->CTRL = 0;
    SCB->ICSR = SCB->ICSR & 0xFDFFFFFF;
    ticks++;
    return;
}

/*
 * Initialise system timer
 */
void tick_init(void)
{
    ticks = 0;
    *((volatile unsigned int*)(SCB->VTOR + (15 << 2))) = (unsigned int) SysTickAlarm;

    SysTick->CTRL = 0;                  // Disable SysTick
    SysTick->LOAD = TICK_DELAY;         // Delay for 10 ms
    SysTick->VAL = 0;                   // Clear current value to 0
    SysTick->CTRL = 0x7;                // Enable SysTick+exception and use processor clock
}

/*
 * get_ticks: Get the number of ticks since processor initialisation.
 */
uint32_t get_ticks()
{
    return ticks;
}

main() llama a tick_init() y luego comienza a escupir printf("ticks=%d\n", get_ticks());en un bucle. Pero se detiene con bastante rapidez, obtengo alrededor de 10 líneas antes de que se bloquee.

Soy muy nuevo en los procesadores ARM, por lo que probablemente sea algo muy simple, pero no puedo verlo.

¿Tiene una cadena de herramientas con depuración? También recomendaría usar la Biblioteca de periféricos estándar de ST para su dispositivo, hace que sea mucho más simple configurar el dispositivo que meter cosas en los registros.
Tendré que comprobar si tengo un depurador disponible. Estoy trabajando en una pasantía y no se nos permite usar la biblioteca de periféricos de ST porque es incompatible con muchas cosas. (Yo no elegí esto.)
¿Es su SysTickAlarm() en realidad un controlador de sysstick? Verifique dos veces su tabla de vectores.
Creo que es *((volatile unsigned int*)(SCB->VTOR + (15 << 2))) = (unsigned int) SysTickAlarm;para eso, pero no estoy seguro.
¿Dónde está tu código, está en RAM? ¿Ha reasignado la RAM a la dirección 0x0? De forma predeterminada, este tipo de asignación intentará modificar el flash que no está permitido. Si su código está en la RAM pero no ha reasignado la RAM a la dirección 0x0, debe establecer el desplazamiento de la tabla de vectores antes de hacer nada con las interrupciones. Publique un ejemplo mínimo completo con script de vinculación (preferiblemente también mínimo pero funcional) y código de inicio.
El código está en flash hasta donde yo sé. Tendré que comprobar si está bien publicar los scripts del enlazador, ya que probablemente sean propiedad de la empresa...

Respuestas (1)

Se ha creado un gran problema al deshacerse de la biblioteca de periféricos estándar (SPL). Este microcontrolador es bastante difícil de aprender con él, y mucho menos sin él. La biblioteca puede tener un diseño horrible, pero tiene la ventaja de que realmente funciona. Le sugiero que primero obtenga un programa de prueba simple que funcione con SPL, luego vuelva a implementar gradualmente su funcionalidad si realmente no puede usarlo (sin embargo, todavía tengo que ver una razón técnica para eso).

Para usar una interrupción en un Cortex-M3/M4, necesita lo siguiente:

  • un montón. El núcleo guarda automáticamente varios registros en la pila cuando se dispara una interrupción. El valor del puntero de pila inicial se lee desde la dirección 0x0 lo primero que se hace cuando se inicia el núcleo. Ese valor normalmente debería ser igual al final de RAM + 1.
  • desplazamiento correcto de la tabla de vectores en el SCB->VTORregistro. Por defecto es 0, que (de nuevo por defecto) es el inicio de flash. Si la combinación de código de inicio/secuencia de comandos del enlazador configura la tabla de vectores correctamente, genial. El SPL hace eso. Parece que no lo haces (a menos que esté en el código que no publicaste). Mire cómo se hace en la biblioteca de periféricos estándar ( startup_stm32f4xx.sy el script de enlace correspondiente para una cadena de herramientas basada en gcc).
  • un manejador tienes eso.
  • la dirección de ese controlador + 1 debe estar en la posición correcta en la tabla de vectores. La forma en que está asignando un valor a una ubicación en la tabla de vectores (supuesta) no funcionará de forma predeterminada, ya que está en flash. Me hace pensar que ha reasignado su RAM para comenzar en la dirección 0, pero no hay nada en su pregunta que lo indique.
  • la interrupción debe estar habilitada en el NVIC y su prioridad establecida. No veo eso en tu código en absoluto. Esto utiliza NVIC->ISER[x]y NVIC->IPR[x]registra. Ver implementación de NVIC_Init()en la SPL y PM00214 sección 4.3.
  • finalmente, el periférico debe configurarse para generar solicitudes de interrupción reales. Parece que estás haciendo eso.

Si después de verificar todo eso todavía no puede hacer que funcione, lo mejor sería publicar un proyecto completo (pero mínimo) para analizar.

He encontrado el SPL más un obstáculo que una ayuda, tal vez porque no he encontrado la documentación adecuada para ello. Usando la hoja de datos STM y los encabezados proporcionados, puedo saber que la forma de configurar el puerto de E/S B0 en E/S normal es configurar el bit 0 y borrar el bit 1 de GPIOB_MODER, y el código C para eso es ( rmw_mask32(&GPIOB->MODER, 3, 1);usando mi rmw_mask32rutina para realizar GPIOB->MODER = (GPIOB->MODER & ~3) | 1, pero usando LDREX/STREX para ser a prueba de interrupciones). ¿Hay alguna buena documentación de fácil búsqueda que diga cómo usar el SPL para tal propósito?
No es mi decisión no usar el SPL. Mi empleador lo requiere. Descubrí el problema con mi solución de temporizador original: no había configurado el reloj correctamente, ahora funciona bien.