• 正文
  • 相关推荐
申请入驻 产业图谱

【CW32模块使用】红外接收模块

01/28 08:25
1354
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

模块来源

模块实物展示:

资料链接:https://pan.baidu.com/s/1dEWVMIFDWb7k1NcsRy5hHA

资料提取码:uucv

规格参数

1.CR2025环保纽扣电池,容量160mah

2.发射距离:8m以上(具体和周围环境、接收端的灵敏度等因素有关)

3.有效角度:60度

4.面贴材料:0.125mmPET,有效寿命2万次。

5.品质稳定,性价比高

6.静态电流3-5uA,动态电流3-5mA。

以上信息见厂家资料文件

移植过程

我们的目标是将例程移植至CW32F030C8T6开发板上【能够实现红外信号接收的功能】。首先要获取资料,查看数据手册应如何实现读取数据,再移植至我们的工程。

3.1查看资料

在光谱中波长自760nm至400um的电磁波称为红外线,它是一种不可见光。红外线通信的例子我们每个人应该都很熟悉,目前常用的家电设备几乎都可以通过红外遥控的方式进行遥控,比如电视机、空调、投影仪等,都可以见到红外遥控的影子。这种技术应用广泛,相应的应用器件都十分廉价,因此红外遥控是我们日常设备控制的理想方式。

红外线的通讯原理

红外光是以特定的频率脉冲形式发射,接收端收到到信号后,按照约定的协议进行解码,完成数据传输。在消费类电子产品里,脉冲频率普遍采用 30KHz 到 60KHz 这个频段,NEC协议的频率就是38KHZ。这个以特定的频率发射其实就可以理解为点灯,不要被复杂的词汇难住了,就是控制灯的闪烁频率(亮灭),和刚学单片机完成闪烁灯一样的意思,只不过是灯换了一种类型,都是灯。

接收端的原理: 接收端的芯片对这个红外光比较敏感,可以根据有没有光输出高低电平,如果发送端的闪烁频率是有规律的,接收端收到后输出的高电平和低电平也是有规律对应的,这样发送端和接收端只要约定好,那就可以做数据传输了。

红外线传输协议可以说是所有无线传输协议里成本最低,最方便的传输协议了,但是也有缺点,距离不够长,速度不够快;当然,每个传输协议应用的环境不一样,定位不一样,好坏没法比较,具体要看自己的实际场景选择合适的通信方式。

NEC协议介绍

NEC协议是众多红外线协议中的一种(这里说的协议就是他们数据帧格式定义不一样,数据传输原理都是一样的),我们购买的外能遥控器、淘宝买的mini遥控器、电视机、投影仪几乎都是NEC协议。像格力空调、美的空调这些设备使用的就是其他协议格式,不是NEC协议,但是只要学会一种协议解析方式,明白了红外线传输原理,其他遥控器协议都可以解出来。

NEC协议一次完整的传输包含: 引导码、8位地址码、8位地址反码、8位命令码、8位命令反码。这里我们主要讲解如何接收红外发送端发送的NEC协议内容。

引导码:由9ms的低电平+4.5ms的高电平组成。

4个字节的数据: 地址码+地址反码+命令码+命令反码。这里的反码可以用来校验数据是否传输正确,有没有丢包。

重点: NEC协议传输数据位的时候,0和1的区分是依靠收到的高、低电平的持续时间来进行区分的。这是解码关键。

数据发送0码:0.56ms低电平+ 0.56ms的高电平。

数据发送1码:0.56ms低电平+1.68ms的高电平

所以,收到一个数据位的完整时间表示方法是这样的:

    收到数据位0:   0.56ms低电平+ 0.56ms的高电平

    收到数据位1:  0.56ms低电平+1.68ms的高电平

还有一个重复码,它是由一个 9ms 的低电平和一个 2.5ms 的高电平组成。当一个红外信号连续发送时,可以通过发送重复码的方式快速发送。

