通过定时器和脉冲控制LED

 

一、定时器

(一)定时器简介

        STM32定时器是STM32微控制器中的重要块,用于生成精确的时间基准。它可以用于测量时间间隔、产生脉冲、实现定时中断等功能。通过配置寄存器,用户可以灵活地控制定时器的工作模式和参数,实现各种实时控制和定时任务。        

(二)定时器类型

5d258fe9f7374c88abc6da56332f3977.png

1、常见定时器

  • SysTick定时器:

SysTick系统时钟位于Cortex-M3内核,是一个24位的递减计数器,主要用于:

1)精确延时,在多任务操作系统中为系统提供时间基准(时基);

2)任务切换,为每个任务分配时间片。

  • WatchDog定时器:

1)作用:当微控制器受到外部干扰或程序中出现不可预知的逻辑故障导致应用程序脱离正常的执行流程时(俗称程序跑飞),在一定的时间间隔内使系统复位,回到初始状态;

2)看门狗设计是用来监视MCU程序运行状态的,是确保系统可靠稳定运行的一种有效措施。

WatchDog又可以分为独立看门狗和窗口看门狗,区别如下:

54ec0a7c42a141e8ad49c9720bb3799b.png

独立看门狗:采用独立时钟,不受其他时钟和总线影响,可在停机和待机模式下工作。适用于独立于主程序之外、能够独立工作且对时间精度要求不高的场合。

窗口看门狗:计数值有一个上限时间和下限时间,低于下限时间或高于上限时间都会触发MCU复位。适用于精确计时窗口起作用的应用程序。

f726f9e0d36e4e3c8d5cd90b3b016898.png

  • 基本定时器(下方重点介绍):TIM6和TIM7。
  • 通用定时器(下方重点介绍):TIM2、TIM3、TIM4和TIM5。
  • 高级定时器(下方重点介绍):TIM1和TIM8。

2、定时器的主要功能

  • 计数:脉冲计数,使用使用微控制器内部的外部时钟(PCLK)来计数,是对固定周期的脉冲信号计数。
  • 定时:时间控制,通过对微控制器内部的时钟脉冲进行计数实现定时功能。
  • 输入捕获:对输入信号进行捕获,实现对脉冲的频率测量,可用于对外部输入信号脉冲宽度的测量,比如测量电机转速。
  • 输出比较:将计数器计数值和设定值进行比较,根据比较结果输出不同电平,用于控制输出波形,比如直流电机的调速。

3、常规定时器

STM32定时器分类比较表

533a0baefd454f488c122e02a0c37abf.png

(1)基本定时器

简介:

STM32有2个基本定时器TIM6和TIM7,可用作: 通用的16位计数或者 产生DAC触发信号,基本定时器的计数模式只有向上计数模式。

d0086469e2254f9fb9b92dfe4bfd4014.png

(2)通用定时器

简介:

        TIM2、TIM3、TIM4、TIM5为STM32的4个独立的16位通用定时器,具有定时、测量输入信号的脉冲长度(输入捕获)、输出所需波形(输出比较、产生PWM、单脉冲输出等)等功能。如下图为通用定时器内部结构框图。

e148772884ec4b50af51995cdff293de.png

        STM32F103系列微控制器的定时器功能十分强大,内部结构也比较复杂,STM32通用定时器TIMx(x=2,3,4,5)主要由时钟源、时钟单元、捕获和比较通道等构成,核心是可编程预分频驱动的16位自动装载计数器。通过对通用定时器内部结构进一步简化得到如下图所示的内部简化图:

cd68a59684d84fd78a4ccfbe1dd67814.png

通用定时器相关参数:

1)通用定时器的时钟源:

如下图为通用定时器框图中有关时钟源的部分:

6209c84251ff4715b72003be0ed5e385.png

当定时器使用内部时钟时,定时器的时钟源统称为TIMxCLK。虽然在系统默认的配置中,TIMxCLK的时钟频率都是72MHz,但其时钟来源并不相同。

  • 定时器TIM2~TIM7挂接在APB1上
  • 定时器TIM1和TIM8挂接在APB2上

若外部晶振的频率为8MHz,则系统默认的时钟频率为72MHz

  • APB1预分频器的分频系数设置为2,则PCLK1=36MHz;
  • APB2预分频系数设置为1,则PCLK2=72MHz,TIM1和TIM8的时钟频率TIMxCLK=72MHz;
  • Cortex系统时钟由AHB时钟(HCLK)8分频得到,即SysTick的频率为9MHz。

