一、序言

很早前就想实现这个红外遥控自学习的这个实验,用于来自己控制房子里如空调等红外遥控设备的自动化,NEC的标准到具体的产品上可能就被厂家定义为不一样了,所以自学习就应该是接收到什么就发送什么,不用管内容是什么!

二、硬件实现原理

由上述原理图可知,当IE为高电平时发送红外光,为低电平时不发送红外光。

在NEC协议中,信息传输是基于38K载波,也就是说红外线是以载波的方式传递。

发送波形如下图所示:

NEC协议规定:

发送协议数据“0” = 发送载波560us + 不发送载波560us

发送协议数据“1” = 发送载波560us+ 不发送载波1680us

发送引导码 = 发送载波9000us + 不发送载波4500us

在红外接收端,如果接收到红外38K载波,则IR输出为低电平,如果不是载波包括固定低电平和固定高电平则输出高电平。在IR端接收的信号如下所示:

                  

三、软件实现自学习

设计原理:

1、 根据接收波形记录电平和电平持续时间,以便于发送。

2、电平记录采用定时器捕获功能,从下降沿接收引导信号开始,每触发一次改变触发方式,从而使每个电平变化都能捕获到。

源码实现如下:

定时器捕获初始化设置(CubeMax自动配置生成):

void MX_TIM4_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {};
TIM_MasterConfigTypeDef sMasterConfig = {};
TIM_IC_InitTypeDef sConfigIC = {}; htim4.Instance = TIM4;
htim4.Init.Prescaler = ;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = ;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = ;
if (HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
} }

定时器捕获中断回调处理:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_4)
{
if(TIM4->CCER & (TIM_CCER_CC4P)) //下降沿触发
{
TIM4->CCER &= ~(TIM_CCER_CC4P); //切换
gu8BitVal = ;
}
else //上升沿触发
{
TIM4->CCER |= TIM_CCER_CC4P; //切换
gu8BitVal = ;
} if(gsInfrared.State == NONE_STATE)
{
gsInfrared.State = RECV_STATE;
}
else if(gsInfrared.State == RECV_STATE)
{
NowTimCnt = HAL_TIM_ReadCapturedValue(&htim4, TIM_CHANNEL_4);
gsInfrared.KeepTime[gsInfrared.SampleCount] = Round(NowTimCnt);
gsInfrared.BitValue[gsInfrared.SampleCount ++] = gu8BitVal;
} TIM4->CNT = ;
}
}

3、设置的定时器溢出时间为10ms,如果10毫秒内不再接收电平变化则默认接收结束,设置结束标志。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim4)
{
if(gsInfrared.State == RECV_STATE)
{
gsInfrared.State = END_STATE;
}
}
}

至此,实现了红外遥控的学习功能,获得的记录数据为记录长度和电平信号数组与电平信号维持的时间数组。

4、发送实现

设置定时器输出38KPWM信号,在记录电平为0是输出记录时间的38K载波信号,如果为1则不输出载波,实现如下:

PWM生成设置(CubeMax自动配置生成):

void MX_TIM5_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = {};
TIM_OC_InitTypeDef sConfigOC = {}; htim5.Instance = TIM5;
htim5.Init.Prescaler = ;
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
htim5.Init.Period = ;
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&htim5) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = ;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim5, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim5); }

发送实现,注意点就是记录为0时发载波,记录为1时不发载波:

void InfraredSend(void)
{
uint16_t Count = ; while(Count < gsInfrared.SampleCount && gsInfrared.State == END_STATE)
{
if(gsInfrared.BitValue[Count] == )
{
TIM5->CCR2 = ;
delay_us(gsInfrared.KeepTime[Count]);
TIM5->CCR2 = ;
}
else
{
TIM5->CCR2 = ;
delay_us(gsInfrared.KeepTime[Count]);
TIM5->CCR2 = ;
} Count ++;
} delay_us();
}

往PWM比较寄存器设置948即为设置38KPWM波,也可在初始化时固定948,在此函数内启停定时器即可;

至此,自学习功能的全部思路已实现,通过对各个不同类型的红外遥控进行功能测试,均成功。

PS:查看很多资料发现很多红外解码未判断低电平时间,个人感觉不是很好,应该是不仅高电平时间得符合,低电平时间也应该符合。

自己写了一个小函数验证了一下,这个函数只是验证,未经仔细推敲,还可优化,仅供参考这一思想。

