• 正文
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

【CW32模塊使用】紅外接收模塊

01/28 08:25
1128
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

模塊來源

模塊實(shí)物展示:

資料鏈接:https://pan.baidu.com/s/1dEWVMIFDWb7k1NcsRy5hHA

資料提取碼:uucv

規(guī)格參數(shù)

1.CR2025環(huán)保紐扣電池,容量160mah

2.發(fā)射距離:8m以上(具體和周圍環(huán)境、接收端的靈敏度等因素有關(guān))

3.有效角度:60度

4.面貼材料:0.125mmPET,有效壽命2萬次。

5.品質(zhì)穩(wěn)定,性價(jià)比高

6.靜態(tài)電流3-5uA,動(dòng)態(tài)電流3-5mA。

以上信息見廠家資料文件

移植過程

我們的目標(biāo)是將例程移植至CW32F030C8T6開發(fā)板上【能夠?qū)崿F(xiàn)紅外信號(hào)接收的功能】。首先要獲取資料,查看數(shù)據(jù)手冊應(yīng)如何實(shí)現(xiàn)讀取數(shù)據(jù),再移植至我們的工程。

3.1查看資料

在光譜中波長自760nm至400um的電磁波稱為紅外線,它是一種不可見光。紅外線通信的例子我們每個(gè)人應(yīng)該都很熟悉,目前常用的家電設(shè)備幾乎都可以通過紅外遙控的方式進(jìn)行遙控,比如電視機(jī)、空調(diào)、投影儀等,都可以見到紅外遙控的影子。這種技術(shù)應(yīng)用廣泛,相應(yīng)的應(yīng)用器件都十分廉價(jià),因此紅外遙控是我們?nèi)粘TO(shè)備控制的理想方式。

紅外線的通訊原理

紅外光是以特定的頻率脈沖形式發(fā)射,接收端收到到信號(hào)后,按照約定的協(xié)議進(jìn)行解碼,完成數(shù)據(jù)傳輸。在消費(fèi)類電子產(chǎn)品里,脈沖頻率普遍采用 30KHz 到 60KHz 這個(gè)頻段,NEC協(xié)議的頻率就是38KHZ。這個(gè)以特定的頻率發(fā)射其實(shí)就可以理解為點(diǎn)燈,不要被復(fù)雜的詞匯難住了,就是控制燈的閃爍頻率(亮滅),和剛學(xué)單片機(jī)完成閃爍燈一樣的意思,只不過是燈換了一種類型,都是燈。

接收端的原理: 接收端的芯片對這個(gè)紅外光比較敏感,可以根據(jù)有沒有光輸出高低電平,如果發(fā)送端的閃爍頻率是有規(guī)律的,接收端收到后輸出的高電平和低電平也是有規(guī)律對應(yīng)的,這樣發(fā)送端和接收端只要約定好,那就可以做數(shù)據(jù)傳輸了。

紅外線傳輸協(xié)議可以說是所有無線傳輸協(xié)議里成本最低,最方便的傳輸協(xié)議了,但是也有缺點(diǎn),距離不夠長,速度不夠快;當(dāng)然,每個(gè)傳輸協(xié)議應(yīng)用的環(huán)境不一樣,定位不一樣,好壞沒法比較,具體要看自己的實(shí)際場景選擇合適的通信方式。

NEC協(xié)議介紹

NEC協(xié)議是眾多紅外線協(xié)議中的一種(這里說的協(xié)議就是他們數(shù)據(jù)幀格式定義不一樣,數(shù)據(jù)傳輸原理都是一樣的),我們購買的外能遙控器、淘寶買的mini遙控器、電視機(jī)、投影儀幾乎都是NEC協(xié)議。像格力空調(diào)、美的空調(diào)這些設(shè)備使用的就是其他協(xié)議格式,不是NEC協(xié)議,但是只要學(xué)會(huì)一種協(xié)議解析方式,明白了紅外線傳輸原理,其他遙控器協(xié)議都可以解出來。

NEC協(xié)議一次完整的傳輸包含: 引導(dǎo)碼、8位地址碼、8位地址反碼、8位命令碼、8位命令反碼。這里我們主要講解如何接收紅外發(fā)送端發(fā)送的NEC協(xié)議內(nèi)容。

引導(dǎo)碼:由9ms的低電平+4.5ms的高電平組成。

4個(gè)字節(jié)的數(shù)據(jù): 地址碼+地址反碼+命令碼+命令反碼。這里的反碼可以用來校驗(yàn)數(shù)據(jù)是否傳輸正確,有沒有丟包。

