cancel
Showing results for 
Search instead for 
Did you mean: 

Timer in PWM Capture Mode

sorena
Associate II
Posted on November 23, 2015 at 15:16

Hello every One
I am trying to read PWM signal parameters (Frequency & Duty Cycle) with timer 2 of STM32f4 IC. According to data sheet There is two modes of reading PWM signal.
1- capture mode which reads Pulse widths
2- Pairing Mode with use of two ICs configuration.
Since I am not expert in working with stm32f4, I provided the code mentioned below to read Frequency & Duty Cycle, with the help of other codes written in this forum.
First of all I want to know : Is that possible to read Duty Cycle with the first mode or not, and if yes how it should be done.?!
Secondly when i run the below code I can not read frequency and Duty Cycle as correct as should be. The entrance signal has the frequency of 45 Hz and a duty of 40 percent. \
I think the problem is with the time base configurations in which the prescaler and period should be defined.
I will be Please if someone can help me to overcome above issues.
Thanks.
Soren




void TIM2_IRQHandler(void)
{
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
if (TIM_GetITStatus(TIM2, TIM_IT_CC1)!= RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
IC2Value_ = TIM_GetCapture1(TIM2);
if (IC2Value_ != 0) {
DutyCycle_ = (TIM_GetCapture2(TIM2) * 100) / IC2Value_;
Frequency_ = (RCC_Clocks.HCLK_Frequency)/2 / IC2Value_;
} else {
/* Reset data */
DutyCycle_ = 0;
Frequency_ = 0;
}
}
}
/* --------------------------------------PWM Input --------------------------------------*/
TIM_ICInitTypeDef TIM_ICInitStruct;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// /* --------------------------------------Timer2--------------------------------------*/
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* GPIOC clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);
/* TIM2 GPIO pin configuration : CH1=PA0, C2=PA1, CH3=PB10, CH4=PB11 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10| GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect pins to TIM3 AF2 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2 );
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2 );
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2 );
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_TIM2 );
/* Intrupt Config */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Time base configuration - SystemCoreClock = 168000000 for 168 MHz board ABP1 @ 42 MHz (DIV4) */
TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (((SystemCoreClock / 1000000) / 2) - 1); // Shooting for 1 MHz, (1us)
TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF; // Maximal
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* Enable capture*/
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
// /* Select the TIM2 Input Trigger: TI2FP2 */
// TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);
TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1);
// /* Select the slave Mode: Reset Mode */
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM2,TIM_MasterSlaveMode_Enable);
/* Enable TIM2 */
TIM_Cmd(TIM2, ENABLE);
/* Enable CC1-4 interrupt */
TIM_ITConfig(TIM2,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3| TIM_IT_CC4, ENABLE);

#stm32f4-timer #mucapture #pwm
14 REPLIES 14
Posted on November 23, 2015 at 18:55

Grief this is such a muddle.

Ok, if you are programming the counter with 1 MHz ticks, it's NOT going to be ticking at the HCLK rate. You'd divide the ticks into 1000000 to get frequency, right? You seem to have pulled a bunch of different code in, an end up configuring CH4 which doesn't support PWM Input.

/**************************************************************************************/
//
// Portion from
// STM32 RC Servo Demo for 168 MHz STM32F4 Discovery - sourcer32@gmail.com
//
/**************************************************************************************/
volatile uint32_t Reading[2]; // Readings from PA0 TIM2_CH1 (1us units)
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
Reading[0] = TIM_GetCapture1(TIM2); // Period (us) ~20000
Reading[1] = TIM_GetCapture2(TIM2); // Duty/Width (us) ~1500 or whatever (600-2400 us)
}
}
/**************************************************************************************/
void TIM2_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* GPIO clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* Configure GPIO input for timer */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // PA0 TIM2_CH1_ETR (using CH1)
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Connect TIM2 pins to AF */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2);
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_Init(&NVIC_InitStructure);
/* Time base configuration - SystemCoreClock = 168000000 for 168 MHz board ABP1 @ 42 MHz (DIV4) */
TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (((SystemCoreClock / 1000000) / 2) - 1); // Shooting for 1 MHz, (1us)
TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF; // Maximal (32-bit)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* Configure PWM Input Capture */
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; // PA0 TIM2_CH1
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
/* Select the TIM2 Input Trigger: TI1FP1 */
TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1);
/* Select the slave Mode: Reset Mode */
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
/* TIM Interrupts enable */
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************************/
while(1)
{
if (Reading[1] != 0xFFFFFFFF)
{
printf(''%10d %10d
'', Reading[0], Reading[1]);
if (Reading[0])
printf(''%lf Hz, Duty %lf %%
'', 1000000.0 / (double)Reading[0], (0 * (double)Reading[1]) / (double)Reading[0] );
Reading[1] = 0xFFFFFFFF; // Invalidate
}
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on November 23, 2015 at 18:57

19999       1499

50.002500 Hz, Duty 7.495375 %

     19999       1499

50.002500 Hz, Duty 7.495375 %

     19999       1499

50.002500 Hz, Duty 7.495375 %

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on November 23, 2015 at 19:02

Using the full precision of the timer...

/**************************************************************************************/
//
// Portion from
// STM32 RC Servo Demo for 168 MHz STM32F4 Discovery - sourcer32@gmail.com
//
/**************************************************************************************/
volatile uint32_t Reading[2]; // Readings from PA0 TIM2_CH1 (84MHz units)
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
Reading[0] = TIM_GetCapture1(TIM2); // Period
Reading[1] = TIM_GetCapture2(TIM2); // Duty/Width
}
}
/**************************************************************************************/
void TIM2_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* GPIO clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* Configure GPIO input for timer */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // PA0 TIM2_CH1_ETR (using CH1)
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Connect TIM2 pins to AF */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2);
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_Init(&NVIC_InitStructure);
/* Time base configuration - 168 MHz board ABP1 @ 42 MHz (DIV4), TIM2 Clock is 84 MHz */
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF; // Maximal (32-bit)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* Configure PWM Input Capture */
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; // PA0 TIM2_CH1
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
/* Select the TIM2 Input Trigger: TI1FP1 */
TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1);
/* Select the slave Mode: Reset Mode */
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
/* TIM Interrupts enable */
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************************/
while(1)
{
if (Reading[1] != 0xFFFFFFFF)
{
printf(''%10d %10d
'', Reading[0], Reading[1]);
if (Reading[0])
printf(''%lf Hz, Duty %lf %%
'', 84000000.0 / (double)Reading[0], (0 * (double)Reading[1]) / (double)Reading[0] );
Reading[1] = 0xFFFFFFFF; // Invalidate
}
}
1679997 125997
000089 Hz, Duty 7.499835 %
1679997 125997
000089 Hz, Duty 7.499835 %
1679997 125997
000089 Hz, Duty 7.499835 %
1679997 125997
000089 Hz, Duty 7.499835 %
1679997 125997
000089 Hz, Duty 7.499835 %

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
sorena
Associate II
Posted on November 24, 2015 at 12:43

