STM32 MCU Learning---PWM Output

Publisher:文江桂青Latest update time:2015-09-23 Source: eefocusKeywords:STM32 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
I spent half a day in the morning getting familiar with the PWM module of stm32. At noon, I used my lunch time to successfully debug the PWM function. Of course, it is a very simple thing, and perhaps many seniors would disdain it.

Today, my biggest feeling is that the Internet is a huge treasure trove. I am really fortunate that in this complex social environment, in an era full of selfishness and self-interest, many netizens on various websites and forums always maintain an open source atmosphere. Learning must be communicated with others, and the Internet provides such an excellent platform.

Let’s stop talking nonsense and get down to business.

Function: Use channel 2 of timer 2 to make PA1 output PWM waveform with frequency 1K and duty cycle 40, and use PA8 to delay and invert the LED light at will to indicate program running.

First, let's get familiar with the PWM related parts of the timer.

[Reprint] STM32 MCU Learning---PWM Output

In fact, PWM is just a comparison function of the timer.

The value in CNT keeps ++ing, and once it is equal to the value in CCRX register, the corresponding action will be taken. This is very similar to AVR microcontroller. In this case, if we want to generate the required PWM signal, we need to set the PWM frequency and PWM duty cycle.

First, let's talk about determining the frequency. Since the clock source of the general timer is PCLK1, and I like to use the default settings of the firmware library, the clock frequency of the timer is determined as follows:

AHB (72MHz) → APB1 divider (default 2) → APB1 clock signal (36MHz) → multiplier (*2 times) → general timer clock signal (72MHz).

The reason why this is the case is recorded in detail in the RCC module learning record, so I won’t go into details.

Therefore, the CK_PSC in the figure is 72MHz.

The following information is also a lot of online search, I will list it:

The STM32 PWM output has two modes, Mode 1 (PWM1) and Mode 2 (PWM2), which are determined by the OCxM bit in the TIMx_CCMRx register ("110" is Mode 1, "111" is Mode 2). The differences between Mode 1 and Mode 2 are as follows:

110: PWM mode 1 - When counting up, channel 1 is at an invalid level (OC1REF=0) once TIMx_CNT=TIMx_CCR1, otherwise it is at a valid level (OC1REF=1).

111: PWM mode 2 - When counting up, channel 1 is at a valid level once TIMx_CNT = TIMx_CCR1, otherwise it is at an invalid level.

From this point of view, Mode 1 and Mode 2 are complementary and opposite to each other, so there is not much difference in their use. I use Mode 1, so the following settings are all set according to Mode 1.

 

The PWM cycle is determined by the timer's auto-reload value and the CNT count frequency. The CNT count clock is obtained by CK_PSC through the divider PSC, so the CNT clock is CK_PSC/division factor. This division factor is determined by TIM_TimeBaseStructure.TIM_Prescaler. The value I set is 72, so the CNT count frequency, that is, the frequency of CK_CNT, is 1MHz.

The next step is to determine the timer auto-reload value. Because CNT will automatically clear to zero every time it is added to the value of the ARR register, of course, the premise is set to up-counting mode, and the PWM period is changed according to this overflow event. Therefore, the frequency of the PWM signal is determined by the value of ARR. The value I set is 1000-1, that is, TIM_TimeBaseStructure.TIM_Period = 1000-1; therefore, the PWM period is 1MHz/1000=1KHz.

Next, we need to determine the duty cycle of PWM. Because CNT will constantly compare the value of CRRX when adding itself to the ARR value, once the two are equal, a match event will occur, but please note that CNT will not pay attention to this, it will continue to ++ until it is equal to ARR. I set the value of CRRX to 400-1, so the duty cycle is determined to be 40%.

Okay, here is the configuration of the library function.

TIMER output PWM implementation steps

1.        Set the RCC clock;

2.        Set up GPIO;

3.        Set the relevant registers of TIMx timer;

4.        Set the PWM related registers of the TIMx timer.

 

First, the main function and global variables are declared. It is very simple and will not be explained.


GPIO_InitTypeDef GPIO_InitStructure;

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_OCInitTypeDef TimOCInitStructure;

