ToB企服应用市场:ToB评测及商务社交产业平台

标题: STM32-笔记20-丈量按键按下时间 [打印本页]

作者: 勿忘初心做自己    时间: 2025-1-4 01:42
标题: STM32-笔记20-丈量按键按下时间
1、按键按下的时间-思路

        我们先检测下降沿信号,检测到以后,在回调函数里切换成检测上升沿信号,当两个信号都检测到的时候,这段时间就是按键按下的时间,如图所示:=>N*(ARR+1)+CCRx的值
N是在这段时间内,可能会发生的N次溢出。CCRx是寄存器中的计数,ARR打满65536-1
N个重装载值+CCRx的值

 2、实验目标


为什么要使用定时器2通道2来捕获按键2?
这个板子只有两个按键。一个是key1,一个是key2,分别对应A0和A1引脚
原理图

产品手册(17页)

在这里可以看到,KEY1是接的定时器2通道1的ETR引脚,通过下图我们可以看出来,ETR引脚是作为一个输入源的,而我们这里是想要一个通道,以是这里使用KEY2
中文参考手册(254页)

1s = 1000 000us
P S C = 7 1 
PSC+1=72
72/72MHZ
1个时间周期 = 1/1000 000 = 1us
这里使用1us来表示记一个数的时,也可以更小,A R R = 6 5 5 3 5,直接把ARR拉满

上面是对时基单位设置
下面是对通道的设置
下 降 沿 捕 获 、 输 入 通 道 2 映 射 在 TI2 上 、 不 分 频 、 不 滤 波
滤波器先不要,对边沿检测时,先对下降沿检测,然后迅速在回调函数中对上升沿进行检测。
分频器不分频
3、实现输入捕获功能

复制项目文件19,重命名为20-实现捕获功能
打开项目文件
创建文件夹ic

加载文件

编译

编译

编译
代码如下:

main.c
  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "led.h"
  4. #include "uart1.h"
  5. #include "ic.h"
  6. int main(void)
  7. {
  8.     HAL_Init();                         /* 初始化HAL库 */
  9.     stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  10.     led_init();//初始化led灯
  11.     uart1_init(115200);
  12.     //printf("hello word!\r\n");
  13.     ic_init(72-1,65536-1);
  14.     while(1)
  15.     {
  16.     }
  17. }
复制代码
ic.c
  1. #include "ic.h"
  2. #include "stdio.h"
  3. TIM_HandleTypeDef ic_handle = {0};
  4. //初始化输入捕获函数
  5. void ic_init(uint16_t psc,uint16_t arr)
  6. {
  7.     TIM_IC_InitTypeDef ic_config = {0};
  8.     ic_handle.Instance = TIM2;//定时器2
  9.     ic_handle.Init.Prescaler = psc;
  10.     ic_handle.Init.Period = arr;
  11.     ic_handle.Init.CounterMode = TIM_COUNTERMODE_UP;//向上计数模式
  12.     HAL_TIM_IC_Init(&ic_handle);
  13.    
  14.     ic_config.ICFilter = 0;//过滤器0,也就是不要过滤器
  15.     ic_config.ICPolarity = TIM_ICPOLARITY_FALLING;//下降沿捕获
  16.     ic_config.ICPrescaler = TIM_ICPSC_DIV1;//每次在捕获输入上检测到边缘时执行捕获
  17.     ic_config.ICSelection = TIM_ICSELECTION_DIRECTTI;//输入捕获触发信号源直接连接到对应通道的输入捕获引脚,这意味着输入捕获触发信号直接作用于定时器的输入捕获电路,不需要通过其他中间寄存器或外部电
  18.     HAL_TIM_IC_ConfigChannel(&ic_handle,&ic_config,TIM_CHANNEL_2);
  19.    
  20.     __HAL_TIM_ENABLE_IT(&ic_handle,TIM_IT_UPDATE);//更新中断
  21.     HAL_TIM_IC_Start_IT(&ic_handle,TIM_CHANNEL_2);
  22.    
  23. }
  24. //初始化MSP函数
  25. void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
  26. {
  27.     if(htim->Instance == TIM2)
  28.     {
  29.         GPIO_InitTypeDef gpio_initstruct;
  30.         
  31.         __HAL_RCC_TIM2_CLK_ENABLE();
  32.         __HAL_RCC_GPIOA_CLK_ENABLE();
  33.         
  34.         gpio_initstruct.Mode = GPIO_MODE_INPUT;//复式推挽输出
  35.         gpio_initstruct.Pin = GPIO_PIN_1;//引脚1
  36.         gpio_initstruct.Pull = GPIO_PULLUP;//上拉输出
  37.         gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;//高速
  38.         
  39.         HAL_GPIO_Init(GPIOA,&gpio_initstruct);
  40.         
  41.         HAL_NVIC_SetPriority(TIM2_IRQn,2,2);//外部中断号,抢占优先级/响应优先级
  42.         HAL_NVIC_EnableIRQ(TIM2_IRQn);
  43.     }
  44.    
  45. }
  46. //中断服务函数
  47. void TIM2_IRQHandler(void)
  48. {
  49.     HAL_TIM_IRQHandler(&ic_handle);
  50. }
  51. //回调函数
  52. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  53. {
  54.     printf("捕获一个下降沿\r\n");
  55. }
