Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HardwareTimer: support 32 bit timers #2071

Open
GianfrancoIU1JSU opened this issue Jul 15, 2023 · 2 comments
Open

HardwareTimer: support 32 bit timers #2071

GianfrancoIU1JSU opened this issue Jul 15, 2023 · 2 comments

Comments

@GianfrancoIU1JSU
Copy link

Describe the bug
The function HardwareTimer::setOverflow() allow to set the overflow value as a uint32_t an then set the value of the STM32 ARR Register to: $overflow - 1$ since the function is meant to recieve as argument the overflow value and not the terminal count.
has shown in the function implementation below.

void HardwareTimer::setOverflow(uint32_t overflow, TimerFormat_t format)
{
uint32_t ARR_RegisterValue;
uint32_t PeriodTicks;
uint32_t Prescalerfactor;
uint32_t period_cyc;
// Remark: Hardware register correspond to period count-1. Example ARR register value 9 means period of 10 timer cycle
switch (format) {
case MICROSEC_FORMAT:
period_cyc = overflow * (getTimerClkFreq() / 1000000);
Prescalerfactor = (period_cyc / 0x10000) + 1;
LL_TIM_SetPrescaler(_timerObj.handle.Instance, Prescalerfactor - 1);
PeriodTicks = period_cyc / Prescalerfactor;
break;
case HERTZ_FORMAT:
period_cyc = getTimerClkFreq() / overflow;
Prescalerfactor = (period_cyc / 0x10000) + 1;
LL_TIM_SetPrescaler(_timerObj.handle.Instance, Prescalerfactor - 1);
PeriodTicks = period_cyc / Prescalerfactor;
break;
case TICK_FORMAT:
default :
PeriodTicks = overflow;
break;
}
if (PeriodTicks > 0) {
// The register specifies the maximum value, so the period is really one tick longer
ARR_RegisterValue = PeriodTicks - 1;
} else {
// But do not underflow in case a zero period was given somehow.
ARR_RegisterValue = 0;
}
__HAL_TIM_SET_AUTORELOAD(&_timerObj.handle, ARR_RegisterValue);
updateRegistersIfNotRunning(_timerObj.handle.Instance);
}

this works fine with 16 bit counters and 32 bit counters when not used in the full range.
but to set the max value of a 32 bit counter the ARR register value should be set to 0xFFFFFFFF to do so the function argument should be set to 0x100000000 but since the argument is defined as uint32_t the value will not fit and overflow as 0

I want to point out that setting the value to the maximum is an important thing, since not only will increase marginally the maximum counter value but more importantly allows to calculate values of thresholds and time elapsed between input capture event easly because
elapsedTicks = capture - oldCapture if all variables are defined as uint32_t and the elapsedTicks guaranteed to be under 0x100000000 will work even if an overflow occurs.
and in the same way when generating a square wave updating the ARR value with the next event value can be done as nextVal = lastVal + delta under the same conditions.
this methods are common practice on timer usage on any $\mu Controller$

workaround
In the actual state the problem can be solved by either

  • compensating for the missing value in the period computation
    if(capture < captureOld) elapsedTicks = capture - oldCapture - 1;
    else elapsedTicks = capture - oldCapture;
  • initializing the timer overflow by accessing directly to the STM32 HAL
    timerObj_t _timerObj;
    _timerObj.handle.Instance = TIM2;
    __HAL_TIM_SET_AUTORELOAD(&_timerObj.handle, 0xFFFFFFFF);

neither of these solution are efficient nor elegant.

Suggested Fixes
to me the easiest way to fix this issue is changing the function definition from

void HardwareTimer::setOverflow(uint32_t overflow, TimerFormat_t format)

to

void HardwareTimer::setOverflow(uint64_t overflow, TimerFormat_t format)

by using this approach the fix should not cause any compatibility issues with existing code

Constatation
here you can see the serial output of the STM32 when measuring a 1hz pulse over an 80Mhz timebase
both generated by the same oscilltor so they are perfectly coherent.
all the readings are perfect except for when the overflow occurs in that case you sistematically get one count more that is due to the missing code.

