Ads

Get STM32 tutorial using HAL at $10 for a limited time!

Sunday, August 30, 2015

STM32F4 Discovery Tutorial 10 - PWM

In this tutorial, I will share how to use PWM on STM32F4 Discovery board. PWM (Pulse Width Modulation) is a technique for generating analog voltage (average value) by using microcontroller's digital outputs. PWM is used in DC motor speed control, servo motor control, dimming LED, audio generation and many more.


PWM signal is a modulated digital logic (0 and 1). PWM signal have a duty cycle. Duty cycle is measured in percentage. The percentage of duty cycle is calculated from time of a digital signal is on over period of time.


PWM signal that has 100% duty cycle would be the same as setting to fully on. 0% duty cycle would be the same as ground. Duty cycle in between 0% and 100% are illustrated on these graphics:


To generate PWM with STM32F4, we can use timer. Timer can count from 0 to a given value and triggering some events in between. In PWM mode, timer can control digital output of 1 or more output channels. When the timer counter reach 0, max, or compared value, the output channel value can be changed to create a PWM signal.

First, we have to setting the timer on STM32F4. We will use TIM4 because the output channel (TIM4_CH1) is connected to PD12 (green LED) that we want to control the brightness. So, we initialize TIM4 with these code:
void TIM_Init()
{
    // Enable clock for TIM4
    // We use TIM4 because green LED (PD12) is connected
    // to TIM4_CH1 GPIO AF mapping
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

    // Timer initialization struct
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;

    // Create 1kHz PWM
    // TIM4 is connected to APB1 bus that have default clock 84MHz
    // So, the frequency of TIM4 is 84MHz
    // We use prescaler 10 here
    // So, the frequency of TIM4 now is 8.4MHz
    TIM_TimeBaseInitStruct.TIM_Prescaler = 10;
    // TIM_Period determine the PWM frequency by this equation:
    // PWM_frequency = timer_clock / (TIM_Period + 1)
    // If we want 1kHz PWM we can calculate:
    // TIM_Period = (timer_clock / PWM_frequency) - 1
    // TIM_Period = (8.4MHz / 1kHz) - 1 = 8399
    TIM_TimeBaseInitStruct.TIM_Period = 8399;
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;

    // Initialize TIM4
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStruct);
    // Start TIM4
    TIM_Cmd(TIM4, ENABLE);
}
After that, we write code for initialize PWM TIM4 on channel 1 TIM4_CH1:
void PWM_Init()
{
    // PWM initialization struct
    TIM_OCInitTypeDef TIM_OCInitStruct;

    // Common PWM settings
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;

    // Duty cycle calculation equation:
    // TIM_Pulse = (((TIM_Period + 1) * duty_cycle) / 100) - 1
    // Ex. 25% duty cycle:
    //     TIM_Pulse = (((8399 + 1) * 25) / 100) - 1 = 2099
    //     TIM_Pulse = (((8399 + 1) * 75) / 100) - 1 = 6299
    // We initialize PWM value with duty cycle of 25%
    TIM_OCInitStruct.TIM_Pulse = 2099;
    TIM_OC1Init(TIM4, &TIM_OCInitStruct);
    TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
}
This is the full code for dimming green LED from 0% brightness to 100% brightness:
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_tim.h"
#include "clock.h"
#include "delay.h"

int main(void)
{
    // Set clock to 168MHz
    CLOCK_SetClockTo168MHz();

    // Delay initialization
    DELAY_Init();

    // TIM4 initialization
    TIM_Init();

    // PWM initialization
    PWM_Init();

    /* Set clock for GPIOD ------------------------------------------ */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    // Set alternate function of GPIOD pin 12 as PWM outputs
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);

    // GPIOD pin 12 as outputs
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOD, &GPIO_InitStruct);
    /* -------------------------------------------------------------- */

    while (1)
    {
        // Set green LED brightness from 0 to max
        int i;
        for (i = 0; i <= 8399; i += 5)
        {
            // If we want to set PWM duty cycle after initialize,
            // we can't use TIM_OCInitStruct.TIM_Pulse,
            // but directly to TIM4 compare register 1 (CCR1)
            TIM4->CCR1 = i;
            DELAY_Ms(1);
        }
    }
}
This is the link for download these clock, delay library if you need.

2 comments :

  1. Hi, if i want to generate 3 pwm at the same time and each one at diferent speed 2mhz 1.5 mhz 1mhz, can be possible?

    ReplyDelete
  2. this link is dead : https://drive.google.com/file/d/0B0lLeFO3_w8KRFkxeVlYaVlrNWs/view

    ReplyDelete