[SOLVED] STM32 PWM duty cycle randomly inverts - Page 1
Forum section: electronics / general

author: mbless
author_rank: Regular Contributor
timestamp: March 17, 2017, 04:30:45 am
content: I am experiencing a strange, intermittent problem. I have an embedded STM32F446 outputting PWM signals on advanced timer 1, channels 2-4 and advanced timer 8, channels 2-4. I'm programming and debugging it with a ST-Link v2 from a Nucleo STM32F446 board.I set the PWM to count up with output compare mode 1. When I first program the board the output duty cycle is as expected, e.g. pulse=100 and period=499 gives ~10% duty cycle. The issue I found is that after I stop the debugger and reprogram the MCU without changing the code, eventually (2-5 flashes) the PWM duty cycle gets inverted; that is, the same pulse=100 and period=499 yields ~80% duty cycle! It behaves like it is counting down, but I have not changed anything in the code. After that time, stopping the debugger and reprogramming does not fix the problem, I have to cycle the power on the board or debugger.Another strange fact is that only channels 2 & 3 from each timer that has the inverse duty cycle. Neither channel 4 appears to be affected by whatever is going on even though it uses the same TIM_OC_InitTypeDef in the initialization.'ve used the same initialization code not the Nucleo board without any issues, so I was thinking it was a hardware problem, but there is never a problem the first time I power on the MCU and program it.Has anyone seen this before or know what is going on? I greatly appreciate any help! I've put the code below.Main code. MX_TIM1_Init and MXTIM8_Init setup the timers. PWM_Init starts the PWM channels and set the pulse value for the duty cycle. These 3 functions are in the other code block below.Code:#include "main.h"#include "stm32f4xx_hal.h"#include "diag/Trace.h"/* Private variables ---------------------------------------------------------*/CRC_HandleTypeDef hcrc;SPI_HandleTypeDef hspi3;TIM_HandleTypeDef htim1;TIM_HandleTypeDef htim8;UART_HandleTypeDef huart4;/* Private function prototypes -----------------------------------------------*/void Error_Handler(void);void SystemClock_Config(void);void MX_CRC_Init(CRC_HandleTypeDef);void GPIO_CLK_Init(void);TIM_HandleTypeDef MX_TIM1_Init(TIM_HandleTypeDef);TIM_HandleTypeDef MX_TIM8_Init(TIM_HandleTypeDef);void PWM_Init(TIM_HandleTypeDef, TIM_HandleTypeDef);SPI_HandleTypeDef MX_SPI3_Init(SPI_HandleTypeDef);UART_HandleTypeDef MX_UART4_Init(UART_HandleTypeDef);void Encoder_Init(void);void HallSensor_Init(void);void TFTpins_Init(void);void LEDs_Init(void);void DRV8301pins_Init(void);void DRV8301_Init(SPI_HandleTypeDef);int main(void){HAL_Init();/* Configure the system clock */SystemClock_Config();MX_CRC_Init(hcrc);/* Initialize all configured peripherals */GPIO_CLK_Init(); // enable GPIO port clockshtim1 = MX_TIM1_Init(htim1); // setup Timer1htim8 = MX_TIM8_Init(htim8); // setup Timer8PWM_Init(htim1, htim8); // start PWM channel countershspi3 = MX_SPI3_Init(hspi3); // setup SPI for DRV8301huart4 = MX_UART4_Init(huart4); // setup UART for FTDIEncoder_Init(); // setup encoder and switch interruptsHallSensor_Init(); // setup hall effect sensor interruptsLEDs_Init(); // setup LEDs and check functionalityDRV8301pins_Init(); // setup pins for DRV8301 (interrupts, current sense, etc.)TFTpins_Init(); // setup pins for TFT screen// DRV8301_Init(hspi3); // initialize DRV8301 settings via SPItrace_printf("System clock: %u Hz\n", SystemCoreClock);while (1){HAL_GPIO_TogglePin(LED_OV24V_GPIO_Port, LED_OV24V_Pin);HAL_Delay(1000);}Code where timers are setup and started.Code:#include "stm32f4xx_hal.h"extern void Error_Handler(void);void PWM_Init(TIM_HandleTypeDef htim1, TIM_HandleTypeDef htim8){// !!! add synchronization !!!// Start counterHAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3);HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_4);// // Set output compare > 0 to check signals__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 3);__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, 4);__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, 499);__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, 0);__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3, 1);__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_4, 2);}/* TIM1 init function */TIM_HandleTypeDef MX_TIM1_Init(TIM_HandleTypeDef htim){TIM_OC_InitTypeDef sConfigOC;htim.Instance = TIM1;htim.Init.Prescaler = 5;htim.Init.CounterMode = TIM_COUNTERMODE_UP;htim.Init.Period = 499;htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim.Init.RepetitionCounter = 0;if (HAL_TIM_PWM_Init(&htim) != HAL_OK){ Error_Handler(); }sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 10;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;if (HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_2) != HAL_OK){ Error_Handler(); }if (HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_3) != HAL_OK){ Error_Handler(); }if (HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_4) != HAL_OK){ Error_Handler(); }return htim;}/* TIM8 init function */TIM_HandleTypeDef MX_TIM8_Init(TIM_HandleTypeDef htim){TIM_OC_InitTypeDef sConfigOC;htim.Instance = TIM8;htim.Init.Prescaler = 5;htim.Init.CounterMode = TIM_COUNTERMODE_UP;htim.Init.Period = 499;htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim.Init.RepetitionCounter = 0;if (HAL_TIM_PWM_Init(&htim) != HAL_OK){ Error_Handler(); }sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 0;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;if (HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_2) != HAL_OK){ Error_Handler(); }if (HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_3) != HAL_OK){ Error_Handler(); }if (HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_4) != HAL_OK){ Error_Handler(); }return htim;}void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* htim_pwm){GPIO_InitTypeDef GPIO_InitStruct;if(htim_pwm->Instance==TIM1){/* Peripheral clock enable */__HAL_RCC_TIM1_CLK_ENABLE();/**TIM1 GPIO ConfigurationPA8 ------> TIM1_CH1PA9 ------> TIM1_CH2PA10 ------> TIM1_CH3PA11 ------> TIM1_CH4*/GPIO_InitStruct.Pin = GPIO_PIN_8|GB_H_Pin|GA_L_Pin|GA_H_Pin;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;HAL_GPIO_Init(GA_H_GPIO_Port, &GPIO_InitStruct);}else if(htim_pwm->Instance==TIM8){/* Peripheral clock enable */__HAL_RCC_TIM8_CLK_ENABLE();/**TIM8 GPIO ConfigurationPC6 ------> TIM8_CH1PC7 ------> TIM8_CH2PC8 ------> TIM8_CH3PC9 ------> TIM8_CH4*/GPIO_InitStruct.Pin = GPIO_PIN_6|GC_L_Pin|GC_H_Pin|GB_L_Pin;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF3_TIM8;HAL_GPIO_Init(GC_L_GPIO_Port, &GPIO_InitStruct);}}void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef* htim_pwm){if(htim_pwm->Instance==TIM1){/* Peripheral clock disable */__HAL_RCC_TIM1_CLK_DISABLE();/**TIM1 GPIO ConfigurationPA8 ------> TIM1_CH1PA9 ------> TIM1_CH2PA10 ------> TIM1_CH3PA11 ------> TIM1_CH4*/HAL_GPIO_DeInit(GA_H_GPIO_Port, GPIO_PIN_8|GB_H_Pin|GA_L_Pin|GA_H_Pin);}else if(htim_pwm->Instance==TIM8){/* Peripheral clock disable */__HAL_RCC_TIM8_CLK_DISABLE();/**TIM8 GPIO ConfigurationPC6 ------> TIM8_CH1PC7 ------> TIM8_CH2PC8 ------> TIM8_CH3PC9 ------> TIM8_CH4*/HAL_GPIO_DeInit(GC_L_GPIO_Port, GPIO_PIN_6|GC_L_Pin|GC_H_Pin|GB_L_Pin);}}

