STM32 定时器输入捕获实现红外遥控数据接收

2024-06-04 05:58

本文主要是介绍STM32 定时器输入捕获实现红外遥控数据接收,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

之前已经写过了一个使用定时器普通计时功能来识别红外遥控数据的文章。本次是使用定时器输入捕获来实现,这种方法比起定时器普通计数来说要更加复杂一些,不过效果会更好。

一、原理

1、红外发射协议

  • 红外发射协议已经在之前的文章中写过,在此就不赘述。

2、定时器计数和输入捕获

  • 定时器就是按照一个特定的频率对计数值进行加一或减一操作,当数值溢出时则产生一个标志或中断。

  • 定时器的输入捕获就是可以测量输入信号的脉冲宽度。

  • 本次就是通过普通计数和输入捕获的结合来实现的。

3、实现方法

  • 利用定时器记录输入信号高脉冲的时间,通过该时间来判断数据是否是同步头信息、数据 1 或者数据 0。

二、实现

1、配置 定时器2 输入捕获通道

  • 示例代码中使用 PA1 管脚,配置为上拉输入模式,复用功能为定时器2的通道2。

  • 定时器采用普通定时器,定时器2,该定时器具有输入捕获功能。

  • 配置定时器的两种工作模式,一个是普通计数器TIM_TimeBaseInit,一个是输入捕获模式TIM_ICInit

  • 配置定时器2的中断源,有两个中断源,一个是更新中断TIM_IT_Update,一个是输入捕获中断TIM_IT_CC2

  • 配置代码如下:

