PWM output on STM32 - Page 1
Forum section: electronics / general

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 22, 2015, 09:23:16 pm
content: I am new to ARM MCUs and I have been trying to find my way around the peripherals using STM's HAL library and STM32Cube. I am having trouble in getting the PWM output to work. Here, I'm trying dim an LED (connected to PA6) on the STM32L100 Discovery Board by using the channel-1 PWM output of timer-3 at 50% duty cycle.However, the LED doesn't turn on at all. I tried using the in-circuit debugging feature and it appears that the timer is not getting configured at all (despite the fact that I initialized it using HAL_TIM_PWM_Init()).Any help would be appreciated. My main.c source code is attached below.Line-96: main()Line-148: Clock settingsLine-396: Timer Initialization routineLine-463: GPIO Initialization routineThere are initialization functions for various other peripherals in the source file but I'm not using any of them right now. Jut timer and GPIO. I have used PWM features before on 8-bit MCUs like PICs, but the PICs that I used didn't have auto-reload features on their timer and hence, I had to use timer overflow interrupts to load back the timer register. However, timer-3 here has auto-reload and so I was unsure of whether I need interrupts. I tried looking up some example codes online and none of them seem to be using interrupts either.

author: richardman
author_rank: Frequent Contributor
timestamp: November 22, 2015, 09:40:44 pm
content: Quote from: Udayan Sinha on November 22, 2015, 09:23:16 pmI just got that working on JumpStart API. This is what you need to do using JumpStart API:Code:timer1.SetPinsForPWM(&porta, 8, 2);timer1.MakePWM(1, 30);Set up timer 1 to emit a 1 Hz PWM with 30% duty cycle on PA8. Simple eh?Anyway, all the examples I found on the web is missing one crucial bit of set up: I do not know how they get their LED to blink at all, but on both the F030 and the F401 boards, I need to enable MOE to get the signal output. I'm allergic to look at HAL/STPeriphLib code, so I don't know what else may be missing in your code, but if you don't have MOE enable - no signal, from my experience.

author: MT
author_rank: Super Contributor
timestamp: November 23, 2015, 12:11:47 am
content: QuoteWhat about direct register writing instead? You save RAM and increase execution speed .Quote from: richardman on November 22, 2015, 09:40:44 pmYes its simple neat and tidy just as it should be but how much code is behind those functions compared to ST SPL? just wondering.QuoteTIM1->BDTR |= TIM_BDTR_MOE; //Enable output stage,OBS! Only ADV timers have BDTR MOE

author: richardman
author_rank: Frequent Contributor
timestamp: November 23, 2015, 02:12:38 am
content: Quote from: MT on November 23, 2015, 12:11:47 amA LOT LESSthan ST'lib. Here's the complete code:Code:void JSAPI_TIMER::MakePWM(unsigned Hz, unsigned duty_cycle_percent){this->MakeTimer(Hz, 0, 0);this->Disable();TIM_TypeDef *TIMx = ((struct _timer *)(this->vp))->base;unsigned tmpccmrx = 0, tmpccer = 0;TIMx->CCER &= ~(TIM_CCER_CC1E|TIM_CCER_CC1P);tmpccmrx = TIMx->CCMR1;tmpccmrx &= ~(TIM_CCMR1_OC1M|TIM_CCMR1_CC1S);tmpccmrx |= TIM_OCMode_PWM1;tmpccmrx |= TIM_OCPreload_Enable;TIMx->CCMR1 = tmpccmrx;TIMx->CCR1 = ((TIMx->ARR+1) * duty_cycle_percent) / 100;TIMx->CCER |= TIM_CCER_CC1P;TIMx->CCER |= TIM_CCER_CC1E;TIMx->BDTR |= TIM_BDTR_MOE;this->Enable();}

author: ralphd
author_rank: Frequent Contributor
timestamp: November 23, 2015, 04:13:14 am
content: Does the compiler optimize these into single writes? If not, would doing it in a single statement help?TIMx->CCER |= TIM_CCER_CC1P;TIMx->CCER |= TIM_CCER_CC1E;

author: richardman
author_rank: Frequent Contributor
timestamp: November 23, 2015, 05:44:38 am
content: Quote from: ralphd on November 23, 2015, 04:13:14 amIt's not clear to me whether CC1P|CC1E can be written together. The STM32 manuals are sort of unclear. In any case, the way I work is to get it right, then iterate to remove dead wood. So that will happen later...

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 23, 2015, 09:58:39 am
content: Appreciate the suggestions guys but I would prefer a HAL-Cube solution mainly because I have already done quite a bit of code on it. I know it just came out last year and is still not adopted widely, but I don't want to switch to some new API now as it would mean transferring my old code too (and I'm on a bit of a deadline right now).Quote from: MT on November 23, 2015, 12:11:47 amYes it would be more efficient, it would also be less portable and need me to go through all the documentation to see which registers to program. HAL APIs works on the entire STM32 line. Plus I don't need that efficiency just yet so I don't see the point in doing all the hard work of going through the peripheral documentation and would prefer sticking to HAL (that's the whole point of having an HAL layer; so you don't mess with the registers unless you really need to).

author: AndreasF
author_rank: Frequent Contributor
timestamp: November 23, 2015, 04:51:08 pm
content: I only had brief look at your code, but I don't see anywhere you're turning on the clock to the timer peripheral itself (similar to the way you do for the GPIOs in MX_GPIO_Init(), e.g. __GPIOC_CLK_ENABLE(); ). This is different from the input clock sources for the timer! That might explain why your timer doesn't seem to get configured at all.