复制代码
ic.h
  1. #ifndef __IC_H__
  2. #define __IC_H__
  3. #include "sys.h"
  4. void ic_init(uint16_t psc,uint16_t arr);
  5. #endif
复制代码
 在ic.c函数中
ic_config.ICSelection = TIM_ICSELECTION_DIRECTTI;//输入捕获触发信号源直接连接到对应通道的输入捕获引脚,这意味着输入捕获触发信号直接作用于定时器的输入捕获电路,不必要通过其他中间寄存器或外部电
如果
ic_config.ICSelection =TIM_ICSELECTION_INDIRECTTI;//设置输入捕获通道的输入信号选择为间接定时器输入模式。

   TIM Input 1 被选择连接到 IC2。
  TIM Input 2 被选择连接到 IC1。
  TIM Input 3 被选择连接到 IC4。
  TIM Input 4 被选择连接到 IC3。
  如果这里的ic_config.ICSelection设置成下面这个参数
ic_config.ICSelection =TIM_ICSELECTION_INDIRECTTI;
则摁KEY1才会响应按键
这是因为定时器2则连接到通道1中

在串口助手中摁KEY2会显示

4、实现一次完备的按键动作

复制项目文件20-实现捕获功能,重命名21-捕获一次完备的按键动作
在上面实现了捕获下降沿,在这里将会实现捕获上升沿,整合就会实现一次完备的按键动作。
具体流程如下:
succeed_flag:是否发生了一次完备的按键动作标志位
falling_flag:下降沿标志,当falling_flag = 0时(默认情况下),代表下降沿捕获乐成;
当falling_flag = 1时(默认情况下),代表上升沿捕获乐成;

这里用到两个函数
   TIM_RESET_CAPTUREPOLARITY(&ic_handle, TIM_CHANNEL_2);//这行代码的作用是重置定时器捕获通道2的触发极性。
TIM_SET_CAPTUREPOLARITY(&ic_handle, TIM_CHANNEL_2, TIM_ICPOLARITY_RISING);//这行代码的意思是:为ic_handle所指向的定时器的第二个通道设置输入捕获极性为上升沿(即输入信号从低电平变为高电平时)。
  在STM32的HAL库中,TIM_RESET_CAPTUREPOLARITY函数用于重置定时器的捕获通道的触发极性。具体来说,这个函数会将指定通道的触发极性设置为默认值,通常是上升沿或下降沿。
