At the beginning, when we learned about timers, it was hard. I’m not going to talk about how hard it was to program, but how hard it was to understand. To learn timer you have to know his story.

For example, if you want to use a timer, decide which timer you want to use, advanced timer or universal timer. If you want to use advanced timers (TIM1, TIM8), what is the difference between them and universal timers? How do we show the difference in the program? The second thing you need to care about is the number of bits you are using. There are usually 16 bits and 32 bits. For example, if TIM1 is 16 bits, then its maximum number of counts is 2^16=65536-1, that is, your tim_timebasestructure.tim_period = X; The maximum value of X is 65535, and you can’t go higher than that. But if you use TIM2 as 32 bits, then its maximum count is 2^32=4294967296-1, which means that your tim_timebasestructure.tim_period = X; The maximum value of X is 4294967295, and you can’t go higher than that. The third is that you should be clear about the clock source of the timer you use. Generally speaking, the clock source of our advanced timer (TIM1, TIM8) is from AHB2, you can remember it like this: The clock frequency of the advanced timer must be higher than that of the general timer, and since 2>1, the clock source of the advanced timer is AHB2, and the clock source of the general timer is AHB1, right?

Note that the TIMx timer of STM32 is twice as long as that of APB1 unless the clock minutes of APB1 are set to 1. When the clock minutes of APB1 are non-frequency, the TIMx timer of STM32 is equal to that of APB1. This explains why I use TIM3 as a universal timer mounted on APB1, which is 36MHz, and TIMx as a universal timer is twice that of APB1, so TIM3 is calculated at 72MHZ. We can’t continue until we know what we need to know, and it’s not just that.

In the last chapter we talked about timers. This chapter is called hot smacking hot and talked about pulse width modulation -PWM. Pulse Width Modulation (PWM), is the English “Pulse Width Modulation” abbreviation, referred to as Pulse Width Modulation, is the use of microprocessor digital output to control the analog circuit of a very effective technology. A simple point, is to control the pulse width.

So when I look at this picture, I’m going to say, well, if you’re smart enough, this thing must have two registers, ARR and CRRx, and those two registers must have been set by you, and they’re all graphed, right?

We assume that the timer works in upward counting PWM mode and outputs 0 when CNT

=CCRx. Then you can get the schematic diagram of PWM as above:

  • When CNT value is less than CCRx, IO output low level (0),
  • When CNT value is greater than or equal to CCRx, IO output high level (1),
  • When CNT reaches the value of ARR, it is reset to zero, and then it starts counting upwards again.

Change the value of CCRx, you can change the duty cycle of PWM output, change the value of ARR, you can change the frequency of PWM output, this is the principle of PWM output. So for PWM we just need to change these two values in the program, everything else is the same.

STM32F429 timer except TIM6 and 7. Any other timer can be used to generate PWM output. Let’s take a look at the classification of timers. This is the timer allocation diagram of F4

One more concept to know here is the channel:

What is a channel? Let’s rephrase it channel = path = pin, so it’s easier to understand that TIM2 has a 4-way timer. TIM5 has four paths, TIM3 has four paths, TIM4 has four paths. Why all the way? For example, if we want to generate eight PWM signal outputs with different cycles and duty cycles, we can choose THE CH1/CH2/CH3/CH4 of TIM2 and THE CH1,CH2,CH3 and CH4 of TIM3 for output. So many channels are needed in order to output/input more signals. For example, if you make a little robot with a little steering gear you need a lot of timers for a lot of channels for a lot of pins. Which pins are the output signals. Obviously, for F4, CH1/CH2/CH3/CH4 of TIM2 corresponds to the four pins PA5 /PA1/PA2/PA3. The CH1/CH2/CH3/CH4 of TIM5 corresponds to the four pins PH10/PH11/PH12/PIO.

To make THE STM32F429 universal timer TIMx generate PWM output, in addition to the registers introduced in the previous chapter, we will also use 4 registers to control PWM. The three registers are: Capture/Compare mode register (TIMX_CCMR1/2) Capture/Compare Enable register (TIMx_CCER) Capture/compare Register (TIMX_CCR1-4) Brake and Dead zone register (TIMx_BDTR) This register is commonly used in advanced timers, namely TIM1 and TIM8. Leave this register alone if you don’t use these two timers. The pulse-width modulation mode generates a signal whose frequency is determined by the TIMx_ARR register value and whose duty cycle is determined by the TIMx_CCRx register value.

Register explanation

Capture/compare mode register (TIMx_CCMR1/2)

This register usually has two: TIMx _CCMR1 and TIMx _CCMR2. TIMx_CCMR1 controls CH1 and CH2, while TIMx_CCMR2 controls CH3 and CH4

