1

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

Clifford
  • 88,407
  • 13
  • 85
  • 165
Gaurav1234
  • 43
  • 7
  • 1
    Check the frequency of your clock sources – doron Dec 26 '20 at 23:22
  • Hi doron, Can you point out what exactly is wrong with this ? – Gaurav1234 Dec 26 '20 at 23:29
  • 1
    Problem might not be in the code but in the clock sources or how somebody the PLLs are configured. Probing with an oscillator-scope may give you more clues. Check out the schematics. – doron Dec 27 '20 at 00:14
  • 1
    Are you sure you want `htim3.Init.Period` to be 0xfffe? It looks like you'd want it to be 0 or 1 (probably 0). Also, your MCU is only running at 216mhz, so you're only going to get ~200 instructions (or less) between interrupts. That's not much at all and you might consider trying to solve the problem in another way. – Russ Schultz Dec 27 '20 at 00:50
  • 1
    be careful with the word "exact" as it wont be, particularly if you are using an OS. the timer signal but the latency to the handler would be expected to vary... – old_timer Dec 27 '20 at 06:52
  • 1
    A 1us interrupt is probably an unreasonable expectation (though not the cause of your 1.2ms). If you want a 1MHz clock output on a pin use the timer's _output compare_ feature to toggle the pin directly without software overhead. On a 216MHz part you could probably do that with interrupts and GPIO if the processor is expected to do _nothing else_ and there are no higher priority interrupts in use and no bus contention with DMA operations for example. Dedicating a processor of that capability to this one task would be wasteful in the extreme. – Clifford Dec 27 '20 at 12:48
  • 1
    _"I am calling the timer"_ What does that mean? You don't call interrupt handlers directly, they are invoked by the hardware when the timer expires. The code fragment that follows that statement does nothing to clarify what you are doing - no context. – Clifford Dec 27 '20 at 12:52
  • 1
    ... should have said 500KHz clock, not 1MHz. – Clifford Dec 27 '20 at 13:06
  • 1
    Explain your arithmetic. How did you determine that period `0xffff-1` and a prescaler of 1 from 216MHz would result in 1us? It would in fact result in 4.85ms. How are you determining that it is 1.2ms (about a quarter of what your code and clock configuration would suggest) ? Your reload value should be 216 (decimal). – Clifford Dec 27 '20 at 18:47
  • 1
    You need to check the datasheet for your part since you have not been specific, but for STM32F765/67/68/69 PB6 can be mapped to TIM4 CH1 so the _that_ timer can drive that pin directly and accurately with zero software overhead. If you must use TIM3, then you would need to use one of PA6, PA7, PB0, PB1, PB4, PB5, PC6, PC7, PC8, or PC9. – Clifford Dec 27 '20 at 19:03
  • @Clifford since the TIM3 is associated with APB1, it runs on the frequency 108 MHZ, as described in the datasheet. So, I have adjusted the prescalar and Period as below : In order to get 1us (1 MHz) from 108 MHZ, - 108000000/1000000 = 108 108 = 1x 108 ; So, Prescalar = 1-1 (Which will be added automatically) Period = 108 - 1 Is this calculation correct ? – Gaurav1234 Dec 28 '20 at 01:48
  • 1
    @Gaurav1234 the quickest way to answer that question is to try it. This is comments not answers, my comments are hints for you to fix the question, not to answer it. I am not sure why you are subtracting 1 from 108 though. Are you measuring the period from rising edge to rising edge? That would explain why you have a period of twice the timer period. It is however academic, as I have said, the period is unreasonable for interrupt handling. – Clifford Dec 28 '20 at 09:03
  • @Clifford Thanks for the input. The problem however is still not solved. I can see the pulse of 3us. Not sure why? Now I am wondering if this is the limit of the hardware timer and it cannot go below this. – Gaurav1234 Dec 28 '20 at 16:15
  • 1
    @Gaurav1234 : You clearly have not taken notice of the comments - you are driving the pin _in software_ with unreasonable expectations of the capability. The timer _hardware_ has no such limitation. You have also not answered any of the questions posed in comments by myself and others that are intended to clarify the question to support an answer. Specifically: 1) What STM32Fx part are you using? 2) How are you measuring the period? 3) How did you calculate the 0xfffe period? 4) Howe are you measuring the period (and what are you measuring)? – Clifford Dec 28 '20 at 17:01
  • 1
    You should answer these questions by updating the question, not by responding in comments. Also it looks like an X-Y problem, what is it that you need to do as such a short interval? Or is it simply that you need to toggle PB6 (for which there is a simpler and more appropriate method). – Clifford Dec 28 '20 at 17:02

1 Answers1

2

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.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • In your previous comment You mentioned: 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. Can you please give some insight into why is this so? – Gaurav1234 Dec 30 '20 at 03:54
  • 1
    @Gaurav1234 That has already been addressed by Russ Schultz and other comments. 1us is in the order of magnitude of a context switch time. The use of an RTOS implies you intend other work to continue concurrently. Your interrupt processing will be a significant proportion of your CPU load where it should be a tiny fraction. Higher priority interrupts will cause the timing to be less than exact too. If you need a more complete answer, you need to post a separate question. – Clifford Dec 30 '20 at 07:45