定时器&PWM呼吸灯-标准库

首言

本文任务:

一. 使用STM32F103的 Tim2~Tim5其一定时器的某一个通道pin(与GPIOx管脚复用,见下图),连接一个LED,用定时器计数方式,控制LED以2s的频率周期性地亮-灭。

二. 接上,采用定时器pwm模式,让 LED 以呼吸灯方式渐亮渐灭,周期为1~2秒,自己调整到一个满意效果。使用Keil虚拟示波器,观察 pwm输出波形。

本文选用的是频率为1kHz,占空比为50%,分辨率为1%的PWM波形,具体公式如下,其中CK_PSC为72M:

本文选择通过更改CCR的值来实现呼吸灯。

一、TIM定时中断

STM32的TIM中断是一种强大的定时器和中断处理功能,广泛用于STM32微控制器中。它能够实现高精度的定时操作,同时提供了灵活的中断处理机制,使得开发人员可以更好地控制和优化系统性能。

首先,STM32的TIM中断通过配置TIM的周期和预分频值,可以生成毫秒级别的定时。这种定时功能非常适合于需要精确控制时间间隔的应用,比如定时器、计数器、时间戳等。同时,TIM还支持向上计数、向下计数、中央对齐等模式,可以根据不同的需求进行选择。

其次,STM32的TIM中断具有捕获功能,可以用于测量输入信号的频率和占空比。这种功能在许多应用中都非常重要,比如马达控制、脉冲宽度调制(PWM)控制等。通过捕获输入信号的边缘,TIM可以计算输入信号的频率和占空比,从而实现对系统的精确控制。

此外,STM32的TIM中断还具有多种触发方式,包括软件触发、外部触发、定时器触发等。这使得开发人员可以根据实际需求选择最适合的触发方式,比如在需要快速响应的事件中可以使用软件触发,而在需要与外部设备同步的情况下可以使用外部触发。

最后,STM32的TIM中断具有优先级可设置的功能,可以根据不同的需求设置不同的中断优先级。这种功能在实时应用中非常重要,比如在同时处理多个中断的情况下,可以通过设置优先级来保证最重要的任务得到优先处理。一般来说,TIM定时中断基本配置步骤为:

1.配置RCC时钟使能。

2.选择时钟源。

3.配置时基单元

4. 配置中断输出控制。

5.配置NVIC6.使能计数器

二、配置PWM

PWM(Pulse Width Modulation)脉冲宽度调制是一种在具有惯性的系统中常用的控制方法。通过调制一系列脉冲的宽度,可以等效地获得所需要的模拟参量,广泛应用于电机控速等领域。

PWM的参数包括频率、占空比和分辨率。其中,频率是指单位时间内脉冲的个数,通常用f表示,单位为Hz。占空比是指在一个脉冲周期内高电平持续的时间所占的比例,用Ton表示,单位为s。而分辨率则是指占空比变化的步距,表示PWM信号对模拟参量的控制精度。

通过对PWM参数的调节,可以实现精确的电机速度控制、灯光亮度调节等功能。PWM的应用范围非常广泛,如工业控制、电力电子、智能家居等领域都有PWM的应用。同时,PWM信号的生成和控制可以通过微控制器、DSP、FPGA等硬件实现,也可以通过软件算法实现。

上图是PWM的基本结构图,由图能够很直观的看出配置PWM的具体流程:

(1)RCC开启时钟

这里包括TIM外设和GPIO外设的时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

(2)配置时基单元

在这里主要是通过TIM定时中断来进行ARR和PSC的配置,具体如下:

TIM_InternalClockConfig(TIM2);//中断输出使能
	//配置TIM定时中断
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=100-1;//周期ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//预分频器PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);

(3)配置输出比较单元

这里包括CCR值、输出比较模式、极性选择、输出使能。

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

以上有四个用结构体来初始化输出比较单元的函数 ,对应四个输出比较单元,但是,因为本文选用的是PA0口,由此选择第一个输出比较单元的函数进行初始化。具体代码如下:

因为对于本文的呼吸灯来说,不需要配置TIM_OC1Init中所有的值,因此,需要借助结构体来进行赋值,结构体赋值的函数如下所示:

void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);

下面四个函数就是用来单独更改CCR值,从而更改占空比:

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

最终配置的代码为:

//初始化输出比较单元
	TIM_OCInitTypeDef TIM_OC1InitStructure;
	TIM_OCStructInit(&TIM_OC1InitStructure);
	TIM_OC1InitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较的模式
	TIM_OC1InitStructure.TIM_OCPolarity =TIM_OCPolarity_High;//设置输出比较极性,有效电平为高电平
	TIM_OC1InitStructure.TIM_OutputState =TIM_OutputState_Enable;//设置输出使能
	TIM_OC1InitStructure.TIM_Pulse =50;//设置CCR
	TIM_OC1Init(TIM2,&TIM_OC1InitStructure);

(4)配置GPIO

