【STM32基础】第四篇、控制PWM占空比

目录

第一部分、STM32定时器的基本知识

1、STM32定时器的分类(图片来自野火文档)

2、开发过程中定时器的常用基础功能

3、定时器挂在的总线

 第二部分、能用的代码

  1、定时器计时代码

2、定时器输出PWM代码

第三部分、总结

第一部分、STM32定时器的基本知识

1、STM32定时器的分类(图片来自野火文档)

        输入捕获通道应用:在测量方波信号的脉宽或者频率。输出比较应用在:PWM波形输出,控制电机转速或者呼吸灯等。

扫描二维码关注公众号,回复: 14739339 查看本文章

2、开发过程中定时器的常用基础功能

        在STM32的使用过程中,我常用的两个功能如下:(1)间隔多少时间来一次定时器中断,可以用做计算时间、退出某一程序或者用来做LCD显示。(2)产生PWM波,通过控制PWM波的占空比,然后实现对电机转速的控制。

        注意:定时器定时时间的计算公式 T =((arr+1)*(psc+1))/72M  (单位为  s秒)

                                                               F = 72M /{(arr+1)*(psc+1) (单位为  Hz赫兹)

3、定时器挂载的总线

        其实在开发过程中,如果经常使用正点原子或者野火例程中的代码,那么这一点常常不会被关注到,因为这些基本的配置代码别人都给你配置好了,所以你在使用的过程中不会出现错误,但是了解一下,对开发是有帮助的。

        STM32的AHB 总线延伸出来的两条 APB2 和 APB1 总线,上面挂载着 STM32 各种各样的特色外设。我们经常说的 GPIO、串口、 I2C、 SPI 这些外设就挂载在这两条总线上。

      记忆方法: APB1 总线上一般挂载的都是比较高级,速度比较快的外设, 而APB2总线上挂载的都是一些比较low的外设。(细节详情请参考:《零死角玩转 STM32—基于野火 F103[MINI]开发板》这本书,上传不了,直接去野火论坛下载野火电子论坛 - Powered by Discuz! (firebbs.cn)

 第二部分、能用的代码

  1、定时器计时代码

        代码写法可能不是最好的,但是用起来没问题。我记录下来,方便自己下次直接Copy,可能对你没帮助哦。

 time.c配置代码 

#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初始化

        注意:这里时间的变量要到前面定义,

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

2、定时器输出PWM代码

        pwm.c文件

#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文件

#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

        main.c文件的简易使用代码,需要更改。

/*设置占空比*/
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  可以更换   
    }
}

第三部分、总结

        这上面的代码如果你想使用,需要你有一定的32开发基础,我这里做笔记,只是为了方便自己下次使用。

猜你喜欢

转载自blog.csdn.net/Learning1232/article/details/125097197