• 【STM32学习】通用定时器的应用实验


    🐱作者:一只大喵咪1201
    🐱专栏:《STM32学习》
    🔥格言:你只管努力,剩下的交给时间!
    图

    在上篇文章中学习了通用定时器的基本原理以后,接下来本喵就介绍一下定时器的使用。

    ⏰定时器中断实验

    定时器中断实验就是利用计数器CNT计数到一定时间后产生更新中断。

    本喵选择TIMER3来实现定时中断,并且用LED1来体现实验效果。

    图
    查看开发板原理图,LED1和PE5口相连,且采用共阳极接法。

    在这个实验中,本喵要实现每隔500ms让LED1的状态反转一次,当然不是使用延时函数实现的,而是使用定时器的更新中断实现的。

    ⌚时钟选择

    这里选择内部时钟(CK_INT)作为时钟源。

    TIM3是挂载在APB1总线上的,所以它内部时钟的来源是APB1时钟。APB1时钟最大是36MHZ的,本喵使用的开发板这里的时钟就是36MHZ。

    图
    接下来就是计算内部时钟(CK_INT)了,这里有一个特殊点

    除非APB1的分频系数是1,否则通用定时器的时钟等于APB1时钟的2倍。

    我们这里将APB1时钟2分频,所以得到的内部时钟(CK_INT)是72MHZ的。

    再就是计算计数器CNT的驱动时钟(CK_CNT)了。

    这里CK_PSC的频率就是内部时钟(CK_INT)的频率,所以也是72MHZ,将CK_PSC经过PSC寄存器分频后得到的就是CK_CNT时钟频率。

    它的流程如下面的框图

    图
    其中N是PSC预分频寄存器中的值。

    计算具体的计时时间按照下面这个公式:

    • Tout(溢出时间) = (ARR+1)*(PSC+1)/Tclk
    • 其中ARR是重装载寄存器中的值,它是从0开始的,所以要加1。
    • PSC是预分频寄存器中的值,它也是从0开始的,所以要加1。
    • Tclk是就是内部时钟(CK_INT)的频率,也是时钟CK_PSC的频率。

    我们想要实现500ms的定时,

    • 将预分频系数PSC设置为7199
    • 代入公式中就可以求出ARR中的值是4999

    ⌚重要寄存器配置

    1. 计数器CNT

    该寄存器是用来计数的,不需要进配置,在将定时器打开以后便自动开始计数,直到与ARR寄存器中的值相匹配产生溢出事件。

    1. 预分频寄存器

    在上面我们按照定时时间已经设定了它的值,所以将7199写入即可。

    1. 自动重装载寄存器ARR

    通过公式计算,我们算出ARR中的值是4999,将其写入即可。

    1. 控制寄存器CR

    图
    这里我们将计算模式设置为向上计算模式,也就将DIR位设为0。

    图
    同时使能CEN位,让CK_CNT时钟开始工作,驱动计数器CNT计数。

    1. 中断使能寄存器DIER

    图

    将UIE位置1,允许产生更新中断。

    ⌚代码实现

    寄存器的配置同样是通过官方提供的标准库实现的,让本喵给大家介绍一下需要哪些库函数。

    定时器初始化函数:

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

    定时器使能函数:

    void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
    • 1

    定时器中断使能函数:

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

    标志位获取和清除

    FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
    void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
    ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
    void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
    
    • 1
    • 2
    • 3
    • 4

    使用这些库函数,按照下面的顺序就可以达到实验目的:

    1. 定时器时钟使能
    2. 初始化定时器,配置ARR,PSC
    3. 开启定时器中断,配置NVIC
    4. 使能定时器
    5. 编写中断服务函数
      在中断服务函数中实现LED1状态的反转。

    看本喵提供的参考代码:

    main.c中的代码

    #include "timer3.h"
    #include "led.h"
    
    int main()
    {
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组是第二组
    	LED_Init();//LED灯初始化,状态是灭
    	TIMER3_Updata_Init(4999,7199);//定时器定时500ms
    	
    	while(1);//等待中断发生
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    timer3.h中的代码

    #ifndef __TIMER3_H
    #define __TIMER3_H
    #include "sys.h"
    
    void TIMER3_Updata_Init(u16 arr,u16 psc);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    timer3.c中的代码

    #include "timer3.h"
    #include "led.h"
    
    void TIMER3_Updata_Init(u16 arr,u16 psc)
    {
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;//创建TIM3结构体
    	NVIC_InitTypeDef NVIC_InitStruct;//创建中断管理结构体
    	
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3
    	
    	TIM_TimeBaseStruct.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
    	TIM_TimeBaseStruct.TIM_Period=arr;//重装载寄存器中的值
    	TIM_TimeBaseStruct.TIM_Prescaler=psc;//预分频寄存器的值
    	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStruct);//初始化
    	
    	NVIC_InitStruct.NVIC_IRQChannel=TIM3_IRQn;//TIM3中断通道
    	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;//TIM3中断使能
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级2
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;//响应优先级2
    	NVIC_Init(&NVIC_InitStruct);//初始化优先级管理
    	
    	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//使能更新中断
    	
    	TIM_Cmd(TIM3,ENABLE);//使能TIM3
    }
    
    //中断服务函数
    void TIM3_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)//确保是更新中断
    	{
    		LED1=!LED1;//反转LED1的状态
    	}
    	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);//清除更新中断标志位
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    代码中都有相应的注释,可以借鉴。

    至于LED1灯的初始化就没必要展示了。

    ⌚效果展示

    图

    ⏰PWM输出实验

    PWM输出使用定时器的4个输出通道中的一个,通过设置CRR中的值改变PWM波的占空比来实现。通过设置ARR中的值确定PWM波的频率。

    本喵使用LED0,也就是开发板上的红灯来体现实验效果。

    图
    从开发板原理图中可以看到,LED0接在PB5口上,并且是共阳极接法。

    图
    从开发手中中可以看到,TIM3的通道2正好部分重映像在了PB5,所以在使用的时候要进行一下重映像的设定。

    在这个实验中,要让LED0红色灯的亮度由弱变强再变弱,如此反复。

    ⌚时钟选择

    同样还是选择内部时钟,这里的内部时钟(CK_INT)不进行分频,所以给PSC寄存器中的值就是0。所以驱动计数器CNT的时钟CK_CNT的频率是72MHZ。

    ⌚重要寄存器配置

    1. 将相应的数据写入TIMx_ARR和TIMx_CCRx寄存器中

    时钟已经选好了,是72MHZ,这里我们设置重装载寄存器ARR的值是899,此时PWM的频率就是72000000/(899+1) = 80KHZ。

    图
    CRR的值单独修改,需要不停地变化,来改变PWM波的占空比。

    1. 选择输出模式

    这里没有中断产生,所以一定不能开启CRR预装载。

    将CCMR2寄存器的OC3M按照下面的描述设置为111,设置成PWM模式2。
    图

    再将使能寄存器CCER的位9

    图
    按照
    图
    这个规则设置为0,也就是输出为高电平有效。

    1. 使能CEN位,打开CK_CNT时钟。

    以上便是一些重要寄存器的设置。

    图

    这是它工作的时序图

    ⌚代码实现

    具体寄存器的配置通过官方提供的库函数来实现。

    需要用到的库函数:

    设置比较值函数:

    void TIM_SetCompareX(TIM_TypeDef* TIMx, uint16_t Compare2);
    
    • 1

    使能输出比较预装载

    void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
    
    • 1

    输出初始化函数:

    void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
    
    • 1

    这些是比较重要的库函数,按照下面的步骤

    1. 使能定时器3和相关IO口时钟。
      使能定时器3时钟:RCC_APB1PeriphClockCmd();
      使能GPIOB时钟:RCC_APB2PeriphClockCmd();
    2. 初始化IO口为复用功能输出,函数:GPIO_Init();
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    3. 这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,所以需要开启AFIO时钟。同时设置重映射。
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
      GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
    4. 初始化定时器:ARR,PSC等:TIM_TimeBaseInit();
    5. 初始化输出比较参数:TIM_OC2Init();
    6. 使能预装载寄存器: TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
      这一步是为了确保在发生一次溢出事件以后,改变CCR中的值,以改变占空比。
    7. 使能定时器。TIM_Cmd();
    8. 不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare2();

    展示下本喵的代码:
    pwm的初始化代码:

    void TIMER3_PWM_Init(u16 arr,u16 psc)
    {
    	GPIO_InitTypeDef GPIO_InitStruct;
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    	TIM_OCInitTypeDef TIM_OCInitStruct;
    	
    	//时钟使能,3个外设
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3的时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB和AFIO时钟
    	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);//部分重映像使能
    	
    	//复用初始化
    	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//推挽复用输出模式
    	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//引脚5
    	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//输出速度50MHZ
    	GPIO_Init(GPIOB,&GPIO_InitStruct);//PB5初始化为复用推挽输出
    	
    	//设置PWM的频率
    	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
    	TIM_TimeBaseInitStruct.TIM_Period=arr;//重装载寄存器中的值
    	TIM_TimeBaseInitStruct.TIM_Prescaler=psc;//预分频系数
    	TIM_TimeBaseInitStruct.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);//初始化PWM波的频率
    	
    	//设置PWM工作模式
    	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM2;//PWM模式2
    	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;//输出极性设置为高
    	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;//比较输出使能
    	TIM_OC2Init(TIM3,&TIM_OCInitStruct);//初始化PWM波输出模式
    	
    	//CCR预装载使能
    	TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);//使能CCR预装载
    	
    	TIM_Cmd(TIM3,ENABLE);//打开定时器3
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    主程序中代码:

    int main()
    {
    	u16 pwm_val = 0;//CCR中的值
    	u8 flag = 1;//增减标志位
    	delay_init();//延时初始化
    
    	TIMER3_PWM_Init(899,0);//PWM的频率就是72000000/(899+1) = 80KHZ
    	
    	while(1)
    	{
    		delay_ms(10);//延时10ms,否则看不到灯的效果
    		if(flag)
    			pwm_val++;//改变占空比
    		else
    			pwm_val--;//改变占空比
    		
    		if(pwm_val > 300)
    			flag=0;
    		if(pwm_val==0)
    			flag = 1;
    		
    		TIM_SetCompare2(TIM3,pwm_val);//将值给CCR寄存器
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    注释中有详细解释,可以参考。

    ⌚效果展示

    图
    由于是GIF,效果不是很明显,红色灯的亮度在由弱到强反复变化。

    ⏰输入捕获实验

    在输入捕获模式下,当检测到ICx信号上相应的边沿后,计数器的当前值被锁存到捕获/比较寄存
    器(TIMx_CCRx)中,根据这个值我们可以计算出输入信号的频率。

    在次实验中,本喵将WK_UP按键按下的时长通过串口发送到电脑屏幕上。

    图
    根据开发板原理图,WK_UP按键和PA0相连,另一端与高电平相连。

    图
    根据STM32芯片的引脚分布,可以看到,TIM5的CH1和PA0的复用的,所以我们这里使用TIM5的CH1通道作为输入。

    ⌚时钟选择

    同样选择内部时钟CK_INT,这里让计数器CNT按照1MHZ的频率来计数,所以预分频计数器的值是71。此时每隔1us就计数一次。

    ⌚重要寄存器配置

    按照输入捕获的工作框图来配置相关寄存器。

    图

    1. 配置输入滤波器为所需的带宽

    配置CCMR1寄存器
    图
    这里为了防止按键发生抖动采用以CK_INT的频率采样8次来滤波,也就是将IC1F[3:0]设置为0011。

    1. 配置捕获信号类型

    将CCEG寄存器的CC1P位

    图
    配置为0,也就是上升沿捕获。

    1. 配置捕获信号的映射端口

    配置CCMR1寄存器的CC1S位

    图
    配置为01,也就是映射在TI1上。

    1. 配置捕获事件

    同样是CCMR1寄存器

    图
    将IC1PSC设置为00,也就是一捕获到信号的上升沿就触发一次捕获

    1. 配置相关中断寄存器

    这里本喵就不列举了,和前面的中断配置是类似的。

    ⌚代码实现

    同样,寄存器的配置仍然是通过官方提供的库函数来实现的。

    需要使用到的重要库函数:

    输入捕获通道初始化函数:

    void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
    
    • 1

    通道极性设置独立函数

    void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity)
    • 1

    获取通道捕获值

    uint32_t TIM_GetCapture1(TIM_TypeDef* TIMx)
    • 1

    这些是比较重要的函数,按照下面的流程来写具体的代码:

    1. 初始化定时器和通道对应IO的时钟。
    2. 初始化IO口,模式为输入:
      GPIO_Init();
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入
    3. 初始化定时器ARR,PSC
      TIM_TimeBaseInit();
    4. 初始化输入捕获通道
      TIM_ICInit();
    5. 如果要开启捕获中断,
      TIM_ITConfig();
      NVIC_Init();
    6. 使能定时器:TIM_Cmd();
    7. 编写中断服务函数:TIMx_IRQHandler();

    展示下本喵的代码:
    timer5.h中的代码

    void TIMER_Incapture_Init(u16 arr, u16 psc)
    {
    	GPIO_InitTypeDef GPIO_InitStruct;
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    	NVIC_InitTypeDef NVIC_InitStruct;
    	TIM_ICInitTypeDef TIM_ICInitStruct;
    	
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//使能TIM5时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA时钟
    	
    	//PA0初始化
    	GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_0;  //PA0 清除之前设置  
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入  
    	GPIO_Init(GPIOA, &GPIO_InitStruct);//PA0下拉输入
    	GPIO_ResetBits(GPIOA,GPIO_Pin_0);	//将PA0下拉到低电平
    
    	//初始化计数部分
    	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//滤波频率和内部时钟相同
    	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
    	TIM_TimeBaseInitStruct.TIM_Period=arr;//将重装载值设为最大
    	TIM_TimeBaseInitStruct.TIM_Prescaler=psc;//预分频系数
    	TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStruct);//初始化计数部分
    	
    		//输入捕获初始化
    	TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;//输入通道CH1
    	TIM_ICInitStruct.TIM_ICFilter=0x3;//8次采样
    	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获
    	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;//不分频
    	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;//映射到TI1上
    	TIM_ICInit(TIM5,&TIM_ICInitStruct);
    	
    	//更新中断和捕获中断初始化
    	NVIC_InitStruct.NVIC_IRQChannel=TIM5_IRQn;//中断通道是TIM5
    	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;//使能通道
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级0
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;//响应优先级2
    	NVIC_Init(&NVIC_InitStruct);
    	
    	TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);//使能更新中断
    	
    	
    	TIM_ITConfig(TIM5,TIM_IT_CC1,ENABLE);//使能通道1输入捕获中断
    	
    	TIM_Cmd(TIM5,ENABLE ); //使能定时器5
    
    }
    
    u8  TIM5CH1_CAPTURE_STA=0;//输入捕获状态		    				
    u16	TIM5CH1_CAPTURE_VAL;//输入捕获值
    
    //定时器5中断服务程序	 
    void TIM5_IRQHandler(void)
    {
    	//没有完成采集进入中断
    	if((TIM5CH1_CAPTURE_STA&0X80)==0)
    	{
    		//发生的是更新中断
    		if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
    		{
    			//已经捕获到高电平,需要累计溢出
    			if(TIM5CH1_CAPTURE_STA&0X40)
    			{
    				//累计达到最大值,强制采集完成
    				if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)
    				{
    					TIM5CH1_CAPTURE_STA|=0X80;//采集完成标志
    					TIM5CH1_CAPTURE_VAL=0XFFFF;//强制最大值
    				}
    				//没有累计到最大
    				else
    					TIM5CH1_CAPTURE_STA++;//溢出加1
    			}
    		}
    		//发生捕获中断
    		if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)
    		{
    			delay_ms(20);//延时消抖
    			if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)
    			{
    				//已经捕获到上升沿,此时捕获是下降沿
    				if(TIM5CH1_CAPTURE_STA&0X40)
    				{
    					TIM5CH1_CAPTURE_STA|=0X80;//捕获完成
    					TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);//获取当前计数值
    					TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);  //CC1P=0 设置为上升沿捕获
    				}
    				//捕获到上升沿
    				else
    				{
    					//开始计时,所有参数从0开始
    					TIM5CH1_CAPTURE_VAL=0;	
    					TIM5CH1_CAPTURE_STA=0;	
    					TIM5CH1_CAPTURE_STA|=0X40;//上升沿标志
    					TIM_SetCounter(TIM5,0);//让计数器CNT从0开始计数
    					TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);	//CC1P=1 设置为下降沿捕获
    				}
    			}
    			
    		}
    	}
    	TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);//清除中断标志位
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    包括定时器5的初始化,以及中断服务函数。

    主函数是接着PWM输出写的

    extern u8  TIM5CH1_CAPTURE_STA;//输入捕获状态			    				
    extern u16	TIM5CH1_CAPTURE_VAL;//输入捕获值
    
    int main()
    {
    	u16 pwm_val = 0;//CCR中的值
    	u8 flag = 1;//增减标志位
    	u32 temp = 0;//计时时间
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    	delay_init();//延时初始化
    	uart_init(115200);
    
    	TIMER3_PWM_Init(899,0);//PWM的频率就是72000000/(899+1) = 80KHZ
    	TIMER_Incapture_Init(0xffff,71);//计数一次用时1us,最大计数65535
    
    	while(1)
    	{
    		delay_ms(10);//延时10ms,否则看不到灯的效果
    		if(flag)
    			pwm_val++;//改变占空比
    		else
    			pwm_val--;//改变占空比
    		
    		if(pwm_val > 300)
    			flag=0;
    		if(pwm_val==0)
    			flag = 1;
    		
    		TIM_SetCompare2(TIM3,pwm_val);//将值给CCR寄存器
    		if((TIM5CH1_CAPTURE_STA&0x80)!=RESET)
    		{
    			temp = (TIM5CH1_CAPTURE_STA&0x3f);//统计溢出次数
    			temp*=65536;//计算溢出时长
    			temp+=TIM5CH1_CAPTURE_VAL;//加当前计时时长
    			printf("High Level time:%d us\r\n",temp);//打印总的高电平时间
    			TIM5CH1_CAPTURE_STA=0;//进行下一次计时
    		}
    
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    注意:

    图
    使用到这样一个标志位。

    当WK_UP处于高电平且超过65536us时,次数计数器CNT就会发生溢出事件,然后计算从0开始计时,如果不将这次溢出记录下来,那么计时时长就会丢失至少65536us,所以我们用这样一个变量的低6位存放溢出的次数。

    第6位是记录捕获到高电平,当高电平被捕获到以后,就开始计时。

    第7位表示捕获完成,此时需要将计时时长发送出去。

    ⌚效果展示

    图

    ⏰总结

    在理解了通用定时器的工作原理后,它的应用还是非常容易实现的,只需要按照步骤,使用合适的库函数即可。希望对各位有所帮助。

  • 相关阅读:
    1075 PAT Judge
    产品经理凭啥年薪百万?看这里
    C++如何判断变量类型
    Vue3 Composition API(案例)
    【LeetCode】链表题总结(持续更新)
    Spring——AOP(Aspect Oriented Programming)
    【Godot】解决游戏中的孤立/孤儿节点及分析器性能问题的分析处理
    一位苦逼程序员的找工作经历
    算法 滑动窗口
    HowHelp免费推广!先到先得
  • 原文地址:https://blog.csdn.net/weixin_63726869/article/details/126302824