cembeddedembedded-linuxmicrocontrollerstm32f7

How to generate exact 1us interrupt on STM32f7xx using Hardware Timers


I am new to interrupt-based programming. In my current project, I need the interrupt generated exactly at 1us interval. Below is the screenshot from the Clock Configuration tab in CubeMX. I am using the TIM3 timer as it can generate the clock frequency of 1us. enter image description here

Below is the TIM3 configuration code.

static void MX_TIM3_Init(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig;
    TIM_MasterConfigTypeDef sMasterConfig;

    htim3.Instance = TIM3;
    htim3.Init.Prescaler = 1-1 ;//0x00;// 0x36; || 0x00//1-1
    htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim3.Init.Period = 0xffff-1;  //0x64; || 0xd7 //0xffff-1
    htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

    if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }

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

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

I am calling the timer

HAL_TIM_IRQHandler(&htim3);
/* USER CODE BEGIN TIM3_IRQn 1 */

HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6);

I see that the interrupt of duration 1.2ms is generated. Can anyone let me why is this happening and how can I reduce the interrupt to 1us duration? Any change required in the timer frequency?

I am also using freeRTOS and other applications are also running on the microcontroller.

Any help in this is highly appreciated.

Thanks in Advance


Solution

  • If your requirement output an accurate 500KHz 1:1 mark/space signal (i.e. 1us high, 1us low), then doing that through interrupts while expecting your system to do other useful work is both impractical an unnecessary. The general purpose timers have a output-compare function that can drive a GPIO pin directly without interrupts or software overhead.

    Only certain pins are connected to the Timer OC channels, so to drive PB6 in this case you would need to use TIM4 Channel 1.

    Also rather than determining and hard-coding timer reload and pulse, you should use the available HAL RCC clock functions (HAL_RCC_GetPCLK1Freq() in this case) to calculate the values to avoid erros, and so that the code will be portable to other systems or will work correctly if you change your clock configuration.

    static void MX_TIM4_Init(void)
    {
        cost uint32_t PULSE_WIDTH = HAL_RCC_GetPCLK1Freq() * 2 / 1000000 ;
        
        htim4.Instance = TIM4 ;
        htim4.Init.Prescaler = 0;
        htim4.Init.CounterMode = TIM_COUNTERMODE_UP ;
        htim4.Init.Period = PULSE_WIDTH * 2 ;
        htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1 ;
        HAL_TIM_PWM_Init( &htim4 ) ;
        
        TIM_MasterConfigTypeDef sMasterConfig ;
        sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
        sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
        HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);
        
        TIM_OC_InitTypeDef sConfigOC ;
        sConfigOC.OCMode = TIM_OCMODE_PWM1;
        sConfigOC.Pulse = PULSE_WIDTH ;
        sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
        sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    
        HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1);
    }
    

    Then elsewhere you need to configure PB6 as an output and start the timer:

      LL_GPIO_InitTypeDef GPIO_InitStruct = {0} ;
      GPIO_InitStruct.Pin = GPIO_PIN_6 ;
      GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE ;
      GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW ;
      GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL ;
      GPIO_InitStruct.Pull = LL_GPIO_PULL_NO ;
      GPIO_InitStruct.Alternate = LL_GPIO_AF_2 ;
    
      LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB)
      LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    
      HAL_TIM_PWM_Start( &htim4, TIM_CHANNEL_1 ) ; 
    

    Thereafter the signal will be maintained indefinitely on PB6 with no GPIO access or interrupt handling.