/*
* Ir input pin
* mode:floating input
* pin: PA1
* GPIO_AF: TIM2_CH2
*/
void Ir_Pin_Init()
{GPIO_InitTypeDef GPIO_InitStructure;GPIO_PinAFConfig(GPIOA,GPIO_PinSource1,GPIO_AF_2);GPIO_InitStructure.GPIO_Pin  =  GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_1); //output 1
}//PA1 TIM2_CH2
//使用GPIO输入捕获实现红外接收
void Remote_Init()
{NVIC_InitTypeDef                NVIC_InitStructure;TIM_TimeBaseInitTypeDef         TIM_TimeBaseStructure;TIM_ICInitTypeDef               TIM_ICInitStructure;/*使能TIM1时钟,默认时钟源为PCLK1(PCLK1未分频时不倍频,否则由PCLK1倍频输出),可选其它时钟源*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);Ir_Pin_Init();TIM_ClearITPendingBit(TIM2,TIM_IT_Update|TIM_IT_CC2); //清除中断和捕获标志位TIM_TimeBaseStructure.TIM_Period = 1000; //设定计数器自动重装值 最大10ms溢出TIM_TimeBaseStructure.TIM_Prescaler = 480-1;   //预分频器,0.1M的计数频率,10us加1.TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMxTIM_ICInitStructure.TIM_Channel = TIM_Channel_2;  // 选择输入端 IC2映射到TI2上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 = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波TIM_ICInit(TIM2, &TIM_ICInitStructure);//初始化定时器输入捕获通道NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断NVIC_InitStructure.NVIC_IRQChannelPriority = 2;  //优先级0级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC2,ENABLE);//允许更新中断 ,允许CC2IE捕获中断TIM_Cmd(TIM2,ENABLE);    //使能定时器2
}

2、添加定时器2的中断服务函数

  • 使用了两种定时器中断源,分别为计数溢出中断和输入捕获中断。但是这两种方式触发中断的中断服务函数是同一个,即void TIM2_IRQHandler(void)

  • 定时器使用的是 TIM2 通用定时器,模式为向上计数。在该模式中,计数器从 0 计数到自动加载值 (TIMx_ARR计数器的内容) ,然后重新从 0 开始计数并且产生一个计数器溢出事件。定时器计数溢出的周期为10ms,该中断的产生说明在10ms内都没有输入捕获来清空计数值,也就是输入信号没有发生变化,说明 10ms 没有收到红外信号了,因此可判断为接收完成。

  • 输入捕获是为了测量高电平的持续时间,因此采用上升沿触发中断,对计数值清零,切换下一次为下降沿触发;在下降沿触发中断时,记下计数值,切换下一次为上升沿触发。因此在下降沿记下的时间即为高电平的时序时间。记录高电平持续时间的原因,是因为红外信号在表示逻辑0、逻辑1时低电平的持续时间的相同的,而高电平的持续时间不同的。

  • 示例代码如下:

//遥控器接收状态
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留
//[4]:标记上升沿是否已经被捕获
//[3:0]:溢出计时器
uint8_t  RmtSta = 0;
uint16_t Dval;         //下降沿时计数器的值
uint32_t RmtRec = 0;   //红外接收到的数据
uint8_t  RmtCnt = 0;   //按键按下的次数//定时器2中断服务程序
void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2,TIM_IT_Update) != RESET)  //计数溢出中断{if(RmtSta & 0x80) //上次有数据被接收到了{RmtSta &= ~0x10;  //取消上升沿已经被捕获标记if((RmtSta & 0x0f) == 0x00) RmtSta |= 1<<6; //电平没有变化后延时10ms,可标记已经完成一次按键的信息采集if((RmtSta & 0x0f) < 14) RmtSta++;  //若进入14,意味着定时器计数溢出了14次,即140mselse{RmtSta &= ~(1<<7); //清空引导标识RmtSta &= 0xf0;   //清空计数器 ,意味着可以下一次的检测//RmtCnt = 0;//RmtSta &= ~(1<<6);}}}if(TIM_GetITStatus(TIM2,TIM_IT_CC2) != RESET)    //输入捕获中断{if(RDATA){TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling); //设置下降沿触发捕获TIM_SetCounter(TIM2,0);    // 清零计数值RmtSta |= 0x10;}else{Dval = TIM_GetCapture2(TIM2);  // 获取计数值,该计数值代表高电平持续时间TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);  // 设置上升沿触发if(RmtSta & 0x10){if(RmtSta & 0x80){if((Dval > 30) && (Dval < 80)){RmtRec <<= 1;RmtRec |= 0;}else if((Dval > 140) && (Dval < 180)){RmtRec <<= 1;RmtRec |= 1;}else if((Dval > 200) && (Dval < 250)){RmtCnt++;    // 重复码RmtSta &= 0xf0;}}else if((Dval > 420) && (Dval < 470)){RmtSta |= 1<<7; //表示接收到同步头RmtCnt = 0;    //清零按键次数}}RmtSta &= ~0x10;}}TIM_ClearFlag(TIM2,TIM_IT_Update|TIM_IT_CC2);
}

3、红外按键扫描函数

  • 该函数放在主循环中,轮训判断按键是否接收完成。如果接收完成则开始分析键值。

  • 该函数返回一个16位的数值,其中低八位表示键值,高八位表示按下的次数,依次来分析长按键和短按键。这一点主要是通过红外协议中重复码的规定来实现的。

  • 红外协议中规定,若按下一个键后没有放开,则会以 108ms 为一个周期发送重复码。重复码表现为2.25ms的高电平。

  • 示例代码如下:

/*SystemKeybuf
*处理红外键盘
*返回值: keybuf
*   0xffff,没有任何按键按下
*   其他,按下的按键键值.
*   keybuf[15:8] : key cnt
*   keybuf[7:0]  : key code
*/
uint16_t Remote_Scan(void)
{uint16_t keybuf = 0xffff;uint8_t sta = 0xff;uint8_t t1,t2;if(RmtSta & (1<<6))//得到一个按键的所有信息了{t1 = RmtRec >> 24;            //得到地址码t2 = (RmtRec >> 16) & 0xff; //得到地址反码//printf("rmtrec = %#x\n",RmtRec);if((t1 == (uint8_t)~t2) && (t1 == REMOTE_ID))//检验遥控识别码(ID)及地址//if(t1 == (uint8_t)~t2)//检验遥控识别码(ID)及地址{//printf("t1 = %d\n",t1);t1 = RmtRec >> 8;t2 = RmtRec;if(t1 == (uint8_t)~t2) sta = t1;//键值正确}if((sta != 0xff))//按键数据正确{if(RmtCnt < 10) // short key{if((RmtSta & 0x80) == 0) // release{keybuf = RmtCnt;keybuf <<= 8;keybuf |= sta;RmtSta &= ~(1<<6); //清除接收到有效按键标识RmtCnt = 0;       //清除按键次数计数器}}#if 0else if(RmtCnt == 10)// && RmtCnt < 15)  // long key{keybuf = RmtCnt;keybuf <<= 8;keybuf |= sta;}else if(RmtCnt > 10){if(((RmtCnt - 10) % 2) == 0)   //每5次重复码处理一次按键 {keybuf = RmtCnt;keybuf <<= 8;keybuf |= sta;}}#elseelse if((sta == KEY_NEXT) || (sta == KEY_PREV) || (sta == KEY_MENU) || (sta == KEY_SELECT)) //仅这四个按键支持连续键功能{if(((RmtCnt - 10) % 2) == 0)   //每2次重复码处理一次按键{keybuf = RmtCnt;keybuf <<= 8;keybuf |= sta;}if((RmtSta & 0x80) == 0) // release{RmtSta &= ~(1<<6); //清除接收到有效按键标识RmtCnt = 0;       //清除按键次数计数器}}else //长按键{//if((RmtSta & 0x80) == 0)  //release ,并非等到按键释放才处理{keybuf = RmtCnt;keybuf <<= 8;keybuf |= sta;RmtSta &= ~(1<<6); //清除接收到有效按键标识RmtCnt = 0;       //清除按键次数计数器RmtRec = 0;       //清除接收值}}#endif}}return keybuf;
}

4、主函数

  • 在 main 函数中,对 IO 口和 定时器进行初始化。

  • 主循环中,通过判断接收完成标志位,对接收完成的按键控制码进行打印。

  • SystemKeyHandle()函数处理每一个按键的操作逻辑。

  • 示例代码如下:

void main()
{uint16_t SystemKeybuf;...//Ir RemoteRemote_Init();while(1){SystemKeybuf = Remote_Scan();    //红外按键扫描if(SystemKeybuf != 0xffff) //如果有按键未处理{//main_printf("SystemKeyBuf = %#x\n",SystemKeybuf);SystemKeyHandle();SystemKeybuf = 0xffff;}}
}

三、演示

如下图为串口打印出接收的红外按键值信息:
这里写图片描述

说明1:这篇文章所使用的方法主要参考自这篇文章,代码仅做了部分修改,并在源代码基础上添加了部分代码,用于实现连续键。

本文档基于 STM32 F1 系列 MCU,固件库版本 3.5。其他 MCU 及固件库仅需要对库函数略作修改。

参考链接1:cortex_m3_stm32嵌入式学习笔记(二十三):红外遥控实验(输入捕捉+解码)

参考链接2:STM32 红外线实验

参考链接3:STM32 红外线实验

更多关于 STM32 的文章

这篇关于STM32 定时器输入捕获实现红外遥控数据接收的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1029255

相关文章

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

解决pandas无法读取csv文件数据的问题

《解决pandas无法读取csv文件数据的问题》本文讲述作者用Pandas读取CSV文件时因参数设置不当导致数据错位,通过调整delimiter和on_bad_lines参数最终解决问题,并强调正确参... 目录一、前言二、问题复现1. 问题2. 通过 on_bad_lines=‘warn’ 跳过异常数据3

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

PyCharm中配置PyQt的实现步骤

《PyCharm中配置PyQt的实现步骤》PyCharm是JetBrains推出的一款强大的PythonIDE,结合PyQt可以进行pythion高效开发桌面GUI应用程序,本文就来介绍一下PyCha... 目录1. 安装China编程PyQt1.PyQt 核心组件2. 基础 PyQt 应用程序结构3. 使用 Q

Python实现批量提取BLF文件时间戳

《Python实现批量提取BLF文件时间戳》BLF(BinaryLoggingFormat)作为Vector公司推出的CAN总线数据记录格式,被广泛用于存储车辆通信数据,本文将使用Python轻松提取... 目录一、为什么需要批量处理 BLF 文件二、核心代码解析:从文件遍历到数据导出1. 环境准备与依赖库