cancel
Showing results for 
Search instead for 
Did you mean: 

PWM input capture

kmorri2004
Associate II
Posted on March 17, 2010 at 08:23

Hello,

I'm attempting to design a stabilization system for a hobby level RC-UAV, using stm8s Discovery dev board.

Right Now I'm just trying to get the mcu to:

1)Capture the input signals on-time and off-time, and store them in respective variables.

2)Output a PWM signal that identical to the input, as well as adjust the on-time and off-time to match.

So far I haven't had any success, if anybody has any suggestions or advice it would be much appreciated. The code that I have gotten the closest to working is the TIM1 or 2 Input capture example included with the firmware. Here's a copy of the portion I use.

/**

  ******************************************************************************

  * @file TIM2_Input_Capture\main.c

  * @brief This file contains the main function for TIM2 Input Capture example.

  * @author STMicroelectronics - MCD Application Team

  * @version V1.1.1

  * @date 06/05/2009

  ******************************************************************************

  *

  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS

  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE

  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY

  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING

  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE

  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.

  *

  * <h2><center>&copy; COPYRIGHT 2009 STMicroelectronics</center></h2>

  * @image html logo.bmp

  ******************************************************************************

  */

/* Includes ------------------------------------------------------------------*/

#include ''stm8s.h''

/**

  * @addtogroup TIM2_Input_Capture

  * @{

  */

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

#define TIM2ClockFreq  ((u32)2000000)

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

u32 LSIClockFreq = 0;

u16 ICValue1 =0, ICValue2 =0;

void main(void)

{

 

    /* Capture only every 8 events!!! */

  /* Enable capture of TI1 */

    TIM2_ICInit(TIM2_CHANNEL_1, TIM2_ICPOLARITY_FALLING, TIM2_ICSELECTION_DIRECTTI, TIM2_ICPSC_DIV8, 0x00);

 

    /* Enable CC1 interrupt */

    TIM2_ITConfig(TIM2_IT_CC1, ENABLE);

   

    /* Enable TIM2 */

  TIM2_Cmd(ENABLE);

       

    /* Clear CC1 Flag*/

  TIM2_ClearFlag(TIM2_FLAG_CC1);

 

    /* Connect LSI to COO pin*/

  GPIO_Init(GPIOE, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);

    CLK_CCOConfig(CLK_OUTPUT_LSI);

    CLK_CCOCmd(ENABLE);

   

   

    /* wait a capture on CC1 */

  while((TIM2->SR1 & TIM2_FLAG_CC1) != TIM2_FLAG_CC1);

  /* Get CCR1 value*/

    ICValue1 = TIM2_GetCapture1();

    TIM2_ClearFlag(TIM2_FLAG_CC1);

 

    /* wait a capture on cc1 */

  while((TIM2->SR1 & TIM2_FLAG_CC1) != TIM2_FLAG_CC1);

  /* Get CCR1 value*/

    ICValue2 = TIM2_GetCapture1();

    TIM2_ClearFlag(TIM2_FLAG_CC1);

  /* Compute LSI clock frequency */

  LSIClockFreq = (8 * TIM2ClockFreq) / (ICValue2 - ICValue1);

 

    /* Insert a break point here */

  while (1);  

}

11 REPLIES 11
lowpowermcu
Associate II
Posted on March 17, 2010 at 08:30

Hi Mken,

I remember that I have used this example and it works fine !

mit besten grüssen,

mcu fan

kmorri2004
Associate II
Posted on March 17, 2010 at 16:49

As the example is now it will start the CC1 timer on the first CC1 FLag active and SR1 low and record its value to ICValue1, then it will record the second instance when CC1 Flag is active and SR1 is low and record its value to ICValue2 however if you use the debugger and watch ICValue1, ICValue2, LSICLKFreq, and add in another variable that is used in an equations such as x = (ICValue2 - ICValue1) you'll notice that ICValue1 will have a value of y and ICValue2 will have a value of y+y.

