cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 - PWM issue on TIM17...

thomas23
Associate II
Posted on January 16, 2017 at 18:28

Hello,

I am using a stm32f030k6t6 microcontroller and I have a problem with my pwm function on TIM17..

It is very strange: when I power up my controller, sometimes my pwm act as PWM1 and sometimes as PWM2.

I didn't code any condition about this in my program, so there is absolutely no reason for my controller to act as PWM1 or PWM2 when it wants. (I've programmed it as PWM1 in my code).

I am using TIM17 CH1 (CH1N is disabled by default), on PA7 (AF5).

(I am using 2 others PWM, TIM3 CH3 and CH4, PWM1 too, and they are working fine..)

What could possibly the cause of the problem? I was wondering if this could be caused by the voltage that is not stable at power up... But still, I've programmed 12 controllers with this program, and 3 of them are working as PWM1, 9 as PWM2, without any reason. The circuit is exactly the same...

Is it a default of the microcontroller (of this version..) ?

This problem drives me mad.. If someone could help, I would be grateful!

Here is the code:

/*

 * ===========================================================

 * Initializes Pins for PWM

 * ===========================================================

 */

void PWM_InitGPIOs(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);         // PA7 (PWM)

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;

    GPIO_Init(GPIOA, &GPIO_InitStructure);                         //

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;

    GPIO_Init(GPIOB, &GPIO_InitStructure);                         //

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5);     //

}

/*

 * ============================================================

 * Configure PWM

 * TIM17 CH1

 * ============================================================

 */

void PWM_Conf(void)

{

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

    TIM_OCInitTypeDef TIM_OCInitStructure;

    PWM_InitGPIOs();

    ////////////////////////////

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM17, ENABLE);

    // Time Base Configuration for TIM17

    TIM_TimeBaseStructure.TIM_Period = (PWM_TOTPERIODS - 1); // = (3M / 10000) - 1 = 299

    TIM_TimeBaseStructure.TIM_Prescaler = (SYSTEMFRQ / PWM_PREDIVISED_FREQ) - 1; // = 48M / 3M - 1 = 15

    TIM_TimeBaseStructure.TIM_ClockDivision = 0;

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM17, &TIM_TimeBaseStructure);

    // Channel 1 configuration on TIM17

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStructure.TIM_Pulse = PWM_PHASE; // = 1, because 0% should not be allowed

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    TIM_OC1Init(TIM17, &TIM_OCInitStructure); // Channel 1 configuration done

    // Enable outputs on TIM17

    TIM_CtrlPWMOutputs(TIM17, ENABLE);

    // TIM17 enable counter

    TIM_Cmd(TIM17, ENABLE);

}

Thank you,

Thomas

#stm32 #pwm #stm32f0
9 REPLIES 9
Posted on January 16, 2017 at 18:34

My unqualified guess is that you don't initialize nor fill-in completely the structures used for setting up the timer.

JW

S.Ma
Principal
Posted on January 16, 2017 at 19:01

The code put PB0 and PB1 as alternate without setting up which alternate (outside the shared code?). If have access to debugger, check out HW registers or dump registers to serial interface. Also make sure all global variables are forced.to zero at reset (startup function should be doing the job unless disabled, which, if missing, make the SRAM random at reset). Debug, inspect peripheral register...

Posted on January 16, 2017 at 18:52

Thank you for your answer.

Even if the problem was about initializing, it cannot explain the fact that my controller sometimes act as pwm1, sometimes as pwm2 without reprogramming it.

There is no 'no-value' in the register. It is either a 1 or a 0 (in this case, 0, if I have not initialized it), and it is not supposed to change between 2 powering up. So the behaviour of the microcontroller should not change either...

Thomas

Posted on January 16, 2017 at 19:08

> There is no 'no-value' in the register. It is either a 1 or a 0 (in this case, 0, if I have not initialized it),

Which register?

If you mean TIMx_CCMRy.OCzM which selects the output compare mode, it's 3 bits wide thus have 8 possible values.

The 'library' function you are using is writing into the whole TIMx_CCMRy register. The struct defined locally is uninitialized as all local nonstatic variables are, thus of random content. Several fields of the struct are merged to write a single register, so it writes some random garbage to it. That's a well deserved punishment for using 'libraries' 😉 .

Btw you can't distinguish inverted output from PWM1/PWM2 without having any other signals output reliably from the same timer and I believe inversion is more probable scenario. Closer investigation of the 'library' function would reveal the absolute truth, but I am not interested.

JW

Posted on January 16, 2017 at 19:21

Thank you for your response.

I have deleted the parts of code that were not relevant for my problem, but I just forgot to delete the part mentioning PB0 and PB1.

Unfortunately, I have no access to debugger.

I haven't modified the file startup_stm32f0xx.s. The only system file I have modified is system_stm32f0xx.c, to set my clock at 48MHz).

