写在前面:前面我们学习了基本定时器、通用定时器的相关理论部分,了解到通用定时器的结构框图,总共包含六大模块:时钟源、控制器、时基单元、输入捕获、公共部分以及输出捕获。对相关模块的使用也做详细的讲解。本节我们主要是对上节的理论部分进行实验的操作,力争对理论部分有更好的掌握。

本节主要操作三个实验:

1、通用定时器中断实验;2通用定时器输出PWM波形;3、通用定时器输入捕获;

基本定时器回顾:STM32---基本定时器(含源码)小白可入_stm32简易定时器-CSDN博客

通用定时器基础回顾:STM32---通用定时器(一)理论基础-CSDN博客

一、通用定时器中断实验

1.1实验描述

        通过STM32的通用定时器完成计数,在中断中点亮LED灯;

        在主函数进行LED1进行亮灭翻转,在利用通用定时器进行中断,在中断中实现LED0的亮灭翻转。

1.2相关寄存器

        使用通用定时器之前,我们需要根据定时器的结构框图,确定以下的一些问题:

1、使用定时器的哪个时钟源?

2、是否使用通用定时器的输入捕获部分?

3、是否使用通用定时器的输出比较部分?

4、是否使用通用定时溢出后的中断?

        在这里我们确定,使用定时器的内部时钟源,不使用输入捕获,也不使用输出捕获,但是需要使用定时器溢出后的中断,因为在中断中我们需要进行点灯。

1.控制寄存器(TIMx_CR1)

(APRE)自动重载寄存器允许位:如果 ARPE 位置 1,ARR 起缓冲作用,即只有在更新事件发生时才会把 ARR的值写入其影子寄存器里;如果 ARPE 位置 0,那么修改自动重载寄存器的值时,该值会马上被写入其影子寄存器中,从而立即生效。

CMS[1:0]选择中央对齐模式:分为边沿对齐(即递增或递减技术模式)、中央对齐模式;

DIR :用于控制定时器的计数方向,递增计数或递减计数;

CEN 位:用于使能计数器的工作,必须要设置该位为 1,计数器才会开始计数。

2、从模式控制寄存器(TIMx_SMCR)

主要用于选择计数器输入的时钟来源; 

  SMS[2:0]位:我们设置 SMS[2:0]=000,禁止从模式,这样 PSC 预分频器的时钟就直接来自内部时钟(CK_INT),按照我们例程 sys_stm32_clock_init 函数的配置,频率为 72Mhz(APB1总线时钟频率的 2 倍)

3、DMA/中断使能寄存器(TIMx_DIER)

 该寄存器用于使能/失能触发 DMA 请求、捕获/比较中断以及更新中断。

4、状态寄存器(TIMx_SR) 

        在通用定时器中断实验我们用到更新中断标志位,当定时器更新中断到来后,位 0(UIF)会由硬件置 1,我们需要在中断服务函数里面把该位清零。

5、计数寄存器(TIMx_CNT) 

        TIM2/TIM3/TIM4/TIM5 的计数寄存器都是 16 位有效的,计数模式可以是递增计数模式、
递减计数模式和中心对齐计数模式,计数值范围 0~65535。可以直接写该寄存器设置计数的初
始值,也可以读取该寄存器获取计数器值 。

6、预分频寄存器(TIMx_PSC)

        定时器的预分频寄存器都是 16 位的,即写入该寄存器的数值范围是 0 到 65535,表示 1 到
65536 分频。

7、自动重载寄存器(TIMx_ARR) 

        自动重载寄存器是低 16 位有效。该寄存器可以由 APRE 位设置是否进行缓冲。计数器的
值会和自动重装寄存器影子寄存器进行比较,当两者相等,定时器就会溢出,从而发生更新事
件,如果打开了更新中断,还会发生更新中断。 

1.3 程序设计

程序源码

链接:https://pan.baidu.com/s/1cYrbkah9Awvf4TZ_-NrN0A 
提取码:1022

timer.c