In this register we care about bits 14, 13, 12, and 9, 8, and the rest is fine by default. Mode setting bit OC4M, which consists of three bits. A total of 7 modes can be configured. We are using PWM mode, so these 3 bits must be set to 110/111. The difference between these two PWM modes is that the output level is of opposite polarity. In addition, CC4S is used to set the channel direction (input/output). The default setting is 00, which is used to set the channel as the output.

Capture/compare enable register (TIM3_CCER)

The enable register is the name of the register, of course, the enable TIM3 timer ah. CC4E is set to 1.

Capture/compare registers (TIMx_CCR1~4)

There are four registers in total, corresponding to four channels CH1~4. We use channel 3. In output mode, the value of this register is compared with the value of CNT, and the corresponding action is generated according to the comparison result. Taking advantage of this, we can control the PWM output pulse width by modifying the value of this register.

Program code configuration

Our text example is channel 3 of timer 3, corresponding to the PB0 pin.

1. Enable the TIM3 clock and configure PB0

To use TIM3, we must first turn on the clock of TIM3, which I believe you have seen so much code, should understand. Here, we also need to configure PB0 as the multiplexing output (of course, we also need to time the CLOCK of GPIOB). This is because TIM3_CH3 channel will use the multiplexing function of PB0 as the output. Only by configuring PB0 as the multiplexing output, can PWM output of TIM3_CH3 be realized through PB0.

GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);    // Enable TIM peripheral clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);  // Enable GPIO peripheral clock
// Set this pin to multiplexed output function, output TIM3 CH3 PWM pulse waveform
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;         //TIM3_CH3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // reuse push-pull output
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
Copy the code

2. Set the ARR and PSC of TIM3

After starting the clock of TIM3, we need to set the values of the two registers ARR and PSC to control the period of the output PWM. This is done in the library via the TIM_TimeBaseInit function, which was explained in the timer interrupt section of the previous section, but will not be explained here. The format of the call is:

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 100;     
TIM_TimeBaseStructure.TIM_Prescaler =360- 1; 	
TIM_TimeBaseStructure.TIM_ClockDivision = 0;  // Set clock split :TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM up count mode
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // Initializes the TIMx base time unit as specified in TIM_TimeBaseInitStruct
Copy the code

(1) Frequency: we use APB1 clock source is 72MHz, here we do not do frequency division, through the configuration of relevant parameters to set the input frequency, calculation method: Input frequency =APB1 clock/(predivision coefficient +1) =72 000 000Hz/360=200 000Hz =200KHZ. The tim_timebasestructure. TIM_Period parameter determines the frequency of the output PWM waveform. The frequency of the output PWM waveform is equal to the input frequency of the timer/tim_timebasestructure. TIM_Period, Here we set the setting period as 200000Hz /100=2000Hz, that is, 0.5ms a cycle. If you want to modify the frequency of your output PWM waveform according to my own configuration above.

3. Set the PWM mode and channel direction of TIM3_CH3 to enable the CH3 output of TIM3

Next, we need to set TIM3_CH3 to PWM mode. We need to control the mode of TIM3_CH3 by configuring the relevant bits of TIM3_CCMR2. In the library function, the PWM channel setting is set through the function TIM_OC1Init()~TIM_OC4Init(), different channel setting function is different, here we use channel 3, so the function used is TIM_OC3Init(). I don’t have much structure here, you can see, for ordinary timer we only need to set the following four parameters can be.

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // Select timer mode :TIM pulse width modulation mode 2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // Enable the comparison output
TIM_OCInitStructure.TIM_Pulse = 500;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // Output polarity :TIM output polarity is high
TIM_OC3Init(TIM3, &TIM_OCInitStructure);  // Initialize the peripheral TIMx with the parameters specified in TIM_OCInitStruct
Copy the code

③ Set duty cycle: Duty cycle = Set duty cycle value/tim_timebasestructure. TIM_Period, where duty cycle is 50/100=50%. 50% duty cycle so the waveform must be half high and half low, right? And we’ll look at that in a moment.

4 can make TIM3

All configured, finally can look at TIM3.

TIM_Cmd(TIM3, ENABLE);/ / can make TIM3
Copy the code

5. Enable the preload value of basic timer 3.

Enable the preloaded register of TIM3 on CCR3

TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //// Enables TIM3's preloaded register on CCR3, that is, the preloaded value of TIM3_CCR3 can only be transmitted to the current register when the update event arrives.
TIM_ARRPreloadConfig(TIM3, ENABLE); // enable TIM3 preload register on ARR
Copy the code

1. There are 3 shadow registers: PSC, ARR, CCRx (x is the corresponding channel), PSC, ARR, CCRx are not shadow registers, but their corresponding “pre-loaded registers”; 2, the shadow register is the real register, but ST does not provide this register, only provide the corresponding pre-loaded register, respectively “PSC,ARR,CCRx” 3, we users can access, can modify or read are pre-loaded registers, TIM3->ARR; TIM3->ARR; TIM3->ARR; These two methods mainly depend on the “APRE” bit in register TIMx->CR1;

  • APRE=0, when the value of ARR is changed, the value of the shadow register is updated immediately;
  • APRE=1, when the ARR value is modified, the value of the shadow register must be updated after the next event occurs;