重點(diǎn): NEC協(xié)議傳輸數(shù)據(jù)位的時(shí)候,0和1的區(qū)分是依靠收到的高、低電平的持續(xù)時(shí)間來進(jìn)行區(qū)分的。這是解碼關(guān)鍵。

數(shù)據(jù)發(fā)送0碼:0.56ms低電平+ 0.56ms的高電平。

數(shù)據(jù)發(fā)送1碼:0.56ms低電平+1.68ms的高電平

所以,收到一個(gè)數(shù)據(jù)位的完整時(shí)間表示方法是這樣的:

    收到數(shù)據(jù)位0:   0.56ms低電平+ 0.56ms的高電平

    收到數(shù)據(jù)位1:  0.56ms低電平+1.68ms的高電平

還有一個(gè)重復(fù)碼,它是由一個(gè) 9ms 的低電平和一個(gè) 2.5ms 的高電平組成。當(dāng)一個(gè)紅外信號(hào)連續(xù)發(fā)送時(shí),可以通過發(fā)送重復(fù)碼的方式快速發(fā)送。

3.2引腳選擇

當(dāng)紅外線接收頭感應(yīng)到有紅外光就輸出低電平,沒有感應(yīng)到紅外光就輸出高電平。因此我們配置紅外引腳為外部中斷下降沿觸發(fā)方式,當(dāng)紅外引腳有下降沿時(shí),我們馬上進(jìn)入中斷處理并接收紅外信號(hào)。

模塊接線圖

3.3移植至工程

引腳配置如下:

//紅外引腳初始化void infrared_goio_config(void){    IR_RCC_GPIO_ENABLE();      // 使能GPIO時(shí)鐘
    GPIO_InitTypeDef GPIO_InitStruct;                   // GPIO初始化結(jié)構(gòu)體
    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;            // 下降沿觸發(fā)中斷    GPIO_Init(IR_PORT, &GPIO_InitStruct);               // 初始化
    // 清除PA0中斷標(biāo)志    GPIOA_INTFLAG_CLR(EXTI_BV);    // 使能NVIC    NVIC_EnableIRQ(EXTI_IRQ);}

紅外信號(hào)的數(shù)據(jù),全部是以時(shí)間長度來確定數(shù)據(jù)是0還是1,而最小的單位要求有560us,已經(jīng)達(dá)到了us級(jí)的測量。

我們在 空白工程中已經(jīng)為大家準(zhǔn)備好了us延時(shí),就在board 文件中。

獲取高低電平時(shí)間

獲取低電平時(shí)間的實(shí)現(xiàn)代碼如下:

//獲取紅外低電平時(shí)間//以微秒us作為時(shí)間參考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;}

當(dāng)引腳為低電平時(shí),將進(jìn)入 while 循環(huán),直到不為低電平時(shí)就結(jié)束循環(huán)。在循環(huán)之中不斷的讓時(shí)間變量time_val累加, 每加一次需要經(jīng)過20us。當(dāng)time_val變量累加時(shí)間大于 500 * 20 = 10000us = 10ms時(shí),判斷為超時(shí),強(qiáng)行結(jié)束該函數(shù),防止阻礙系統(tǒng)運(yùn)行。

獲取高電平時(shí)間的代碼同理:

//獲取紅外高電平時(shí)間//以微秒us作為時(shí)間參考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;}

引導(dǎo)碼與重復(fù)碼判斷

引導(dǎo)碼是由一個(gè) 9ms 的低電平和一個(gè) 4.5ms 的高電平組成。每當(dāng)接收到一個(gè)紅外信號(hào)時(shí),第一個(gè)數(shù)據(jù)就是引導(dǎo)碼。我們通過判斷紅外信號(hào)的第一個(gè)數(shù)據(jù)是否是引導(dǎo)碼,來決定是否要進(jìn)行后面的數(shù)據(jù)接收處理。

重復(fù)碼是由一個(gè) 9ms 的低電平和一個(gè) 2.5ms 的高電平組成。當(dāng)我們的紅外遙控一直按住按鍵時(shí),就會(huì)發(fā)出重復(fù)碼,我們可以檢測重復(fù)碼,來確定是否要連續(xù)觸發(fā)重復(fù)動(dòng)作,比如長按開機(jī),長按加速等等。