However if you switch out the GPIO and LSI output commands with a PWM out  on a different channel and say set the PWM out to 50Hz with a 10% on-time and try to have this example tell you either your on-time or your off-time, it can't do it because it counts for the whole cycle, as in if it starts counting at the first falling edge then on the 2nd falling edge it takes the new counter value which is one entire pulse period which at 50Hz is 40000 counts.

Which isn't what I'm trying to accomplish, I want to capture the number of counts the inputs on-time is store it in a variable then reset the counter (since I can only use one channel of the timers per channel of the reciever) and capture the number of counts the input off-time is and store it in a different variable, add the two variables together to get the pulses period so I could use it for the PWM out prescaler period, and then use either the on-time variable or the off-time variable to control the duty-cycle of the PWM out, and still have the Capture counter be dynamic so if the on-time of the pulse changes the on-time of the output changes the same amount.

lowpowermcu
Associate II
Posted on March 18, 2010 at 10:43

Hi Mken C- learner,

I don't understand your interpretation of the example.

My own understanding of the example is:

Timer 2 channel 1 is configured to capture external signal on falling edge, the signal in input is divided by 8.

The timer 2 (TIM2) counter starts counting when executing this function: TIM2_Cmd(ENABLE);

when a falling edge is detected the CC1 flag is set and CCR1 register is stored in ICValue1 when executing ICValue1 = TIM2_GetCapture1();

when the second falling edge is detected the CC1 is set again and CCR1 register is stored in ICValue2 when executing ICValue2 = TIM2_GetCapture1();

PS: when the CC1 is set the counter value is automatically saved in the CCR1 register

I hope this helps.

Mit besten grüssen,

MCU fan

kmorri2004
Associate II
Posted on March 18, 2010 at 19:14

My apologys I hadn't looked through the rm0016 document well enough to understand how the CC1 Flag worked, what I'm trying to say is that when CC1's Flag is active and it stores the value in ICValue1 it doesn't reset the counter on the next Flag activation. Basicly if CC1 flag is activated at say 15000 counts, that value is stored in ICValue1, but the counter is reset after the capture so that when the CC1 flag is activated a second time at 45000 counts from start, then that value gets placed into ICValue2, and the only time the counter gets reset is when it overflows.

What I'm wanting to do is start the counter at either the falling edge or the rising edge (which ever I decide to set it to) and have the counter reset on the opposite edge.

So for example of what I'd Like to do is, when a rising edge is detected the counter starts, then at the falling edge the counter stops then that value is placed in ICValue1 and the counter is restarted at the falling edge and is then stopped at the next rising edge, captured and saved to ICValue2, and the cycle is repeated again for the next pulse cycle.

Also after looking threw the RM0016 document I found the PWM input example that does just that HOWEVER it only works for input on channel 1 or channel 2 on TIM1 but I need to use 4 channels, since the the signals coming from my RC-receiver are set up so that when CH1's pulse goes low CH2's pulse goes high, and when CH2's pulse goes low CH3's pulse goes high, and so on for 8 channels although I'm only gonna use 4 of them.

Here an image of the servo pulses from the receiver.

0690X00000603O0QAI.jpg

And here is how I wanna try and capture the signals.

0690X00000603O5QAI.jpg

I apologize if the images are oversized.

lowpowermcu
Associate II
Posted on March 19, 2010 at 08:50

Hi Mken C- learner,

I have looked for in the reference manual if something could help you.

PWM input couldn't help you because it counts the cycle report and total period but what you need in your application is measuring both high and low levels duration. Isn't it ?

But I have a question: why you don't ICValue 2 - ICValaue1 and you have the duration you need ?

If the solution above isn't suitable for you, I can suggest to look for in the reference manual how to use the trigger selection ''TS'' bits in SMCR register it could help you because there description on how to reset the counter on external event.

Keep me informed if you have updates / questions ?

Mit besten grüssen,

MCU fan

kmorri2004
Associate II
Posted on March 19, 2010 at 10:01

