STM32 学习12 输入捕获与触摸按键

一、输入捕获介绍

1. 概念

输入捕获(Input Capture)是一种用于测量外部信号脉冲宽度或者频率的技术,常用于测量传感器输出、编码器信号、脉冲调制信号等。输入捕获通常通过定时器模块来实现输入捕获功能。

2. STM32F1 资源

STM32F1除了基本定时器TIM6和TIM7,其它定时器都具有输入捕获功能。

3. 捕获原理

捕获原理是指通过定时器模块捕获外部信号的特定事件,例如脉冲的上升沿或下降沿,以便测量脉冲宽度或频率。 输入捕获时,相应的ICx检测到跳变沿,TIMx_CCRx寄存器记录TIMx_CNT计数值;下次跳变时,对TIMx_CNT值进行比较。

图示如下:
在这里插入图片描述
注意CNT计数的次数是 N ∗ A R R + C C R ∗ x 2 N*ARR+CCR*x2 NARR+CCRx2,因为在检测周期里计数可能多次溢出,需要记录溢出次数N。
由计数效数*CNT计数周期,即捕获到信号持续时间。

二、输入捕获配置步骤

1. 使能时钟、设置端口模式

定时器需要的头文件在 stm32f10_tim.h 文件中。
下面示例使用 PA0/TIM5_CH1 引脚。
在这里插入图片描述

RCC_APB1PerihpClockCmd(RCC_APB1Periph_TIM5,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// GPIO 设置为输入拉低
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;

2. 初始化定时器

配置定时器的时钟源、预分频器、计数模式等。

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

3. 设置捕获参数

开启捕获。

void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStrcut);

typedef struct{
   uint16_t TIM_Channel;		// 通道
   uint16_t TIM_ICPolarity;		// 捕获极性
   uint16_t TIM_ICSelection;	// 映射
   uint16_t TIM_ICPRescaler;	// 分频系数
   uint16_t TIM_ICFilter;		// 滤波器长度
} TIM_ICInitTypeDef;

// 设置通道1
TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Rising);

4. 开启捕获和定时器中断(溢出中断|更新中断)

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
// 捕获通道1
TIM_ITConfig(TIM5, TIM_IT_Update|TIM_IT_CC1,ENABLE);

NVIC_Init()

6. 编写定时器中断服务函数

TIM5_IRQHandler

7. 使能定时器

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

三、代码实现

1. catch_utils.h

#ifndef __CATCH_UTILS_H__
#define __CATCH_UTILS_H__
#include "stm32f10x.h"
#include "stdio.h"

// 定时器溢出的次数
static u8 TIM5_Overflow = 0;
// 捕获到上升沿
static u8 TIM5_Capture = 0;
void catch_gpio_init(u16 period, u16 prescaler);
void catch_timer_enable(void);
#endif

2. catch_utils.c

#include "catch_utils.h"

#define RISING 0
#define FALLING 1


/**
 * @brief  捕获初始化
*/
void catch_gpio_init(u16 period, u16 prescaler)
{
    // GPIO 初始化
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 定时器初始化
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = period;
    TIM_TimeBaseStructure.TIM_Prescaler = prescaler;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);

    // 输入捕获初始化
    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0x00;
    TIM_ICInit(TIM5, &TIM_ICInitStructure);
    TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Rising);

    // 开启捕获和定时器中断
    TIM_ITConfig(TIM5, TIM_IT_Update | TIM_IT_CC1, ENABLE);
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/**
 * 中断函数
*/
void TIM5_IRQHandler(void)
{
    // 如果捕获到,要翻转一下捕获方向,如果捕获到下降沿,就设置为上升沿,并计算捕获时间并输出
    if (TIM_GetITStatus(TIM5, TIM_IT_CC1))
    {
        if (TIM5_Capture == RISING)
        {
            printf("catch rising irq, TIM5_Capture= %d \n", TIM5_Capture);
            TIM5_Capture = FALLING;
            // 关闭定时器
            TIM_Cmd(TIM5, DISABLE);
            TIM_SetCounter(TIM5, 0);
            TIM5_Overflow = 0;
            TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Falling);
            TIM_Cmd(TIM5, ENABLE);
        }
        else
        {
            // 捕获到下降沿
            TIM5_Capture = RISING;
            // 计算 总时间
            u16 time = TIM_GetCapture1(TIM5);
            u32 total_time = TIM5_Overflow * 0xffff + time;
            printf("catch falling irq, capture time= %d, overflow count = %d \n, total=%dus", time, TIM5_Overflow, total_time);
            TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Rising);
        }
        TIM_ClearITPendingBit(TIM5, TIM_IT_CC1);
    }else if(TIM_GetITStatus(TIM5, TIM_IT_Update)){
        TIM5_Overflow++;
        TIM_ClearITPendingBit(TIM5, TIM_IT_Update);
    }
}
/**
 * @brief 使能定时器
*/
void catch_timer_enable(void)
{
    TIM_Cmd(TIM5, ENABLE);
}

