[STM32 Basics] Part 4, Control PWM Duty Cycle

Table of contents

The first part, the basic knowledge of STM32 timer

1. Classification of STM32 timers (pictures from wildfire documents)

2. Commonly used basic functions of timers in the development process

3. The bus on which the timer is connected

 The second part, the code that can be used

  1. Timer timing code

2. Timer output PWM code

Part Three, Summary

The first part, the basic knowledge of STM32 timer

1. Classification of STM32 timers (pictures from wildfire documents)

        Input capture channel application: measuring the pulse width or frequency of a square wave signal. Output comparison is used in: PWM waveform output, control of motor speed or breathing light, etc.

2. Commonly used basic functions of timers in the development process

        During the use of STM32, the two functions I commonly use are as follows: (1) How long does it take to interrupt the timer, which can be used to calculate time, exit a certain program, or be used for LCD display. (2) Generate a PWM wave, and then control the motor speed by controlling the duty cycle of the PWM wave.

        Note: The formula for calculating the timing of the timer is  T =((arr+1)*(psc+1))/72M (the unit is s seconds)

                                                               F = 72M /{(arr+1)*(psc+1) (unit is Hz)

3. The bus on which the timer is mounted

        In fact, in the development process, if you often use the code in the punctual atom or the wildfire routine, then this point is often not paid attention to, because these basic configuration codes have been configured for you by others, so when you use it There will be no errors, but understanding it is helpful for development.

        The two APB2 and APB1 buses extended from the AHB bus of STM32 are mounted with various special peripherals of STM32. The peripherals such as GPIO, serial port, I2C, and SPI that we often talk about are mounted on these two buses.

     Memory method: Generally , more advanced and faster peripherals are mounted on the APB1 bus, while some relatively low peripherals are mounted on the APB2 bus. (For details, please refer to: "Zero Dead Angle Fun with STM32 - Based on the Wildfire F103[MINI] Development Board" book, if you can't upload it, go directly to the Wildfire Forum to download the Wildfire Electronic Forum- Powered by Discuz! (firebbs.cn)

 The second part, the code that can be used

  1. Timer timing code

        The code may not be the best written, but it works fine. I recorded it so that I can copy directly next time, it may not help you.

 time.c configuration code 

#include "timer.h"
//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u32 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 计数到5000为500ms
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(  //使能或者失能指定的TIM中断
		TIM3, //TIM2
		TIM_IT_Update ,
		ENABLE  //使能
		);
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
							 
}


extern int hour_return;
extern int min_return;
extern int sec_return;


void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
		{
			TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
			sec_return++;
					
				                                   
			if(sec_return == 60) 
			{
				sec_return = 0;
				min_return++;
				if(min_return == 60)
				{
					min_return = 0;
					hour_return ++;
					if(hour_return == 24) hour_return = 0;
					
				}
			}
		}
}

main.c initialization

        Note: The time variable here must be defined earlier,

//主函数
int main()
{
/*定时器*/		
	TIM3_Int_Init(9999,7199);         //设置延时1s
    while(1)
    {
    }
}

2. Timer output PWM code

        pwm.c file

#include "pwm.h" 

//电机初始化
void Motor_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PC端口时钟
	
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				
 GPIO_Init(GPIOA, &GPIO_InitStructure);					 
 GPIO_ResetBits(GPIOA,GPIO_Pin_5);						 
}

//定时器复用功能引脚初始化
static void ADVANCE_TIM_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	// 输出比较通道 GPIO 初始化
	RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);

	// 输出比较通道互补通道 GPIO 初始化
	RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1N_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1N_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ADVANCE_TIM_CH1N_PORT, &GPIO_InitStructure);

	// 输出比较通道刹车通道 GPIO 初始化
	RCC_APB2PeriphClockCmd(ADVANCE_TIM_BKIN_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_BKIN_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ADVANCE_TIM_BKIN_PORT, &GPIO_InitStructure);
	// BKIN 引脚默认先输出低电平
	GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);
}

/*电机占空比*/

extern unsigned char ADVANCE_TIM_PULSE;

