Working with STM32F7 and Timers: PWM mode

In the previous guide (here), we discussed how to use the timer to generate accurate delay. In this guide, we shall look at another application for timer which is to generate PWM signal on two channels and fading two leds.

In this guide, we will cover the following:

  • What is PWM
  • Configure the timer and GPIO to generate PWM signal
  • Connection 
  • Code
  • Demo

1. What is PWM:

PWM is a technique used to emulate “analog signal” by rapidly turning on-off the pin. This allow the mcu to vary the power delivered to the load such as motor (will be covered later). The PWM has three main characteristics;

  • Frequency:

which describe the duration time of the entire signal

  • Duty cycle

The term duty cycle describes the proportion of ‘on’ time to the regular interval or ‘period’ of time; a low duty cycle corresponds to low power, because the power is off for most of the time. Duty cycle is expressed in percent, 100% being fully on.

  • Amplitude 

Which is the voltage level of the PWM (3.3v for STM32F7).

For more details, please check this wikipedia article (here

2. Configure the timer and GPIO to generate PWM Signal:

First we need to locate which pins connected to TIMER2_CH1 and TIMER2_CH2.

From the datasheet, we can find the related timer channels

From the datasheet, we can see that TIM2_CH1 is connected to PA0 and CH2 is connected to PA1.

Hence, we can start configuring the timer and GPIO

We start off by setting GPIO

First thing first, we need to enable clock access to GPIOA as following: (for enabling clock access, check this topic)

RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN;

Then setting the pins in alternate mode as following:

GPIOA->MODER&=~	GPIO_MODER_MODER0|GPIO_MODER_MODER1; //reset PA0 and PA1
GPIOA->MODER|=	GPIO_MODER_MODER0_1|GPIO_MODER_MODER1_1; //set PA0 and PA1 as alternate mode

Then select which alternate function, in our case it is AF1

#define AF01 0x01
GPIOA->AFR[0]|=(AF01<<0)|(AF01<<4);

Now we can configure the timer to generate PWM signal

We start off by enabling clock access to TIMER2 as following:

RCC->APB1ENR|=RCC_APB1ENR_TIM2EN; //enable clock access to tim2

Set the prescaller to 0 (no divider)

TIM2->PSC=0; //set prescaller to 0 (no divider)

Set the maximum value to 255 ( this will give frequency of 62.5KHz)

TIM2->ARR=255; //set the maximum count value

Reset the current counter

TIM2->CNT=0; //reset the current count

Now, we configure the mode to be PWM mode as following:

TIM2->CCMR1=TIM_CCMR1_OC1M_1|TIM_CCMR1_OC1M_2|TIM_CCMR1_OC2M_1|TIM_CCMR1_OC2M_2; //configure the pins as PWM

And enable the channel as following:

TIM2->CCER|=TIM_CCER_CC1E|TIM_CCER_CC2E; //enbale channel1 and channel2

Finally, we enable the timer as following:

TIM2->CR1=TIM_CR1_CEN; //enable timer

hence, the initializing function shall be this

void GPIO_Init(void)
{
#define AF01 0x01
RCC->AHB1ENR|=	RCC_AHB1ENR_GPIOAEN;// enable clock access
GPIOA->MODER&=~	GPIO_MODER_MODER0|GPIO_MODER_MODER1; //reset PA0 and PA1
GPIOA->MODER|=	GPIO_MODER_MODER0_1|GPIO_MODER_MODER1_1; //set PA0 and PA1 as alternate mode
	
GPIOA->AFR[0]|=(AF01<<0)|(AF01<<4);


}
void  Timer2_init(void){
RCC->APB1ENR|=RCC_APB1ENR_TIM2EN; //enable clock access to tim2
TIM2->PSC=0; //set prescaller to 0 (no divider)
TIM2->ARR=255; //set the maximum count value
TIM2->CNT=0; //reset the current count
TIM2->CCMR1=TIM_CCMR1_OC1M_1|TIM_CCMR1_OC1M_2|TIM_CCMR1_OC2M_1|TIM_CCMR1_OC2M_2; //configure the pins as PWM
TIM2->CCER|=TIM_CCER_CC1E|TIM_CCER_CC2E; //enbale channel1 and channel2
TIM2->CR1=TIM_CR1_CEN; //enable timer
}

3. Connection:

4. Code:

#include "stm32f7xx.h"                  // Device header
void GPIO_Init(void);
void Timer2_init(void);
void delay(int ms );
#define rate 255
int main(void)
{

GPIO_Init();
Timer2_init();
while(1)
{
for (int i=0;i<rate;i++){
TIM2->CCR1=i;
TIM2->CCR2=rate-i;
delay(2);
}
for (int i=255;i>0;i--){
TIM2->CCR1=i;
TIM2->CCR2=rate-i;
delay(2);
}

}
}

void GPIO_Init(void)
{
#define AF01 0x01
RCC->AHB1ENR|=	RCC_AHB1ENR_GPIOAEN;// enable clock access
GPIOA->MODER&=~	GPIO_MODER_MODER0|GPIO_MODER_MODER1; //reset PA0 and PA1
GPIOA->MODER|=	GPIO_MODER_MODER0_1|GPIO_MODER_MODER1_1; //set PA0 and PA1 as alternate mode
	
GPIOA->AFR[0]|=(AF01<<0)|(AF01<<4);


}
void  Timer2_init(void){
RCC->APB1ENR|=RCC_APB1ENR_TIM2EN; //enable clock access to tim2
TIM2->PSC=0; //set prescaller to 0 (no divider)
TIM2->ARR=255; //set the maximum count value
TIM2->CNT=0; //reset the current count
TIM2->CCMR1=TIM_CCMR1_OC1M_1|TIM_CCMR1_OC1M_2|TIM_CCMR1_OC2M_1|TIM_CCMR1_OC2M_2; //configure the pins as PWM
TIM2->CCER|=TIM_CCER_CC1E|TIM_CCER_CC2E; //enbale channel1 and channel2
TIM2->CR1=TIM_CR1_CEN; //enable timer
}


void delay(int ms)

{

volatile int i;

for(; ms>0 ;ms--)

{

for(i =0; i<3195;i++);

}

}

5. Demo:

Happy coding 😀

4 Comments

  • MehmetAli AYDIN Posted April 21, 2024 2:28 pm

    TIM2->CCMR1=TIM_CCMR1_OC1M_1|TIM_CCMR1_OC1M_2|TIM_CCMR1_OC2M_1|TIM_CCMR1_OC2M_2; //configure the pins as PWM
    ——————————————————————————————————————————————–

    TIM2->CCMR1 | =
    Why is there no or operator?

    • Husamuldeen Posted April 22, 2024 5:10 am

      You can use either | or =.
      The | will modify only the required bits from 0 to 1 while = will set the entire register to the desired value.

  • MehmetAli AYDIN Posted April 23, 2024 6:01 pm

    TIM2->CCMR1=
    TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2|
    TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2;
    I couldn’t see any value assigned to the first and second bits of OC1M.

    • Husamuldeen Posted April 28, 2024 1:02 pm

      Hi,
      Is the PWM working properly ?

Add Comment

Your email address will not be published. Required fields are marked *