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

相关文章

Java注解之超越Javadoc的元数据利器详解

《Java注解之超越Javadoc的元数据利器详解》本文将深入探讨Java注解的定义、类型、内置注解、自定义注解、保留策略、实际应用场景及最佳实践,无论是初学者还是资深开发者,都能通过本文了解如何利用... 目录什么是注解?注解的类型内置注编程解自定义注解注解的保留策略实际用例最佳实践总结在 Java 编程

一文教你Python如何快速精准抓取网页数据

《一文教你Python如何快速精准抓取网页数据》这篇文章主要为大家详细介绍了如何利用Python实现快速精准抓取网页数据,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录1. 准备工作2. 基础爬虫实现3. 高级功能扩展3.1 抓取文章详情3.2 保存数据到文件4. 完整示例

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

python处理带有时区的日期和时间数据

《python处理带有时区的日期和时间数据》这篇文章主要为大家详细介绍了如何在Python中使用pytz库处理时区信息,包括获取当前UTC时间,转换为特定时区等,有需要的小伙伴可以参考一下... 目录时区基本信息python datetime使用timezonepandas处理时区数据知识延展时区基本信息

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环