IM_SET_CAPTUREPOLARITY(&ic_handle, TIM_CHANNEL_2, TIM_ICPOLARITY_RISING); 这行代码是在使用STM32 HAL库(硬件抽象层库)进行定时器(TIM)的输入捕获设置时使用的。
代码流程:定义一个布局体,用于存放标志位和统计时间,在回调函数中,当用户按下按键,最初赋值是下降沿检测,检测到下降沿触发制止,响应回调函数,在回调函数中,初始的相应完备按键的标志位为0,执行if,初始响应上升沿标志位为0,执行else,在else中,将响应上升沿标志位 置1,而且将检测下降沿重置为检测上升沿,则在下次进入回调函数时,由于检测完备按键的标志位依旧为0,进入if,检测上升沿标志位为1,进入if,执行上升沿函数代码段。
代码如下:
ic.c
  1. #include "ic.h"
  2. #include "stdio.h"
  3. #include "string.h"
  4. struct
  5. {
  6.     uint8_t succeed_flag;//完整的按键动作标志位
  7.     uint8_t rising_flag;//上升标志位
  8.     uint8_t falling_flag;//下降标志位
  9.     uint16_t timeout_cnt;//时间统计
  10.    
  11. }capture_status={0};//全部初始化为0
  12. TIM_HandleTypeDef ic_handle = {0};
  13. //初始化输入捕获函数
  14. void ic_init(uint16_t psc,uint16_t arr)
  15. {
  16.     TIM_IC_InitTypeDef ic_config = {0};
  17.     ic_handle.Instance = TIM2;//定时器2
  18.     ic_handle.Init.Prescaler = psc;
  19.     ic_handle.Init.Period = arr;
  20.     ic_handle.Init.CounterMode = TIM_COUNTERMODE_UP;//向上计数模式
  21.     HAL_TIM_IC_Init(&ic_handle);
  22.    
  23.     ic_config.ICFilter = 0;//过滤器0,也就是不要过滤器
  24.     ic_config.ICPolarity = TIM_ICPOLARITY_FALLING;//下降沿捕获
  25.     ic_config.ICPrescaler = TIM_ICPSC_DIV1;//每次在捕获输入上检测到边缘时执行捕获
  26.     ic_config.ICSelection = TIM_ICSELECTION_DIRECTTI;//输入捕获触发信号源直接连接到对应通道的输入捕获引脚,这意味着输入捕获触发信号直接作用于定时器的输入捕获电路,不需要通过其他中间寄存器或外部电
  27.     HAL_TIM_IC_ConfigChannel(&ic_handle,&ic_config,TIM_CHANNEL_2);
  28.    
  29.     __HAL_TIM_ENABLE_IT(&ic_handle,TIM_IT_UPDATE);//更新中断
  30.     HAL_TIM_IC_Start_IT(&ic_handle,TIM_CHANNEL_2);
  31.    
  32. }
  33. //初始化MSP函数
  34. void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
  35. {
  36.     if(htim->Instance == TIM2)
  37.     {
  38.         GPIO_InitTypeDef gpio_initstruct;
  39.         
  40.         __HAL_RCC_TIM2_CLK_ENABLE();
  41.         __HAL_RCC_GPIOA_CLK_ENABLE();
  42.         
  43.         gpio_initstruct.Mode = GPIO_MODE_INPUT;//复式推挽输出
  44.         gpio_initstruct.Pin = GPIO_PIN_1;//引脚1
  45.         gpio_initstruct.Pull = GPIO_PULLUP;//上拉输出
  46.         gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;//高速
  47.         
  48.         HAL_GPIO_Init(GPIOA,&gpio_initstruct);
  49.         
  50.         HAL_NVIC_SetPriority(TIM2_IRQn,2,2);//外部中断号,抢占优先级/响应优先级
  51.         HAL_NVIC_EnableIRQ(TIM2_IRQn);
  52.     }
  53.    
  54. }
  55. //中断服务函数
  56. void TIM2_IRQHandler(void)
  57. {
  58.     HAL_TIM_IRQHandler(&ic_handle);
  59. }
  60. //回调函数
  61. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  62. {
  63.     if(htim->Instance == TIM2)
  64.     {
  65.         if(capture_status.succeed_flag == 0)//完整按键捕获标志位,等于0代表不完整
  66.         {
  67.             if(capture_status.rising_flag == 1)//上升沿按键捕获标志,等于1代表捕获到上升沿
  68.             {
  69.                 printf("捕获一个上升沿\r\n");
  70.                 memset(&capture_status,0,sizeof(capture_status));//对一整个结构体的清零
  71.                 TIM_RESET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2);//重置捕获通道2
  72.                 TIM_SET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_FALLING);//配置捕获为下升沿
  73.             
  74.             }
  75.             else
  76.             {
  77.                 //未捕获到上升沿,现在是下降沿
  78.                 printf("捕获一个下降沿\r\n");
  79.                 memset(&capture_status,0,sizeof(capture_status));//对一整个结构体的清零
  80.                 capture_status.rising_flag = 1;//置位1,表示接下来的边沿检测是上升沿的
  81.                 TIM_RESET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2);//重置捕获通道2
  82.                 TIM_SET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_RISING);//配置捕获为上升沿
  83.             }
  84.         }
  85.     }
  86. }
