Tengo un software que se ejecuta en un STM32F103 (también me gustaría poder usar el F4) donde me gustaría poder "marcar la hora" de eventos (como interrupciones en pines externos) al microsegundo más cercano (si no es mejor).
Como el software se ejecutará por algún tiempo, quiero usar un temporizador de 64 bits, y para mí tenía sentido usar el temporizador SysTick incorporado. He creado SysTickMajor (un contador de 64 bits) que se incrementa cada vez que SysTick se desborda, y una función getSystemTime que combina este contador y el valor actual de SysTick para obtener un tiempo preciso de 64 bits:
#define SYSTICK_RANGE 0x1000000 // 24 bit
volatile long long SysTickMajor = SYSTICK_RANGE;
voif onInit(void) {
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
SysTick_Config(SYSTICK_RANGE-1);
/* Super priority for SysTick - is done over absolutely everything. */
NVIC_InitStructure.NVIC_IRQChannel = SysTick_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void SysTick_Handler(void) {
SysTickMajor+=SYSTICK_RANGE;
}
long long getSystemTime() {
long long t1 = SysTickMajor;
long long time = (long long)SysTick->VAL;
long long t2 = SysTickMajor;
// times are different and systick has rolled over while reading
if (t1!=t2 && time > (SYSTICK_RANGE>>1))
return t2 - time;
return t1-time;
}
El único problema es que esto no parece ser 100% preciso, por ejemplo, si hago lo siguiente:
bool toggle = false;
while (true) {
toggle = !toggle;
LED1.write(toggle);
long long t = getSystemTime()() + 1000000;
while (getSystemTime() < t);
}
No obtendré una buena onda cuadrada; de vez en cuando se producen fallos (aunque la interrupción de SysTick se termina muy rápidamente), porque en los puntos el tiempo devuelto por getSystemTime es completamente incorrecto.
ACTUALIZACIÓN: cuando esto sucede (estoy registrando el valor de tiempo en una interrupción activada por un evento externo) vuelco los últimos 3 valores a la serie. Repetidamente me sale cosas como:
>0x278-E2281A
0x278-E9999A
0x278-0001E5
>0x5BE-F11F51
0x5BE-F89038
0x5BE-0000FB
He pegado guiones para representar qué bits provienen de SysTick.
Ahora solo para evitar dudas, he usado el código de starblue a continuación. También lo he cambiado para usar un valor de 32 bits para SysTickMajor, y he eliminado a SysTick- > VAL, por si acaso.
¡Parece que el SysTick_Handler no está adelantando a la otra interrupción!
Ahora verifiqué esto y tenía las prioridades de Preemption erróneas anteriormente, pero ahora establezco NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
y luego configuro las interrupciones: la prioridad de preferencia de SysTick 0, y la otra interrupción es 7.
¿Hay algo más que deba hacer para que la preferencia funcione?
ACTUALIZACIÓN2: creé un pequeño caso de prueba para este problema exacto (SysTick no sustituye a otras interrupciones) y lo he puesto aquí:
STM32 Problemas de prioridad de interrupción (preferencia)
Entonces, ¿qué hay de malo con el código de arriba? ¿Hay una mejor manera de obtener un buen temporizador de alta resolución? Parece algo obvio que la gente querría hacer pero estoy luchando por encontrar ejemplos.