2)预分频器PSC:

如图为预分频器时序图:

24cb1e974d45489d9c6c87f928c3e85c.png

简介:

PSC参数代表预分频器(Prescaler)。预分频器用于将定时器的时钟频率分频,以便获得所需的计数频率。在定时器工作时,计数器将以预分频器所设定的频率递增,直到达到设定的自动重载值(ARR,Auto-Reload Register)为止,然后重新开始计数。

限制:可以以1~65535之间的任意数值对时钟源CK_PSC的时钟频率进行分频,输出CK_CNT脉冲供计数器CNT进行计数。

计数器计数频率计算公式:CK_CNT = CK_PSC / (PSC + 1)

3)计数器CNT:

如图为计数器时序图:

398f6d02cd1440d3a705cfbc35b5cd31.png

简介:

  • TIMxCNT是一个16位的寄存器,计数范围为1~65535,可以向上计数、向下计数或向下向上双向计数。
  • 要得到想要的计数值,需要对输入时钟频率进行分频。
  • 当计数值达到设定值时,便产生溢出事件,溢出时产生中断或DMA请求,然后再由自动装载寄存器进行重新加载或更新。
  • 计数器溢出中断属于软件中断,执行相应的定时器中断服务程序。、

计数器溢出频率计算公式:CK_CNT_OV = CK_CNT / (ARR + 1)                           

                                                                  = CK_PSC / (PSC + 1) / (ARR + 1)

4)自动装载寄存器ARR

定时器的定时时间主要取决于定时周期和预分频因子,这里ARR+1是因为计数器都是从0开始计数的。计算公式为:

定时时间=(ARR+1)×(预分频值PSC+1)/输入时钟频率  或者  

T=(TIM_Period +1)*(TIM_Prescaler +1)/TIMxCLK

例如下方实践中要求延时2秒闪烁及定时器设为2s,假设系统时钟为72MHz,通用定时器时钟TIMxCLK为72MHz,设置如下:

预分频系数PSC=6000-1;    ARR=4000-1;

则,定时时间=4000×36000/72000000=2s。

(3)高级定时器

92acfc2ca40c40c0b29a97a6dfe3e808.png

简介:TIM1和TIM8是STM32的2个16位的高级定时器,高级定时器相比基本定时器、通用定时器,功能更为强大   

功能: 高级定时器除了通用定时器的功能外,还支持更高级的特性,如相位锁定回路(PLL)、编码器接口、三角波生成等。

用途场景: 适用于需要更高级、复杂计时功能的应用,例如音频处理、电机控制、编码器接口等。重复计数器、死区生成、互补输出、刹车输入等功能。

(三)定时器配置

1、定时器标准外设库接口函数

简介:

STM32F103系列微控制器的定时器库函数存放在STM32标准外设库的stm32f10x_tim.c和stm32f10x_tim.h文件中,stm32f10x_tim.h头文件中声明了定时器有关的库函数以及相关的宏定义、结构体等。由于STM32的定时器功能较为强大,其库函数也较多,如下图所示:

ff07e9dcb6f94526a7fa396892bc0146.png

TIM_TimeBaseInit()库函数

用于定时器TIMx的参数初始化,函数原型:

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

typedef struct
{
  uint16_t TIM_Prescaler;   
  uint16_t TIM_CounterMode;      
  uint16_t TIM_Period;          
  uint16_t TIM_ClockDivision;   
  uint8_t TIM_RepetitionCounter; 
} TIM_TimeBaseInitTypeDef;  

其中

(1)typedef struct(第二行)是被定义在定义在stm32f10x_tim.h头文件中。

(2)uint16_t TIM_Prescaler(第四行),TIMx预分频寄存器TIMx_PSC的值,数值上等于TIMx计数器TIMxCNT的预分频系数减1

(3)uint8_t TIM_RepetitionCounter(第八行),为TIM1和TIM8高级定时器所特有。

(4)TIM_CounterMode(第五行):TIMx计数器TIMxCNT的计数模式分为向上计数和向下计数以及三种中央对其模式计数。

  • TIM_CounterMode_Up:向上计数模式,从0递增,计到TIMx_ARR计数器的自动装载值,并产生计数溢出脉冲
  • TIM_CounterMode_Down:向下计数模式,从TIMx_ARR计数器的自动装载值递减,计到0,并产生计数溢出脉冲
  • TIM_CounterMode_CenterAligned1:中央对齐模式1计数器交替地向上和向下计数,输出比较中断标志位,只在计数器向下计数时被设置;
  • TIM_CounterMode_CenterAligned2:中央对齐模式2计数器交替地向上和向下计数,输出比较中断标志位,只在计数器向上计数时被设置;
  • TIM_CounterMode_CenterAligned3:中央对齐模式3计数器交替地向上和向下计数,输出比较中断标志位,在计数器向下和向上计数时均可被设置。