复制代码
打开串口助手
按下按键2,串口显示

5、丈量按键按下时间

回顾:如何丈量按键按下的时间盘算?
在按键按下的刹时,检测到下降沿时,立刻把计时器关闭,将计数器置0,再打开(重启一下),使其从头开始计数,在未检测到上升沿之前,计数器中间可能会发生频频溢出,当检测到上升沿时,关闭计数器,查询当前计数器寄存器中的值。

复制项目文件21-捕获一次完备的按键动作,重命名为22-丈量按键按下时间
   __HAL_TIM_DISABLE(&ic_handle); 这行代码是在使用STM32 HAL库时,用于禁用(或制止)一个定时器的功能
    __HAL_TIM_SET_COUNTER(&ic_handle, 0); 这行代码在使用STM32 HAL库时,用于设置定时器的计数器值。
    __HAL_TIM_ENABLE(&ic_handle);//用于打开计数器
    HAL_TIM_ReadCapturedValue(&ic_handle, TIM_CHANNEL_2); 这行代码在使用STM32 HAL库时,用于读取定时器指定通道的捕获值。
  
代码如下:
main.c
  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "led.h"
  4. #include "uart1.h"
  5. #include "ic.h"
  6. int main(void)
  7. {
  8.     HAL_Init();                         /* 初始化HAL库 */
  9.     stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  10.     led_init();//初始化led灯
  11.     uart1_init(115200);
  12.     //printf("hello word!\r\n");
  13.     ic_init(72-1,65536-1);
  14.     while(1)
  15.     {
  16.         pressed_time_get();
  17.         delay_ms(500);
  18.     }
  19. }