int main(void)
{
      

 

 

      rcc_cfg();
      gpio_cfg();
      tim2_cfg();
      pwm_cfg();
// 
  while (1)
  {
   
    GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET);
 
 delay();

   
    GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET);
 
    delay();
  }
}

 

The following is the configuration of the IO port:

void gpio_cfg()
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 
  GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_In it(GPIOA, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

What needs to be noted here is that the PWM output port must be configured as multiplexed push-pull output. I don’t know the reason, but I just copy it anyway.

 [page]

 

The following is the TIM configuration function. The comments are very clear and no further explanation is required:

void tim2_cfg()
{
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

  TIM_DeInit(TIM2);
  TIM_InternalClockConfig(TIM2);
  //The pre-scaling factor is 72, so the counter clock is 72MHz/72 = 1MHz
  TIM_TimeBaseStructure.TIM_Prescaler = 72;
  //Set the clock division
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  //Set the counter mode to the up counting mode
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  //Set the count overflow size, and generate an update event for every 1000 counts
  TIM_TimeBaseStructure.TIM_Period = 1000-1;
  //Apply the configuration to TIM2
  TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);

  //Disable ARR preload buffer
  TIM_ARRPreloadConfig(TIM2, DISABLE);
 
  TIM_Cmd(TIM2, ENABLE);   //Enable TIMx peripherals
}

 

 

Next is the key PWM configuration function:

void pwm_cfg()

{

      //Set the default value

       TIM_OCStructInit(&TimOCInitStructure);

       //PWM mode 1 output

       TimOCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

       //Set the duty cycle, duty cycle = (CCRx/ARR)*100% or (TIM_Pulse/TIM_Period)*100%

       TimOCInitStructure.TIM_Pulse = 400-1;

       //TIM output comparison polarity high

       TimOCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

       // Enable output status

       TimOCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

       //CH2 output of TIM2

       TIM_OC2Init(TIM2, &TimOCInitStructure);

       //Set TIM2 PWM output to enable

       TIM_CtrlPWMOutputs(TIM2,ENABLE);

}

The output comparison unit structure of the stm32 firmware library is defined separately from the time base unit of the timer, and the PWM mode is just a value of the output comparison structure member TimOCInitStructure.TIM_OCMode. After filling this structure, it must be mapped to a timer and implemented with the TIM_OCXInit function. I used an X to indicate that there is more than one such function. In fact, the general timer of stm32 has four channels, and each channel corresponds to an initialization function. It's really tangled here! Finally, the PWM output function of the timer must be enabled. Note that the TIM_CtrlPWMOutputs (TIM2, ENABLE) function is outputs instead of output, which means that TIM2 has more than one channel! It's complicated and cumbersome!

The following is the structure prototype of the output comparison unit:

typedef struct
{
  uint16_t TIM_OCMode;       

  uint16_t TIM_OutputState;  

  uint16_t TIM_OutputNState; 

  uint16_t TIM_Pulse;        

  uint16_t TIM_OCPolarity;   

  uint16_t TIM_OCNPolarity;  

  uint16_t TIM_OCIdleState;  

  uint16_t TIM_OCNIdleState; 
} TIM_OCInitTypeDef;

The members without color are only for advanced timers, and do not need to be taken care of by general timers.

There is also a member called TimOCInitStructure.TIM_OCPolarity that needs attention. What is its function? The information I found online is as follows:

[Reprint] STM32 MCU Learning---PWM Output

As mentioned above, PWM has two modes: PWM1 and PWM2. These two modes can only control up to OCXREF. TIM_OCPolarity can control whether OC1 is directly equal to OCXREF or reverse polarity! OC1 is the final PWM signal.

Here is a small episode. I used an oscilloscope to measure the PWM signal and found that the signal was bipolar. Then I changed TIM_OCPolarity and measured it again. It was still bipolar, just reversed. I really thought that the stm32 microcontroller could output bipolar PWM. Later, I changed the oscilloscope to DC gear (it was AC gear before), and the waveform moved vertically from zero potential. Pay attention next time!

Keywords:STM32 Reference address:STM32 MCU Learning---PWM Output

Previous article:Detailed explanation of 8 modes of GPIO in STM32
Next article:Installation of arm-linux 4.3.2 cross toolchain

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号