(5) uint16_t TIM_Period(第六行);  下一个更新事件时装入自动重装载寄存器TIMx_ARR的周期值,数值上等于TIMx计数器TIMxCNT的计数周期减1。

  •   TIM_Period是一个16位无符号整型数据,取值范围为0~65535。    
  •  定时器定时时间主要取决于预分频因子TIM_Prescaler和定时周期TIM_Period

T=(TIM_Prescaler+1)*(TIM_Period+1)/TIMxCLK    

  • TIMxCLK为定时器TIMx的时钟频率,STM32F103的TIMxCLK默认设置为72MHz。

(6) uint16_t TIM_ClockDivision(第七行); 与TIMx_CR寄存器中bit[9:8]的CKD[1:0]对应,用于计数器工作模式时滤除高频干扰,有如下选择:

  • “00”——采样频率基准fDTS=定时器输入频率fCK_INT;
  • “01”——fDTS=fCK_INT/2;
  • “10”——fDTS=fCK_INT/4。

2、定时器标准外设库配置

3ca08e81ee7d4874a4bf2e8368af4da1.png

(1)步骤一:TIM2时钟使能

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

(2)步骤二:初始化定时器参数,设置自动重装值,分频系数,计数方式

定时器初始化使用TIM_TimeBaseInit()函数,如下所示:

void TIM_TimeBaseInit(TIM_TypeDef*TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
  • 第一个参数TIMx是具体定时器,x可为2~4
  • 第二个参数为TimeBaseInitStruct结构体指针。

下面以初始化通用定时器TIM2为例,进行以下配置:

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义TIM初始化类型结构体变量
TIM_TimeBaseStructure.TIM_Period = (36000-1); //设置自动重载计数周期值
TIM_TimeBaseStructure.TIM_Prescaler = (4000-1); //设置分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分频因子
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置为向上计数方式
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //初始化定时器TIM2
TIM_Cmd(TIM2,ENABLE); //使能TIM2定时器

(3)步骤三:设置TIM2允许更新中断

为了避免在初始化定时器时进入中断,需要在初始化过程中清除中断标志。中断在使用前必须先使能再使用,如使能定时器TIM2的更新模式中断,则调用库函数TIM_ITConfig(),函数原型为:

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

其中:

  • 第一个参数TIMx是具体定时器,x可为2~4。
  • 第二个参数用来指明所使能定时器的中断类型。
  • 第三个参数为TIMx中断的状态,该参数可取值为ENABLE(使能)或者DISABLE(失能)。

(4)步骤四:TIM2中断优先级设置

如图为定时中断基本结构

de9bf9bcaabb4cbb812aa5b81c5fafc4.png

结合先前学习的中断编程,参考博客:中断编程入门-CSDN博客​​​​​​

对TIM2中断做如下所示的配置:

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

(5)步骤五:使能TIM2

使用TIM_Cmd()函数使能相应的定时器,其函数原型为:

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState) ;

其中:

  • 第一个参数TIMx是具体定时器,x可为2~4
  • 第二个参数为TIMx的状态,该参数可取值为ENABLE(使能)或者DISABLE(失能)

例如,使能定时器TIM2的更新中断,配置如下:              

 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

(6)步骤六:编写中断服务函数

STM32标准外设库函数中用来读取中断状态的库函数为:

ITStatus TIM_GetITStatus (TIM_TypeDef* TIMx, uint16_t) ;

清除定时器相应中断标志位的函数为:

void TIM_ClearITPendingBit (TIM_TypeDef* TIMx, uint16_t TIM_IT) ;

 例如想要通过定时器设定某时间后GPIOA5引脚的灯反转,则中断服务函数如下所示:

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)((1- GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5))));
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

因此我们可以得到一个TIM2中断服务函数的模板以便以后使用时可以直接填入。

/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

二、PWM

(一)PWM简介

PWM基本结构

473941e810a7467695cdc39a9054c6ac.png

