红外遥控学习,万能遥控解决方案
红外遥控学习,万能遥控解决方案
- 1. 原理
- 2. 思路
- 3. 红外遥控接收
- 3.1 初始化定时器
- 3.2 定时器输入捕获
- 3.3 获取数据
- 3.4 红外接收测试
- 3.5 测试过程
- 4. 发送程序
- 4.1 初始化定时器和定时器的通道
- 4.1发送函数
1. 原理
目前电视机、空调等家电大部分还是采用的红外遥控的,有时项目需要把遥控嵌入到自己的设备中,或者又是物联网需要控制家电,此时就需要智能学习和发送了,红外遥控电路图如下:
左侧为红外发送电路,右侧有红外接收电路。
● 发送端:
以NEC协议为例(实际测试中遵循NEC协议的不多),信息传输是基于38K载波,也就是说红外线是以载波的方式传递。
发送协议数据“0” = 发送载波560us + 不发送载波560us
发送协议数据“1” = 发送载波560us+ 不发送载波1680us
发送的波形如下图所示,下图中为 0 0 0 0 1 0 1
● 接收端:
在红外接收端,如果接收到红外38K载波,则IR输出为低电平,如果不是载波包括固定低电平和固定高电平则输出高电平。在IR端接收的信号如下所示:
NEC协议规定:
发送协议数据“0” = 发送载波560us + 不发送载波560us
发送协议数据“1” = 发送载波560us+ 不发送载波1680us
发送引导码 = 发送载波9000us + 不发送载波4500us
● 总结:
1、接收端收到38K载波脉冲为低电平,没有则为高电平;所以在设计发送的时候,发送低电平应该开启38KHZ的PWM,发送高电平则关闭PWM(默认为高电平)
2、数据“0” 和数据“1” 是由接收到的一个高电平和一个低电平组合而来,一般来说高电平时间等于低电平时间为数据0,其它则为1
参考: STM32之红外遥控信号自学习实现
2. 思路
思路简单,简述为: 捕获——>保存——>发射
先捕获一个红外遥控按键的全部信息,不管内容是什么,再保存到flash中,就是多个高低电平的持续时间,然后再一一发射出去。这样不管是什么协议都能成功。
3. 红外遥控接收
3.1 初始化定时器
具体见代码和详细的注释:
// ir_inputCapture_send.c
/******************************************************************** @brief 定时器16输入捕获初始化/红外遥控初始化、设置IO以及TIM16_CH1的输入捕获、10ms溢出
********************************************************************/
void TIM16_Remote_Init(void)
{ TIM_IC_InitTypeDef TIM16_CH1Config; TIM16_Handler.Instance = TIM16; //通用定时器16TIM16_Handler.Init.Prescaler=(48-1); //预分频器,1M的计数频率,1us加1.TIM16_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器TIM16_Handler.Init.Period = (60000-1); //自动装载值,16位最大65536,此处设置60ms的计时TIM16_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; //时钟分频因子HAL_TIM_IC_Init(&TIM16_Handler);//初始化TIM1输入捕获参数TIM16_CH1Config.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; //注意下降沿捕获 TIM16_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI; //映射到TI4上TIM16_CH1Config.ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频TIM16_CH1Config.ICFilter=0x03; // 0 //IC4F=0003 8个定时器时钟周期滤波HAL_TIM_IC_ConfigChannel(&TIM16_Handler, &TIM16_CH1Config, TIM_CHANNEL_1);//配置TIM4通道4__HAL_TIM_ENABLE_IT(&TIM16_Handler,TIM_IT_UPDATE); //使能更新中断 HAL_TIM_IC_Stop_IT(&TIM16_Handler,TIM_CHANNEL_1);
}
// stm32f0xx_hal_msp.c.c
//定时器16底层驱动,时钟使能,引脚配置
//此函数会被上述的HAL_TIM_IC_Init()调用
//htim:定时器句柄
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{GPIO_InitTypeDef GPIO_InitStruct;if(htim->Instance==TIM16){/************TIM16_CH1************/GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_TIM16_CLK_ENABLE(); //使能TIM16时钟__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟GPIO_Initure.Pin = GPIO_PIN_6; //PB9GPIO_Initure.Mode = GPIO_MODE_AF_PP; //复用输入GPIO_Initure.Pull = GPIO_PULLUP; //上拉GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH; //高速GPIO_Initure.Alternate = GPIO_AF5_TIM16;HAL_GPIO_Init(GPIOA,&GPIO_Initure);HAL_NVIC_SetPriority(TIM16_IRQn,2,0); //设置中断优先级,抢占优先级2,子优先级0HAL_NVIC_EnableIRQ(TIM16_IRQn); //开启ITM16中断}
}
3.2 定时器输入捕获
红外编码是由多个字节编码组成的,根据持续的高低电平时间不同,而形成的编码信号,此时可以通过定时器捕获来实现。
定时器输入捕获中断回调函数如下,红外信号基本都是低电平起,可先用下降沿捕获:
// ir_inputCapture_send.c
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行
{if(htim->Instance==TIM16) {if(READ_IR_GPIO()) //上升沿捕获 {Dval = HAL_TIM_ReadCapturedValue(&TIM16_Handler, TIM_CHANNEL_1); //读取CCR1也可以清CC4IF标志位TIM_RESET_CAPTUREPOLARITY(&TIM16_Handler, TIM_CHANNEL_1); //一定要先清除原来的设置!!TIM_SET_CAPTUREPOLARITY(&TIM16_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); //CC1P=1 设置为下降沿捕获if(0 == irRun.receive.cnt) { __HAL_TIM_SET_COUNTER(&TIM16_Handler,0); //清空定时器值 return ;} if(irRun.receive.cnt<(MAX_IR_LEARN_DATA_SIZE - 1)) { // 保存在数组中的 0 2 4 6 8 中irRun.buf[irRun.receive.cnt++] = Dval;} else { RcvRemoteFinish();}__HAL_TIM_SET_COUNTER(&TIM16_Handler,0); //清空定时器值 }else //下降沿捕获 {Dval = HAL_TIM_ReadCapturedValue(&TIM16_Handler, TIM_CHANNEL_1);TIM_RESET_CAPTUREPOLARITY(&TIM16_Handler,TIM_CHANNEL_1); TIM_SET_CAPTUREPOLARITY(&TIM16_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING); //配置TIM16通道1上升沿捕获//当前这一次,不计数,下一次上升沿才计数if(0 == irRun.receive.cnt) { __HAL_TIM_SET_COUNTER(&TIM16_Handler,0); //清空定时器值 return;} if(irRun.receive.cnt < (MAX_IR_LEARN_DATA_SIZE-1)) //1 3 5 7 9{irRun.buf[irRun.receive.cnt++] = Dval;if(irRun.receive.cnt>=20) {irRun.receive.dataComplete = 1; }}else {RcvRemoteFinish();}__HAL_TIM_SET_COUNTER(&TIM16_Handler,0); //清空定时器 放最后}}
}
// ir_inputCapture_send.c
//60m定时器计时后溢出中断的回调函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance==TIM16) //定时器更新(溢出)中断回调函数 10ms溢出{ if(irRun.receive.dataComplete) //至少已经收到了20个字节,才算接收到一帧信息{ RcvRemoteFinish();} else {irRun.receive.cnt = 0; //重新来过,此帧数据无效}}
}
// ir_inputCapture_send.c
/* 当前红外遥控一帧结束。触发条件:① 接收到的红外字节个数已经超过最大允许的长度 (HAL_TIM_IC_CaptureCallback)② 如上述的定时器计时后溢出中断的回调函数 (HAL_TIM_PeriodElapsedCallback)
*/
static void RcvRemoteFinish(void)
{//1. 先失能 避免重洗掉前面保存的一帧完整数据TIM16_Remote_Disable();//2. 重新设置下降沿捕获,以防上升沿捕获的TIM_SET_CAPTUREPOLARITY(&TIM16_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); //CC1P=1 设置为下降沿捕获//3-1. 清零数据存在的标志irRun.receive.dataComplete = 0; //先清零//4-2. (多等待了60ms),不管如何都算完成了irRun.receive.complete = 1; // irRun.receive.complete 为一帧接收的完整标志irRun.receive.len = irRun.receive.cnt - 1; //一帧接收的最大长度,irRun.receive.cnt 多了1个 //清零接收长度 缓冲数据irRun.receive.cnt = 0;
}
至此为止 数据保存在 irRun.buf 中, 个数为 irRun.receive.len 。
3.3 获取数据
具体见代码和详细的注释:
// 获取一帧数据在上层调用
// 返回值: 0, 没有任何按键按下
// 1 ,按下的按键键值.
// ir_inputCapture_send.c
u8 GetRemoteRcvBuf(uint16_t *buf, uint32_t *len, uint8_t isRecord)
{ if(irRun.receive.complete && !irRun.receive.dataComplete) { //完成一帧的标记, 且现在没有正在捕获的信号TIM_SET_CAPTUREPOLARITY(&TIM16_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); //CC1P=1 设置为下降沿捕获//最多拷贝MAX_IR_LEARN_DATA_SIZE个, 第二次保护,前有一次保护措施 if(irRun.receive.len >= MAX_IR_LEARN_DATA_SIZE-1) irRun.receive.len = MAX_IR_LEARN_DATA_SIZE-1; if(irRun.receive.len >= GetFuncLen() && GetFuncLen()>20 && isRecord) {irRun.receive.len = GetFuncLen();if(irRun.receive.len >= MAX_IR_COMPARE)irRun.receive.len = MAX_IR_COMPARE;}for(uint8_t i=0; i < irRun.receive.len; i++) { //i<98buf[i] = irRun.buf[i+1]; //[97+1]}*len = irRun.receive.len;//可以中断接收另外的数据呢irRun.receive.cnt = 0; irRun.receive.complete = 0;irRun.receive.len = 0;return 1;} return 0;
}
上层应用调用:
// app.c
ack = GetRemoteRcvBuf(&userSave.irFunctionBuf[0], &userSave.irFunctionLen, 0);
得到的红外编码保存在 userSave.irFunctionBuf[] 中,数据长度在 userSave.irFunctionLen 中。当然验证后有效,就可以保存在Flash中了,掉电后不丢失。
红外接收到此结束,接下来验证一番:
3.4 红外接收测试
测试代码,直接在main.c中调用:
#ifdef SYS_USE_DEBUG
u16 test_cnt = 0;
void All_Remote_Test()
{u8 ack = 0;TIM16_Remote_Enable(); //使能红外遥控捕获while(1) {ack = GetRemoteRcvBuf(&userSave.irFunctionBuf[0], &userSave.irFunctionLen, 0);if(ack == 1) {ack = 0;LED_ON();test_cnt++;TIM16_Remote_Enable(); //使能红外遥控捕获}LED_OFF();}
}
#endif
3.5 测试过程
左边使用 Keil 的 Debug调试,捕获一帧红外遥控信号;
右边使用 Kingst 数字信号逻辑分析仪,接收到的此帧数据;
遥控器采用三星的电视机遥控器(不是典型NEC协议),对比如下,左边任意一个字节的数字(持续时间us)都能与右边完美对应上,证明了以上程序的可行性。
4. 发送程序
原理弄懂,红外发送程序相对来说就简单很多
4.1 初始化定时器和定时器的通道
定时器不分频的情况下,需要产生38KHZ的频率的方波,Period 需要设置成: 48000/38=1263.158
具体见代码和详细的注释:
//ir_inputCapture_send.c
//定时器不分频,需要产生38KHZ的频率的方波,Period 需要设置成: 48000/38=1263.158
void TIM3_PWM_Init(void)
{ TIM_MasterConfigTypeDef MasterConfig;TIM3_Handler.Instance = TIM3; //定时器3TIM3_Handler.Init.Prescaler = 0; //定时器分频TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数模式TIM3_Handler.Init.Period = 1264; ///48000/38=1263.158TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;TIM3_Handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;HAL_TIM_PWM_Init(&TIM3_Handler); //初始化PWMMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;MasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;HAL_TIMEx_MasterConfigSynchronization(&TIM3_Handler, &MasterConfig);TIM3_CH4Handler.OCMode = TIM_OCMODE_PWM1; //模式选择PWM1TIM3_CH4Handler.Pulse = 632; //设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%TIM3_CH4Handler.OCPolarity = TIM_OCPOLARITY_HIGH; //输出比较极性为低 TIM3_CH4Handler.OCFastMode = TIM_OCFAST_DISABLE;HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4); //配置TIM3通道4HAL_TIM_PWM_MspInit(&TIM3_Handler);HAL_TIM_PWM_Stop(&TIM3_Handler,TIM_CHANNEL_4);//关闭PWM通道4}
4.1发送函数
发送函数记住核心即可:低电平的时候开启PWM,高电平关闭。
/******************************************************************** @brief 红外PWM控制的发送,在轮询中操作 低电平的时候开启PWM,高电平关闭* @retval None
********************************************************************/
void IrPwmMultiSend(u16 *buf, u32 len)
{//1、关闭红外接收中断TIM16_Remote_Disable(); //停止捕获TIM16的通道1//先停止HAL_TIM_PWM_Stop(&TIM3_Handler,TIM_CHANNEL_4);delay_ms(10);for(u8 i=0; i<len; i++) {//偶数 [0] [2] [4] [6] 低电平的时候开启PWM__HAL_TIM_SET_COUNTER(&TIM3_Handler,0);if(i%2 == 0) { HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);}else {HAL_TIM_PWM_Stop(&TIM3_Handler,TIM_CHANNEL_4);}delay_us(buf[i]);}HAL_TIM_PWM_Stop(&TIM3_Handler,TIM_CHANNEL_4);TIM16_Remote_Enable(); //开始捕获TIM16的通道1
}
上层调用:
//app.c
IrPwmMultiSend(&userSave.irPowerBuf[0], userSave.irPowerLen); //发送开机指令
验证的话,直接看要操控的目标,如电视机是否生效了,如果不行,在验证发出的波形,是否满足要求。
红外遥控学习,万能遥控解决方案相关推荐
- 学习型红外遥控器设计(2) 红外遥控学习方案设计
学习型红外遥控器设计(0) 摘要 (1) 绪论 (2) 方案设计 (3) 遥控解码 (4) 编码还原 (5) 硬件实现 (6) 总结展望 2.1 设计目标及要求 本文设计的万能学 ...
- 鸿蒙系统有没有红外遥控,红外遥控与蓝牙遥控的区别
现如今社会无线技术运用越来越广泛,大大提高了生活质量水平,而远程遥控技术又称为遥控技术,是指实现对被控目标的遥远控制,广泛用于社会的各行各业. 红外遥控是一种无线.非接触控制技术,具有抗干扰能力强,信 ...
- 小爱音箱 电脑 麦克风_开箱,小米小爱音箱万能遥控版,这样的操作你知道吗?...
小爱音箱万能遥控版采用白色的包装盒,正面是音箱的图片,支持QQ音乐库,海量优质有声内容,600家的使用技能,支持WiFi,蓝牙和红外,可以语音遥控家电. 包装盒的侧面,还印有小米小爱音箱万能遥控版的产 ...
- MobCtrl万能遥控-手机控制电脑软件简介[官方]
MobCtrl万能遥控手机远程控制电脑软件详细介绍 作者:郑海波 zhb931706659@126.com 官方网站:http://www.mobctrl.net MobCtrl用户QQ交流群:321 ...
- 无线433远距离遥控时有杂波的解决方案
无线433远距离遥控时有杂波的解决方案 一般情况下,我们用逻辑分析仪检测到的无线433模块的波形是如图一一样稳定而正常的.但是当遥控器发送功率不够(电池电量低时,认证机构不允许太大发射功率的遥控器走向 ...
- ESP8266+红外模块制作万能网路遥控器
ESP8266+红外模块制作万能网路遥控器 通过红外模块模拟遥控发射指令 接入blinker平台,通过手机可以远程控制. 前提是先读取出你要操控设备遥控的发射码出来.参考<Arduino 红外接 ...
- 深度学习过拟合解决方案
本文转自:https://blog.csdn.net/zhang2010hao/article/details/89339327 1.29.深度学习过拟合解决方案 1.29.1.解决方案 对于深度学习 ...
- (私人收藏)商务工作学习万能简约大气PPT模板
商务工作学习万能简约大气PPT模板 https://pan.baidu.com/s/1aPnPZ285N5VSSErro1cPng ehoa
- 在 2017 年,学习“万能” 语言
在 2017 年,学习"万能" 语言 来源:可译网 译者:ericzc 在我还是个新人的时候,我碰见了个同学,他宣称自己可以用任意我能说出名字的编程语言编程.吃惊之余,我满怀不信的 ...
- 万能遥控程序c语言,51单片机万能红外遥控解码程序
51hei单片机论坛里流传的遥控解码程序现在都弱爆了根本解不了现在的遥控自己写个万能红外遥控解码 本程序中需要用的头文件下载:http://www.51hei.com/mcu/2564.html // ...
最新文章
- 用树莓派的方式打开小米手机:摇晃手机控制小车,前进后退加转弯,成本不到350元 | 开源...
- Redis进阶-分布式存储 Sequential partitioning Hash partitioning
- 面试了57位数据分析师,我发现牛逼的人都有这4个特质
- sendmessage和postmessage的区别
- python 实现装饰器设计模式
- 山东师范大学志愿推荐系统邀请码_快看点邀请码填写HGC1QK快看点邀请码填写HGC1QK快看点邀请码大家千万不要乱填写哦...
- python3前面加b_Python3 字符串前面加u,r,b的含义
- (转)如何检查系统是否支持Zend Optimizer
- python的自省基础
- 设置代理,多进程爬虫
- 软件教程给MyEclipse 10增加SVN功能
- bbsmax mysql_mysql 常用,使用经验
- 谈debug版本可以正常运行,而在release下运行出错之原因及避免类似情况发生
- 如何理解原码一位乘法的计算过程
- 深度学习笔记----拓扑结构动态变化网络(Dropout,Drop Connect, Stochastic Depth, BranchyNet,Blockdrop,SkipNet)
- mvp的全称_MVP是什么意思?全称是什么?
- 电商私域流量搭建规划社群运营推广sop销售转化计划书表格模板方案
- android之学习的网站
- nmap常用命令/使用教程
- 响铃:“消费茧房”才是拼多多最像今日头条的地方
热门文章
- 数据库基本操作和常用命令
- 考勤打卡记录数据库表结构_中控zktime5.0考勤管理系统数据库表结构.doc
- RFC2544背靠背测试——信而泰Renix测试软件实操
- android jni stl,Android NDK中C++ STL库动态和静态链接
- 开源:通用的日志分析工具(LogViewer)
- Scratch(三十五):FlyBird
- 增强型绿植植被指数_辽宁省增强型植被指数EVI
- Java 菜鸟入门 | 常用进制转换
- android 打好的补丁位置,Android 6.0上打完第一个补丁后再打新补丁Crash
- MTK如何烧录IMEI码(俗称串号)