How to change the value of the shadow register immediately, instead of the next event The method is as follows:

  • Set ARPE=0, TIM_ARRPreloadConfig(TIM3, DISABLE);
  • If ARPE=1, TIM_ARRPreloadConfig(TIM3, ENABLE);

TIM_ARRPreloadConfig is set to DISABLE and ENABLE, which only allows or disables writing new values to the ARR buffer while the timer is working, so that the overwritten values can be loaded when the update event occurs. At the beginning of initialization you have set “tim_timebasestructure.tim_period =100; The reason is that you didn’t write the interrupt service function or you didn’t write new values to the ARR buffer in the interrupt service function at all, so it doesn’t matter if you set it to DISABLE or ENABLE, but the safest thing to do is to use it.

To summarize the code:

void TIM3_PWM_Init(a)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // Enable TIM clock
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);// Enable GPIO peripheral clock

   // Set this pin to multiplexed output function, output TIM3 CH3 PWM pulse waveform
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;         //TIM3_CH3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // reuse push-pull output
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);	
	TIM_TimeBaseStructure.TIM_Period = 100; // Output PWM waveform frequency = timer input frequency/tim_timebasestructure. TIM_Period, 200 000Hz/100=2000Hz, that is, 0.5ms a cycle
	TIM_TimeBaseStructure.TIM_Prescaler =360- 1;// Non-frequency input frequency =APB1 clock/(pre-division coefficient +1) =72 000 000Hz/360=200 000Hz =200K
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; // Set clock split :TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM up count mode
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // Select timer mode :TIM pulse width modulation mode 2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // Enable the comparison output
	TIM_OCInitStructure.TIM_Pulse = 50; Tim_timebasestructure. TIM_Period, 500/1000=50%
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // Output polarity :TIM output polarity is high
	TIM_OC3Init(TIM3, &TIM_OCInitStructure);  
		
    TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //CH3 preloading is enabled
    TIM_ARRPreloadConfig(TIM3, ENABLE); // enable TIM3 preload register on ARR
	TIM_Cmd(TIM3, ENABLE);  / / can make TIM3
}
Copy the code

6. Writing of main functions

The main function is very simple, just a while function.

 int main(void)
 {	
	SystemInit ();	 
	TIM3_PWM_Init();
   	while(1) {}}Copy the code

After debugging in keil according to the method described in the previous chapter, it can be clearly seen that the period is 0.5ms, which is the same as the one we set above. The duty cycle is also 50%, which is 1/2.

Of course, the PWM wave we set here is dead, that is to say, fixed 50%. So in this actual project, we generally require variable PWM, the most typical is to control the speed of the motor. As an example, the following is the drive chip of the motor and the pin distribution diagram of TB6612.

TB6612 pin distribution:  VM PWMA--------->TIM3_CH3(PBO) VCC AIN2--------->GPIOB_12 GND AIN1--------->GPIOB_13 AO1 STBY--------->GPIOB_14 AO2 BIN1--------->GPIOB_15 BO2 BIN2--------->GPIOA_12 BO1 PWMB--------->TIM3_CH4(PB1)Copy the code

Here we use the level of GPIOB_12 and GPIOB_13 to control the motor forward and backward, the specific level depends on the truth table. Use TIM3_CH3(PB0) to control the motor speed. Set different values, the speed of the motor is fast or slow. So which function is used to control it?

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);Copy the code

Of course, for the other channels, there is a function name of the form TIM_SetComparex(x=1,2,3,4). Now we can control the PWM wave of TIM3’s CH3 output. That is, a wheel spins faster or slower. Here I use a little if statement to change the value of channel 3 on timer 3.

 int main(void)
 {	
	u16 i=0;  
	SystemInit ();	 
	TIM3_PWM_Init();
   	while(1)
	{
		i++;
		TIM_SetCompare3(TIM3,i);	  // Channel 3 of timer 3
 		if(i==100) i=0; }}Copy the code

Here is the result:

It is obvious that the PWM wave is constantly changing, which corresponds to the change of motor speed or the brightness of the small lamp.

So far we understand how STM32 output PWM. To summarize, we use timer 3 to output PWM, and TIM_Period and TIM_Prescaler to determine the frequency and period of output PWM. Use TIM_Pulse to determine duty cycle. If you are simply using timer 3 for timing then TIM_Period and TIM_Prescaler determine the time required for timing, i.e. how long it takes to enter the interrupt service function to execute the corresponding instruction. It’s just different in different places, but it’s still the same. Can you be smart enough?

STM32 Chapter 6 – Timer details