1、介绍及其功能

        PWM(Pulse Width Modulation,脉冲宽度调制)是一种利用脉冲宽度即占空比实现对模拟信号进行控制的技术,即是对模拟信号电平进行数字表示的方法。

        广泛应用于电力电子技术中,比如PWM控制技术在逆变电路中的应用;PWM还应用于直流电机调速,如变频空调的交直流变频调速,除实现调速外,还具有节能等特性。

下图是一个周期为10ms(频率为100Hz)的PWM波形图

6d433b0f1d934e8080b5f94804e95270.png

2、占空比

占空比(Duty Cycle),是指在一个周期内,高电平时间占整个信号周期的百分比,即高电平时间与周期的比值: 占空比=Tp/T。

09bedc414b9e4f23bc2918b7171175c2.png87a2d36ed19e4563ba03a978d413ea3a.png

(二)PWM的工作原理

1、产生PWM的定时器:

  • 在STM32的定时器中除了TIM6和TIM7,其他定时器都可以用来产生PWM输出;
  • 高级定时器TIM1和TIM8可以同时产生多达7路的PWM输出;
  • 通用定时器能同时产生多达4路的PWM输出

2、产生PWM的原因:

  • STM32中每个定时器有4个输入通道:TIMx_CH1~TIMx_CH4;
  • 每个通道对应1个捕获/比较寄存器TIMx_CRRx,将寄存器值和计数器值相比较,通过比较结果输出高低电平,从而得到PWM信号;
  • 脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。

3、PWM的工作原理解释

e4bb10d81e884372a36ff9bcfde2ea41.png

  • 在PWM的一个周期内,定时器从0开始向上计数,在0-t1时间段,定时器计数器TIMx_CNT值小于TIMx_CCRx值,输出低电平;
  • 在t1-t2时间段,定时器计数器TIMx_CNT值大于TIMx_CCRx值,输出高电平;
  • 当定时器计数器的值TIMx_CNT达到ARR时,定时器溢出,重新从0开始向上计数,如此循环。

(三)PWM标准外设库输出配置过程

8c009a89bdfa4bc3a9fd8a2c7c4505c1.png

(1)步骤一:配置PWM输出通道,开启TIM2时钟

STM32规定了具体的引脚作为PWM输出引脚,选择不同的引脚时,还必须使用重定向功能(Remap)。

定时器TIM1的引脚复用功能映像:

048dc1dbe69544118087bea056f99172.png

 定时器TIM2的引脚复用功能映像:

2873e8d3cca34f3d9c9e3b8424237112.png

定时器TIM3的引脚复用功能映像:

59a941b8cd5f4f819af60dcaed901952.png

  • 使用定时器3的通道2(TIM2_CH1)作为PWM的输出引脚;
  • 对PA0引脚进行配置,配置PA0为复用推挽输出模式(GPIO_Mode_AF_PP)代码如下所示:

	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO重映射*/
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

(2)步骤二:初始化定时器TIM2

设置预分频系数PSC、自动重载计数值ARR、计数模式、时钟分割等,代码如下所示。下方实验中要求呼吸灯的周期为1~2s,且设置为向上计数法则如下配置。

	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择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_TimeBaseInit,配置TIM2的时基单元
	

(3)步骤三:设置TIM2_CH1的PWM模式,使能TIM2的CH1输出

如下配置TIM2_CH1的PWM模式使能TIM2的CH1能够正常输出,配置情况如下所示:

    TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1

(4)步骤四:使能定时器TIM2

 最后使能TIM2,定时器开始运行:

TIM_Cmd(TIM2, ENABLE);

三、实际应用

(一)关于定时器的题目

1、题目要求

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

2、代码

(1)Timer.c

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:定时中断初始化
  * 参    数:无
  * 返 回 值:无
  */
void Timer_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;				//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
																//若不清除此标志位,则开启中断后,会立刻进入一次中断
																//如果不介意此问题,则不清除此标志位也可
	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

(2)LED.c

#include "stm32f10x.h"                  // Device header

void LED_Init(void)
{
   GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(GPIOA,&GPIO_InitStructure);
}

 (3)main.c

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

int main(void)
{
	LED_Init();
	Timer_Init();	
	while (1)
	{
	}
}


void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)((1- GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5))));
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

3、实物效果

定时器控制LED

(二)关于PWM的题目

1、题目要求

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

2、代码

(1)PWM.c

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO重映射*/
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA0引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式		
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择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_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
}

(2)main.c

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

uint8_t i;			//定义for循环的变量