Thank you very much for the code you provided. I have to check it in future days and will ask my questions later. 

Thanks again
sorena
Associate II
Posted on December 08, 2015 at 09:33

Hi clive

Thanks for the corrected code. I will check it and report the results. 

I have a unique question which I think that is the question of many stm32f4 developers 

around the web. The question is that, how many PWM measurements (fr & DC) can one implement  using a single timer, with stm32f4 devices.?

Because in stm32f4 devices we have multiple channels for a timer and in first look, it seems that we could have 4 input measurements simultaneously. Also when you look at the datasheet, timer diagrams shows the fact in this way. But in practice there is no possibility of reading multiple channels simultaneously. 

I need stm32f4 to put on the way of radio control so I need 8 PWM inputs , and 8 outputs at least, is this possible?

How can I overcome?

I will be glad if you clarify me about the issue?

Thank you in advance for your Help.

Posted on December 08, 2015 at 12:58

I have a unique question which I think that is the question of many stm32f4 developers

And which, oddly, has been asked and answered a few dozen times.

Each timer has a SINGLE counting element. PWM Input mode uses two channel slots (1 and 2), and RESETs the only counter. So you're at ONE signal per TIM.

To get to four signals per TIM you need to use Input Capture mode and trigger on both edges, and use that to measure the mark and space of the signal (delta one measurement to the next) and from this you can determine the frequency and duty of the signals. It requires more interrupts and management, but not overwhelming at 50 Hz. I'm pretty sure I've demonstrated this method.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
sorena
Associate II
Posted on December 08, 2015 at 14:05

So you say that it is possible to have 4 signals on a single timer with use of input capture mode. Meaning, there should be a method to detect polarity of signal on 4 GPIO PINs connected to 4 channels of a timer.

I have to try for that. 

Do you have any sample code on this?

rob239955_stm1_stmicro
Associate II
Posted on December 08, 2015 at 14:23

Clive, since you seem to know what you're talking about - and I dont want to start a new thread.

I'm trying to do a similar thing ( PWM input capture) with a STM32F030F4P6 which is the smallest of the set.

I've also used the STCube tool to generate code but there are a couple of things I can't get my head around  and I don't want to cut and paste everyone elses code before I fully understand.

1) The Cube tool allows setting up the Channels Input capture as Direct or Indirect. I can't find any reference to 'Indirect' in any of the ST datasheets, so either they are using different terminology or I've blatently missed something.

2) The suggested technique of targeting the two channels to the same input is eluding me.

I can see how its supposedly done in firmware, but I can't see a way to do it using STCube tool.

The data sheet and cube tool only suggests there are different pins for each of the channels - not the same.

Advice from anywhere appreciated.

Thanks.

Posted on December 08, 2015 at 16:10

I'm not an advocate for the Cube tools, you'll need to dig through that yourself.

The Indirect mode basically allows the routing of the filtered/edged CH1 (TI1) signal into CH2/CCR2, and separately TI2 into CH1/CCR1. CH3/4 also pair, but not in PWM Input mode. Look at the block diagram from the TIM as it relates to the input conditioning, and routing between the CH1/2 and CH3/4 pairs.

What PWM Input mode does is assign one channel latch to each edge, and resets at the period. So typically one latch holds the ticks for the entire period, ie rising-to-rising edges, while the second latch holds the tick for the mark duty, ie rising-to-falling edges.

It's particularly useful for high frequencies, where you don't want to have heavy interrupt loading, and can just read the latch periodically.

The resetting is a pain, as is the need to use either CH1/CH2 as an input, because it effectively soaks up a timer per signal.

0690X00000602ajQAA.jpg

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..