-
Notifications
You must be signed in to change notification settings - Fork 55
/
Copy pathFrequency_Dutycycle_measurement.ino
144 lines (120 loc) · 5.23 KB
/
Frequency_Dutycycle_measurement.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
Frequency and dutycycle measurement
This example shows how to configure HardwareTimer to measure external signal frequency and dutycycle.
The input pin will be connected to 2 channel of the timer, one for rising edge the other for falling edge.
Each time a rising edge is detected on the input pin, hardware will save counter value into one of the CaptureCompare register.
Each time a falling edge is detected on the input pin, hardware will save counter value into the other CaptureCompare register.
External signal (signal generator for example) should be connected to `D2`.
*/
/*
Note: Please verify that for your board,'pin' used for PWM has HardwareTimer capability
This is specially true for F1 serie (BluePill, ...)
*/
#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION < 0x01090000)
#error "Due to API change, this sketch is compatible with STM32_CORE_VERSION >= 0x01090000"
#endif
#define pin D2
uint32_t channelRising, channelFalling;
volatile uint32_t FrequencyMeasured, DutycycleMeasured, LastPeriodCapture = 0, CurrentCapture, HighStateMeasured;
uint32_t input_freq = 0;
volatile uint32_t rolloverCompareCount = 0;
HardwareTimer *MyTim;
/**
@brief Input capture interrupt callback : Compute frequency and dutycycle of input signal
*/
void TIMINPUT_Capture_Rising_IT_callback(void)
{
CurrentCapture = MyTim->getCaptureCompare(channelRising);
/* frequency computation */
if (CurrentCapture > LastPeriodCapture)
{
FrequencyMeasured = input_freq / (CurrentCapture - LastPeriodCapture);
DutycycleMeasured = (HighStateMeasured * 100) / (CurrentCapture - LastPeriodCapture);
}
else if (CurrentCapture <= LastPeriodCapture)
{
/* 0x1000 is max overflow value */
FrequencyMeasured = input_freq / (0x10000 + CurrentCapture - LastPeriodCapture);
DutycycleMeasured = (HighStateMeasured * 100) / (0x10000 + CurrentCapture - LastPeriodCapture);
}
LastPeriodCapture = CurrentCapture;
rolloverCompareCount = 0;
}
/* In case of timer rollover, frequency is to low to be measured set values to 0
To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision. */
void Rollover_IT_callback(void)
{
rolloverCompareCount++;
if (rolloverCompareCount > 1)
{
FrequencyMeasured = 0;
DutycycleMeasured = 0;
}
}
/**
@brief Input capture interrupt callback : Compute frequency and dutycycle of input signal
*/
void TIMINPUT_Capture_Falling_IT_callback(void)
{
/* prepare DutyCycle computation */
CurrentCapture = MyTim->getCaptureCompare(channelFalling);
if (CurrentCapture > LastPeriodCapture)
{
HighStateMeasured = CurrentCapture - LastPeriodCapture;
}
else if (CurrentCapture <= LastPeriodCapture)
{
/* 0x1000 is max overflow value */
HighStateMeasured = 0x10000 + CurrentCapture - LastPeriodCapture;
}
}
void setup()
{
Serial.begin(115200);
// Automatically retrieve TIM instance and channelRising associated to pin
// This is used to be compatible with all STM32 series automatically.
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
channelRising = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM));
// channelRisings come by pair for TIMER_INPUT_FREQ_DUTY_MEASUREMENT mode:
// channelRising1 is associated to channelFalling and channelRising3 is associated with channelRising4
switch (channelRising) {
case 1:
channelFalling = 2;
break;
case 2:
channelFalling = 1;
break;
case 3:
channelFalling = 4;
break;
case 4:
channelFalling = 3;
break;
}
// Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished.
MyTim = new HardwareTimer(Instance);
// Configure rising edge detection to measure frequency
MyTim->setMode(channelRising, TIMER_INPUT_FREQ_DUTY_MEASUREMENT, pin);
// With a PrescalerFactor = 1, the minimum frequency value to measure is : TIM counter clock / CCR MAX
// = (SystemCoreClock) / 65535
// Example on Nucleo_L476RG with systemClock at 80MHz, the minimum frequency is around 1,2 khz
// To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision.
// The maximum frequency depends on processing of both interruptions and thus depend on board used
// Example on Nucleo_L476RG with systemClock at 80MHz the interruptions processing is around 10 microseconds and thus Max frequency is around 100kHz
uint32_t PrescalerFactor = 1;
MyTim->setPrescaleFactor(PrescalerFactor);
MyTim->setOverflow(0x10000); // Max Period value to have the largest possible time to detect rising edge and avoid timer rollover
MyTim->attachInterrupt(channelRising, TIMINPUT_Capture_Rising_IT_callback);
MyTim->attachInterrupt(channelFalling, TIMINPUT_Capture_Falling_IT_callback);
MyTim->attachInterrupt(Rollover_IT_callback);
MyTim->resume();
// Compute this scale factor only once
input_freq = MyTim->getTimerClkFreq() / MyTim->getPrescaleFactor();
}
void loop()
{
/* Print frequency and dutycycle measured on Serial monitor every seconds */
Serial.print((String)"Frequency = " + FrequencyMeasured);
Serial.println((String)" Dutycycle = " + DutycycleMeasured);
delay(1000);
}