int main(void)
{
	/*模块初始化*/
	PWM_Init();			//PWM初始化
	
	while (1)
	{
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(i);			//依次将定时器的CCR寄存器设置为0~100,PWM占空比逐渐增大,LED逐渐变亮
			Delay_ms(15);			//延时500ms
		}
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(100 - i);	//依次将定时器的CCR寄存器设置为100~0,PWM占空比逐渐减小,LED逐渐变暗
			Delay_ms(15);		//延时500ms
		}
	}
}

3、Keil波形图dc479b7fcc764dfe8437bfbd654dda3f.png

4、实物效果

呼吸灯

四、总结

学习完定时器相关内容,就像掌握了一个闹钟。通过自己设定时间,当时间走完则自动运行预先选择做出的行动,为以后的单片机开发提供了一个新的思路。在学习了PWM之后,发现原来点灯也变得有趣起来了,可以通过赋予脉冲使得灯逐亮逐灭,为后续学习舵机等外设打好了基础。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/665010.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Visual Studio Code使用(C++项目新建,运行)

VS Code 直接在官网下载安装。 接下来安装插件&#xff0c;下图是C所需的对应插件 1.新建项目 VS Code下载安装完成后&#xff0c;直接进入欢迎页&#xff1a; 在访达/文件夹中新建一个文件夹&#xff0c;欢迎页点击【打开】&#xff0c;选择刚刚新建的文件夹。点击第一个图…

材质技术在AI去衣中的作用

随着人工智能技术的飞速发展&#xff0c;越来越多的应用场景开始涌现。其中&#xff0c;AI去衣技术作为一种新兴的图像处理技术&#xff0c;已经在很多领域得到了广泛的应用。而在AI去衣技术中&#xff0c;材质技术起到了至关重要的作用。本文将详细介绍材质技术在AI去衣中的作…

autocad背景色、引线文字大小

一、改变背景 在命令行输入op&#xff0c;回车&#xff0c;弹出配置对话框&#xff1a; 二、改变引线文字大小 选中引线&#xff0c;右键选择【特性】&#xff0c;在文字选项卡中设置文字高度&#xff1a;

【机器学习】探索未来科技的前沿:人工智能、机器学习与大模型

文章目录 引言一、人工智能&#xff1a;从概念到现实1.1 人工智能的定义1.2 人工智能的发展历史1.3 人工智能的分类1.4 人工智能的应用 二、机器学习&#xff1a;人工智能的核心技术2.1 机器学习的定义2.2 机器学习的分类2.3 机器学习的实现原理2.4 机器学习的应用2.5 机器学习…

JVM-之GC日志

一、 开启gc 日志 在项目中开启GC 日志打印后会查看gc 日志如下 nohup java -Xms768m -Xmx768m -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath./dumplog/dumplog.log -Xloggc:./dumplog/gc.log -XX:PrintGCDetails -XX:PrintGCDateStamps -XX:PrintHeapAtGC -jar xxxx…

校园安保巡逻机器人

2023年8月5日&#xff0c;陕西西安一高校实验室起火冒烟&#xff0c;导致学校化学实验室发生火灾。2022年8月3日&#xff0c;一名歹徒持械闯入江西吉安安福县城的一家私立幼儿园&#xff0c;对着无辜的幼儿行凶伤人&#xff0c;造成3死6伤。 像这样的事故有不断地发生&#xf…

[个人笔记] 记录docker-compose使用和Harbor的部署过程

容器技术 第三章 记录docker-compose使用和Harbor的部署过程 容器技术记录docker-compose使用和Harbor的部署过程Harborhttps方式部署&#xff1a;测试环境部署使用自签名SSL证书https方式部署&#xff1a;正式环境部署使用企业颁发的SSL证书给Docker守护进程添加Harbor的SSL证…

世界500强是如何解决邮件大附件影响业务问题的

电子邮件已成为众多企业沟通和文件传输的重要工具。然而&#xff0c;邮件发送大附件时&#xff0c;企业往往会遇到一系列挑战。本文将探讨邮件发送大附件的优劣势&#xff0c;分析其对业务可能造成的问题&#xff0c;并介绍500强企业是如何解决这些问题的。 邮件发送大附件的优…

NSS题目练习5

[NISACTF 2022]babyupload 打开后尝试上传php&#xff0c;jpg&#xff0c;png文件都没成功 查看源代码发现有个/source文件 访问后下载压缩包发现有一个python文件 搜索后知道大致意思是&#xff0c;上传的文件不能有后缀名&#xff0c;上传后生成一个uuid&#xff0c;并将uuid…