#include "./BSP/TIMER/timer.h"
#include "./BSP/LED/led.h"
/**
* @brief通用定时器定时中断初始化函数
* @note
*通用定时器的时钟来自 APB1,当 PPRE1 ≥ 2 分频的时候
*通用定时器的时钟为 APB1 时钟的 2 倍, 而 APB1 为 36M, 所以定时器时钟 = 72Mhz
*定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
*              Ft=定时器工作频率,单位:Mhz
* @param       arr: 自动重装值。
* @param       prc: 时钟预分频数
* @retval无
*/
TIM_HandleTypeDef timer_handle;/* 定义句柄 */
void timer_init(uint16_t prc,uint16_t arr)/* 定义变量:1、arr自动重装值2、prc预分频系数*/
{
    timer_handle.Instance=TIM5;         /* 定时器TIM5基地址 */
    timer_handle.Init.Prescaler=prc;    /* 预分频系数 */
    timer_handle.Init.Period=arr;       /* 自动重装载值 */
    timer_handle.Init.CounterMode=TIM_COUNTERMODE_DOWN; /* 递减计数模式 */
     HAL_TIM_Base_Init(&timer_handle);     
     HAL_TIM_Base_Start_IT(&timer_handle);  /* 使能定时器以及使能定时器更新中断 */
    
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance==TIM5)/* 判断是否为TIM5 */
    {
        __HAL_RCC_TIM5_CLK_ENABLE();    /* 使能定时器时钟 */
        HAL_NVIC_EnableIRQ(TIM5_IRQn);  /* 使能定时器TIM5中断 */
        HAL_NVIC_SetPriority(TIM5_IRQn,2,2);/* 设置中断优先级 */
    }
}
/**
* @brief定时器中断服务函数
* @param无
* @retval无
*/
void TIM5_IRQHandler (void)
{
    HAL_TIM_IRQHandler(&timer_handle);  /* 定时器公共处理函数,会自动清除定时器溢出中断标志位 */

}
/**
* @brief定时器溢出中断回调函数
* @param句柄
* @retval无
*/
 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)    
 {
        if(htim->Instance==TIM5)
    {
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
    }
 }

led.c

#include "./BSP/LED/led.h"
void LED_init()//LED初始化
{
      __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitTypeDef gpio_init_struct;
    gpio_init_struct.Mode=GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pin=GPIO_PIN_5;
    gpio_init_struct.Speed=GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &gpio_init_struct);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
    
       __HAL_RCC_GPIOE_CLK_ENABLE();
    gpio_init_struct.Mode=GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pin=GPIO_PIN_5;
    gpio_init_struct.Speed=GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOE, &gpio_init_struct);
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_SET);

}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/timer.h"

int main(void)
{
    HAL_Init();                              /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);      /* 设置时钟, 72Mhz */
    delay_init(72);                          /* 延时初始化 */
     LED_init();
    timer_init(7200-1,5000-1);              /* 设置预分频器系数,以及重装在寄存器的值 */
    while(1)
    { 
      delay_ms(500);
      HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
        
    }
}

1.4实验现象

通用定时器点灯

二、通用定时器输出PWM波形

2.1 实验描述

        使用通用定时器实现PWM波形的输出;PWM:脉冲宽度调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。

        在此实验中,我们需要输出电平信号,所以我们需要用到输出比较,通过计数器的值同捕获/比较寄存器的值进行比较,输出信号,再通过控制输出比较部分的工作模式,有效电平,达到PWM波形的输出。

        上图为PWM波形产生的原理,确定一个自动重载寄存器的值,设定一个比较值,让计数器的值不断增加(递增模式),与比较值进行比较,当大于比较值时,输出一种电平信号,当小于比较值时,输出另一种电平信号(具体输出那种信号,由输出模式控制)。

        由此可见,影响PWM波形的周期参数为:自动重装载寄存器ARR的值,

                        影响PWM波形的占空比的参数为:捕获/比较寄存器CRR的值。

        定时器产生 PWM 的方式有许多种,下面我们以边沿对齐模式(即递增计数模式/递减计数
模式)为例,PWM 模式 1 或者 PWM 模式 2 产生 PWM 的示意图。

         使用 TIM3 通道 2(由 PB5 复用)输出 PWM, PB5 引脚连接了 LED0,从而实现 PWM 输
出控制 LED0 亮度,达到呼吸灯的效果。

2.2 相关寄存器

1.捕获/比较模式寄存器 1/2(TIMx_CCMR1/2)

        TIMx _CCMR1 和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 CH2,而 TIMx_CCMR2 控制CH3 和 CH4。

        OC2M[2:0]就是对应着通道 2 的模式设置,此部分由 3 位组成。总共可以配置成 8 种