3.2引脚选择

当红外线接收头感应到有红外光就输出低电平,没有感应到红外光就输出高电平。因此我们配置红外引脚为外部中断下降沿触发方式,当红外引脚有下降沿时,我们马上进入中断处理并接收红外信号。

模块接线图

3.3移植至工程

引脚配置如下:

//红外引脚初始化void infrared_goio_config(void){    IR_RCC_GPIO_ENABLE();      // 使能GPIO时钟
    GPIO_InitTypeDef GPIO_InitStruct;                   // GPIO初始化结构体
    GPIO_InitStruct.Pins  = IR_PIN;                     // GPIO引脚    GPIO_InitStruct.Mode  = GPIO_MODE_INPUT_PULLUP;     // 上拉输入    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;            // 速度高    GPIO_InitStruct.IT    = GPIO_IT_FALLING;            // 下降沿触发中断    GPIO_Init(IR_PORT, &GPIO_InitStruct);               // 初始化
    // 清除PA0中断标志    GPIOA_INTFLAG_CLR(EXTI_BV);    // 使能NVIC    NVIC_EnableIRQ(EXTI_IRQ);}

红外信号的数据,全部是以时间长度来确定数据是0还是1,而最小的单位要求有560us,已经达到了us级的测量。

我们在 空白工程中已经为大家准备好了us延时,就在board 文件中。

获取高低电平时间

获取低电平时间的实现代码如下:

//获取红外低电平时间//以微秒us作为时间参考void get_infrared_low_time( uint32_t *low_time ){    uint32_t time_val = 0;
    while( GPIO_ReadPin(IR_PORT, IR_PIN) == 0 )    {        if( time_val>= 500 )        {            *low_time = time_val;            return;        }        delay_us(20);        time_val++;    }    *low_time = time_val;}

当引脚为低电平时,将进入 while 循环,直到不为低电平时就结束循环。在循环之中不断的让时间变量time_val累加, 每加一次需要经过20us。当time_val变量累加时间大于 500 * 20 = 10000us = 10ms时,判断为超时,强行结束该函数,防止阻碍系统运行。

获取高电平时间的代码同理:

//获取红外高电平时间//以微秒us作为时间参考void get_infrared_high_time(uint32_t *high_time){    uint32_t time_val = 0;    while( GPIO_ReadPin(IR_PORT, IR_PIN) == 1 )    {        if( time_val >= 250 )        {            *high_time = time_val;            return;        }        delay_us(20);        time_val++;    }    *high_time = time_val;}

引导码与重复码判断

引导码是由一个 9ms 的低电平和一个 4.5ms 的高电平组成。每当接收到一个红外信号时,第一个数据就是引导码。我们通过判断红外信号的第一个数据是否是引导码,来决定是否要进行后面的数据接收处理。

重复码是由一个 9ms 的低电平和一个 2.5ms 的高电平组成。当我们的红外遥控一直按住按键时,就会发出重复码,我们可以检测重复码,来确定是否要连续触发重复动作,比如长按开机,长按加速等等。

/****************************************************************** * 函 数 名 称:guide_and_repeat_code_judgment * 函 数 说 明:引导 和 重复 码 判断 * 函 数 形 参:无 * 函 数 返 回:1:不是引导码   2:重复码  0:引导码 * 作       者:LC * 备       注:以20微秒us作为时间参考                引导码:由一个 9ms 的低电平和一个 4.5ms 的高电平组成                重复码:由一个 9ms 的低电平和一个 2.5ms 的高电平组成******************************************************************/uint8_t guide_and_repeat_code_judgment(void){    uint32_t out_time=0;    get_infrared_low_time(&out_time);    //time>10ms             time <8ms    if((out_time > 500) || (out_time < 400))    {        return 1;    }    get_infrared_high_time(&out_time);    // x>5ms  或者 x<2ms    if((out_time > 250) || (out_time < 100))    {        return 1;    }
    //如果是重复码  2ms < time < 3ms    if((out_time > 100) && (out_time < 150))    {        return 2;    }
    return 0;}

