Temporizador1 en stm32f4

Estoy probando stm32 después de mucho tiempo programando en avr, y parece que estoy luchando con el temporizador. Quiero usar el temporizador para manejar congelamientos:

 HAL_TIM_Base_Start_IT(&htim1);
while ((GPIOA->IDR  & GPIO_PIN_3) == 0x08) 
{
        Clk_h
    DWT_Delay(200);
    Clk_l
    DWT_Delay(200);
}
 HAL_TIM_Base_Stop_IT(&htim1);

En el código de arriba estoy esperando cuando GPIO_PIN_3 estará en un estado bajo. La cuestión es que es posible que permanezca en estado alto para siempre, por lo que quiero iniciar el temporizador 1 y después de 500 ms debería activar la interrupción. El problema es que mientras el bucle atrapa 0, tomó alrededor de 100 us, y mi temporizador se configuró con:

static void MX_TIM1_Init(void)
{

  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 16799;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 4999;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

así que es mucho más que 100us, pero lo que sea que dispare una vez, no sé por qué. Según tengo entendido, el temporizador, después de iniciarlo, debería comenzar a contar y cuando se alcance el valor, activará la interrupción. Pero aquí, incluso si inmediatamente inicio y detengo el temporizador:

  HAL_TIM_Base_Start_IT(&htim1);
 HAL_TIM_Base_Stop_IT(&htim1);

se activará una vez de todos modos. Lo que necesito es activar la función de interrupción solo cuando se produce un desbordamiento en el registro de conteo. Aquí está mi función de desbordamiento, se encuentra en stm32f4xx_it.c:

void TIM1_UP_TIM10_IRQHandler(void)
{
  /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 0 */

  /* USER CODE END TIM1_UP_TIM10_IRQn 0 */
  HAL_TIM_IRQHandler(&htim1);
  /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 1 */
    sprintf(str123,"<<FAIL>>");
    CDC_Transmit_FS((uint8_t*)str123,strlen(str123));
  /* USER CODE END TIM1_UP_TIM10_IRQn 1 */
}
¿Qué significan "Clk_h" y "Clk_l"?
@LongPham son solo algunas macros: #define Clk_h HAL_GPIO_WritePin (GPIOA, GPIO_PIN_5, GPIO_PIN_SET); #define Clk_l HAL_GPIO_WritePin (GPIOA, GPIO_PIN_5, GPIO_PIN_RESET)
No uso HAL, pero los registros del temporizador 1 están precargados, por lo que debe emitir un evento de actualización para cargar nuevos valores en ellos. En algún lugar de la HAL (¿quizás Base_Init?) estará haciendo esto, lo que dará como resultado que se establezca el indicador de interrupción de actualización. Es por eso que el temporizador se activa cuando habilita las interrupciones. Solo necesita borrar el UIF en el registro SR (escriba 1 en él; hay una función HAL en algún lugar para hacer esto) antes de iniciar el temporizador/habilitar interrupciones.
Además, si viene de AVR, le recomendaría no usar HAL. El temporizador es bastante sencillo de usar, el HAL, no tanto.
@Jon ¿Podría ser tan amable de mostrarme un ejemplo del uso de registros para manejar un temporizador como respuesta? Quiero probarlo.

Respuestas (3)

Realmente no me gusta el HAL, así que aquí le mostramos cómo hacer lo que quiere simplemente accediendo directamente al periférico del temporizador:

// SETUP STUFF:

// Enable the timer clock. I use the HAL for this
// as it adds the required startup delay. The code
// is pretty simple though.
__HAL_RCC_TIM1_CLK_ENABLE();

// Reset the control register. This gives us the 
// default operation which is counting up with no
// divider.
TIM1->CR1 = 0;

// Set prescaler
TIM1->PSC = 16799;

// Will generate interrupt when this value is reached
TIM1->ARR = 4999;

// The PSC and ARR values are currently in the preload
// registers. To load them into the active registers we
// need an update event. We can do this manually as
// follows (or we could wait for the timer to expire).
TIM1->EGR |= TIM_EGR_UG;

// Timer is now ready to use.

// POLLING OPERATION:

// Next we setup the interrupts. We should first clear
// the update interrupt flag in case it has already been
// set.
TIM1->SR = ~TIM_SR_UIF;

// Then we can enable the update interrupt source
TIM1->DIER |= TIM_DIER_UIE;

// Note: we also need to setup the interrupt channel on
// the NVIC. Once that is done the isr will fire
// when the timer reaches 5000.

// We can now start the timer running...
TIM1->CR1 |= TIM_CR_CEN;

while ((GPIOA->IDR  & GPIO_PIN_3) == 0x08) 
{
    Clk_h
    DWT_Delay(200);
    Clk_l
    DWT_Delay(200);
}

// ...and stop the timer when we're done
TIM1->CR1 &= ~TIM_CR_CEN;

// Note if we want to repeat the polling loop again we should
// issue another TIM1->EGR |= TIM_EGR_UG event as this
// resets the timer to zero.
Buen trabajo, me ganó por unos minutos. Tenga en cuenta que si no TIM1->SRvuelve a borrar en el controlador de interrupciones, obtendrá interrupciones interminables. HAL no lo hará por ti si no usas HAL :)
PSCsiempre está almacenado en el búfer, pero ARRno lo está (a menos que se haya establecido el ARPEbit de CR1, pero lo está borrando).
@Jon está bien, pero cómo controlar la interrupción, ¿esa es la función? o puedo tomar la función de berendi?
tampoco funciona, copié tu código. Puedo ver que CNT está contando en el depurador, pero la función ininterrumpida no se activa
@james_bk, ¿configuró el NVIC?
@Jon lo hice en Cube, elegí NVIC "TIM1update interrupt" en la configuración de TIM1. Tal vez se trate nuevamente de problemas con HAL y no lo habilite correctamente. ¿Hay algún registro que habilite NVIC?