//定时器模式配置
void ADVANCE_TIM_Mode_Config(void)
{
	// 开启定时器时钟,即内部时钟 CK_INT=72M
	ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);

	/*--------------------时基结构体初始化-------------------------*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	// 自动重装载寄存器的值,累计 TIM_Period+1 个频率后产生一个更新或者中断
	TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD;
	// 驱动 CNT 计数器的时钟 = Fck_int/(psc+1)
	TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC;
	// 时钟分频因子 ,配置死区时间时需要用到
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	// 计数器计数模式,设置为向上计数
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
	// 重复计数器的值,没用到不用管
	TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
	// 初始化定时器
	TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);

	/*--------------------输出比较结构体初始化-------------------*/
	TIM_OCInitTypeDef TIM_OCInitStructure;
	// 配置为 PWM 模式 1
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	// 输出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	// 互补输出使能
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
	/* 设置占空比大小 */
	TIM_OCInitStructure.TIM_Pulse = ADVANCE_TIM_PULSE;
	// 输出通道电平极性配置
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	// 互补输出通道电平极性配置
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	// 输出通道空闲电平极性配置
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
	// 互补输出通道空闲电平极性配置
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(ADVANCE_TIM, TIM_OCPreload_Enable);

	/*-------------------刹车和死区结构体初始化-------------------*/
	// 有关刹车和死区结构体的成员具体可参考 BDTR 寄存器的描述
	TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
	TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
	TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
	TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
	// 输出比较信号死区时间配置,具体如何计算可参考 BDTR:UTG[7:0]的描述
	// 这里配置的死区时间为 152ns
	TIM_BDTRInitStructure.TIM_DeadTime = 11;
	TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
	// 当 BKIN 引脚检测到高电平的时候,输出比较信号被禁止,就好像是刹车一样
	TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
	TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
	TIM_BDTRConfig(ADVANCE_TIM, &TIM_BDTRInitStructure);

	// 使能计数器
	TIM_Cmd(ADVANCE_TIM, ENABLE);
	// 主输出使能,当使用的是通用定时器时,这句不需要
	TIM_CtrlPWMOutputs(ADVANCE_TIM, ENABLE);

 }

 void ADVANCE_TIM_Init(void)
{
	ADVANCE_TIM_GPIO_Config();
	ADVANCE_TIM_Mode_Config();		
}

        pwm.h file

#ifndef __PWM_H
#define __PWM_H
#include "stdio.h"	
#include "sys.h" 

//电机
#define MOTOR PAout(5)	// PA5
#define PWM_ShaChe PBout(12)	// PB12
void Motor_Init(void);

//宏定义
/************高级定时器 TIM 参数定义,只限 TIM1 和 TIM8************/
// 当使用不同的定时器的时候,对应的 GPIO 是不一样的,这点要注意
// 这里我们使用高级控制定时器 TIM1

#define ADVANCE_TIM TIM1
#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1

/********PWM 的周期和占空比******************/
/*可供修改公式  F = TIM_CLK/{(ARR+1)*(PSC+1)*/
/* TIM_CLK 等于 72MHZ*/
#define ADVANCE_TIM_PERIOD (80-1)   //ARR 即自动重装载寄存器的值
#define ADVANCE_TIM_PSC (900-1)     //PSC 即计数器时钟的分频因子
//#define ADVANCE_TIM_PULSE1 10        //和ARR的数值有关,ARR的一半表示PWM高低各占一半(计数到40反转)

#define ADVANCE_TIM_IRQ TIM1_UP_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_UP_IRQHandler

// TIM1 输出比较通道
#define ADVANCE_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADVANCE_TIM_CH1_PORT GPIOA
#define ADVANCE_TIM_CH1_PIN GPIO_Pin_8

// TIM1 输出比较通道的互补通道
#define ADVANCE_TIM_CH1N_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADVANCE_TIM_CH1N_PORT GPIOB
#define ADVANCE_TIM_CH1N_PIN GPIO_Pin_13

// TIM1 输出比较通道的刹车通道
#define ADVANCE_TIM_BKIN_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADVANCE_TIM_BKIN_PORT GPIOB
#define ADVANCE_TIM_BKIN_PIN GPIO_Pin_12

static作用
静态函数不能被其它文件所用;
其它文件中可以定义相同名字的函数,不会发生冲突;
//static void ADVANCE_TIM_GPIO_Config(void);
void ADVANCE_TIM_Mode_Config(void);

void ADVANCE_TIM_Init(void);

#endif

        Easy-to-use code for the main.c file , which needs to be changed.

/*设置占空比*/
unsigned char ADVANCE_TIM_PULSE = 0;
int main()
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//中断控制器分组设置
    /*高级定时器PWM初始化 */
    ADVANCE_TIM_Init();    //PWM   PA8
    Motor_Init();          //电机初始化PA5
    while(1)
    {
        ADVANCE_TIM_PULSE = 40;
        ADVANCE_TIM_Mode_Config();   //PWM  可以更换   
    }
}

Part Three, Summary

        If you want to use the above code, you need to have a certain 32 development foundation. I am taking notes here, just for the convenience of my next use.

Guess you like

Origin blog.csdn.net/Learning1232/article/details/125097197