误差设计:±200us(拍脑袋值)

void InFraredDataDeal(void)
{
uint32_t DataBuff = ;
uint16_t Count = ; if(gsInfrared.State == END_STATE)
{
gsInfraredData.State = ; do
{
switch(gsInfraredData.State)
{
case : //引导码识别
{ if(gsInfrared.KeepTime[] >= && gsInfrared.KeepTime[] <= && gsInfrared.BitValue[] == )
{
if(gsInfrared.KeepTime[] >= && gsInfrared.KeepTime[] <= && gsInfrared.BitValue[] == )
{
if(gsInfrared.KeepTime[] >= && gsInfrared.KeepTime[] <= && gsInfrared.BitValue[] == )
{
Count = ;
gsInfraredData.State = ;
}
}
else if(gsInfrared.KeepTime[] >= && gsInfrared.KeepTime[] <= && gsInfrared.BitValue[] == )
{
if(gsInfrared.KeepTime[] >= && gsInfrared.KeepTime[] <= && gsInfrared.BitValue[] == )
{
gsInfraredData.ReDataCount ++;
gsInfraredData.State = ;
}
}
else
{
gsInfraredData.State = ;
} }
else
{
gsInfraredData.State = ;
} }
break; case : //数据解析
{ if(gsInfrared.KeepTime[Count + ] >= && gsInfrared.KeepTime[Count + ] <= && gsInfrared.BitValue[Count + ] == )
{ if(gsInfrared.BitValue[Count] == )
{
if(gsInfrared.KeepTime[Count] >= && gsInfrared.KeepTime[Count] <= )
{
DataBuff <<= ;
DataBuff |= ;
}
else if(gsInfrared.KeepTime[Count] >= && gsInfrared.KeepTime[Count] <= && gsInfrared.BitValue[Count] == )
{
DataBuff <<= ;
DataBuff |= ;
}
else
{
gsInfraredData.State = ;
}
}
} if(Count < gsInfrared.SampleCount)
{
Count += ;
}
else
{
gsInfraredData.State = ;
} }
break; case : //成功解析
{
gsInfraredData.Data = DataBuff;
gsInfraredData.State = ; }
break; default:
{
gsInfraredData.State = ; //解析结束 }
break;
} }
while(gsInfraredData.State != ); gsInfrared.State = NONE_STATE;
gsInfrared.SampleCount = ;
} }

解析的话一般高位在前,所以左移,经测试帧格式为:引导码+用户码+用户码反码+命令码+命令码反码,能成功解析数据!解析的话根据具体协议,具体分析。

最新文章

  1. JS实现文字截取(雾)
  2. System.InvalidOperationException : 不应有 &lt;Response xmlns=&#39;&#39;&gt;。
  3. windows下jenkins常见问题填坑
  4. c/c++ 输入输出缓冲区
  5. Apache配置多个监听端口
  6. MVC中修改报错
  7. shell获取日期(昨天,明天,上月,下月)
  8. C语言中命名空间的实现
  9. 【转】Mysql 根据时间戳按年月日分组统计
  10. 7.广播和多播,IGMP协议
  11. 【视频编解码&#183;学习笔记】2. H.264简介
  12. python使用魔法函数创建可切片类型
  13. C++ 生成洛伦兹的蝴蝶
  14. idea properties文件中文无法正常显示
  15. java 将mysql中Blob类型转为字符串或数字
  16. BFS广度优先搜索 炸弹人
  17. 【转】 UI自动化测试的关注点
  18. 一道Javascript面试题引发的血案
  19. 安卓脱壳&amp;&amp;协议分析&amp;&amp;burp辅助分析插件编写
  20. python selenium2 有关cookie操作实例及如何绕开验证码

热门文章

  1. sift特征点检测和特征数据库的建立
  2. matplotlib中中文字体配置
  3. TimeUtils
  4. ORACLE内存管理之ASMM AMM
  5. MySQL闪回工具之myflash 和 binlog2sql
  6. ORA-20011
  7. 洛谷P4124 手机号码
  8. duration of lease 1 0.5 0.875 DHCP 租借时间 续租时间 重新绑定时间
  9. java操作solr
  10. Delphi XE2 之 FireMonkey 入门(28) - 数据绑定: TBindingsList: 表达式函数测试: SelectedText()、CheckedState()