完整红外数据接收

具体接收流程:【判断是否接收到引导码】->【接收数据】->【判断数据是否正确】。

//接收红外数据void receiving_infrared_data(void){    uint16_t group_num = 0,data_num = 0;    uint32_t time=0;    uint8_t bit_data = 0;    uint8_t ir_value[5] = {0};
    uint8_t guide_and_repeat_code = 0;
    //等待引导码    guide_and_repeat_code = guide_and_repeat_code_judgment();    //如果不是引导码则结束解析    if(  guide_and_repeat_code == 1 ) return;
    //共有4组数据    //地址码+地址反码+命令码+命令反码    for(group_num = 0; group_num < 4; group_num++ )        {        //接收一组8位的数据        for( data_num = 0; data_num < 8; data_num++ )        {            //接收低电平            get_infrared_low_time(&time);            //如果不在0.56ms内的低电平,数据错误            if((time > 60) || (time < 20))            {                return ;            }
            time = 0;            //接收高电平            get_infrared_high_time(&time);            //如果是在1200us<t<2000us范围内则判断为1            if((time >=60) && (time < 100))            {                bit_data = 1;            }            //如果是在200us<t<1000us范围内则判断为0            else if((time >=10) && (time < 50))            {                bit_data = 0;            }
            //groupNum表示第几组数据            ir_value[ group_num ] <<= 1;
            //接收的第1个数为高电平;在第二个for循环中,数据会向右移8次            ir_value[ group_num ] |= bit_data;
            //用完时间要重新赋值            time=0;        }    }    //判断数据是否正确,正确则保存数据    infrared_data_true_judgment(ir_value);}

判断数据是否正确,可以通过将正常数据取反,与反码比较。如果不一致说明数据不对。

typedef struct INFRARED_DATA{
    uint8_t AddressCode;            //地址码    uint8_t AddressInverseCode;     //地址反码    uint8_t CommandCode;            //命令码    uint8_t CommandInverseCode;     //命令反码
}_INFRARED_DATA_STRUCT_;_INFRARED_DATA_STRUCT_ InfraredData;

//红外数据是否正确判断uint8_t infrared_data_true_judgment(uint8_t *value){    //判断地址码是否正确    if( value[0] != (uint8_t)(~value[1]) )  return 0;    //判断命令码是否正确    if( value[2] != (uint8_t)(~value[3]) )  return 1;
    //串口输出查看接收到的数据    printf("%x %x %x %xrn",value[0],value[1],value[2],value[3]);    //保存正确数据    InfraredData.AddressCode        = value[0];    InfraredData.AddressInverseCode = value[1];    InfraredData.CommandCode        = value[2];    InfraredData.CommandInverseCode = value[3];}
//获取红外发送过来的命令uint8_t get_infrared_command(void){    return InfraredData.CommandCode;}//清除红外发送过来的数据void clear_infrared_command(void){    InfraredData.CommandCode = 0x00;}

最后,记得在外部中断服务函数中,调用红外接收函数。