Thanks lowpowermcu for the help, using ICValue2 - ICValue1 only works to a degree since the counter doesn't reset until overflow which causes random numbers in both variables.

However I did manage to get it 90% figured out, I have it now where the mcu can replicate and match 2.5 of the 4 inputs, channels 3 and 4 work perfectly but channel 2 is changing with ch1 commands and ch1 simply is being captured correctly, instead ch1 is counting the entire period from high pulse to high pulse....

EUREKA!!

 I just figured out how to do it, here's a copy of the code there are a few variables that aren't needed I'm just using them to try and figure out how to duplicate the inputs cycle time to clean up and stabilize the outputs. Also some of the comments are incorrect due to my copy and pasting them from the example code and not modifying them.

#include ''stm8s.h''

u16 CH1on =0, CH2on =0, CH3on = 0, CH4on = 0;

u16 CH1ontime = 0, CH2ontime = 0, CH3ontime = 0, CH4ontime = 0;

u16 dtcy = 0, periodcount = 39999, periodcountX = 0, periodcounty = 0, periodcountz = 0;

void main(void){

   

    TIM1_DeInit();

       

    /* Enable capture*/

    TIM1_ICInit(TIM1_CHANNEL_1, TIM1_ICPOLARITY_RISING, TIM1_ICSELECTION_DIRECTTI, TIM1_ICPSC_DIV1, 0);

    TIM1_ICInit(TIM1_CHANNEL_2, TIM1_ICPOLARITY_FALLING, TIM1_ICSELECTION_DIRECTTI, TIM1_ICPSC_DIV1, 0);

    TIM1_ICInit(TIM1_CHANNEL_3, TIM1_ICPOLARITY_FALLING, TIM1_ICSELECTION_DIRECTTI, TIM1_ICPSC_DIV1, 0);

    TIM1_ICInit(TIM1_CHANNEL_4, TIM1_ICPOLARITY_FALLING, TIM1_ICSELECTION_DIRECTTI, TIM1_ICPSC_DIV1, 0);

       

    /*Set TIM1 SMCR register to trigger counter reset on Ch1 Rising Edge*/

    TIM1->SMCR = 0x44;

       

    /* Enable TIM2 */

  TIM1_Cmd(ENABLE);

   

    /* Enable CC1 interrupt */

    TIM1_ITConfig(TIM1_IT_CC1, ENABLE);

    TIM1_ITConfig(TIM1_IT_CC2, ENABLE);

    TIM1_ITConfig(TIM1_IT_CC3, ENABLE);

    TIM1_ITConfig(TIM1_IT_CC4, ENABLE);

       

    /* Clear CC1 Flag*/

  TIM1_ClearFlag(TIM1_FLAG_CC1);

    TIM1_ClearFlag(TIM1_FLAG_CC2);

    TIM1_ClearFlag(TIM1_FLAG_CC3);

    TIM1_ClearFlag(TIM1_FLAG_CC4);

       

    TIM2_DeInit();

   

    TIM3_DeInit();

           

    for (;;){   

   

    while((TIM1->SR1 & TIM1_FLAG_CC1) != TIM1_FLAG_CC1);

  /* Get CCR1 value*/

    CH1ontime = TIM1_GetCapture1();

    TIM1_ClearFlag(TIM1_FLAG_CC1);

   

    while((TIM1->SR1 & TIM1_FLAG_CC2) != TIM1_FLAG_CC2);

  /* Get CCR1 value*/

    CH2on = TIM1_GetCapture2();

    TIM1_ClearFlag(TIM1_FLAG_CC2);

   

    while((TIM1->SR1 & TIM1_FLAG_CC3) != TIM1_FLAG_CC3);

  /* Get CCR1 value*/

    CH3on = TIM1_GetCapture3();

    TIM1_ClearFlag(TIM1_FLAG_CC3);

   

    while((TIM1->SR1 & TIM1_FLAG_CC4) != TIM1_FLAG_CC4);

  /* Get CCR1 value*/

    CH4on = TIM1_GetCapture4();

    TIM1_ClearFlag(TIM1_FLAG_CC4);

   

    periodcountX = (CH2on + CH2ontime);

    periodcounty = (CH3on + CH3ontime);

    periodcountz = (CH4on + CH4ontime);

   

    CH2ontime = (periodcount-CH2on);

   

    CH3ontime = (periodcount-(CH3on - CH2on));

   

    CH4ontime = (periodcount-(CH4on - CH3on));

   

    /*TIM1_PWMIConfig(TIM1_CHANNEL_2,TIM1_ICPOLARITY_RISING, TIM1_ICSELECTION_DIRECTTI, TIM1_ICPSC_DIV1,0);*/

    TIM2_TimeBaseInit(TIM2_PRESCALER_1, periodcount);   

    // Initialise output channel 2 of TIM3.

  TIM2_OC3Init(TIM2_OCMODE_PWM1, TIM2_OUTPUTSTATE_ENABLE, CH1on, TIM2_OCPOLARITY_LOW);

  // Initialise output channel 2 of TIM3.

  TIM2_OC1Init(TIM2_OCMODE_PWM1, TIM2_OUTPUTSTATE_ENABLE, CH2ontime, TIM2_OCPOLARITY_LOW);   

  // Enable TIM3.

  TIM2_Cmd(ENABLE);

   

    /*TIM1_PWMIConfig(TIM1_CHANNEL_2,TIM1_ICPOLARITY_RISING, TIM1_ICSELECTION_DIRECTTI, TIM1_ICPSC_DIV1,0);*/

    TIM3_TimeBaseInit(TIM3_PRESCALER_1, periodcount);   

    // Initialise output channel 2 of TIM3.

  TIM3_OC2Init(TIM3_OCMODE_PWM1, TIM3_OUTPUTSTATE_ENABLE, CH3ontime, TIM3_OCPOLARITY_LOW);

  // Initialise output channel 2 of TIM3.

  TIM3_OC1Init(TIM3_OCMODE_PWM1, TIM3_OUTPUTSTATE_ENABLE, CH4ontime, TIM3_OCPOLARITY_LOW);   

  // Enable TIM3.

  TIM3_Cmd(ENABLE);

    }

}