姚班带队OpenCSG,超越Devin,创造大模型编程领域新纪录

摘要 来自中国大模型初创公司OpenCSG推出的StarShip CodeGen Agent&#xff0c;以23.67%的成绩刷新了普林斯顿SWEBench(大模型真实独立编程评测)排行榜&#xff0c;取得了全球第二名的成绩&#xff0c;同时创造了非GPT-4o基模的最高纪录&#xff08;SOTA&#xff09;。 SWEBen…

Leecode---动态规划--爬楼梯 / 杨辉三角

爬楼梯题目&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 思路&#xff1a; 设跳上 n 级台阶有 f(n) 种跳法。在所有跳法中&#xff0c;青蛙的最后一步只有两种情况&#xff1a; 跳上…

java多态——向下转型

引入 前面我尝试了一下这个代码 package b;public class main_ {public static void main(String[] args) {//向上转型&#xff0c;父类的引用转向了子类的father_ animalnew graduate();Object objnew graduate();System.out.println(animal.name);System.out.println(obj.n…

【验证码识别】Yolov8入门到实战点选验证码数据集分类训练,孪生训练,导出onnx,搭建部署接口

【验证码识别】Yolov8入门到实战点选验证码数据集分类训练&#xff0c;孪生训练&#xff0c;导出onnx&#xff0c;搭建部署接口 文章目录 【验证码识别】Yolov8入门到实战点选验证码数据集分类训练&#xff0c;孪生训练&#xff0c;导出onnx&#xff0c;搭建部署接口声明一、标…

冯喜运:5.31晚间黄金原油行情还会跌吗?独家操作策略建议

【黄金消息面分析】&#xff1a;在金融市场的波动中&#xff0c;黄金作为传统的避险资产&#xff0c;其价格走势一直受到投资者的密切关注。周五(5月31日)&#xff0c;现货黄金小幅波动&#xff0c;目前稳定在2340美元关口上方。美国核心PCE通胀数据作为美联储的首选通胀指标&a…

【力扣】LCR 130. 衣橱整理

一、题目描述 二、算法思路 这是⼀道非常典型的「搜索」类问题。 我们可以通过「深搜」或者「宽搜」&#xff0c;从 [0, 0] 点出发&#xff0c;按照题目的要求&#xff08;选择 向右移动一格 或 向下移动一格&#xff0c;但不能移动到衣柜之外 &#xff09;一直往 [m - 1, …

Nuxt3项目实现 OG:Image

目录 前言 1、安装 2、设置网站 URL 3、启用 Nuxt DevTools 4、创建您的第一个Og:Image a. 定义OG镜像 b. 查看您的Og:Image 5、自定义NuxtSeo模板 a. 定义 NuxtSeo模板 b. 使用其他可用的社区模板 6、创建自己的模板 a. 定义组件 BlogPost.vue b. 使用新模板 c.…

Tuxera Ntfs For Mac 2023的具体使用方法

大家都知道由于操作系统的原因&#xff0c;在苹果电脑上不能够读写NTFS磁盘&#xff0c;但是&#xff0c;今天小编带来的这款tuxera ntfs 2024 mac 破解版&#xff0c;完美的解决了这个问题。这是一款在macOS平台上使用的磁盘读写软件&#xff0c;能够实现苹果Mac OS X系统读写…

视频汇聚EasyCVR平台GA/T 1400视图库应用:助力社会治安防控效能提升

在信息化、智能化的时代浪潮下&#xff0c;公安视频图像信息应用系统的发展与应用显得尤为重要。GA/T 1400标准&#xff0c;全称为《公安视频图像信息应用系统》&#xff0c;作为公安行业的一项重要标准&#xff0c;其视图库的应用在提升公安工作效能、加强社会治安防控等方面发…

数据结构 | 二叉树(基本概念、性质、遍历、C代码实现)

1.树的基本概念 树是一种 非线性 的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有一个特殊的结点&#xff0c;称为根…

社交媒体数据恢复:云信Demo

一、准备工作 登录您的网易云信demo账号&#xff0c;确保您具有管理员权限。 确认您要恢复的数据类型&#xff0c;例如聊天记录、文件传输记录等。 确保您熟悉网易云信demo的后台管理界面和功能。 二、数据备份 在进行数据恢复之前&#xff0c;请先备份您现有的数据&#…