模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110 或者 111,分别对应 PWM 模式 1
和 PWM 模式 2。两种 PWM 模式的区别就是输出有效电平的极性相反。

       OC2PE 控制输出比较通道 2 的预装载使能,实际就是控制 CCR2 寄存器是否进行缓冲。因为 CCR2 寄存器也是有影子寄存器的,影子寄存器才是真正起作用的寄存器。

        CC2S[1:0]用于设置通道 2 的方向(输入/输出)默认设置为 0,就是设置通道作为输出使用。

2.捕获/比较使能寄存器(TIMx_CCER)

该寄存器控制着各个输入输出通道的开关和极性。

         要让 TIM3 的 CH2 输出 PWM 波,这里我们要使能 CC2E 位,该位是通道 2 输入/输出使能位,要想 PWM 从 IO 口输出,这个位必须设置为 1。CC2P 位是设置通道2 的输出极性。

3.捕获/比较寄存器 1/2/3/4(TIMx_CCR1/2/3/4)  

        在输出模式下,捕获/比较寄存器影子寄存器的值与 CNT 的值比较,根据比较结果产生相
应动作,利用这点,我们通过修改这个寄存器的值,就可以控制 PWM 的占空比了。 

2.3程序设计

程序源码

链接:https://pan.baidu.com/s/1lsbm3VWhuhO4I5BEoQHNZQ 
提取码:1022

 pwm.c

#include "./BSP/PWM/pwm.h"
 
TIM_HandleTypeDef btim_pwm_handle; /* 定义句柄 */ 


/*** @brief通用定时器 TIM3 通道2 PWM 输出初始化函数(使用 PWM 模式 1)
* @note*通用定时器的时钟来自 APB1,当 D2PPRE1≥2 分频的时候
*通用定时器的时钟为 APB1 时钟的 2 倍, 而 APB1 为 36M, 所以定时器时钟 = 72Mhz
*定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
*              Ft=定时器工作频率,单位:Mhz
* @param       arr: 自动重装值。
* @param       psc: 时钟预分频数
* @retval
*/
 void btim_pwm_init(uint16_t arr,uint16_t psc)
 {  
   btim_pwm_handle.Instance=TIM3;   /* 定时器TIM3基地址 */
   btim_pwm_handle.Init.Period=arr; /* 自动重装载值 */
   btim_pwm_handle.Init.Prescaler=psc;/* 预分频系数 */
   btim_pwm_handle.Init.CounterMode=TIM_COUNTERMODE_UP;  /* 递增计数模式 */
    HAL_TIM_PWM_Init(&btim_pwm_handle); /* 初始化PWM */
     
   TIM_OC_InitTypeDef timx_oc_pwm_struct={0};/* 定义结构体,并赋初值为0,这一点是很重要的 */
   timx_oc_pwm_struct.OCMode=TIM_OCMODE_PWM1;/* 设置输出PWM模式,此处采用模式1 */
   timx_oc_pwm_struct.Pulse=arr/2;/* 设置捕获/比较寄存器的值,此处设置为自动重装载值的一半,则输出的PWM波形的占空比为50% */
   timx_oc_pwm_struct.OCPolarity=TIM_OCPOLARITY_LOW;/* 输出比较极性为低 */
    HAL_TIM_PWM_ConfigChannel(&btim_pwm_handle,&timx_oc_pwm_struct,TIM_CHANNEL_2);/* 定时器的 PWM 通道设置初始化函数 */
    HAL_TIM_PWM_Start(&btim_pwm_handle, TIM_CHANNEL_2);/* 定时器的 PWM 输出启动函数,参数1为句柄,参数2为通道数 */
 }
 
