STM32单片机编程入门实战:HX1838红外遥控模块源码详解

文章目录

  • 一.概要
  • 二.HX1838红外遥控套件主要参数
  • 三.NEC协议的红外遥控编码
  • 四.HX1838红外遥控键值
  • 五.红外解码通讯流程图
  • 六.STM32单片机红外遥控接收解码实验
  • 七.CubeMX工程源代码下载
  • 八.小结
  • 一.概要

    这是一款新型的超薄38K通用红外遥控器,采用的是NEC编码格式,主要用于车
    载 MP3、灯光设配、数码相框、单片机开发板和学习板等场合。因其是基于无
    线遥控,因此人们在使用起来显得方便、有效。

    二.HX1838红外遥控套件主要参数

    红外遥控距离:大于8米
    发射管红外波长:940nm
    晶振频率:455KHZ的晶振
    载波频率:38KHZ
    编码:编码格式为NEC
    尺寸:86* 40* 6mm
    电源:CR2025/1600mAH

    模块接口说明:

    1.S 信号脚
    2.+ 电源正极:电压3.3v
    3.- 电源负极:接地

    三.NEC协议的红外遥控编码

    NEC 载波频率为 38Khz,NEC协议使用脉冲位置调制(PPM)技术。每个比特位的持续时间(高电平与低电平的比例)用于区分逻辑“0”和逻辑“1”。例如,一个比特“1”可能由较短的高电平和较长的低电平组成,而比特“0”则相反。
    数据帧格式

    同步引导码:9ms 低电平 + 4.5ms 高电平。
    逻辑0码 :560us低电平 + 560us高电平。
    逻辑1码 :560us低电平 + 1.69 ms 高电平。
    结束码 :560us 低电平。
    数据帧格式:引导码 + 地址 + 地址反码 + 键值 + 键值反码 + 结束码。
    重复连发码格式:9ms 低电平 + 2.25ms高电平 + 结束码。

    同步引导码通常由9ms的低电平和接下来的4.5ms的高电平组成,用于标识数据的开始。
    结束码不用于数据解析,但有助于分隔不同的数据包。
    重复连发码是一直按住遥控器上的一个键,命令帧也只会发送一次。 只要按键保持按下状态,就会每110毫秒发送一次重复连发码。
    高位在前,即首先收到的是高位的数据。

    四.HX1838红外遥控键值

    遥控键值是指遥控器上各个按键在电子设备中对应的特定代码值,‌这些值用于识别按键操作并执行相应的功能。‌不同的按键在电子设备中具有不同的键值,‌以便正确地控制设备的操作。‌以下是一些常见的遥控键值及其功能说明。

    五.红外解码通讯流程图

    六.STM32单片机红外遥控接收解码实验

    硬件准备:

    STLINK接STM32F103C8T6小系统板,STLINK接电脑USB口。

    用杜邦线连接板子与OLED显示屏:
    板子3.3V–模块VCC
    板子GND—模块GND
    板子PB10–模块SCL
    板子PB11–模块SDA
    用杜邦线把红外模块与开发板相连:
    板子3.3—-模块+
    板子A8—–模块S
    板子G——模块-

    打开STM32CubeMX软件,新建工程

    Part Number处输入STM32F103C8,再双击就创建新的工程

    配置下载口引脚

    配置外部晶振引脚

    配置系统主频

    定时器1通道1捕捉打开,PA8脚是输入捕捉脚

    打开定时器1中断

    配置工程文件名,保存路径,KEIL5工程输出方式

    生成工程

    用Keil5打开工程

    添加OLED驱动文件

    添加传感器相关代码

    主要代码

    void HX1838_demo(void)
    {
        hx1838_cap_start();//定时器1通道1,输入捕获启动
        while(1)
        {
            if(cap_frame)//标记捕获到新的数据
            {   
                hx1838_proc(hx1838_data_decode());//解析数据
                cap_frame = 0;
    						
    		
            }
        }
        
    }
    //根据键码值显示
    void hx1838_proc(uint8_t res)
    {
        if(res == 0)
        {    
            return;
        }
        
        if(res == 2)
        {   
            return;
        }      
        switch(rx.data._rev.key_val)
        {
            case 162:
    				   OLED_ShowNum(48,6,1,3,16);//显示1
                break;
            
            case 98:
               OLED_ShowNum(48,6,2,3,16);//显示2
                break;
            
            case 226:
                OLED_ShowNum(48,6,3,3,16);//显示3
                break;
            
            case 34:
                OLED_ShowNum(48,6,4,3,16);//显示4
                break;
            
            case 2:
                OLED_ShowNum(48,6,5,3,16);//显示5
                break;
            
            case 194:
                OLED_ShowNum(48,6,6,3,16);//显示6
                break;
            
            case 224:
               OLED_ShowNum(48,6,7,3,16);//显示7
                break;
            
            case 168:
                OLED_ShowNum(48,6,8,3,16);//显示8
                break;
            
            case 144:
                OLED_ShowNum(48,6,9,3,16);//显示9
                break;
            
            case 152:
                OLED_ShowNum(48,6,0,3,16);//显示0
                break;
            
            case 104:
    			 OLED_ShowChar(48,6,'*');//显示ASCII字符	  
                break;
            
            case 176:
    			OLED_ShowChar(48,6,'#');//显示ASCII字符	 
                break;
                    
            case 24:
            
    			OLED_ShowChar(48,6,'^');//显示ASCII字符	 
                break;
                    
            case 16:
    			OLED_ShowChar(48,6,'<');//显示ASCII字符	
                break;
            
            case 74:
        
    			OLED_ShowChar(48,6,'v');//显示ASCII字符	
                break;
            
            case 90:
    			OLED_ShowChar(48,6,'>');//显示ASCII字符	
                break;
            
            case 56:
            	OLED_ShowString(48,6,"OK"); 
                break;
            
            default:
                
                break;
            
        }
    }
    
    /* 电平捕获中断回调 */
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
        static uint16_t tmp_cnt_l,tmp_cnt_h;
    	if(TIM1 == htim->Instance)
    	{
            switch(cap_pol)                                                                                         //根据极性标志位判断捕获是低电平还是高电平
            {   
                /* 捕获到下降沿 */
                case 0:
                    tmp_cnt_l = HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);                                    //记录当前时刻
                    TIM_RESET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_1);                                               //复位极性配置
                    TIM_SET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);                //改变极性
                    
                    cap_pol = 1;                                                                                    //极性标志位改为上升沿
                    
                    if(sta_idle)                                                                                    //如果当前为空闲状态,空闲捕获到的时序,为第一个下降沿
                    {
                        rx_rcv_init();
                        break;                                                                                      //返回
                    }
                    rx_frame[cap_pulse_cnt] = tim_udt_cnt * 10000 + tmp_cnt_l - tmp_cnt_h;                          //与上次捕获的计时作差,记录值
                    tim_udt_cnt = 0;                                                                                //溢出次数清0
                    RX_DBG("(%2d)%4d us:H\r\n",cap_pulse_cnt,rx_frame[cap_pulse_cnt]);                              //DBG:打印捕获到的电平及其时长
                    cap_pulse_cnt++;                                                                                //计数++
                    break;
                
                /* 捕获到上升沿 */
                case 1:
                    tmp_cnt_h = HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
                    TIM_RESET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_1);               
                    TIM_SET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING);
                    
                    cap_pol = 0;   
                    if(sta_idle)
                    {
                        rx_rcv_init();
                        break;
                    }
                    rx_frame[cap_pulse_cnt] = tim_udt_cnt * 10000 + tmp_cnt_h - tmp_cnt_l;
                    tim_udt_cnt = 0;
                    RX_DBG("(%2d)%4d us:L\r\n",cap_pulse_cnt,rx_frame[cap_pulse_cnt]);
                    cap_pulse_cnt++;
                    break;
                
                default:
                    break;
            }
        }
    }
    //数据解码
    uint8_t hx1838_data_decode(void)
    {
        memcpy(rx.src_data,rx_frame,RX_SEQ_NUM*4);
        memset(rx_frame,0x00,RX_SEQ_NUM*4);   
        RX_DBG("========= rx.src[] =================\r\n");
        for(uint8_t i = 0;i<=(RX_SEQ_NUM*2);i++)
        {
            RX_DBG("[%d]%d\r\n",i,rx.src_data[i]);
        }
        RX_DBG("========= rx.rec =================\r\n");
        if(appro(rx.src_data[0],9000) && appro(rx.src_data[1],4500)) //#1. 检测前导码,9ms,4.5ms
        {
            uint8_t tmp_idx = 0;
            rx.repet_cnt  = 0;                                       //按键重复个数清0
            for(uint8_t i = 2;i<(RX_SEQ_NUM*2);i++)                 //#2. 检测数据
            {
                if(!appro(rx.src_data[i],560))
                {
                    RX_DBG("%d,err:%d != 560\r\n",i,rx.src_data[i]);
                    return 0;
                }
                i++;
                if(appro(rx.src_data[i],1680))
                {
                    rx.data.rev |= (0x80000000 >> tmp_idx);                          //第 tmp_idx 为置1
                    tmp_idx++;
                }
                else if(appro(rx.src_data[i],560))
                {
                    rx.data.rev &= ~(0x80000000 >> tmp_idx);                         //第 tmp_idx 位清0
                    tmp_idx++;
                }
                else
                {
                    RX_DBG("%d,err:%d != 560||1680\r\n",i,rx.src_data[i+1]);
                    return 0;
                }
            }
        }
        else if(appro(rx.src_data[0],9000) && appro(rx.src_data[1],2250) && appro(rx.src_data[2],560))
        {
            rx.repet_cnt++;
            return 2;
        }
        else
        {
            RX_DBG("前导码检测错误\r\n");
            return 0;
        }
        return 1;
    }
    

    七.CubeMX工程源代码下载

    链接:https://pan.baidu.com/s/1gjSuBfk8WQlEEQnDuYIOQw
    提取码:dv24
    如果链接失效,可以联系博主给最新链接
    程序下载下来之后解压就行

    八.小结

    HX1838红外接收模块采用HX1838红外接收管,灵敏度高,数字量输出,带有红色指示灯。新型的超薄 38K 通用红外遥控器,采用的是 NEC 编码格式,适合单片机开发板和学习板等场合。

    作者:光子物联单片机

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32单片机编程入门实战:HX1838红外遥控模块源码详解

    发表回复