Skip to content

Commit 0878b66

Browse files
ABOSTMfpistm
authored andcommitted
HardwareTimer: Fix ARR and CCRx computation
* protect setOverflow against underflow. * remove -1 for MICROSEC_COMPARE_FORMAT, HERTZ_COMPARE_FORMAT and TICK_COMPARE_FORMAT. * remove -1 in setCount() too * Manage special case where ARR = MAX_RELOAD and 100% expected. Fixes #897
1 parent ac7c4ce commit 0878b66

File tree

1 file changed

+26
-10
lines changed

1 file changed

+26
-10
lines changed

cores/arduino/HardwareTimer.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ HardwareTimer::HardwareTimer(TIM_TypeDef *instance)
8787

8888
/* Configure timer with some default values */
8989
_timerObj.handle.Init.Prescaler = 0;
90-
_timerObj.handle.Init.Period = 0xFFFF; // 16bit max value
90+
_timerObj.handle.Init.Period = MAX_RELOAD;
9191
_timerObj.handle.Init.CounterMode = TIM_COUNTERMODE_UP;
9292
_timerObj.handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
9393
#if defined(TIM_RCR_REP)
@@ -452,6 +452,7 @@ uint32_t HardwareTimer::getOverflow(TimerFormat_t format)
452452
void HardwareTimer::setOverflow(uint32_t overflow, TimerFormat_t format)
453453
{
454454
uint32_t ARR_RegisterValue;
455+
uint32_t PeriodTicks;
455456
uint32_t Prescalerfactor;
456457
uint32_t period_cyc;
457458
// Remark: Hardware register correspond to period count-1. Example ARR register value 9 means period of 10 timer cycle
@@ -460,20 +461,27 @@ void HardwareTimer::setOverflow(uint32_t overflow, TimerFormat_t format)
460461
period_cyc = overflow * (getTimerClkFreq() / 1000000);
461462
Prescalerfactor = (period_cyc / 0x10000) + 1;
462463
LL_TIM_SetPrescaler(_timerObj.handle.Instance, Prescalerfactor - 1);
463-
ARR_RegisterValue = (period_cyc / Prescalerfactor) - 1;
464+
PeriodTicks = period_cyc / Prescalerfactor;
464465
break;
465466
case HERTZ_FORMAT:
466467
period_cyc = getTimerClkFreq() / overflow;
467468
Prescalerfactor = (period_cyc / 0x10000) + 1;
468469
LL_TIM_SetPrescaler(_timerObj.handle.Instance, Prescalerfactor - 1);
469-
ARR_RegisterValue = (period_cyc / Prescalerfactor) - 1;
470+
PeriodTicks = period_cyc / Prescalerfactor;
470471
break;
471472
case TICK_FORMAT:
472473
default :
473-
ARR_RegisterValue = overflow - 1;
474+
PeriodTicks = overflow;
474475
break;
475476
}
476477

478+
if (PeriodTicks > 0) {
479+
// The register specifies the maximum value, so the period is really one tick longer
480+
ARR_RegisterValue = PeriodTicks - 1;
481+
} else {
482+
// But do not underflow in case a zero period was given somehow.
483+
ARR_RegisterValue = 0;
484+
}
477485
__HAL_TIM_SET_AUTORELOAD(&_timerObj.handle, ARR_RegisterValue);
478486
}
479487

@@ -520,14 +528,14 @@ void HardwareTimer::setCount(uint32_t counter, TimerFormat_t format)
520528
uint32_t Prescalerfactor = LL_TIM_GetPrescaler(_timerObj.handle.Instance) + 1;
521529
switch (format) {
522530
case MICROSEC_FORMAT:
523-
CNT_RegisterValue = ((counter * (getTimerClkFreq() / 1000000)) / Prescalerfactor) - 1 ;
531+
CNT_RegisterValue = ((counter * (getTimerClkFreq() / 1000000)) / Prescalerfactor);
524532
break;
525533
case HERTZ_FORMAT:
526-
CNT_RegisterValue = (uint32_t)((getTimerClkFreq() / (counter * Prescalerfactor)) - 1);
534+
CNT_RegisterValue = (uint32_t)(getTimerClkFreq() / (counter * Prescalerfactor));
527535
break;
528536
case TICK_FORMAT:
529537
default :
530-
CNT_RegisterValue = counter - 1;
538+
CNT_RegisterValue = counter;
531539
break;
532540
}
533541
__HAL_TIM_SET_COUNTER(&(_timerObj.handle), CNT_RegisterValue);
@@ -699,11 +707,12 @@ void HardwareTimer::setCaptureCompare(uint32_t channel, uint32_t compare, TimerC
699707

700708
switch (format) {
701709
case MICROSEC_COMPARE_FORMAT:
702-
CCR_RegisterValue = ((compare * (getTimerClkFreq() / 1000000)) / Prescalerfactor) - 1 ;
710+
CCR_RegisterValue = ((compare * (getTimerClkFreq() / 1000000)) / Prescalerfactor);
703711
break;
704712
case HERTZ_COMPARE_FORMAT:
705-
CCR_RegisterValue = (getTimerClkFreq() / (compare * Prescalerfactor)) - 1;
713+
CCR_RegisterValue = getTimerClkFreq() / (compare * Prescalerfactor);
706714
break;
715+
// As per Reference Manual PWM reach 100% with CCRx value strictly greater than ARR (So ARR+1 in our case)
707716
case PERCENT_COMPARE_FORMAT:
708717
CCR_RegisterValue = ((__HAL_TIM_GET_AUTORELOAD(&(_timerObj.handle)) + 1) * compare) / 100;
709718
break;
@@ -727,10 +736,17 @@ void HardwareTimer::setCaptureCompare(uint32_t channel, uint32_t compare, TimerC
727736
break;
728737
case TICK_COMPARE_FORMAT:
729738
default :
730-
CCR_RegisterValue = compare - 1;
739+
CCR_RegisterValue = compare;
731740
break;
732741
}
733742

743+
// Special case when ARR is set to the max value, it is not possible to set CCRx to ARR+1 to reach 100%
744+
// Then set CCRx to max value. PWM is then 1/0xFFFF = 99.998..%
745+
if ((__HAL_TIM_GET_AUTORELOAD(&(_timerObj.handle)) == MAX_RELOAD)
746+
&& (CCR_RegisterValue == MAX_RELOAD + 1)) {
747+
CCR_RegisterValue = MAX_RELOAD;
748+
}
749+
734750
__HAL_TIM_SET_COMPARE(&(_timerObj.handle), timChannel, CCR_RegisterValue);
735751
}
736752

0 commit comments

Comments
 (0)