/**
* @brief定时器底层驱动,时钟使能,引脚配置此函数会被 HAL_TIM_PWM_Init()调用
* @param       htim:定时器句柄
* @retval无
*/ 
 void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
 {
     if(htim->Instance==TIM3)/* 判断是否为定时器3 */
     {
      __HAL_RCC_TIM3_CLK_ENABLE();/* 使能定时器时钟 */
      __HAL_RCC_GPIOB_CLK_ENABLE();/* 使能输出IO的时钟 */
         
        GPIO_InitTypeDef gpio_init_struct;
        gpio_init_struct.Mode=GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_PULLUP;
        gpio_init_struct.Pin=GPIO_PIN_5;
        gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOB, &gpio_init_struct);
         __HAL_RCC_AFIO_CLK_ENABLE();/* 使能重映射时钟 */
         __HAL_AFIO_REMAP_TIM3_PARTIAL(); /* IO 口 REMAP 设置,设置重映射 */
     }
 }

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/PWM/pwm.h"
uint8_t dir=1;
uint16_t ccr=0;
int main(void)
{
    HAL_Init();                              /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);      /* 设置时钟, 72Mhz */
    delay_init(72);                          /* 延时初始化 */
    led_init();                              /* LED初始化 */
    btim_pwm_init(500-1,72-1);/* 72M/72=1M 的计数频率,自动重装载为 500,那么 PWM 频率为 1M/500=2kHZ */
    while(1)
    { 
        delay_ms(10);
        if(dir) ccr++;
        else  ccr--;
        
        if(ccr>400)   dir=0;
        if(ccr==0) dir=1;
        
       __HAL_TIM_SET_COMPARE(&btim_pwm_handle,TIM_CHANNEL_2,ccr); /* 修改比较值控制占空比,达到呼吸灯的效果 */

    }
}

2.4实验现象

PWM呼吸灯

示波器测量PWM波形

三、通用定时器输入捕获实验

3.1 实验描述

        输入捕获模式可以用来测量脉冲宽度或者测量频率,我们用来测量脉冲的宽度,即给通用定时器的输入捕获端一个高电平,测量出高电平的时间(低电平相同)。

        使用 TIM5_CH1 来做输入捕获,捕获 PA0 上的高电平脉宽,并将脉宽时间通过串口打
印出来,然后通过按 WK_UP 按键,模拟输入高电平。

输入捕获脉宽测量原理:

t1 到 t2 的时间段,就是我们需要测量的高电平时间测量方法为:

        假设定时器的计数器工作在递增模式,设置输入通道为上升沿触发,则在t1时刻,由于上升沿的到来,就会发生捕获事件,在捕获事件中(中断),我们将计数器的值清零,并将触发方式改为下降沿触发。        

        这样在t2时刻,由于下降沿触发,就会再次发生捕获事件。捕获事件发生时,计数器的值会被所存放捕获/比较寄存器中。

        这样我们将捕获/比较寄存器中的值再加上一系列的溢出次数(溢出次数可以通过更新中断统计),就能算出高电平脉冲的时间。

计数个数=N*(ARR+1)+ CCRx2。N溢出次数,ARR自动重装载值,CCRx2时间t2点,捕获/比较寄存器的值.

高电平时间=计数个数*计数3.2器计1个数的时间;

3.2相关寄存器

TIMx_ARR、TIMx_PSC、TIMx_CCMR1、TIMx_CCER、TIMx_DIER、TIMx_CR1、TIMx_CCR1 这些寄存器在前面的章节都有提到。

1.捕获/比较模式寄存器 1/2(TIMx_CCMR1/2)

        该寄存器在输入模式和输出模式下,功能是不一样的,TIMx_CCMR1 寄存器对应于通道 1 和通道 2 的设置,CCMR2 寄存器对应通道 3和通道 4。

        CC1S[1:0],这两个位用于 CCR1 的通道配置,这里我们设置 IC1S[1:0]=01,也就是配
置 IC1 映射在 TI1 上。

        输入捕获 1 预分频器 IC1PSC[1:0],这个比较好理解。我们是 1 次边沿就触发 1 次捕获,所
以选择 00 就行了。

        输入捕获 1 滤波器 IC1F[3:0],这个用来设置输入采样频率和数字滤波器长度。一般不需要进行滤波

  2.捕获/比较使能寄存器(TIMx_CCER)

        使能输入捕获,必须设置 CC1E=1,而 CC1P 则根据自己的需要来配置。我们这里是保留默认设置值 0,即高电平触发捕获。 

此外我们需要开启DMA/中断使能寄存器:用于计数溢出次数。

3.3程序设计

链接:https://pan.baidu.com/s/1ZinCNBBShkMFLSA-bZlo6w 
提取码:1022

程序文件

timer.c

#include "./BSP/TIMER/timer.h"
#include "./BSP/LED/led.h"