lowpowermcu
Associate II
Posted on March 19, 2010 at 22:24

Hi Mken,

I have a question before sinking in the code.

What is the precision that you need when measuring the servo pulses ? What is the minimum, maximum and steps of the PWM signals to be measured ? Is it of some µs or ms ? what about your clock configuration ?

This detail could considerably simplify your code.

With regards,

MCU fan

kmorri2004
Associate II
Posted on March 20, 2010 at 06:17

I can't find it at the moment but I remember reading that 5uS is equivalent to 1 degree on the servo, I'll be  running Brushless motors but they still use the same exact pulses as a rc servo. The minimum ontime is 1mS neutral is 1.5mS and Maximum is 2ms, I want it as precise as possible. As for the clock I have the Timers running at default 2MHz, the code could very well be simplified down to possible 6-10lines if the PWMIConfig command didn't ONLY work for TIM1 CH1 or TIM1 CH2 since it uses the complementary channel but the trigger is only available for TI1F_ED, TI1FP1, and TI2FP2, also the fact that this is the only way I've actually gotten the code to work with all 4 channels at once, although so far it ONLY produces a good pulse on the oscilliscope, tryed it on some servos and the servos all swing to max limit one way and don't respond to any command signals even with the controls set to neutral position.

lowpowermcu
Associate II
Posted on March 20, 2010 at 09:16

Hi Mken,

I have an other question is there a synchronization between the four signals or they are independent ?

Is the pwm frame constant as you have drawn in the figure above: 22.5ms. If it the case why you need the off-time, you need the on-time and you can sustract the frame duration from the on-time to compute the off-time ????

I am trying to understand more your application so I can suggest you the better config for timer.

Mit besten grüssen,

MCU Lüfter