CW32模块使用 红外接收模块

描述

一、模块来源

模块实物展示:
 

红外接收模块


资料链接: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=60) && (time < 100))
            {
                bit_data = 1;
            }
            //如果是在200us=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

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分