STM32---通用定时器(二)相关实验

news2024/7/6 20:58:11

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

本节主要操作三个实验:

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实验、通用定时器输入捕获实验;进行代码书写以及实验测试。大家学习的时候,多多动手定有收获。

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣刷题日记——L724. 寻找数组的中心下标

1. 前言 今天是力扣刷题日记的第二天,今天依旧是一道简单题啊,慢慢来,先看看题目是什么吧。 2. 题目描述 给你一个整数数组 nums ,请计算数组的 中心下标。 数组 中心下标 是数组的一个下标,其左侧所有元素相加的和…

数据结构入门(3)顺序表和链表

1.线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的一条直…

汤唯短发造型:保留经典和适合自己的风格,也许才是最重要的

汤唯短发造型:保留经典和适合自己的风格,也许才是最重要的 汤唯短发造型登上Vogue四月刊封面,引发网友热议。#李秘书讲写作#说说是怎么回事? 这次Vogue四月刊的封面大片,汤唯以一头短发亮相,身穿五颜六色的…

Python笔记:函数

Python函数定义规则: 函数代码块以def关键词开头,后接函数标识符名称和圆括号()。任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。return [表达式] 结束函数,选择性地返回一个值给调用方,不带表…

力扣(LeetCode)142.环形链表 II

本博客讲解一道以前大厂面试常考的链表oj题 ——————————————————————— 题目介绍: 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通…

【C语言】操作符相关知识点

移位操作符 << 左移操作符 >>右移操作符 左移操作符 移位规则&#xff1a; 左边抛弃、右边补0 右移操作符 移位规则&#xff1a; 首先右移运算分两种&#xff1a; 1.逻辑移位 左边用0填充&#xff0c;右边丢弃 2.算术移位 左边用原该值的符号位填充&#xff0c;…

桥接模式以及在JDBC源码剖析

介绍&#xff1a; 1、桥接模式是指&#xff1a;将实现和抽象放在两个不同类层次中&#xff0c;使两个层次可以独立改变 2、是一种结构型设计模式 3、Bridge模式基于类的最小设计原则&#xff0c;通过使用封装、聚合以及继承等行为让不同的类承担不同的职责。 4、特点&#xff1…

【智能算法】樽海鞘群算法(SSA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2017年&#xff0c;Mirjalili受到樽海鞘集群行为启发&#xff0c;提出了樽海鞘群算法(Salp Swarm Algorithm, SSA)。 2.算法原理 2.1算法思想 樽海鞘集群是领导者-追随者类型算法&#xff0c;整体…

基于SpringBoot的“医院信管系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“医院信管系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 功能结构图 系统首页界面图 用户注册界面图 医生…

CANopen转Profinet网关连接西门子PLC与变流器通讯

CANopen转Profinet网关&#xff08;XD-COPNm20&#xff09;在智能领域&#xff0c;变流器的应用非常广泛&#xff0c;变流器一般会采用CANopen协议。现场采用台达的变流器&#xff08;支持CANopen协议&#xff09;作为CANopen从站&#xff0c;S7-1500系列PLC做主站&#xff0c;…

软件设计不是CRUD(14):低耦合模块设计理论——行为抽象与设计模式(上)

是不是看到“设计模式”四个字,各位读者就觉得后续内容要开始讲一些假大空的内容了?各位读者是不是有这样的感受,就是单纯讲设计模式的内容,网络上能找到很多资料,但是看过这些资料后读者很难将设计模式运用到实际的工作中。甚至出现了一种声音:设计模式是没有用的,应用…

Python安装第三方库

前言&#xff1a;大部分时候我们都是使用pip install去安装一些第三方库&#xff0c;但是偶尔也会有部分库无法安装&#xff08;最典型的就是dlib这个库&#xff09;&#xff0c;需要采取别的方法解决&#xff0c;这里做笔记记录一下。 使用国内镜像源安装 因为pypi的服务器在…

Java后端八股文之Redis

文章目录 1. Redis是什么&#xff1f;2. Redis为什么这么快&#xff1f;3. 为什么要使用缓存&#xff1f;4. Redis几种使用场景&#xff1a;5. Redis的Zset底层为什么要使用跳表而不是平衡树、红黑树或者B树&#xff1f;6.Redis持久化6.1 什么是RDB持久化6.1.1RDB创建快照会阻塞…

大数据开发-FLUME安装部署与实战案例

文章目录 前言安装部署配置修改案例:采集文件内容上传至HDFS案例:采集网站日志上传HDFS前言 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简…

es 查询案例分析

场景描述&#xff1a; 有这样一种场景&#xff0c;比如我们想搜索 title&#xff1a;Brown fox body&#xff1a;Brown fox 文章索引中有两条数据&#xff0c;兔子和狐狸两条数据 PUT /blogs/_bulk {"index": {"_id": 1}} {"title": "…

Oracle Primavera P6 数据库升级

前言 为了模拟各种P6测试&#xff0c;我常常会安装各种不同版本的p6系统&#xff0c;无论是P6服务&#xff0c;亦或是P6客户端工具Professional&#xff0c;在今天操作p6使用时&#xff0c;无意识到安装在本地的P6 数据库&#xff08;21.12&#xff09;出现了与Professional软…

对于stm32中printf函数的移植方法

一、准备工作 使用printf之前需要先打开工程选项&#xff0c;把use microLIB选项打开。microlib是keil为嵌入式平台优化的一个精简库&#xff0c;本文使用到的printf将会用到这个microlib。 二、对printf进行重定向 将printf打印的东西输出到串口&#xff0c;由于printf默认输…

关于分布式分片,你该知道的事儿

关于分布式分片&#xff0c;你该知道的事儿 前言一、关于分片方式的那些事儿1.1 按照Hash划分1.2 按照区间范围划分1.3 按照数据量划分1.4 来些例子1.4.1 Redis的分片划分1.4.2 Mongo的分片划分 二、关于分区再平衡的那些事儿2.1 基于固定分片数量2.2 基于动态分片数量2.3 基于…

让生活更加精致的APP?

晚上好&#xff0c;今天博主来介绍几款帮助你条理生活的APP&#xff0c;让你的生活更加精致&#xff0c;充满仪式感。 一&#xff0e;格志日记 一款以“格子”的方式记录日记的APP&#xff0c;非常简单明了&#xff0c;用户可以依据自己的喜好&#xff0c;来自由定义或者删除格…

初阶数据结构之---堆的应用(堆排序和topk问题)

引言 上篇博客讲到了堆是什么&#xff0c;以及堆的基本创建和实现&#xff0c;这次我们再来对堆这个数据结构更进一步的深入&#xff0c;将讲到的内容包括&#xff1a;向下调整建堆&#xff0c;建堆的复杂度计算&#xff0c;堆排序和topk问题。话不多说&#xff0c;开启我们今…