Where can I check that the startup function is not disabled (without using a dubugger)? (the only problem is on TIM17.. I suppose I should have many other problems if the SRAM was random at reset?)

I found on another forum a kind of strange problem like this one, on the same pin (PA7) with the same micro... is it possible that the problem comes from this pin? I can't test on another pin.. can't change my pcb for the moment. But all I can say is that my PWMs on PB0 and PB1 are working correctly (and I am initializing them exactly the same way).

Thomas

Posted on January 16, 2017 at 19:49

TIM_OCInitTypeDef TIM_OCInitStructure = { 0 }; // Stop this larger structure containing random crap from stack

Post complete and compilable examples that minimally demonstrate the problem.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on January 16, 2017 at 20:03

TIM_OCInitTypeDef TIM_OCInitStructure = { 0 }; // Stop this larger structure containing random crap from stack

While this probably works most of the time, the proper initialization value might be different from all-0.

The officially recommended method in this particular case is to use TIM_OCStructInit().

Below is a quote from the SPL's manual, note the '

Configuring only a few members of the structure

' part.

I don't and won't use the 'libraries'.

Jan

STM32F0xx Standard Peripherals Library: How to use the Library

Peripheral initialization and configuration

This section describes step-by-step how to initialize and configure a peripheral using the Peripheral’s Drivers. The peripheral will be referred to as PPP.

  1. In the main application file, declare a PPP_InitTypeDef structure, for example: PPP_InitTypeDef PPP_InitStructure;

The PPP_InitStructure is a working variable located in data memory area. It allows initializing one or more PPP instances.

  1. Fill the PPP_InitStructure variable with the allowed values of the structure member.

There are two ways of doing this:

  • Configuring the whole structure by following the procedure described below:

PPP_InitStructure.member1 = val1;

PPP_InitStructure.member2 = val2;

PPP_InitStructure.memberN = valN; /* where N is the number of the structure members */

The previous initialization step can be merged in one single line to optimize the code size:

PPP_InitTypeDef PPP_InitStructure = { val1, val2,.., valN}

  • Configuring only a few members of the structure: in this case the user should modify the PPP_InitStructure variable that has been already filled by a call to the PPP_StructInit(..) function. This ensures that the other members of the PPP_InitStructure variable are initialized to the appropriate values (in most cases their default values).

PPP_StructInit(&PPP_InitStructure);

PP_InitStructure.memberX = valX;

PPP_InitStructure.memberY = valY; /*where X and Y are the members the user wants to configure*/
  1. Initialize the PPP peripheral by calling the PPP_Init(..) function.

PPP_Init(PPP, &PPP_InitStructure);

  1. At this stage the PPP peripheral is initialized and can be enabled by making a call to PPP_Cmd(..) function.

PPP_Cmd(PPP, ENABLE);

The PPP peripheral can then be used through a set of dedicated functions. These functions are specific to the peripheral. 

Posted on January 16, 2017 at 20:42

The SPL does have specific structure initialization functions, but at the very least zeroing the structure, as the typical globalized equivalent would, allows for some consistency in any failure, and for the fact the library frequently ORs things together. Local/auto variables are very dangerous in this regard as there is no guarantee of the entry state of the stack space.

/**

* @brief Fills each TIM_OCInitStruct member with its default value.

* @param TIM_OCInitStruct: pointer to a TIM_OCInitTypeDef structure which will

* be initialized.

* @retval None

*/

void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct)

{

/* Set the default configuration */

TIM_OCInitStruct->TIM_OCMode = TIM_OCMode_Timing;

TIM_OCInitStruct->TIM_OutputState = TIM_OutputState_Disable;

TIM_OCInitStruct->TIM_OutputNState = TIM_OutputNState_Disable;

TIM_OCInitStruct->TIM_Pulse = 0x00000000;

TIM_OCInitStruct->TIM_OCPolarity = TIM_OCPolarity_High;

TIM_OCInitStruct->TIM_OCNPolarity = TIM_OCPolarity_High;

TIM_OCInitStruct->TIM_OCIdleState = TIM_OCIdleState_Reset;

TIM_OCInitStruct->TIM_OCNIdleState = TIM_OCNIdleState_Reset;

}
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on January 16, 2017 at 20:49

I generally don't use libraries either, I prefer to clearly see what register I am programming, but still I am using them sometimes when I program multiple functions using the same initializing, like here (as I was initializing PB0 (TIM3), PB1 (TIM3), and PA7 (TIM17) as PWMs, but only PA7 was not correctly working).

If the structure was wrong, it would have probably been wrong for the 3 PWMs I suppose. (But in the future I will be careful about using this structure without clearing it proprely, thank you for the advice).

I finally found out what was the problem.

TIM15 /16 / 17 are a bit different from other timers, and BDTR register cannot be ignored in this case.

I have initialized BDTR register, and it seems like everything is fine now! I have a real PWM1 (in this case I mean that active high corresponds to my CCR1 register), and there is no more random fail...

Thomas