/****************************************************************** * 函 數(shù) 名 稱:guide_and_repeat_code_judgment * 函 數(shù) 說 明:引導(dǎo) 和 重復(fù) 碼 判斷 * 函 數(shù) 形 參:無 * 函 數(shù) 返 回:1:不是引導(dǎo)碼   2:重復(fù)碼  0:引導(dǎo)碼 * 作       者:LC * 備       注:以20微秒us作為時(shí)間參考                引導(dǎo)碼:由一個(gè) 9ms 的低電平和一個(gè) 4.5ms 的高電平組成                重復(fù)碼:由一個(gè) 9ms 的低電平和一個(gè) 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;    }
    //如果是重復(fù)碼  2ms < time < 3ms    if((out_time > 100) && (out_time < 150))    {        return 2;    }
    return 0;}

完整紅外數(shù)據(jù)接收

具體接收流程:【判斷是否接收到引導(dǎo)碼】->【接收數(shù)據(jù)】->【判斷數(shù)據(jù)是否正確】。

//接收紅外數(shù)據(jù)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;
    //等待引導(dǎo)碼    guide_and_repeat_code = guide_and_repeat_code_judgment();    //如果不是引導(dǎo)碼則結(jié)束解析    if(  guide_and_repeat_code == 1 ) return;
    //共有4組數(shù)據(jù)    //地址碼+地址反碼+命令碼+命令反碼    for(group_num = 0; group_num < 4; group_num++ )        {        //接收一組8位的數(shù)據(jù)        for( data_num = 0; data_num < 8; data_num++ )        {            //接收低電平            get_infrared_low_time(&time);            //如果不在0.56ms內(nèi)的低電平,數(shù)據(jù)錯(cuò)誤            if((time > 60) || (time < 20))            {                return ;            }
            time = 0;            //接收高電平            get_infrared_high_time(&time);            //如果是在1200us<t<2000us范圍內(nèi)則判斷為1            if((time >=60) && (time < 100))            {                bit_data = 1;            }            //如果是在200us<t<1000us范圍內(nèi)則判斷為0            else if((time >=10) && (time < 50))            {                bit_data = 0;            }
            //groupNum表示第幾組數(shù)據(jù)            ir_value[ group_num ] <<= 1;
            //接收的第1個(gè)數(shù)為高電平;在第二個(gè)for循環(huán)中,數(shù)據(jù)會(huì)向右移8次            ir_value[ group_num ] |= bit_data;
            //用完時(shí)間要重新賦值            time=0;        }    }    //判斷數(shù)據(jù)是否正確,正確則保存數(shù)據(jù)    infrared_data_true_judgment(ir_value);}

判斷數(shù)據(jù)是否正確,可以通過將正常數(shù)據(jù)取反,與反碼比較。如果不一致說明數(shù)據(jù)不對。

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

//紅外數(shù)據(jù)是否正確判斷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;
    //串口輸出查看接收到的數(shù)據(jù)    printf("%x %x %x %xrn",value[0],value[1],value[2],value[3]);    //保存正確數(shù)據(jù)    InfraredData.AddressCode        = value[0];    InfraredData.AddressInverseCode = value[1];    InfraredData.CommandCode        = value[2];    InfraredData.CommandInverseCode = value[3];}
//獲取紅外發(fā)送過來的命令uint8_t get_infrared_command(void){    return InfraredData.CommandCode;}//清除紅外發(fā)送過來的數(shù)據(jù)void clear_infrared_command(void){    InfraredData.CommandCode = 0x00;}

最后,記得在外部中斷服務(wù)函數(shù)中,調(diào)用紅外接收函數(shù)。

void EXTI_HANDLER(void){        if(IR_PORT->ISR_f.EXTI_PIN)   // 中斷標(biāo)志位        {                if(GPIO_ReadPin(IR_PORT, IR_PIN) == GPIO_Pin_RESET)  // 如果是低電平                {                        //接收一次紅外數(shù)據(jù)                        receiving_infrared_data();                }                GPIOA_INTFLAG_CLR(EXTI_BV); // 清除標(biāo)志位        }}

移植步驟中的導(dǎo)入.c和.h文件與【CW32模塊使用】DHT11溫濕度傳感器相同,只是將.c和.h文件更改為bsp_ir_receiver.c與bsp_ir_receiver.h。這里不再過多講述,移植完成后面修改相關(guān)代碼。

以下為完成紅外接收代碼:

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時(shí)鐘

        GPIO_InitTypeDef GPIO_InitStruct;                // GPIO初始化結(jié)構(gòu)體
        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;         // 下降沿觸發(fā)中斷        GPIO_Init(IR_PORT, &GPIO_InitStruct);            // 初始化
        // 清除PA0中斷標(biāo)志        GPIOA_INTFLAG_CLR(EXTI_BV);        // 使能NVIC        NVIC_EnableIRQ(EXTI_IRQ);}



//獲取紅外低電平時(shí)間//以微秒us作為時(shí)間參考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;}
//獲取紅外高電平時(shí)間//以微秒us作為時(shí)間參考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;}
/****************************************************************** * 函 數(shù) 名 稱:guide_and_repeat_code_judgment * 函 數(shù) 說 明:引導(dǎo) 和 重復(fù) 碼 判斷 * 函 數(shù) 形 參:無 * 函 數(shù) 返 回:1:不是引導(dǎo)碼   2:重復(fù)碼  0:引導(dǎo)碼 * 作       者:LC * 備       注:以20微秒us作為時(shí)間參考                引導(dǎo)碼:由一個(gè) 9ms 的低電平和一個(gè) 4.5ms 的高電平組成                重復(fù)碼:由一個(gè) 9ms 的低電平和一個(gè) 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;    }
    //如果是重復(fù)碼  2ms < time < 3ms    if((out_time > 100) && (out_time < 150))    {        return 2;    }
    return 0;}
//紅外數(shù)據(jù)是否正確判斷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]);    //保存正確數(shù)據(jù)    InfraredData.AddressCode        = value[0];    InfraredData.AddressInverseCode = value[1];    InfraredData.CommandCode        = value[2];    InfraredData.CommandInverseCode = value[3];}
//接收紅外數(shù)據(jù)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;
    //等待引導(dǎo)碼    guide_and_repeat_code = guide_and_repeat_code_judgment();    //如果不是引導(dǎo)碼則結(jié)束解析    if(  guide_and_repeat_code == 1 )    {        printf("errrn");        return;    }
    //共有4組數(shù)據(jù)    //地址碼+地址反碼+命令碼+命令反碼    for(group_num = 0; group_num < 4; group_num++ )        {        //接收一組8位的數(shù)據(jù)        for( data_num = 0; data_num < 8; data_num++ )        {            //接收低電平            get_infrared_low_time(&time);            //如果不在0.56ms內(nèi)的低電平,數(shù)據(jù)錯(cuò)誤            if((time > 60) || (time < 20))            {                return ;            }
            time = 0;            //接收高電平            get_infrared_high_time(&time);            //如果是在1200us<t<2000us范圍內(nèi)則判斷為1            if((time >=60) && (time < 100))            {                bit_data = 1;            }            //如果是在200us<t<1000us范圍內(nèi)則判斷為0            else if((time >=10) && (time < 50))            {                bit_data = 0;            }
            //groupNum表示第幾組數(shù)據(jù)            ir_value[ group_num ] <<= 1;
            //接收的第1個(gè)數(shù)為高電平;在第二個(gè)for循環(huán)中,數(shù)據(jù)會(huì)向右移8次            ir_value[ group_num ] |= bit_data;
            //用完時(shí)間要重新賦值            time=0;        }    }    //判斷數(shù)據(jù)是否正確,正確則保存數(shù)據(jù)    infrared_data_true_judgment(ir_value);}
//獲取紅外發(fā)送過來的命令uint8_t get_infrared_command(void){    return InfraredData.CommandCode;}//清除紅外發(fā)送過來的數(shù)據(jù)void clear_infrared_command(void){    InfraredData.CommandCode = 0x00;}

void EXTI_HANDLER(void){        if(IR_PORT->ISR_f.EXTI_PIN)   // 中斷標(biāo)志位        {                if(GPIO_ReadPin(IR_PORT, IR_PIN) == GPIO_Pin_RESET)  // 如果是低電平                {                        //接收一次紅外數(shù)據(jù)                        receiving_infrared_data();                }                GPIOA_INTFLAG_CLR(EXTI_BV); // 清除標(biāo)志位        }}

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

移植驗(yàn)證

在自己工程中的main主函數(shù)中,編寫如下。

/* * 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();        // 開發(fā)板初始化
    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");            }
    }}

移植現(xiàn)象:

模塊移植成功案例代碼:

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

提取碼:LCKF

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

以開放、共享、互助為理念,致力于構(gòu)建武漢芯源半導(dǎo)體CW32系列MCU生態(tài)社區(qū)。無論是嵌入式MCU小自還是想要攻破技術(shù)難題的工程師,亦或是需求解決方案的產(chǎn)品經(jīng)理都可在CW32生態(tài)社區(qū)汲取營養(yǎng)、共同成長。

B站