复制代码
ic.c
  1. #include "ic.h"
  2. #include "stdio.h"
  3. #include "string.h"
  4. struct
  5. {
  6.     uint8_t succeed_flag;//完整的按键动作标志位
  7.     uint8_t rising_flag;//上升标志位
  8.     uint8_t falling_flag;//下降标志位
  9.     uint16_t timeout_cnt;//时间统计
  10.    
  11. }capture_status={0};//全部初始化为0
  12. uint16_t last_cnt = 0;
  13. TIM_HandleTypeDef ic_handle = {0};
  14. //初始化输入捕获函数
  15. void ic_init(uint16_t psc,uint16_t arr)
  16. {
  17.     TIM_IC_InitTypeDef ic_config = {0};
  18.     ic_handle.Instance = TIM2;//定时器2
  19.     ic_handle.Init.Prescaler = psc;
  20.     ic_handle.Init.Period = arr;
  21.     ic_handle.Init.CounterMode = TIM_COUNTERMODE_UP;//向上计数模式
  22.     HAL_TIM_IC_Init(&ic_handle);
  23.    
  24.     ic_config.ICFilter = 0;//过滤器0,也就是不要过滤器
  25.     ic_config.ICPolarity = TIM_ICPOLARITY_FALLING;//下降沿捕获
  26.     ic_config.ICPrescaler = TIM_ICPSC_DIV1;//每次在捕获输入上检测到边缘时执行捕获
  27.     ic_config.ICSelection = TIM_ICSELECTION_DIRECTTI;//输入捕获触发信号源直接连接到对应通道的输入捕获引脚,这意味着输入捕获触发信号直接作用于定时器的输入捕获电路,不需要通过其他中间寄存器或外部电
  28.     HAL_TIM_IC_ConfigChannel(&ic_handle,&ic_config,TIM_CHANNEL_2);
  29.    
  30.     __HAL_TIM_ENABLE_IT(&ic_handle,TIM_IT_UPDATE);//更新中断
  31.     HAL_TIM_IC_Start_IT(&ic_handle,TIM_CHANNEL_2);
  32.    
  33. }
  34. //初始化MSP函数
  35. void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
  36. {
  37.     if(htim->Instance == TIM2)
  38.     {
  39.         GPIO_InitTypeDef gpio_initstruct;
  40.         
  41.         __HAL_RCC_TIM2_CLK_ENABLE();
  42.         __HAL_RCC_GPIOA_CLK_ENABLE();
  43.         
  44.         gpio_initstruct.Mode = GPIO_MODE_INPUT;//复式推挽输出
  45.         gpio_initstruct.Pin = GPIO_PIN_1;//引脚1
  46.         gpio_initstruct.Pull = GPIO_PULLUP;//上拉输出
  47.         gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;//高速
  48.         
  49.         HAL_GPIO_Init(GPIOA,&gpio_initstruct);
  50.         
  51.         HAL_NVIC_SetPriority(TIM2_IRQn,2,2);//外部中断号,抢占优先级/响应优先级
  52.         HAL_NVIC_EnableIRQ(TIM2_IRQn);
  53.     }
  54.    
  55. }
  56. //中断服务函数
  57. void TIM2_IRQHandler(void)
  58. {
  59.     HAL_TIM_IRQHandler(&ic_handle);
  60. }
  61. //回调函数
  62. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  63. {
  64.     if(htim->Instance == TIM2)
  65.     {
  66.         if(capture_status.succeed_flag == 0)//完整按键捕获标志位,等于0代表不完整
  67.         {
  68.             if(capture_status.rising_flag == 1)//上升沿按键捕获标志,等于1代表捕获到上升沿
  69.             {
  70.                 printf("捕获一个上升沿\r\n");
  71.                 capture_status.succeed_flag = 1;//检测到一个完整的按键动作
  72.                
  73.                 TIM_RESET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2);//重置捕获通道2
  74.                 TIM_SET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_FALLING);//配置捕获为下升沿
  75.                 last_cnt = HAL_TIM_ReadCapturedValue(&ic_handle, TIM_CHANNEL_2);//用于读取定时器指定通道的捕获值。
  76.             }
  77.             else
  78.             {
  79.                 //未捕获到上升沿,现在是下降沿
  80.                 printf("捕获一个下降沿\r\n");
  81.                 memset(&capture_status,0,sizeof(capture_status));//对一整个结构体的清零
  82.                 capture_status.rising_flag = 1;//置位1,表示接下来的边沿检测是上升沿的
  83.                 __HAL_TIM_DISABLE(&ic_handle); //用于禁用(或停止)一个定时器的功能
  84.                 __HAL_TIM_SET_COUNTER(&ic_handle, 0);//用于设置定时器的计数器值。
  85.                 TIM_RESET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2);//重置捕获通道2
  86.                 TIM_SET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_RISING);//配置捕获为上升沿
  87.                 __HAL_TIM_ENABLE(&ic_handle);//用于打开计数器
  88.             }
  89.         }
  90.     }
  91. }
  92. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//溢出中断回调函数
  93. {
  94.     if(htim->Instance == TIM2)
  95.     {
  96.        if(capture_status.succeed_flag == 0)//在没有产生完整按键时
  97.        {
  98.            if(capture_status.rising_flag == 1)//边沿检测标志位等于1,代表在产生下降沿之后,上升沿之前这段时间,产生的溢出进行计数
  99.            {
  100.                capture_status.timeout_cnt++;
  101.            }
  102.        }
  103.     }
  104. }
  105. void pressed_time_get(void)
  106. {
  107.     if(capture_status.succeed_flag == 1)//产生完整按键之后,执行下面代码,未产生完整按键,不执行下面代码
  108.     {
  109.         printf("按下时间为:%d \r\n",capture_status.timeout_cnt * 65536 + last_cnt);
  110.         memset(&capture_status,0,sizeof(capture_status));//对一整个结构体的清零
  111.     }
  112. }
复制代码
ic.h
  1. #ifndef __IC_H__
  2. #define __IC_H__
  3. #include "sys.h"
  4. void ic_init(uint16_t psc,uint16_t arr);
  5. void pressed_time_get(void);
  6. #endif
复制代码
打开串口助手
按下KEY2
可盘算按键按下的时间


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4