推广 热搜: 高德  agm  应用  红外热像仪  红外  成像  夜视仪  监控  高德红外  健康 

STM32定时器实现红外接收与解码

   日期:2023-02-23 00:07:16     来源:网络整理    作者:本站编辑    浏览:68    评论:0    
一、NEC协议

红外遥控是一种比较常用的通讯方式,目前红外遥控的编码方式中,应用比较广泛的是NEC协议。

NEC协议的特点如下:

• 载波频率为38KHz;
• 8位地址和8位指令长度;
• 地址和命令2次传输(确保可靠性);
• PWM脉冲位置调制,以发射红外载波的占空比代表“0”和“1”。

其逻辑1与逻辑0的表示,如下图所示:

可以看到,逻辑1的位时间为2.25ms,脉冲时间560us;逻辑0的位时间为1.12ms,脉冲时间560us。

一个完整的NEC数据包如下:

首次发送的是9ms高电平+4.5ms低电平,为引导码。

接下来是8bit的地址码+8bit地址码的反码+8bit命令码+8bit命令码的反码。

以上是一个正常的数据包,但可能存在一种情况:当长按某个键时,发送的是以110ms为周期的重复码,如下图所示:

重复码由9ms高电平和2.25ms的低电平以及560us的高电平组成。

二、解码程序        

在上面的图中可以看到,逻辑1和逻辑0的位时间是不同的,占空比也不同。所以我们可以根据位时间的长短来解码,也可以根据占空比的不同(1/2或1/4)来解码,或者二者同时作为解码条件,这里我们介绍根据位时间来解码。

需要注意的是,很多红外一体接收头为了提高接受灵敏度。输入高电平,其输出的是相反的低电平。

下图为示波器实际捕获的一组数据:

可以看到,空闲时为高电平,引导码为9ms低电平+4.5ms高电平。根据位时间解码的话,我们就不必关系高低电平各自的时间,只需关系总时间就行,即:引导码为13.5ms,逻辑1为2.25ms,逻辑0为1.12ms。
首先,用STM32CubeMx配置定时器。系统时钟等的配置这里不在赘述,参考其它教程。
这里使用TIM3的Channel1作为捕获通道,配置如下:

•  定时器时钟为内部时钟;
•  Channel1配置为输入捕获模式;
•  分频系数为63,因为系统时钟为64M,这样定时器实际时钟为64/(63+1)=1M,主要是为了程序中方便计算;
•  捕获方式为下降沿捕获;
•  最后别忘了打开定时器的中断。

最后一步,生成代码。在生成的TIM3中断函数中,屏蔽生成的中断处理还是,添加自己的解码程序如下:
uint32_t TIM3_Over_Cnt = 0;//tim3溢出次数uint32_t TIM3_Sum_Cnt = 0;//两次下降沿之间的时间间隔uint32_t cnt0 = 0;uint8_t IR_Data[60];
void TIM3_IRQHandler(void){ /* USER CODE BEGIN TIM3_IRQn 0 */
/* USER CODE END TIM3_IRQn 0 */// HAL_TIM_IRQHandler(&htim3); /* USER CODE BEGIN TIM3_IRQn 1 */ if(__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_UPDATE)) //定时器溢出中断 { __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE); //清除中断标记 TIM3_Over_Cnt++; } cnt0 = __HAL_TIM_GET_COUNTER(&htim3); TIM3_Sum_Cnt = (TIM3_Over_Cnt << 16) + cnt0;//获取计数器的值 __HAL_TIM_SetCounter(&htim3,0);//清零重新计数 TIM3_Over_Cnt = 0;//清零重新计数
if (__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_CC1) != RESET)//TIM3CH1捕获中断 { if(StartRevFlag == 1)//接收到引导码,开始解码 { if(TIM3_Sum_Cnt > 36000)//大于36ms认为是结束 { RevComplete = 1;//解码完成 IR_Tick = 0; } else if(RevComplete == 0) { if(TIM3_Sum_Cnt > 1000 && TIM3_Sum_Cnt < 1300)//1ms~1.3ms认为是低电平 IR_Data[IR_Idx] = 0; else if(TIM3_Sum_Cnt > 2100 && TIM3_Sum_Cnt < 2400)//2.1ms~2.4ms认为是高电平 IR_Data[IR_Idx] = 1; else //接收错误,重新开始 StartRevFlag = 0; IR_Idx++; if(IR_Idx > 59) IR_Idx = 59; }
} else { if(TIM3_Sum_Cnt > 13000 && TIM3_Sum_Cnt < 14000)//13~14ms引导码 { StartRevFlag = 1; } IR_Tick = 0; RevComplete = 0;//解码完成标志置零 IR_Idx = 0;//有效解码位 TIM3_Over_Cnt = 0; TIM3_Sum_Cnt = 0;//定时器计数清零 } __HAL_TIM_CLEAR_IT(&htim3, TIM_IT_CC1); }
/* USER CODE END TIM3_IRQn 1 */}

解码程序根据每次捕获下降沿之间的间隔判断是引导码还是逻辑1或逻辑0,接收到引导码之后,再开始将解码的数据保存下来,最后通过也是时长来判断解码结束。这里没有判断重复码,有兴趣的小伙伴可以自己加上。

中断函数中,只是将每一位解码并保存,最后还需要在主程序中组合成字节并判断处理。
void IR_Rev(){  uint8_t num = IR_Idx / 8;  uint8_t IRValue[8];
if(RevComplete == 1 && StartRevFlag == 1 && IR_Tick > 20) { if(num > 7) num = 7;
for(uint8_t j=0;j<num;j++)//将每一位解码数据组合成字节数据 { for(uint8_t i = 0;i< 8;i++) { IRValue[j] = IRValue[j]>>1; if(IR_Data[j*8+i]) IRValue[j] |= 0x80; } } if(IRValue[0] == 0x00 && IRValue[1] == 0xFF)//地址码正确 {      switch(IRValue[2])//判断数据码 { case 0x46: KeyValue = S_key_Menu; break; case 0x43: KeyValue = S_key_Set; break; case 0x40: KeyValue = S_key_Rst; break; case 0x15: KeyValue = S_key_Down; break; case 0x09: KeyValue = S_key_Up;          break; } } StartRevFlag = 0; RevComplete = 0; IR_Tick = 0; }}

作者:Mr张工

来源:嵌入式技术开发


温馨提示:

因最近微信公众平台推送规则改变,很多读者反馈没有及时看到更新的文章。根据最新规则,建议多点击“推荐阅读、分享、收藏”等,成为常读用户。

推荐阅读:


请点下【在看】给小编加鸡腿




 
打赏
 
更多>同类资讯
0相关评论

推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报  |  皖ICP备20008326号-23
Powered By DESTOON