void EXTI_HANDLER(void){        if(IR_PORT->ISR_f.EXTI_PIN)   // 中断标志位        {                if(GPIO_ReadPin(IR_PORT, IR_PIN) == GPIO_Pin_RESET)  // 如果是低电平                {                        //接收一次红外数据                        receiving_infrared_data();                }                GPIOA_INTFLAG_CLR(EXTI_BV); // 清除标志位        }}

移植步骤中的导入.c和.h文件与【CW32模块使用】DHT11温湿度传感器相同,只是将.c和.h文件更改为bsp_ir_receiver.c与bsp_ir_receiver.h。这里不再过多讲述,移植完成后面修改相关代码。

以下为完成红外接收代码:

bsp_ir_receiver.c

/* * Change Logs: * Date           Author       Notes * 2024-06-24     LCKFB-LP    first version */
#include "bsp_ir_receiver.h"#include "stdio.h"#include "board.h"


typedef struct INFRARED_DATA{
    uint8_t AddressCode;            //地址码    uint8_t AddressInverseCode;     //地址反码    uint8_t CommandCode;            //命令码    uint8_t CommandInverseCode;     //命令反码
}_INFRARED_DATA_STRUCT_;

_INFRARED_DATA_STRUCT_ InfraredData;
//红外引脚初始化void infrared_goio_config(void){        IR_RCC_GPIO_ENABLE();      // 使能GPIO时钟

        GPIO_InitTypeDef GPIO_InitStruct;                // GPIO初始化结构体
        GPIO_InitStruct.Pins  = IR_PIN;                  // GPIO引脚        GPIO_InitStruct.Mode  = GPIO_MODE_INPUT_PULLUP;  // 上拉输入        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;         // 速度高        GPIO_InitStruct.IT    = GPIO_IT_FALLING;         // 下降沿触发中断        GPIO_Init(IR_PORT, &GPIO_InitStruct);            // 初始化
        // 清除PA0中断标志        GPIOA_INTFLAG_CLR(EXTI_BV);        // 使能NVIC        NVIC_EnableIRQ(EXTI_IRQ);}



//获取红外低电平时间//以微秒us作为时间参考void get_infrared_low_time( uint32_t *low_time ){    uint32_t time_val = 0;
    while( GPIO_ReadPin(IR_PORT, IR_PIN) == 0 )    {        if( time_val>= 500 )        {            *low_time = time_val;            return;        }        delay_us(20);        time_val++;    }    *low_time = time_val;}
//获取红外高电平时间//以微秒us作为时间参考void get_infrared_high_time(uint32_t *high_time){    uint32_t time_val = 0;    while( GPIO_ReadPin(IR_PORT, IR_PIN) == 1 )    {        if( time_val >= 250 )        {            *high_time = time_val;            return;        }        delay_us(20);        time_val++;    }    *high_time = time_val;}
/****************************************************************** * 函 数 名 称:guide_and_repeat_code_judgment * 函 数 说 明:引导 和 重复 码 判断 * 函 数 形 参:无 * 函 数 返 回:1:不是引导码   2:重复码  0:引导码 * 作       者:LC * 备       注:以20微秒us作为时间参考                引导码:由一个 9ms 的低电平和一个 4.5ms 的高电平组成                重复码:由一个 9ms 的低电平和一个 2.5ms 的高电平组成******************************************************************/uint8_t guide_and_repeat_code_judgment(void){    uint32_t out_time=0;    get_infrared_low_time(&out_time);    //time>10ms             time <8ms    if((out_time > 500) || (out_time < 400))    {        return 1;    }    get_infrared_high_time(&out_time);    // x>5ms  或者 x<2ms    if((out_time > 250) || (out_time < 100))    {        return 1;    }
    //如果是重复码  2ms < time < 3ms    if((out_time > 100) && (out_time < 150))    {        return 2;    }
    return 0;}
//红外数据是否正确判断uint8_t infrared_data_true_judgment(uint8_t *value){    //判断地址码是否正确    if( value[0] != (uint8_t)(~value[1]) )  return 0;    //判断命令码是否正确    if( value[2] != (uint8_t)(~value[3]) )  return 1;

    printf("%x %x %x %xrn",value[0],value[1],value[2],value[3]);    //保存正确数据    InfraredData.AddressCode        = value[0];    InfraredData.AddressInverseCode = value[1];    InfraredData.CommandCode        = value[2];    InfraredData.CommandInverseCode = value[3];}
