Consejos de diseño para operaciones asíncronas que requieren un retraso al usar STM32F7

0

Se necesita un consejo n00b. Estoy tratando de encontrar el mejor método para retrasar una operación de valor relativamente pequeño (10-5000 microsegundos) sin ralentizar el ciclo de ejecución principal. Supongo que estoy malinterpretando algo. Mi enfoque hasta ahora ha sido el siguiente:

Núcleo de 216 MHz (F767)

  • Cree TIM2 con Prescaler = 0 y 216 * 10 ^ 6 período / ARR para obtener un contador de alta resolución.
  • La interrupción recibe un evento de señal externa (120 Hz).
  • Realice cualquier GPIO y tiempo dentro del controlador de interrupciones (también se intentó lanzar un temporizador y hacerlo allí; no hace una diferencia).

Debido a que necesito demoras de resolución de microsegundos, estoy haciendo algo como:

startTime = __HAL_TIM_GetCounter(&htim2);
while(GetMicros(__HAL_TIM_GetCounter(&htim2) - startTime) < delayMicros);

uint32_t GetMicros(uint32_t counterValue)
{
    return counterValue / (108 / 1000 / 1000); // (216 MHz with APB2/2 = 108)
}

Pero lo que he notado es que cuanto mayor es la demora, más se ralentiza el bucle while de mi programa (el LED parpadea mucho más lento cuanto más larga es la demora):

while(1){
  HAL_Delay(50);
  HAL_GPIO_TogglePin(GPIOB, Green_Pin);
}

¿Hay una mejor estructura para realizar demoras precisas? ¿O estoy haciendo algo realmente simple, realmente mal?

    
pregunta Michael Brown

1 respuesta

2

Ha sido difícil encontrar un ejemplo completo para implementar esto correctamente, pero la siguiente debería ser la forma correcta de hacerlo (usando EXTI0 para interrumpir, usando TIM2 para demora):

// 216Mhz clock
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0; // PSC
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 216000000-1; // ARR
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
...
void TIM2_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim2);
}
void EXTI3_IRQHandler(void)
{
    __HAL_TIM_SetAutoreload(&htim2, (216.0 * delayInMicroseconds) - 1);
    __HAL_TIM_SetCounter(&htim2, 0);
    HAL_TIM_Base_Start_IT(&htim2);
}
// this is called internally, you just need to define it
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        HAL_TIM_Base_Stop_IT(&htim2);
        HAL_GPIO_WritePin(Triac_Out_GPIO_Port, Triac_Out_Pin, GPIO_PIN_SET);
    }
}

Parece que no puedes modificar dinámicamente con frecuencia htim2.Init.Period , debes llamar a SetAutoreload , lo que cambia el Period para ti. Intenté alternativamente llamar a HAL_TIM_Base_Init() después de cambiar el período, pero introduce un retraso significativo, por lo que SetAutoreload es el camino a seguir. Después de llamarlo, también debe restablecer el contador del temporizador a 0. En la devolución de llamada, debe detener el temporizador para evitar que se active en cada intervalo.

Nota importante : en mi experimento particular, ya que estaba usando TIM2 y TIM3, noté que TIM3 se dispara a una velocidad increíblemente rápida en comparación con TIM2 con la misma configuración (en STM32F76xx). Me tomó un tiempo descubrir la razón, pero es porque TIM2 es un temporizador de 32 bits y TIM3 es un temporizador de 16 bits. En mi opinión, ellos (ST) deberían codificar por colores o mostrar la resolución en el software STM32CubeMX, ya que no era evidente en absoluto qué temporizadores eran diferentes. Solo lo descubrí al ingresar valores grandes de Periodo (ARR) en la interfaz de usuario y no me permitió establecer el mismo valor en TIM3, y luego descubrí esto como simplemente una nota al pie en la ST Hoja de datos del temporizador STM32 . Cambiar a usar TIM5 (el único otro temporizador de 32 bits) solucionó mi problema, ¡perdí un día por esto!

    
respondido por el Michael Brown

Lea otras preguntas en las etiquetas