stm32与红外遥控器(NEC协议)

1.器件简介

本次测试采用R903V1红外接收头与NEC协议的红外遥控器,接收头原理图如下:

器件的供电电压VCC在2.7V~5.5V之间,输出电压VOUT正常在0.2v ~(VCC-0.3±0.2)v,注意高低电平输出脉冲宽度最小都在400us~800us之间。

NEC 码的位定义:一个脉冲对应 560us 的连续载波,一个逻辑 1 传输需要 2.25ms(560us脉冲+1680us 低电平),一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平)。而遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到的信号为:逻辑 1 应该是 560us 低+1680us 高,逻辑 0 应该是 560us 低+560us 高。同时NEC码还规定了连发码由 9ms 低电平+2.5m 高电平+0.56ms 低电平+97.94ms 高电平组成。

NEC协议的数据格式:同步码头、地址码、地址反码、控制码、控制反码。同步码由一个 9ms 的低电平和一个 4.5ms 的高电平组成,地址码、地址反码、控制码、控制反码均是8 位数据格式。正常是按照低位在前,高位在后的顺序发送,但是我测试是按照高位在前,低位在后的。采用反码是为了增加传输的可靠性(可用于校验)。

 

2.硬件连接

简单说一下,红外接收器电压接3.3V,VOUT连接单片机的输入捕获引脚即可。

 

3.软件设计

使用stm32单片机的输入捕获功能,用stm32cubemx进行设置:

 

这里解释主要参数部分:

prescaler:预分频器,设置为72

counter mode:计数模式,设为向上计数

counter period:计数周期,设为65535

polarity selection:边沿检测方式,选择下降沿检测,程序里还是改成了上升沿检测

input filter:输入滤波器,设为8,是指连续采集到8个一样的高/低电平才计作高/低电平

在MAIN中开启输入捕获:

  while (1)
  {
        switch(cap_state)
        {
            case 0:
                __HAL_TIM_SET_CAPTUREPOLARITY(&htim5, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING);//设置为上升沿捕获
                HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_2);    //启动输入捕获
                cap_state++;
                break;
        }
        
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
main循环体
/定时器输入捕获中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行
{
    if(htim->Instance==TIM5)
    {        
        switch(cap_state)
        {
            case 1:
                rise_value[rise_i] = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_2);//取上升沿时刻
                __HAL_TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2,TIM_ICPOLARITY_FALLING);  //设置为下降沿捕获
                cap_state++;
                break;
            case 2:
                fall_value[fall_i] = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_2);//取下降沿时刻
                if(fall_value[fall_i] > rise_value[rise_i])    
                {
                    rising_time[t_i] = fall_value[fall_i] - rise_value[rise_i];//高电平时间获取
                }else
                {
                    rising_time[t_i] = 65535 + fall_value[fall_i] - rise_value[rise_i];//高电平时间获取
                }
                //控制码
                if(same_i == 1)
                {
                    cnt+=1;
                    //地址码1~8
                    if(t_i<8)
                    {
                        if(rising_time[t_i]>500&&rising_time[t_i]<600)
                        {
                            address <<= 1;
                            address+=0;
                        }else if(rising_time[t_i]>1500&&rising_time[t_i]<1800)
                        {
                            address <<= 1;
                            address+=1;
                        }
                    }else if(t_i>16&&t_i<25)//控制码17~24
                    {
                        if(rising_time[t_i]>500&&rising_time[t_i]<600)
                        {
                            rec <<= 1;
                            rec+=0;
                        }else if(rising_time[t_i]>1500&&rising_time[t_i]<1800)
                        {
                            rec <<= 1;
                            rec+=1;
                        }
                    }
                        t_i++;
                        rise_i++;
                        fall_i++;
                }
                //同步码
                if(rising_time[t_i]>4300&&rising_time[t_i]<4700)
                {
                    //同步码正确
                    same_i=1;
                    t_i++;
                    rise_i++;
                    fall_i++;
                }
                HAL_TIM_IC_Stop_IT(&htim5,TIM_CHANNEL_2); //停止捕获
                cap_state=0;
                break;
        }
    }
}
输入捕获中断回调
//按键中断回调
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    //KEY0
    if (GPIO_Pin==GPIO_PIN_5)
    {
        HAL_Delay(20);/* 延时一小段时间,消除抖动 */
        if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)==0)
        {
            HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
            
            for(int i=0;i<=fall_i;i++)
            {
                if(fall_value[i] > rise_value[i])    
                {
                    rising_time[i] = fall_value[i] - rise_value[i];//高电平时间获取
                }else
                {
                    rising_time[i] = 65535 + fall_value[i] - rise_value[i];//高电平时间获取
                    printf("hear:%d\r\n",i);
                }
                printf("高电平[%d]:%d us 下降沿时刻[%d]:%d us 上升沿时刻[%d]:%d us\r\n",i,rising_time[i],i,fall_value[i],i,rise_value[i]);
            }
            
            printf("地址:%d  控制码:%lld\r\n",address,rec);
            rise_i=fall_i=same_i=t_i=cnt=address=rec=0;
        }
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_5);
    }
}
按键中断回调

大致说一下程序流程,在main中循环开启上升沿检测输入捕获功能(case 0),接着在输入捕获中断回调里记下上升沿触发时刻(rise_value),并改为下降沿检测捕获,再记下下降沿触发时刻(fall_value)并关闭捕获功能。

高电平持续时间(rising_time) = 下降沿触发时刻(fall_value) - 上升沿触发时刻(rise_value)

获取到一系列高电平以后(看示波器图),根据高电平持续时间进行数值移位换算,可以得到对应的地址码和控制码,串口打印如下:

 打印结果中的hear是下降沿时刻溢出的情况,计算时需要在下降沿时刻基础上再加65535才可以。

感兴趣的可以试一试,需要原工程的留言我发。

posted @ 2020-10-23 10:51  墨迹打铁匠  阅读(1704)  评论(1编辑  收藏  举报
 >>>转载请注明出处<<<