2025-08-14 1:40 AM
So I want to use a capture compare to measure an input frequency 0.1Hz to 100kHz. Peripheral clock is set to 32MHz to cover full range and maximise speed of calculations. At lower frequency I have to count the number of times the over capture call-back is called, to put into the calculation. However there are a few frequencies where the over capture and edge detection IRQs have a beating effect, I have delt with this on other processors by ensuring the order IRQs are delt with but it doesn't seem to be working here.
Sometimes the last over capture is called first, other times in is not, leading to a large jump in my calculated result. As anyone any experience with this issue on the STM32 TIM peripherals?
/******************************************************************************
Function : Calculate_Frequency
Return : Frequency post calculation as a float
*****************************************************************************/
#define TIMCLOCK 32000000.00 //System clock value post PLL
#define PRESCALAR 1
unsigned int Pending_Result;
unsigned int Frequency_Counter_Overflow;
unsigned int IC_Val1_OFC;
unsigned int IC_Val2_OFC;
unsigned int Overflow_Difference;
unsigned int Last_Overflow_Count;
unsigned int IC_Val1;
unsigned int IC_Val2;
unsigned int Difference;
unsigned int IC_Val1_Array[8];
unsigned int IC_Val2_Array[8];
unsigned int Difference_Array[8];
unsigned int frequency_Array[8];
unsigned int Sum_Array[8];
unsigned int Overflow_Difference_Array[8];
unsigned int IC_Val1_OFC_Array[8];
unsigned int IC_Val2_OFC_Array[8];
unsigned int x;
unsigned int Sum_Used;
float frequency;
//This table removes the need for a multiplication, speeding up the capture to output time.
unsigned long Overflow_Lookup[256] = {
0, 65535, 131070, 196605, 262140, 327675, 393210, 458745, 524280, 589815, 655350,
720885, 786420, 851955, 917490, 983025, 1048560, 1114095, 1179630, 1245165, 1310700,
1376235, 1441770, 1507305, 1572840, 1638375, 1703910, 1769445, 1834980, 1900515, 1966050,
2031585, 2097120, 2162655, 2228190, 2293725, 2359260, 2424795, 2490330, 2555865, 2621400,
2686935, 2752470, 2818005, 2883540, 2949075, 3014610, 3080145, 3145680, 3211215, 3276750,
3342285, 3407820, 3473355, 3538890, 3604425, 3669960, 3735495, 3801030, 3866565, 3932100,
3997635, 4063170, 4128705, 4194240, 4259775, 4325310, 4390845, 4456380, 4521915, 4587450,
4652985, 4718520, 4784055, 4849590, 4915125, 4980660, 5046195, 5111730, 5177265, 5242800,
5308335, 5373870, 5439405, 5504940, 5570475, 5636010, 5701545, 5767080, 5832615, 5898150,
5963685, 6029220, 6094755, 6160290, 6225825, 6291360, 6356895, 6422430, 6487965, 6553500,
6619035, 6684570, 6750105, 6815640, 6881175, 6946710, 7012245, 7077780, 7143315, 7208850,
7274385, 7339920, 7405455, 7470990, 7536525, 7602060, 7667595, 7733130, 7798665, 7864200,
7929735, 7995270, 8060805, 8126340, 8191875, 8257410, 8322945, 8388480, 8454015, 8519550,
8585085, 8650620, 8716155, 8781690, 8847225, 8912760, 8978295, 9043830, 9109365, 9174900,
9240435, 9305970, 9371505, 9437040, 9502575, 9568110, 9633645, 9699180, 9764715, 9830250,
9895785, 9961320, 10026855, 10092390, 10157925, 10223460, 10288995, 10354530, 10420065,
10485600, 10551135, 10616670, 10682205, 10747740, 10813275, 10878810, 10944345, 11009880,
11075415, 11140950, 11206485, 11272020, 11337555, 11403090, 11468625, 11534160, 11599695,
11665230, 11730765, 11796300, 11861835, 11927370, 11992905, 12058440, 12123975, 12189510,
12255045, 12320580, 12386115, 12451650, 12517185, 12582720, 12648255, 12713790, 12779325,
12844860, 12910395, 12975930, 13041465, 13107000, 13172535, 13238070, 13303605, 13369140,
13434675, 13500210, 13565745, 13631280, 13696815, 13762350, 13827885, 13893420, 13958955,
14024490, 14090025, 14155560, 14221095, 14286630, 14352165, 14417700, 14483235, 14548770,
14614305, 14679840, 14745375, 14810910, 14876445, 14941980, 15007515, 15073050, 15138585,
15204120, 15269655, 15335190, 15400725, 15466260, 15531795, 15597330, 15662865, 15728400,
15793935, 15859470, 15925005, 15990540, 16056075, 16121610, 16187145, 16252680, 16318215,
16383750, 16449285, 16514820, 16580355, 16645890, 16711425};
float Calculate_Frequency (void)
{
if (Pending_Result == 1)
{
Overflow_Difference = (IC_Val2_OFC - IC_Val1_OFC); //work out how many periods have passed between edges
if (Overflow_Difference > 255) //it's a very slow frequency so we can work it out
{
if (IC_Val2 > IC_Val1) //no interim period to deal with
{
Difference = (Overflow_Difference * 0xFFFF) + (IC_Val2 - IC_Val1); //(total passed periods * full period count) + (second edge time - first edge time)
}
else if (IC_Val1 > IC_Val2) //interim period to deal with
{
Difference = (((Overflow_Difference) * 0xFFFF) - IC_Val1) + IC_Val2; //((total passed periods * full period count) - first edge time[interim]) + second edge time
}
}
else
{
if (IC_Val2 > IC_Val1) //no interim period to deal with
{
Difference = Overflow_Lookup[Overflow_Difference] + (IC_Val2 - IC_Val1); //(total passed periods * full period count) + (second edge time - first edge time)
Sum_Used = 1;
}
else if (IC_Val1 > IC_Val2) //interim period to deal with
{
Difference = (Overflow_Lookup[Overflow_Difference] - IC_Val1) + IC_Val2; //((total passed periods * full period count) - first edge time[interim]) + second edge time
Sum_Used = 2;
}
}
Frequency_Counter_Overflow = 0; //reset the period counter
__HAL_TIM_SET_COUNTER(&htim2, 0); // reset the counter
TIM2->SR &= ~TIM_SR_UIF; //clear the global IRQ flag, before re-enabling the IRQs
TIM2->SR = ~(TIM_SR_CC1OF | TIM_SR_TIF); // Clear the bitwise flags
__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_CC1); //restart IRQ now calculations are complete
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
frequency = TIMCLOCK/Difference;
Last_Overflow_Count = Overflow_Difference;
Pending_Result = 0; //clear the flag that a result is awaiting processing
//Bug trap code ----------------------------------------------------------------
IC_Val1_Array[x] = IC_Val1;
IC_Val2_Array[x] = IC_Val2;
Difference_Array[x] = Difference;
frequency_Array[x] = frequency;
Sum_Array[x] = Sum_Used;
Overflow_Difference_Array[x] = Overflow_Difference;
IC_Val1_OFC_Array[x] = IC_Val1_OFC;
IC_Val2_OFC_Array[x] = IC_Val2_OFC;
x++;
if (x>7)
{
x=0;
}
//Bug trap code end -------------------------------------------------------------
return (frequency);
}
else
{
if (Frequency_Counter_Overflow >= (Last_Overflow_Count + 250))
{
return(200000); //dummy value to show no calculation as been done, but enough time as passed that there is no valid signal
}
else
{
return(300000); //dummy value to show no calculation as been done
}
}
}
/******************************************************************************
ISR : Input Capture Callback
*****************************************************************************/
unsigned int Is_First_Captured;
unsigned int Miss_Intrim_Edge;
extern unsigned int Both_Edges;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (Pending_Result == 0) //there is not already a result awaiting processing
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
if (Is_First_Captured==0) // if the first edge is not captured
{
IC_Val1_OFC = Frequency_Counter_Overflow; //record the period in which this edge occurred
IC_Val1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // read the first value
Is_First_Captured = 1; // set the first captured as true
}
else if ((Both_Edges == 1) && (Miss_Intrim_Edge == 0)) //miss the alternate edge, in all edges mode
{
Miss_Intrim_Edge = 1;
}
else if ((Both_Edges == 0) || (Miss_Intrim_Edge == 1)) // If the first edge is captured, now we will capture the second edge
{
IC_Val2_OFC = Frequency_Counter_Overflow; //record the period in which this edge occurred
IC_Val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // read second value
Is_First_Captured = 0; // set it back to false
Miss_Intrim_Edge = 0; // set it back to false
Pending_Result = 1; //set flag that a result calculation is required
__HAL_TIM_DISABLE_IT(htim, TIM_IT_CC1); //stop IRQ to maximise calculation time
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
}
}
}
}
/******************************************************************************
ISR : Input Capture overflow
*****************************************************************************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
Frequency_Counter_Overflow ++; //a full timer period as passed, increment the period counter
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2);
}
Solved! Go to Solution.
2025-08-27 6:19 AM
So I have changed the STM32L010C6 for a STM32U031C6T6, which as a 32 bit timer on the same pin for Tim2 channel 1.
However when I configure the device I can't get an interrupt to run (I have tried 2 different uC).
So I had access to Tim2 channel 4, changed the setup to channel 4 throughout and this works perfectly, the 32 bit timer has enough range to cover the measurements I need.
My only issue now is that I need to solution to be Tim2 channel 1 as I already have hardware built.
Should I raise this as a separate question in relation to this issue? .
2025-08-27 7:00 AM
Hello, did you try to map TIM2_CH1 on a different I/O.
As far as I could see from the STM32U031x4/6/8 data sheet, there are 3 possibilities:
TIM2_CH1 mapped on PA0 (AF1)
TIM2_CH1 mapped on PA5 (AF1)
TIM2_CH1 mapped on PA15 (AF1)
Also did you check that TIM2_DIER.CC1IE bit is set when you use TIM12_CH1?
2025-08-27 7:01 AM
Hello @SandMan
What "issues", exactly, do you encounter with TIM2 channel 1?
What tests/investigations/debugging have you done to find out what's going on?
2025-08-27 7:52 AM
Thanks for the reply,
So I have a square wave into PA0, I have configured the timer as Tim2 Channel 1 capture compare direct mode, internal clk, and the IRQ (or callbcak) are never reached in the code.
I have moved the square wave (via a wire link) to PA1 Tim2 Channel 2, same settings and the capture works as expected.
I have also moved the square wave to PA3 Tim2 Channel 4 same settings and this also works.
Back to channel 1 still not working.
I have had a good look through the HAL settings Vs the data sheets and can't see anything wrong.
I have viewed SFRs and all registers seem to be set correctly, yet the IRQ never runs for Tim2 Channel 1.
2025-08-27 7:57 AM - edited 2025-08-27 7:58 AM
Start with checking if you have the PA0 pin of mcu physically connected to the point where you apply the signal. Think of solder bridges if you use something like Nucleo, or of bad solder joints, shorts, incorrect documentation, if you use your own board. A simple good check is to reverse things: set PA0 as GPIO output, run a simple "blinky"-like program and observe signal at the expected connection point.
Then read out and check/post relevant GPIO, TIM, NVIC registers content. A generic "interrupt does not fire" checklist here.
You are still talking about the 'L0 as in the initial post, or the 'U0?
JW
2025-08-27 8:15 AM
Thanks,
Tried 2 different U mcu's neither work in the mode described.
It's my own PCB and hence why I am keen to get the tracked pin PA0 working, I will work through the check list.
2025-09-01 6:00 AM
Thank you everyone for your help, I have moved to the STM32U and moved to Tim 2 chan 2 and this is the path of least resistance, it's a small soldered link on my PCB.