红外遥控是一种比较常用的通讯方式,目前红外遥控的编码方式中,应用比较广泛的是NEC协议。
NEC协议的特点如下:
其逻辑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)来解码,或者二者同时作为解码条件,这里我们介绍根据位时间来解码。
需要注意的是,很多红外一体接收头为了提高接受灵敏度。输入高电平,其输出的是相反的低电平。
下图为示波器实际捕获的一组数据:
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
//将每一位解码数据组合成字节数据 {
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;
}
}
END
来源:嵌入式技术开发
→点关注,不迷路←