author: cyberfish
author_rank: Regular Contributor
timestamp: March 17, 2017, 01:32:24 pm
content: Is your debugger resetting the system after each flash?If not, you are restarting the program when the peripherals are not in reset state, and it's very hard to guarantee any kind of correctness in that case.It's possible that there is a hardware problem, too. I imagine ST probably didn't test this kind of use cases too much. When you are stopping and starting the CPU in an embedded system with a bunch of peripherals running in parallel, it becomes impossible to test every possible scenario.One guess is from the errata sheet (http://www.st.com/content/ccc/resource/technical/document/errata_sheet/c3/6b/f8/32/fc/01/48/6e/DM00155929.pdf/files/DM00155929.pdf/jcr:content/translations/en.DM00155929.pdf) -QuoteSome register writes may be ignored if you write them too soon after enabling peripheral clock. Not sure how this interacts with debugging.Once you observe the strange behaviour, you can also try to read out the register values, and see if they are what you expect.

author: Jeroen3
author_rank: Super Contributor
timestamp: March 17, 2017, 02:07:14 pm
content: Code:__HAL_RCC_TIM1_CLK_ENABLE();__NOP();__HAL_RCC_TIM1_FORCE_RESET();__NOP();__HAL_RCC_TIM1_RELEASE_RESET();__HAL_RCC_TIM1_FORCE_RESET() is used toexplicitlyreset the peripheral.You couldassumeyour init function is run only after system reset. But, in engineering, assumptions can be dangerous.What if you perform a software reset where you only reset the Program Counter? What if the program counter takes a hike due to ESD?Running init code on initialized peripherals could result in undefined behavior.

author: mbless
author_rank: Regular Contributor
timestamp: March 18, 2017, 07:32:42 pm
content: Thanks for the replies. Yes, the debugger resets the system after flashing. I tried stopping the debugger and resetting the system after the inverted PWM appeared, but the inverted output was still present.I also tried the force reset and release to no success. In fact, it made it so channels 2 & 3 were always inverted instead of randomly inverting.What did work was configuring the inverted output. It has something to do with the timers be advanced. The CubeMX code had the inverted output config calls, but I deleted them since I didn't have them during my testing.Code:sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 100;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;