author: MT
author_rank: Super Contributor
timestamp: November 23, 2015, 05:46:43 pm
content: Quote from: richardman on November 23, 2015, 02:12:38 amIndeed,

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 23, 2015, 05:49:10 pm
content: Quote from: AndreasF on November 23, 2015, 04:51:08 pmI did configure the clock (lines 409-410)). Beyond that I don't think there's anything else to do. I have used the timer as a simple time base in the same way and it worked just fine.

author: richardman
author_rank: Frequent Contributor
timestamp: November 23, 2015, 07:40:33 pm
content: Did you enable MOE as I suggested?

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 23, 2015, 08:08:15 pm
content: Quote from: richardman on November 23, 2015, 07:40:33 pmMT posted earlier that it's only for advanced timers. I am using a general purpose timer.

author: richardman
author_rank: Frequent Contributor
timestamp: November 23, 2015, 08:27:51 pm
content: Would it kill you to try a one-line change and see if it works?

author: MT
author_rank: Super Contributor
timestamp: November 23, 2015, 09:26:56 pm
content: Quote from: richardman on November 23, 2015, 08:27:51 pmDont you trust the hoard of coders at ST and their quality work?But who knows whit ST stranger things have happened!

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 23, 2015, 09:32:21 pm
content: Quote from: richardman on November 23, 2015, 08:27:51 pmIt's a GENERAL PURPOSE TIMER. Doesn't have one. I checked.I found an example code on PWM for the Nucleo board in the Cube Examples (attached below). They have just done everything that I did to configure the timers. I'm pretty lost now. Don't know what else I can do.Think I'm gonna implement the error_handler() and see if any function returns an error. If not, then I have no other conclusion besides that there is a bug in the HAL PWM API

author: MT
author_rank: Super Contributor
timestamp: November 23, 2015, 09:36:01 pm
content: Bug in HAL's is not uncommon, sooo it seams that you have to dig into the register definitions to fix this after all!

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 23, 2015, 09:39:13 pm
content: Quote from: MT on November 23, 2015, 09:36:01 pmYou seem pretty happy at prospect of me doing extra work

author: MT
author_rank: Super Contributor
timestamp: November 23, 2015, 11:11:49 pm
content: Yes! Welcome to the land of ST!

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 23, 2015, 11:18:25 pm
content: Quote from: MT on November 23, 2015, 11:11:49 pmWell they (the Discovery boards) can be cheaper than an Arduino O0I only need to learn how to use 3 more peripherals: SPI, I2C & DAC. Hopefully that shouldn't be problem. Actually timer is the only peripheral I have had issues with so far.

author: kaadam
author_rank: Contributor
timestamp: November 24, 2015, 06:51:13 am
content: Hello!I bearly checked your code. As far as i noticed, you didn't initialize the PA6 pin at all.And let me give you some advice. Use separate source files. I know it can be a pain in the ass, but your code will be less clean, and more readable. And yeah, the HAL driver contains bug, but not this ridiculous.

author: crispus
author_rank: Regular Contributor
timestamp: November 24, 2015, 06:53:29 am
content: Hi,I use STM32 F0 Discovery board and tested PWM on the green LED: timer 3, channel 4.I generated the code using STM32CubeMX (and I can see you did the same way), but in your code I don't see the PA6 GPIO initialization or __TIMx_CLK_ENABLE:Code:void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base){GPIO_InitTypeDef GPIO_InitStruct;if(htim_base->Instance==TIM3){/* USER CODE BEGIN TIM3_MspInit 0 *//* USER CODE END TIM3_MspInit 0 *//* Peripheral clock enable */__TIM3_CLK_ENABLE();/**TIM3 GPIO ConfigurationPC9 ------> TIM3_CH4*/GPIO_InitStruct.Pin = GPIO_PIN_9;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_LOW;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);/* USER CODE BEGIN TIM3_MspInit 1 *//* USER CODE END TIM3_MspInit 1 */}}L.E. I see that it was already answered. You may want to check ChibiOS HAL. It can be used independently, and is much, much cleaner.

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 25, 2015, 11:25:12 pm
content: Found the bloody problemIt seems that depending on whether you configure the timer in Cube for time base, PWM, Capture or Compare, it produces a different start-up code during code generation for each configuration. I was using my PWM code in a project in which I had originally configured the timer to be used as a time base in Cube. So the PWM didn't run (coz the start-up code is different) despite the fact that the code was correct. Likewise, in a project which had the timer configured for PWM in Cube, it wouldn't run in time base mode even if we initialize the timer correctly and do everything right. I would infer that this is the same for the other modes.Thanks a lot for all the help and suggestions guys!Quote from: crispus on November 24, 2015, 06:53:29 amGPIO initialization for PWM and __TIMx_CLK_ENABLE() is not needed. It runs fine without them.

author: Matter45
author_rank: Newbie
timestamp: March 08, 2017, 11:25:38 am
content: just thought I add a solution to this problem:https://www.youtube.com/watch?v=1BNlCkBN82A&feature=youtu.beyou need to add a line in the stm32f0xx_hal_msp.c file:GPIO_InitStruct.Alternate = GPIO_AF1_TIM3;then putting in HAL_TIM_PWM_Start in the main like usual will make it work.