TIM_HandleTypeDef tim_ic_handled;/* 定义句柄*/
/*** @brief通用定时器 TIM5通道1输入捕获初始化函数
* @note
*通用定时器的时钟来自 APB1,当 PPRE1 ≥ 2 分频的时候
*通用定时器的时钟为 APB1 时钟的 2 倍, 而 APB1 为 36M, 所以定时器时钟 = 72Mhz
*定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
*              Ft=定时器工作频率,单位:Mhz
*
* @param       arr: 自动重装值
* @param       psc: 时钟预分频数
* @retval
无
*/
void tim_ic_init(uint16_t arr,uint16_t psc)
{
   tim_ic_handled.Instance=TIM5;                         /* 定时器基地址:定时器 5 */
   tim_ic_handled.Init.Period=arr;                       /* 自动重装载值:arr */  
   tim_ic_handled.Init.Prescaler=psc;                    /* 预分频系数:psc */  
   tim_ic_handled.Init.CounterMode=TIM_COUNTERMODE_UP;   /* 计数模式:向上计数 */        
   HAL_TIM_IC_Init(&tim_ic_handled); 
    
   TIM_IC_InitTypeDef tim_ic_cap_csh_struct={0};
   tim_ic_cap_csh_struct.ICFilter=0;                            /* 通道配置滤波器:不滤波 */
   tim_ic_cap_csh_struct.ICPolarity=TIM_ICPOLARITY_RISING;      /* 通道捕获方式:上升沿捕获 */
   tim_ic_cap_csh_struct.ICPrescaler=TIM_ICPSC_DIV1;            /* 通道输入分频:不分频 */
   tim_ic_cap_csh_struct.ICSelection=TIM_ICSELECTION_DIRECTTI;  /* 通道映射 */  
   HAL_TIM_IC_ConfigChannel(&tim_ic_handled, &tim_ic_cap_csh_struct, TIM_CHANNEL_1);
   
    __HAL_TIM_ENABLE_IT(&tim_ic_handled, TIM_IT_UPDATE);    /* 使能更新中断 ,单独使能定时器中断*/ 
     HAL_TIM_IC_Start_IT(&tim_ic_handled, TIM_CHANNEL_1);  /* 使能通道输入以及使能捕获中断 */
 
}
/**
* @brief通用定时器输入捕获初始化接口HAL 库调用的接口,用于配置不同的输入捕获
* @param       htim:定时器句柄
* @note
此函数会被 HAL_TIM_IC_Init()调用
* @retval
无
*/
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{    
   
    if(htim->Instance==TIM5)
    {
   __HAL_RCC_TIM5_CLK_ENABLE();                     /* 使能定时器5的时钟 */
   __HAL_RCC_GPIOA_CLK_ENABLE();                    /* 使能捕获IO的时钟 */
   GPIO_InitTypeDef gpio_init_struct; 
   gpio_init_struct.Mode=GPIO_MODE_AF_PP;           /* IO的工作模式:复用推挽输出 */
   gpio_init_struct.Pin=GPIO_PIN_0;                 /* IO的引脚:PA0 */
   gpio_init_struct.Pull=GPIO_PULLDOWN;             /* IO上下拉电阻:下拉电阻 */
   gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;      /* IO输出速度:高速 */
   HAL_GPIO_Init(GPIOA, &gpio_init_struct);
    
    HAL_NVIC_SetPriority(TIM5_IRQn, 2, 2);           /* 设置中断优先级 */
    HAL_NVIC_EnableIRQ(TIM5_IRQn);                    /* 开启定时器5中断 */
    }
    
  }