因为想要用定时器来控制引脚,这里PWM所对应的GPIO口应该设置为复用推挽输出模式,引脚的控制权才能交给片上外设。PWM波形才能通过引脚输出完整配置代码如下所示:

    //开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//初始化GPIO口
	GPIO_InitTypeDef GPIOA_Initleds;
	GPIOA_Initleds.GPIO_Mode =GPIO_Mode_AF_PP;//推挽输出模式 
	GPIOA_Initleds.GPIO_Pin =GPIO_Pin_0;//输出引脚为PA0-15全部作为输出端口
	GPIOA_Initleds.GPIO_Speed =GPIO_Speed_50MHz;//输出速率为50MHz
	GPIO_Init (GPIOA,&GPIOA_Initleds);//&是取地址的意思

本文需要实现一个呼吸灯,选择通过改变CCR的值来改变灯的亮度,那么这里就需要借助TIM _SetComparel 函数,该函数是专门用来通道1的CCR值的。

(5)打开运行控制,启动计数器

用TIM_Cmd使能TIM2即可,如下:

	TIM_Cmd(TIM2,ENABLE);

本文将PWM的初始化和LED的初始化封装成了两个头文件 

三、程序编写

任务二:实现一个呼吸灯,选择通过改变CCR的值来改变灯的亮度,那么这里就需要借助TIM _SetComparel 函数,该函数是专门用来通道1的CCR值的。因此之前设置的CCR值不需要了,可以先写个零,本文建立了一个更改CCR值的函数,具体代码如下所示:

void PWM_SetCompare1(uint16_t RECCR)
{
	TIM_SetCompare1(TIM2,RECCR);
}

接下来只需要在主函数中调用该函数,赋予不同的RECCR值就可以实现灯亮度的变化。 

所以,综上编写主函数程序:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "PWM.h"

uint8_t i=0;
uint8_t j=0;

int main(void)
{
	LED_Init();
	PWM_Init();
	while (1)
	{
		if(j==0)
		{
				for(i=0;i<=100;i++)
			{
				PWM_SetCompare1(i);
				Delay_ms(10);
			}
			j=100;
		}
		if(i==101)
		{
			for(j=100;j>0;j--)
			{
				PWM_SetCompare1(j);
				Delay_ms(10);
			}
			i=0;
		}
	}
}

 PWM的初始化函数以及更改CCR函数为,并且封装成了PWM.h头文件:

#include "Device/Include/stm32f10x.h"   // Device header

void PWM_Init(void)
{
	//初始化RCC
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	//配置TIM定时中断
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=100-1;//周期ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//预分频器PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	//初始化输出比较单元
	TIM_OCInitTypeDef TIM_OC1InitStructure;
	TIM_OCStructInit(&TIM_OC1InitStructure);
	TIM_OC1InitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较的模式
	TIM_OC1InitStructure.TIM_OCPolarity =TIM_OCPolarity_High;//设置输出比较极性,有效电平为高电平
	TIM_OC1InitStructure.TIM_OutputState =TIM_OutputState_Enable;//设置输出使能
	TIM_OC1InitStructure.TIM_Pulse =0;//设置CCR
	TIM_OC1Init(TIM2,&TIM_OC1InitStructure);
	
	TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t RECCR)
{
	TIM_SetCompare1(TIM2,RECCR);
}

GPIO的初始化函数如下所示,并且封装成了LED.h的头文件:

#include "Device/Include/stm32f10x.h"   // Device header

void LED_Init(void)
{
	//开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//初始化GPIO口
	GPIO_InitTypeDef GPIOA_Initleds;
	GPIOA_Initleds.GPIO_Mode =GPIO_Mode_AF_PP;//推挽输出模式 
	GPIOA_Initleds.GPIO_Pin =GPIO_Pin_0;//输出引脚为PA0-15全部作为输出端口
	GPIOA_Initleds.GPIO_Speed =GPIO_Speed_50MHz;//输出速率为50MHz
	GPIO_Init (GPIOA,&GPIOA_Initleds);//&是取地址的意思
}

那么在任务二的基础上,我们可以借用同样的函数,只需要将void PWM_SetCompare1(uint16_t RECCR);中的RECCR给一个定值即可,那么主函数的程序如下:

int main(void)
{
	LED_Init();
	PWM_Init();
	while (1)
	{
		PWM_SetCompare1(0);
		Delay_s(2);
		PWM_SetCompare1(100);
		Delay_s(2);
	}
}

四、结果展示

任务一:

由图可以看出来高低电平持续时间均为两秒 

任务二:

五、总结

通过这次的实验,我大致了解了STM32中的定时中断功能,明白了几种基本的定时中断模式,了解了通用定时器中的输出比较功能,通过设置频率,占空比以及分辨率来实现类模拟信号,再通过PA0引脚复用输出到端口,从而实现LED呼吸灯。虽然说标准库的操作很繁琐,但是逻辑步骤还是十分清晰的,也加强了我编程检查错误避免错误的能力。

六、参考

(1)https://www.bilibili.com/video/BV1th411z7sn?p=16&vd_source=6b998429e4d25a33748fad4477a9a498

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值