captureOld capture    difference
4059789198 4139789198 80000000
4139789198 4219789198 80000000
4219789198 4821903    80000001
4821903    84821903   80000000
84821903   164821903  80000000
164821903  244821903  80000000

after applying one of the above workaround the error never appears again

captureOld capture    difference
4106049847 4186049847 80000000
4186049847 4266049847 80000000
4266049847 51082551   80000000
51082551   131082551  80000000
131082551  211082551  80000000
211082551  291082551  80000000

here there is a the code on witch i've found the bug:

#include <Arduino.h>

HardwareTimer *FCT; //FCT Frequency Counter Timer
volatile uint32_t captureOld = 0, capture;
volatile uint32_t period;
volatile bool newMeas = false;

void IC_Callback(){
    capture = FCT->getCaptureCompare(LL_TIM_CHANNEL_CH1);
    Serial.print(captureOld);    //print inside an interrupt, bad practice but accettable for debugging
    Serial.print(" ");
    Serial.print(capture);
    Serial.print(" ");
    period = capture - captureOld;
    Serial.println(period);

    captureOld = capture;
    newMeas = true;
}

void setup()
{
    Serial.begin(115200);

    FCT = new HardwareTimer(TIM2);

    FCT->setMode(LL_TIM_CHANNEL_CH1,TIMER_INPUT_CAPTURE_RISING,PA_0);
    FCT->setPrescaleFactor(1);

    FCT->setOverflow(0xFFFFFFFF);//max value on 32 bit          // invert the comment on these line to passs 
    //timerObj_t _timerObj;                                     //from the bug to the workaround
    //_timerObj.handle.Instance = TIM2;                         //
    //__HAL_TIM_SET_AUTORELOAD(&_timerObj.handle, 0xFFFFFFFF);  //

    FCT->updateRegistersIfNotRunning(_timerObj.handle.Instance);
    FCT->attachInterrupt(LL_TIM_CHANNEL_CH1,IC_Callback);
    FCT->resume();

}


void loop()
{
    if (newMeas){
        //Serial.println(period);
        newMeas = false;
    }
}


// unrelated to the issue
// this was the clock configuration to use a 10Mhz extenal oscillator
// that was important to have the 1pps signal exactly coherent to the clock
// otherwise the problem can be hard to spot
// this may be replicable using a wave generated by another timer
extern "C" void SystemClock_Config(void){...}

Desktop :

  • OS: Windows 10
  • Platformio version: core 6.1.7 on clion
  • STM32 core version: 2.6.0
  • Upload method: Default

Board:

  • Name: Nucleo L476RG
  • Extra hardware used if any: squarewave generator
@fpistm
Copy link
Member

fpistm commented Jul 15, 2023

Hi @GianfrancoIU1JSU
Thanks for the detailed issue. You can provide a PR then we will review it.

GianfrancoIU1JSU added a commit to GianfrancoIU1JSU/Arduino_Core_STM32 that referenced this issue Jul 16, 2023
GianfrancoIU1JSU added a commit to GianfrancoIU1JSU/Arduino_Core_STM32 that referenced this issue Jul 16, 2023
GianfrancoIU1JSU added a commit to GianfrancoIU1JSU/Arduino_Core_STM32 that referenced this issue Jul 16, 2023
@fpistm fpistm added this to To do in STM32 core based on ST HAL via automation Jul 17, 2023
@fpistm fpistm added this to the 2.7.0 milestone Jul 17, 2023
@fpistm fpistm changed the title Setting 32 bit timers overflow value to maximum is not possible HardwareTimer: support 32 bit timers Jul 21, 2023
@fpistm
Copy link
Member

fpistm commented Jul 21, 2023

Hi @GianfrancoIU1JSU
As stated in your PR, the implementation to support 32 bits timers required more works. I've changed the title to support 32 bits timers instance.
The IS_TIM_32B_COUNTER_INSTANCE could be used to deal with.

Ex of limitation

#define MAX_RELOAD ((1 << 16) - 1) // Currently even 32b timers are used as 16b to have generic behavior

@fpistm fpistm removed this from the 2.7.0 milestone Nov 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging a pull request may close this issue.

2 participants