STM32 GPIO PWM Simple Example for Blackpill/Bluepill dev board

 STM32 GPIO PWM Simple Example

 

Arduino hardware and Arduino IDE can easily let GPIO pin to work on PWM mode, simplyuse analogWrite(), ie analogWrite(pin, 128); simple and easy.


 

Unfortunately STM32F### hardware and STM32CubeIDE need quite a lot more work to achieve this.

Here is some simple steps to let STM32F401 or STM32F103 to set GPIO to work on PWM mode on the fly, as simple as I can make it to be. 

To be able to see the GPIO pins's PWM signal, a oscilloscope is needed, but any cheap one should work, including those 30$ low cost ones. 😜

Certain multi-meter can see pwm frequency and PWM % as well, but we cannot see the wave form.

Choose the Right Pin and Timer.

STM32 Pin PWM mode requires TIMER to support it.  We must choose the pin(s) or timer carefully, certain pins only work with certain timer; certain timer can only work with certain pins. That means, if our project uses a Timer for other purpose, then certain pins cannot be used for PWM. 

How Many PWM GPIO Pins do we need?

Certain Timer only supports one GPIO pin, some other timers supports up to 4 GPIO pins.

For exampale, STM32F401 (Blackpill), TIM10 (PB8) and TIM11(PB9) only supports one GPIO pins.

TIM9 support two GPIO Pins PA2 and PA3. 

TIM3 supports 4 GPIO pins - PA6, PA7, PB0, PB1.

If we need, we can config TIMER3 to control 4 different GPIO pins at the same time, they must run at the same frequency, but can have different PWM value.

Choose the Pins for this test

We choose PB0 and PB1 for this test, so must use TIM3. Let TIM3 to control PM0 and PB1 to output PWM signal at the same time.

Timer Setting for Pins

In STM32CubeIDE, Choose Timer3,  

Activate Internal Clock, choose Channel3: PWM Generation for PB0, and Channel4 Generation for PB1. Each channel controls one GPIO pin.



 Set the Right Frequency for the Timer

One timer can run at a fixed frequency, which can be easily set in the STM32CUBEIDE. I assume in many use cases we only require the GPIO pins to run at a fixed frequency, let's use 1KHz in this test.

Confirm the System clock speed first

Reason: it's needed when we set the timer's clock.

Blackpill STM32F401CCU6: 84MHz Max , Bluepill STM32F103C8T6 72Mhz Max.

For example, Blackpill STM32F401 system clock setting:


For more info we can refer to MCU datasheet, chapter Functional Overview,  Timers and watchdogs, which timer works for which pins etc.

Set Timer Frequency for 1KHz

In this test we will set the PWM to run at 1KHz

So in Tim3 setting, 84MHz / 1KHz/ 255 = 329 :

set Prescaler = 329 (330-1), and Counter Period = 255-1 ( this is for the full PWM period) , so if we set 25, it's 10% PWM, we set 128, it's 50% PWM.  255 is perfect for PWM Max value, which is the same as Arduino, and perfect for Uint8.

To check if the Timer is now running at 1KHz:

84MHz / 329/254=1K


The rest is not important or can be left as default values.

Start the Timer in Software

// define timer constant for easier use, and easily change should we need to
#define TIMER_PWM htim3
#define PWM_OUTPUT1 TIM_CHANNEL_3
#define PWM_OUTPUT2 TIM_CHANNEL_4

Must start timer first, for both channels. before the main loop while(1)

 HAL_TIM_PWM_Start(&TIMER_PWM, PWM_OUTPUT1);
 HAL_TIM_PWM_Start(&TIMER_PWM, PWM_OUTPUT2);

Set the PWM in Software Code

Option1  Registor and hardcode:   

                 htim3.Instance->CCR3 = pwm;

                 htim3.Instance->CCR4 = pwm;

so we can set pwm value like this, 128, 255 etc.

CCR3 is for channel3, which is for PB0

CCR4 is for channel4, which is for PB1

Option2 HAL Function

if directly handling register is not ideal, we can use HAL function

    __HAL_TIM_SET_COMPARE( &TIMER_PWM,  TIM_CHANNEL_3,  85 );   //  255/3=85

    __HAL_TIM_SET_COMPARE( &TIMER_PWM,  TIM_CHANNEL_4,  128 ); // 50% of 255

We need to provide timer and channel and the pwm value. so we set 33% and 50% for two GPIO pins as above.

That's it, a little bit more than Arduino's analogWrite(), but inthe end, we can achieve different PWM frequency with STM32, and still just one line of code to set the PWM.

We can see both PWM signal are correct, 33% and 50%.


Let oscilloscope measure it, seems right.


 

Edge value testing

Because we set the Counter Period (Auto Reload Reg) to be 255-1. To make sure when we set 255 PWM value, it's fully turn on instead of 0 ( overflow or other unthinkable strange reasons like many others when we work on mcu and electronics), we can test the Pin's voltage by using a multi-meter. it should measure 3.3V and not 0.


Change PWM on the fly

in the main loop, we keep changing both pins's PWM value,  so in oscilloscope we can see the signals keep changing.

 

Check the video, seems working, both PWM signals keep changing from 0 to 255 ( 0% to 100%)




 

Comments

Popular posts from this blog

STM32 and U8G2 to draw on LCD12864 Using hardware SPI

Using STM32 DMA to speed up hardware SPI and U8G2 - Attempt 2 - buffered DMA

DIY Weller Solder Tweezers Controller