//接收红外数据void receiving_infrared_data(void){    uint16_t group_num = 0,data_num = 0;    uint32_t time=0;    uint8_t bit_data = 0;    uint8_t ir_value[5] = {0};
    uint8_t guide_and_repeat_code = 0;
    //等待引导码    guide_and_repeat_code = guide_and_repeat_code_judgment();    //如果不是引导码则结束解析    if(  guide_and_repeat_code == 1 )    {        printf("errrn");        return;    }
    //共有4组数据    //地址码+地址反码+命令码+命令反码    for(group_num = 0; group_num < 4; group_num++ )        {        //接收一组8位的数据        for( data_num = 0; data_num < 8; data_num++ )        {            //接收低电平            get_infrared_low_time(&time);            //如果不在0.56ms内的低电平,数据错误            if((time > 60) || (time < 20))            {                return ;            }
            time = 0;            //接收高电平            get_infrared_high_time(&time);            //如果是在1200us<t<2000us范围内则判断为1            if((time >=60) && (time < 100))            {                bit_data = 1;            }            //如果是在200us<t<1000us范围内则判断为0            else if((time >=10) && (time < 50))            {                bit_data = 0;            }
            //groupNum表示第几组数据            ir_value[ group_num ] <<= 1;
            //接收的第1个数为高电平;在第二个for循环中,数据会向右移8次            ir_value[ group_num ] |= bit_data;
            //用完时间要重新赋值            time=0;        }    }    //判断数据是否正确,正确则保存数据    infrared_data_true_judgment(ir_value);}
//获取红外发送过来的命令uint8_t get_infrared_command(void){    return InfraredData.CommandCode;}//清除红外发送过来的数据void clear_infrared_command(void){    InfraredData.CommandCode = 0x00;}

void EXTI_HANDLER(void){        if(IR_PORT->ISR_f.EXTI_PIN)   // 中断标志位        {                if(GPIO_ReadPin(IR_PORT, IR_PIN) == GPIO_Pin_RESET)  // 如果是低电平                {                        //接收一次红外数据                        receiving_infrared_data();                }                GPIOA_INTFLAG_CLR(EXTI_BV); // 清除标志位        }}

bsp_ir_receiver.h

/* * Change Logs: * Date           Author       Notes * 2024-06-24     LCKFB-LP    first version */
#ifndef _BSP_IR_RECEIVER_H__#define _BSP_IR_RECEIVER_H__
#include "board.h"
#define IR_RCC_GPIO_ENABLE()     __RCC_GPIOA_CLK_ENABLE()#define IR_PORT                  CW_GPIOA#define IR_PIN                   GPIO_PIN_2
#define EXTI_PIN                 PIN2#define EXTI_BV                  bv2#define EXTI_IRQ                 GPIOA_IRQn#define EXTI_HANDLER             GPIOA_IRQHandler
void infrared_goio_config(void);uint8_t get_infrared_command(void);void clear_infrared_command(void);#endif

移植验证

在自己工程中的main主函数中,编写如下。

/* * Change Logs: * Date           Author       Notes * 2024-06-24     LCKFB-LP    first version */#include "board.h"#include "stdio.h"#include "bsp_uart.h"#include "bsp_ir_receiver.h"
int32_t main(void){    board_init();        // 开发板初始化
    uart1_init(115200);        // 串口1波特率115200

    //红外接收初始化    infrared_goio_config();
    printf("Start!!!rn");
    while(1)    {            //如果按下遥控的【1】键            if( get_infrared_command() == 0xA2 )            {                    clear_infrared_command();                    printf("按下【1】按键! rn");            }
    }}

移植现象:

模块移植成功案例代码:

链接:https://pan.baidu.com/s/1Yln6MD82bPkgS2x-YMnfCQ?pwd=LCKF

提取码:LCKF

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

以开放、共享、互助为理念,致力于构建武汉芯源半导体CW32系列MCU生态社区。无论是嵌入式MCU小自还是想要攻破技术难题的工程师,亦或是需求解决方案的产品经理都可在CW32生态社区汲取营养、共同成长。