Skip to content

Commit b7632c4

Browse files
authored
Merge pull request #12 from ABOSTM/master
Create examples for HardwareTimer library
2 parents 89308d3 + 824017c commit b7632c4

File tree

5 files changed

+336
-0
lines changed

5 files changed

+336
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
All-in-one setPWM
3+
This example shows how to configure a PWM with HardwareTimer in one single function call.
4+
PWM is generated on `LED_BUILTIN` if available.
5+
No interruption callback used: PWM is generated by hardware.
6+
Once configured, there is no CPU load.
7+
*/
8+
9+
#if defined(LED_BUILTIN)
10+
#define pin LED_BUILTIN
11+
#else
12+
#define pin D2
13+
#endif
14+
15+
void setup()
16+
{
17+
// no need to configure pin, it will be done by HardwareTimer configuration
18+
// pinMode(pin, OUTPUT);
19+
20+
// Automatically retrieve TIM instance and channel associated to pin
21+
// This is used to be compatible with all STM32 series automatically.
22+
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
23+
uint32_t channel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM));
24+
25+
26+
// Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished.
27+
HardwareTimer *MyTim = new HardwareTimer(Instance);
28+
29+
// Configure and start PWM
30+
// MyTim->setPWM(channel, pin, 5, 10, NULL, NULL); // No callback required, we can simplify the function call
31+
MyTim->setPWM(channel, pin, 5, 10); // 5 Hertz, 10% dutycycle
32+
}
33+
34+
35+
void loop()
36+
{
37+
/* Nothing to do all is done by hardware. Even no interrupt required. */
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
Frequency and dutycycle measurement
3+
This example shows how to configure HardwareTimer to measure external signal frequency and dutycycle.
4+
The input pin will be connected to 2 channel of the timer, one for rising edge the other for falling edge.
5+
Each time a rising edge is detected on the input pin, hardware will save counter value into one of the CaptureCompare register.
6+
Each time a falling edge is detected on the input pin, hardware will save counter value into the other CaptureCompare register.
7+
External signal (signal generator for example) should be connected to `D2`.
8+
9+
*/
10+
11+
#define pin D2
12+
13+
uint32_t channelRising, channelFalling;
14+
volatile uint32_t FrequencyMeasured, DutycycleMeasured, LastPeriodCapture = 0, CurrentCapture, HighStateMeasured;
15+
uint32_t input_freq = 0;
16+
HardwareTimer *MyTim;
17+
18+
/**
19+
@brief Input capture interrupt callback : Compute frequency and dutycycle of input signal
20+
21+
*/
22+
void TIMINPUT_Capture_Rising_IT_callback(HardwareTimer*)
23+
{
24+
CurrentCapture = MyTim->getCaptureCompare(channelRising);
25+
/* frequency computation */
26+
if (CurrentCapture > LastPeriodCapture)
27+
{
28+
FrequencyMeasured = input_freq / (CurrentCapture - LastPeriodCapture);
29+
DutycycleMeasured = (HighStateMeasured * 100) / (CurrentCapture - LastPeriodCapture);
30+
}
31+
else if (CurrentCapture <= LastPeriodCapture)
32+
{
33+
/* 0x1000 is max overflow value */
34+
FrequencyMeasured = input_freq / (0x10000 + CurrentCapture - LastPeriodCapture);
35+
DutycycleMeasured = (HighStateMeasured * 100) / (0x10000 + CurrentCapture - LastPeriodCapture);
36+
}
37+
38+
LastPeriodCapture = CurrentCapture;
39+
}
40+
41+
42+
/**
43+
@brief Input capture interrupt callback : Compute frequency and dutycycle of input signal
44+
45+
*/
46+
void TIMINPUT_Capture_Falling_IT_callback(HardwareTimer*)
47+
{
48+
/* prepare DutyCycle computation */
49+
CurrentCapture = MyTim->getCaptureCompare(channelFalling);
50+
51+
if (CurrentCapture > LastPeriodCapture)
52+
{
53+
HighStateMeasured = CurrentCapture - LastPeriodCapture;
54+
}
55+
else if (CurrentCapture <= LastPeriodCapture)
56+
{
57+
/* 0x1000 is max overflow value */
58+
HighStateMeasured = 0x10000 + CurrentCapture - LastPeriodCapture;
59+
}
60+
}
61+
62+
63+
void setup()
64+
{
65+
Serial.begin(115200);
66+
67+
// Automatically retrieve TIM instance and channelRising associated to pin
68+
// This is used to be compatible with all STM32 series automatically.
69+
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
70+
channelRising = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM));
71+
72+
// channelRisings come by pair for TIMER_INPUT_FREQ_DUTY_MEASUREMENT mode:
73+
// channelRising1 is associated to channelFalling and channelRising3 is associated with channelRising4
74+
switch (channelRising) {
75+
case 1:
76+
channelFalling = 2;
77+
break;
78+
case 2:
79+
channelFalling = 1;
80+
break;
81+
case 3:
82+
channelFalling = 4;
83+
break;
84+
case 4:
85+
channelFalling = 3;
86+
break;
87+
}
88+
89+
// Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished.
90+
MyTim = new HardwareTimer(Instance);
91+
92+
// Configure rising edge detection to measure frequency
93+
MyTim->setMode(channelRising, TIMER_INPUT_FREQ_DUTY_MEASUREMENT, pin);
94+
95+
// With a PrescalerFactor = 1, the minimum frequency value to measure is : TIM counter clock / CCR MAX
96+
// = (SystemCoreClock) / 65535
97+
// Example on Nucleo_L476RG with systemClock at 80MHz, the minimum frequency is around 1,2 khz
98+
// To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision.
99+
// The maximum frequency depends on processing of both interruptions and thus depend on board used
100+
// Example on Nucleo_L476RG with systemClock at 80MHz the interruptions processing is around 10 microseconds and thus Max frequency is around 100kHz
101+
uint32_t PrescalerFactor = 1;
102+
MyTim->setPrescaleFactor(PrescalerFactor);
103+
MyTim->setOverflow(0x10000); // Max Period value to have the largest possible time to detect rising edge and avoid timer rollover
104+
MyTim->attachInterrupt(channelRising, TIMINPUT_Capture_Rising_IT_callback);
105+
MyTim->attachInterrupt(channelFalling, TIMINPUT_Capture_Falling_IT_callback);
106+
107+
MyTim->resume();
108+
109+
// Compute this scale factor only once
110+
input_freq = MyTim->getTimerClkFreq() / MyTim->getPrescaleFactor();
111+
}
112+
113+
114+
void loop()
115+
{
116+
/* Print frequency and dutycycle measured on Serial monitor every seconds */
117+
Serial.print((String)"Frequency = " + FrequencyMeasured);
118+
Serial.println((String)" Dutycycle = " + DutycycleMeasured);
119+
delay(1000);
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
Input capture
3+
This example shows how to configure HardwareTimer in inputcapture to measure external signal frequency.
4+
Each time a rising edge is detected on the input pin, hardware will save counter value into CaptureCompare register.
5+
External signal (signal generator for example) should be connected to `D2`.
6+
Measured frequency is displayed on Serial Monitor.
7+
*/
8+
9+
#define pin D2
10+
11+
uint32_t channel;
12+
volatile uint32_t FrequencyMeasured, LastCapture = 0, CurrentCapture;
13+
uint32_t input_freq = 0;
14+
HardwareTimer *MyTim;
15+
16+
void InputCapture_IT_callback(HardwareTimer*)
17+
{
18+
CurrentCapture = MyTim->getCaptureCompare(channel);
19+
/* frequency computation */
20+
if (CurrentCapture > LastCapture) {
21+
FrequencyMeasured = input_freq / (CurrentCapture - LastCapture);
22+
}
23+
else if (CurrentCapture <= LastCapture) {
24+
/* 0x1000 is max overflow value */
25+
FrequencyMeasured = input_freq / (0x10000 + CurrentCapture - LastCapture);
26+
}
27+
LastCapture = CurrentCapture;
28+
}
29+
30+
31+
void setup()
32+
{
33+
Serial.begin(115200);
34+
35+
// Automatically retrieve TIM instance and channel associated to pin
36+
// This is used to be compatible with all STM32 series automatically.
37+
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
38+
channel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM));
39+
40+
// Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished.
41+
MyTim = new HardwareTimer(Instance);
42+
43+
// Configure rising edge detection to measure frequency
44+
MyTim->setMode(channel, TIMER_INPUT_CAPTURE_RISING, pin);
45+
46+
// With a PrescalerFactor = 1, the minimum frequency value to measure is : TIM counter clock / CCR MAX
47+
// = (SystemCoreClock) / 65535
48+
// Example on Nucleo_L476RG with systemClock at 80MHz, the minimum frequency is around 1,2 khz
49+
// To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision.
50+
// The maximum frequency depends on processing of the interruption and thus depend on board used
51+
// Example on Nucleo_L476RG with systemClock at 80MHz the interruption processing is around 4,5 microseconds and thus Max frequency is around 220kHz
52+
uint32_t PrescalerFactor = 1;
53+
MyTim->setPrescaleFactor(PrescalerFactor);
54+
MyTim->setOverflow(0x10000); // Max Period value to have the largest possible time to detect rising edge and avoid timer rollover
55+
MyTim->attachInterrupt(channel, InputCapture_IT_callback);
56+
MyTim->resume();
57+
58+
// Compute this scale factor only once
59+
input_freq = MyTim->getTimerClkFreq() / MyTim->getPrescaleFactor();
60+
}
61+
62+
63+
void loop()
64+
{
65+
/* Print frequency measured on Serial monitor every seconds */
66+
Serial.println((String)"Frequency = " + FrequencyMeasured);
67+
delay(1000);
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
PWM full configuration
3+
This example shows how to fully configure a PWM with HardwareTimer.
4+
PWM is generated on `LED_BUILTIN` if available.
5+
PWM is generated by hardware: no CPU load.
6+
Nevertheless, in this example both interruption callback are used on Compare match (Falling edge of PWM1 mode) and update event (rising edge of PWM1 mode).
7+
Those call back are used to toggle a second pin: `pin2`.
8+
Once configured, there is only CPU load for callbacks executions.
9+
*/
10+
11+
// 'pin' PWM will be mangaed automatically by hardware whereas 'pin2' PWM will be managed by software through interrupt callback
12+
#if defined(LED_BUILTIN)
13+
#define pin LED_BUILTIN
14+
15+
#if LED_BUILTIN == D3
16+
#error LED_BUILTIN == D3
17+
#else
18+
#define pin2 D3
19+
#endif
20+
21+
#else
22+
#define pin D2
23+
#define pin2 D3
24+
#endif
25+
26+
void Update_IT_callback(HardwareTimer*)
27+
{ // Update event correspond to Rising edge of PWM when configured in PWM1 mode
28+
digitalWrite(pin2, LOW); // pin2 will be complementary to pin
29+
}
30+
31+
void Compare_IT_callback(HardwareTimer*)
32+
{ // Compare match event correspond to falling edge of PWM when configured in PWM1 mode
33+
digitalWrite(pin2, HIGH);
34+
}
35+
36+
void setup()
37+
{
38+
// No need to configure pin, it will be done by HardwareTimer configuration
39+
// pinMode(pin, OUTPUT);
40+
41+
// Need to configure pin2, as it is not managed by HardwareTimer
42+
pinMode(pin2, OUTPUT);
43+
44+
// Automatically retrieve TIM instance and channel associated to pin
45+
// This is used to be compatible with all STM32 series automatically.
46+
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
47+
uint32_t channel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM));
48+
49+
50+
// Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup function is finished.
51+
HardwareTimer *MyTim = new HardwareTimer(Instance);
52+
53+
MyTim->setMode(channel, TIMER_OUTPUT_COMPARE_PWM1, pin);
54+
// MyTim->setPrescaleFactor(8); // Due to setOverflow with MICROSEC_FORMAT, prescaler will be computed automatically based on timer input clock
55+
MyTim->setOverflow(100000, MICROSEC_FORMAT); // 10000 microseconds = 10 milliseconds
56+
MyTim->setCaptureCompare(channel, 50, PERCENT_COMPARE_FORMAT); // 50%
57+
MyTim->attachInterrupt(Update_IT_callback);
58+
MyTim->attachInterrupt(channel, Compare_IT_callback);
59+
MyTim->resume();
60+
}
61+
62+
63+
void loop()
64+
{
65+
/* Nothing to do all is done by hardware. Even no interrupt required. */
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
Timebase callback
3+
This example shows how to configure HardwareTimer to execute a callback at regular interval.
4+
Callback toggles pin.
5+
Once configured, there is only CPU load for callbacks executions.
6+
*/
7+
8+
#if defined(LED_BUILTIN)
9+
#define pin LED_BUILTIN
10+
#else
11+
#define pin D2
12+
#endif
13+
14+
void Update_IT_callback(HardwareTimer*)
15+
{ // Toggle pin. 10hz toogle --> 5Hz PWM
16+
digitalWrite(pin, !digitalRead(pin));
17+
}
18+
19+
20+
void setup()
21+
{
22+
#if defined(TIM1)
23+
TIM_TypeDef *Instance = TIM1;
24+
#else
25+
TIM_TypeDef *Instance = TIM2;
26+
#endif
27+
28+
// Instantiate HardwareTimer object. Thanks to 'new' instanciation, HardwareTimer is not destructed when setup() function is finished.
29+
HardwareTimer *MyTim = new HardwareTimer(Instance);
30+
31+
// configure pin in output mode
32+
pinMode(pin, OUTPUT);
33+
34+
MyTim->setMode(2, TIMER_OUTPUT_COMPARE); // In our case, channekFalling is configured but not really used. Nevertheless it would be possible to attach a callback to channel compare match.
35+
MyTim->setOverflow(10, HERTZ_FORMAT); // 10 Hz
36+
MyTim->attachInterrupt(Update_IT_callback);
37+
MyTim->resume();
38+
}
39+
40+
41+
void loop()
42+
{
43+
/* Nothing to do all is done by hardware. Even no interrupt required. */
44+
}

0 commit comments

Comments
 (0)