Mi solución sería casi idéntica a la de @Jon, pero iniciaría el temporizador en modo de disparo único, para evitar una segunda interrupción cuando el procesamiento de la primera lleva demasiado tiempo. De esta manera, no se necesita la función de parada del temporizador.

void TIM1_Init() {
    // edit: added clock and interrupt enable
    __HAL_RCC_TIM1_CLK_ENABLE();
    NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 0);
    NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);

    TIM1->PSC = 16799;         // prescaler
    TIM1->EGR = TIM_EGR_UG;    // generate an update event to load the prescaler
    TIM1->ARR = 4999;          // counter limit
    TIM1->SR = 0;              // clear interrupt status after the update event
    TIM1->DIER = TIM_DIER_UIE; // enable interrupt on update (overflow) event
}

void TIM1_Stop() {
    TIM1->CR1 = 0;             // stop timer by clearing CEN (and everything else) in CR1
    TIM1->CNT = 0;             // reset counter, so it will start from 0 at restart
}

void TIM1_Start() {
    TIM1->CR1 = TIM_CR1_CEN    // start the timer
        | TIM_CR1_OPM;         // in one-pulse-mode
}

void TIM1_UP_TIM10_IRQHandler(void) {
    TIM1->SR = 0;
    strcpy(str123,"<<FAIL>>"); // sprintf should not be used in an interrupt handler
    CDC_Transmit_FS((uint8_t*)str123,strlen(str123));
}
entonces, si entiendo que la interrupción se disparará a TIM1_UP_TIM10_IRQHandler, porque no tenemos parada aquí. Necesito no disparar la función de interrupción, cuando la aplicación no está congelada en un bucle
no parece funcionar, TIM1_Start(); no me da nada, cuando debería activar TIM1_UP_TIM10_IRQHandler
@James_BK Código agregado para habilitar el reloj y las interrupciones, y la parada del temporizador. Si aún no funciona, intente reducir el ARRvalor del período del temporizador ( ), en caso de que su reloj vaya más lento de lo esperado.

Lo siguiente es para TIM2 pero se pueden aplicar los mismos pasos para TIM1

#include "stm32f401xc.h"  // chip title


int data = 0;

int main()
{

NVIC_EnableIRQ(TIM2_IRQn); // Enable NVIC interrupt

RCC -> APB1ENR |= RCC_APB1ENR_TIM2EN;
RCC -> AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

GPIOA->MODER = 1; // output_pp
GPIOA->ODR = 0;   // logic low

TIM2->DIER |= TIM_DIER_UIE; // enable interrupt
TIM2-> ARR = (uint32_t)0xff0;    // counter ceilling (max value the counter can has them start counting from the beginning)

TIM2-> PSC = 100;           // prescaler value
TIM1->SR = 0;               // disable all flags

TIM2-> CR1 |= 1;            // start timer



while(1)
{
    GPIOA ->ODR = data ;  // data is updated inside TIM2 ISR
}

    return 0;
}


void TIM2_IRQHandler(void) {
    TIM2->SR = 0;            // clear TIM2 ISR flag to not enter again once finished
    data = (1 ^ data) & 1;  // toggle the value of data 1 -> 0 -> 1 -> ...
}