uint8_t g_tim5csh_cap_sta =0;    /* 设置输入捕获状态 */
uint16_t g_tim5csh_cap_val=0;    /* 设置捕获值 */
  
  
 void TIM5_IRQHandler(void)
 {
    HAL_TIM_IRQHandler(&tim_ic_handled);/*定时器公共处理函数 */
 }
  /**
* @brief定时器输入捕获中断处理回调函数
* @param       htim:定时器句柄指针
* @note该函数在 HAL_TIM_IRQHandler 中会被调用
* @retval无
*/
  void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  {
    if(htim->Instance==TIM5)
    {
      if((g_tim5csh_cap_sta & 0x80)==0)         /* 还没能成功捕获 */
      {
            if((g_tim5csh_cap_sta & 0x40))    /* 捕获到一个下降沿 */
            {  
                 g_tim5csh_cap_sta |=0x80;      /* 标记捕获到一个下降沿 ,成功捕获到一个高电平脉冲 */
                 g_tim5csh_cap_val = HAL_TIM_ReadCapturedValue(&tim_ic_handled,TIM_CHANNEL_1);  /* 获取捕获值 */
                 TIM_RESET_CAPTUREPOLARITY(&tim_ic_handled,TIM_CHANNEL_1); /*清除之前的下降沿触发模式 */ 
                 TIM_SET_CAPTUREPOLARITY(&tim_ic_handled,TIM_CHANNEL_1, TIM_ICPOLARITY_RISING );/*设置新的触发方式:上升沿触发模式 */
            }
                
            else        /* 捕获到一个上升沿降沿 */
          {    
            g_tim5csh_cap_sta =0;    
            g_tim5csh_cap_val=0;
            g_tim5csh_cap_sta |=0x40;   /* 标记捕获到一个上升沿 */
            __HAL_TIM_DISABLE(&tim_ic_handled); /* 失能定时器5 */ 
            __HAL_TIM_SET_COUNTER(&tim_ic_handled,0);/*计数器值清零 */ 
            TIM_RESET_CAPTUREPOLARITY(&tim_ic_handled,TIM_CHANNEL_1); /*清除之前的上升沿触发模式 */ 
            TIM_SET_CAPTUREPOLARITY(&tim_ic_handled,TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING );/*设置新的触发方式:下降沿触发模式 */
            __HAL_TIM_ENABLE(&tim_ic_handled);/* 使能定时器5 */ 
           
         }     
      }
    }      
  }
/**
* @brief定时器输入捕获中断处理回调函数
* @param       htim:定时器句柄指针
* @note该函数在 HAL_TIM_IRQHandler 中会被调用
* @retval无
*/
  void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  {
        if(htim->Instance==TIM5)
        {
        
            if((g_tim5csh_cap_sta & 0x80)==0)
            {
                if((g_tim5csh_cap_sta & 0x40))//非0和等于1是两码事
                {
                    if((g_tim5csh_cap_sta & 0x3f)==0x3f)
                    {
                        TIM_RESET_CAPTUREPOLARITY(&tim_ic_handled,TIM_CHANNEL_1); /*清除之前的下降沿触发模式 */ 
                        TIM_SET_CAPTUREPOLARITY(&tim_ic_handled,TIM_CHANNEL_1, TIM_ICPOLARITY_RISING );/*设置新的触发方式:上升沿触发模式 */                
                       g_tim5csh_cap_sta |=0x80;
                       g_tim5csh_cap_val=0xffff; 
                    }
                    else
                    {g_tim5csh_cap_sta++;}                
                }           
            }                   
        } 
  }

  

led.c

#include "./BSP/LED/led.h"
void led_init()
{
    
     GPIO_InitTypeDef gpio_init_struct;
    __HAL_RCC_GPIOE_CLK_ENABLE();
    gpio_init_struct.Mode=GPIO_MODE_OUTPUT_OD;
    gpio_init_struct.Pin=GPIO_PIN_5;
    gpio_init_struct.Speed=GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOE, &gpio_init_struct);
    __HAL_RCC_GPIOB_CLK_ENABLE();
    gpio_init_struct.Mode=GPIO_MODE_OUTPUT_OD;
    gpio_init_struct.Pin=GPIO_PIN_5;
    gpio_init_struct.Speed=GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &gpio_init_struct);

}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/timer.h"
uint32_t temp=0;
int main(void)
{
    HAL_Init();                              /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);      /* 设置时钟, 72Mhz */
    delay_init(72);                          /* 延时初始化 */
    led_init();                              /* LED初始化 */
    usart_init(115200);
    tim_ic_init(0xffff ,72-1);
    
    while(1)
    { 
      
      if(g_tim5csh_cap_sta &0x80) /* 是否完成一次按键按下 */
      {
          
        temp=g_tim5csh_cap_sta &0x3f;
        temp*=65536;
        temp += g_tim5csh_cap_val;
        printf("HIGH:%d us\r\n", temp);
            g_tim5csh_cap_sta=0;
      }          
        
       HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
       delay_ms(1000);
    }
}

3.4实验现象

20240309_175827

总结:本节我们结合上节的通用定时器的基础理论,分别实现了:通用定时器中断实验、通用定时器输出PWM实验、通用定时器输入捕获实验;进行代码书写以及实验测试。大家学习的时候,多多动手定有收获。

创作不易,还请大家多多点赞支持,有问题欢迎评论区讨论!!!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