Skip to content

Commit e0c5be1

Browse files
matthijskooijmanfpistm
authored andcommitted
HardwareTimer: Allow delaying initialization to setup method
Previously, the only way to initialize a HardwareTimer instance was to pass an instance to the constructor. When using a global (more specifically, with static storage duration) HardwareTimer instance, the initialization would happen early in startup, before main() and setup() had a chance to run. This would mean that on any errors (e.g. no timer found for a specific pin when using e.g. pinmap_peripheral), the board would just lock up in early startup, unable to show a meaningful error at all. To prevent this, you would have to allocate the HardwareTimer object dynamically on the heap, but that is not always a good idea from a memory management perspective. This commit adds an argumentless constructor that does not initialize the timer yet, and a setup() method that accepts the timer instance and does the actual initialization. This allows delaying the actual initialization until in or after the sketch setup() function, and also makes it easier to change the timer to use dynamically, based on e.g. user input or EEPROM contents or similar. Note that de-initializing a timer and switching to using a different one is not currently supported, trying to call setup() twice results in an error.
1 parent aa43125 commit e0c5be1

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

cores/arduino/HardwareTimer.cpp

+35
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,53 @@
3535
/* Private Variables */
3636
timerObj_t *HardwareTimer_Handle[TIMER_NUM] = {NULL};
3737

38+
/**
39+
* @brief HardwareTimer constructor: make uninitialized timer
40+
* Before calling any methods, call setup to select and setup
41+
* the timer to be used.
42+
* @retval None
43+
*/
44+
HardwareTimer::HardwareTimer()
45+
{
46+
_timerObj.handle.Instance = nullptr;
47+
}
48+
3849
/**
3950
* @brief HardwareTimer constructor: set default configuration values
51+
* The timer will be usable directly, there is no need to call
52+
* setup(). Using this constructor is not recommended for
53+
* global variables that are automatically initalized at
54+
* startup, since this will happen to early to report any
55+
* errors. Better use the argumentless constructor and call the
56+
* setup() method during initialization later.
4057
* @param Timer instance ex: TIM1, ...
4158
* @retval None
4259
*/
4360
HardwareTimer::HardwareTimer(TIM_TypeDef *instance)
61+
{
62+
_timerObj.handle.Instance = nullptr;
63+
setup(instance);
64+
}
65+
66+
/**
67+
* @brief HardwareTimer setup: configuration values. Must be called
68+
* exactly once before any other methods, except when an instance is
69+
* passed to the constructor.
70+
* @param Timer instance ex: TIM1, ...
71+
* @retval None
72+
*/
73+
void HardwareTimer::setup(TIM_TypeDef *instance)
4474
{
4575
uint32_t index = get_timer_index(instance);
4676
if (index == UNKNOWN_TIMER) {
4777
Error_Handler();
4878
}
4979

80+
// Already initialized?
81+
if (_timerObj.handle.Instance) {
82+
Error_Handler();
83+
}
84+
5085
HardwareTimer_Handle[index] = &_timerObj;
5186

5287
_timerObj.handle.Instance = instance;

cores/arduino/HardwareTimer.h

+3
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,12 @@ using callback_function_t = std::function<void(void)>;
9898
/* Class --------------------------------------------------------*/
9999
class HardwareTimer {
100100
public:
101+
HardwareTimer();
101102
HardwareTimer(TIM_TypeDef *instance);
102103
~HardwareTimer(); // destructor
103104

105+
void setup(TIM_TypeDef *instance); // Setup, only needed if no instance was passed to the constructor
106+
104107
void pause(void); // Pause counter and all output channels
105108
void pauseChannel(uint32_t channel); // Timer is still running but channel (output and interrupt) is disabled
106109
void resume(void); // Resume counter and all output channels

0 commit comments

Comments
 (0)