3. main.c

#include "gpio_utils.h"
#include "rcc_utils.h"
#include "stm32f10x.h"
#include "sys_tick_utils.h"
#include "led_utils.h"
#include "usart_utils.h"
#include "stdio.h"
#include "catch_utils.h"

// 主函数
int main(void)
{
	GPIO_Configuration(); // 调用GPIO配置函数
	// tick 初始化
	sys_tick_init(72);
	led_all_off();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	USART3_Init(9600);
	printf("starting...");

	// led 初始化
	custom_led_init();

	int i = 0;
	// 捕获初始化
	catch_gpio_init(0xffff, 72 - 1);
	catch_timer_enable();
	
	while (1) // 无限循环
	{
		delay_ms(990);
		led_lightn(i);
		i++;
		if(i>9){
			i=0;
		}
	}
}

四、触摸按键捕获

我的开发板没有带触摸组件,所以本实验使用外置的触摸按键模块。
在这里插入图片描述
这个触摸按键在感应到触摸时,会在SIG引脚输出高电平。 直接把SIG接在 PA0 上即可使用。
但该电路对原KEY_UP按键模块有影响,开发板按键按下的时候,PA0 获取不到足够高的电平。

运行中串口输出示例

在这里插入图片描述

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要使用STM32输入捕获功能来检测按键,可以按照以下步骤进行操作: 1. 配置GPIO口为输入模式,用于连接按键的引脚。 2. 配置TIM(定时器)的一个通道为输入捕获模式,以便捕获按键引脚的状态变化。 3. 在硬件初始化函数中配置GPIO和TIM。 4. 在主循环中检测输入捕获事件并处理按键状态。 下面是一个简单的示例代码,演示如何使用STM32 HAL库来实现按键检测: ```c #include "stm32f4xx_hal.h" // 定义按键引脚 #define BUTTON_PIN GPIO_PIN_0 #define BUTTON_PORT GPIOA // 定义定时器和通道 #define TIM_HANDLE htim2 #define TIM_CHANNEL TIM_CHANNEL_1 // 按键状态变量 GPIO_PinState buttonState = GPIO_PIN_RESET; // 输入捕获回调函数 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM_HANDLE.Instance && htim->Channel == TIM_CHANNEL) { // 获取捕获值 uint32_t captureValue = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL); // 处理按键状态 if (captureValue > 1000) { buttonState = GPIO_PIN_SET; // 按下 } else { buttonState = GPIO_PIN_RESET; // 松开 } } } int main(void) { // STM32初始化代码 // 配置GPIO口为输入模式 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = BUTTON_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; // 或者使用GPIO_PULLDOWN HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct); // 配置TIM为输入捕获模式 TIM_IC_InitTypeDef sConfigIC; sConfigIC.ICPolarity = TIM_ICPOLARITY_BOTHEDGE; // 检测上升沿和下降沿 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; sConfigIC.ICFilter = 0; HAL_TIM_IC_ConfigChannel(&TIM_HANDLE, &sConfigIC, TIM_CHANNEL); // 启动输入捕获 HAL_TIM_IC_Start_IT(&TIM_HANDLE, TIM_CHANNEL); while (1) { // 处理按键状态 if (buttonState == GPIO_PIN_SET) { // 按键按下后的操作 } else { // 按键松开后的操作 } } } ``` 在上述示例代码中,我们首先定义了按键引脚、定时器和通道以及按键状态变量。然后在`main`函数中,我们配置了GPIO口为输入模式,并将定时器配置为输入捕获模式。在主循环中,我们通过检查按键状态来执行相应的操作。 请注意,示例代码中使用了STM32 HAL库,你需要根据自己使用的具体型号和开发环境进行相应的配置和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程圈子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值