天天看點

STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)

一、環境介紹

MCU: STM32F103ZET6

程式設計軟體環境: keil5

紅外線傳輸協定:  NEC協定---38KHZ載波:。NEC協定是紅外遙控協定中常見的一種。

解碼思路:  外部中斷 + 定時器方式

代碼風格:   子產品化程式設計,寄存器直接操作方式

完整keil工程源碼下載下傳(解壓即可編譯運作測試):  

https://download.csdn.net/download/xiaolong1126626497/19863275

二、NEC協定與解碼思路介紹

2.1 采用的相關硬體

 圖1:  這是NEC協定的紅外線遙控器:  如果自己手機沒有紅外線遙控器的功能,可以淘寶上買一個小遙控器來學習測試,成本不高,這個遙控器也可以自己做,能解碼當然也可以編碼發送,隻需要一個紅外光發射管即可。

STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)
     圖2: 這是紅外線接收頭子產品。如果自己的開發闆沒有自帶這個接收頭,那就單獨買一個接收頭子產品,使用杜邦線接到開發闆的IO口上即可用來測試學習,接線很友善。
STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)
STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)
圖3: 這是紅外線發射管,如果自己想做遙控器的發射端,自己做遙控器,那麼就可以直接購買這種子產品即可。
STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)

2.2 紅外線協定介紹

在光譜中波長自760nm至400um的電磁波稱為紅外線,它是一種不可見光。紅外線通信的例子我們每個人應該都很熟悉,目前常用的家電裝置幾乎都可以通過紅外遙控的方式進行遙控,比如電視機、空調、投影儀等,都可以見到紅外遙控的影子。這種技術應用廣泛,相應的應用器件都十分廉價,是以紅外遙控是我們日常裝置控制的理想方式。

紅外線的通訊原理: 紅外光是以特定的頻率脈沖形式發射,接收端收到到信号後,按照約定的協定進行解碼,完成資料傳輸,在消費類電子産品裡,脈沖頻率普遍采用 30KHz 到 60KHz 這個頻段,NEC協定的頻率就是38KHZ。 這個以特定的頻率發射其實就可以了解為點燈,不要被複雜的詞彙難住了,就是控制燈的閃爍頻率(亮滅),和剛學單片機完成閃光燈一樣的意思,隻不過是燈換了一種類型,都是燈。 接收端的原理:  接收端的晶片對這個紅外光比較敏感,可以根據有沒有光輸出高低電平,如果發送端的閃爍頻率是有規律的,接收端收到後輸出的高電平和低電平也是有規律對應的,這樣發送端和接收端隻要約定好,那就可以做資料傳輸了。

紅外線傳輸協定可以說是所有無線傳輸協定裡成本最低,最友善的傳輸協定了,但是也有缺點,距離不夠長,速度不夠快;當然,每個傳輸協定應用的環境不一樣,定位不一樣,好壞沒法比較,具體要看自己的實際場景選擇合适的通信方式。  

2.3 NEC協定介紹

NEC協定是衆多紅外線協定中的一種(這裡說的協定就是他們資料幀格式定義不一樣,資料傳輸原理都是一樣的),我們購買的外能遙控器、淘寶買的mini遙控器、電視機、投影儀幾乎都是NEC協定。  像格力空調、美的空調這些裝置使用的就是其他協定格式,不是NEC協定,但是隻要學會一種協定解析方式,明白了紅外線傳輸原理,其他遙控器協定都可以解出來。

下圖是NEC協定傳輸一次資料的完整格式:

STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)

NEC協定一次完整的傳輸包含:    引導碼、8位使用者碼、8位使用者反碼、8位資料碼、8位資料反碼。

(注意:下面的解釋都是站在紅外線接收端的角度來進行說明的,就是解碼端的角度)

引導碼:  由9ms的高電平+4.5ms的低電平組成。

4個位元組的資料:  使用者碼+使用者反碼+資料碼+資料反碼。  這裡的反碼可以用來校驗資料是否傳輸正确,有沒有丢包。

重點:  NEC協定傳輸資料位的時候,0和1的區分是依靠收到的高、低電平的持續時間來進行區分的---這是解碼關鍵。

标準間隔時間:0.56ms

收到資料位0:  0.56ms

收到位1:  1.68ms

是以,收到一個資料位的完整時間表示方法是這樣的:

收到資料位0:   0.56m低電平+ 0.56ms的高電平

收到資料位1:  0.56ms低電平+1.68ms的高電平

紅外線接收頭子產品輸出電平的原理: 紅外線接收頭感應到有紅外光就輸出低電平,沒有感應到紅外光就輸出高電平。

這是使用邏輯分析采集紅外線接收頭輸出的信号:

STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)

這是采集紅外線遙控器上的LED燈輸出電平時序圖,剛好和接收端相反:

STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)

​​

單片機編寫解碼程式的時候,常見的方式就是采用外部中斷+定時器的方式進行解析,中斷可以設定為低電平觸發,因為接收頭沒有感應到紅外光預設是輸出高電平,如果收到NEC引導碼,就會輸出低電平,進入到中斷服務函數,完成解碼,解碼過程中開啟定時器記錄每一段的高電平、低電平的持續時間,按照NEC協定進行判斷,完成最終解碼。

STM32可以使用輸入捕獲方式完成解碼,其實輸入捕獲就是外部中斷+定時器的組合,隻不過是STM32内部封裝了一層。

外部中斷伺服器裡的解碼程式如下(這個在其他單片機上思路是一樣的):

/*
函數功能:  外部中斷線9_5服務函數
*/
void EXTI9_5_IRQHandler(void)
{
    u32 time;
    u8 i,j,data=0;
  
    //清除中斷線9上的中斷請求
        EXTI->PR|=1<<9;
  
    time=Infrared_GetTime_L();       //得到低電平時間
    if(time<7000||time>10000)return; //标準時間: 9000us
    time=Infrared_GetTime_H();       //得到高電平時間
    if(time<3000||time>5500)return;  //标準時間4500us
    
    //正式解碼NEC協定
    for(i=0;i<4;i++)
    {
        for(j=0;j<8;j++)
        {
             time=Infrared_GetTime_L();       //得到低電平時間
             if(time<400||time>700)return;    //标準時間: 560us
             
             time=Infrared_GetTime_H();       //得到高電平時間
             if(time>1400&&time<1800)         //資料1 1680us
             {
                data>>=1;
                data|=0x80;
             }
             else if(time>400&&time<700)   //資料0 560us
             {
                data>>=1;
             }
             else return;
        }
        InfraredRecvData[i]=data; //存放解碼成功的值
      }
    
      //解碼成功
      InfraredRecvState=1;
}      

三、核心完整代碼

STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)
STM32入門開發: NEC紅外線協定解碼(超低成本無線傳輸方案)

本程式的解碼思路是: 将紅外線接收子產品的輸出腳接到STM32的PB9上,配置STM32的PB9為外部中斷模式,下降沿電平觸發;如果收到紅外線信号就進入到中斷服務函數裡解碼,如果解碼過程中發現資料不符合要求就終止解碼,如果資料全部符合要求就按照協定接收,直到解碼完成,設定标志位,在main函數裡列印解碼得到的資料。

代碼都是子產品化程式設計,閱讀起來也很友善。

3.1  紅外線解碼.c

#include "nec_Infrared.h"
u8 InfraredRecvData[4]; //存放紅外線解碼接收的資料
u8 InfraredRecvState=0; //0表示未接收到資料,1表示接收到資料 
/*
函數功能: 紅外線解碼初始化(接收)
*/
void Infrared_RecvInit(void)
{
    Infrared_Time6_Init(); //定時器初始化
    
    /*1. 配置GPIO口*/
    RCC->APB2ENR|=1<<3; //PB
    GPIOB->CRH&=0xFFFFFF0F;
    GPIOB->CRH|=0x00000080;
    GPIOB->ODR|=1<<9;
  
    /*2. 配置外部中斷*/
    EXTI->IMR|=1<<9; //外部中斷線9,開放中斷線的中斷請求功能
    EXTI->FTSR|=1<<9; //中斷線9_下降沿
  
    RCC->APB2ENR|=1<<0; //開啟AFIO時鐘
    AFIO->EXTICR[2]&=~(0xF<<1*4);
    AFIO->EXTICR[2]|=0x1<<1*4;
    STM32_NVIC_SetPriority(EXTI9_5_IRQn,1,1);
}
 
/*
函數功能: 初始化定時器,用于紅外線解碼
*/
void Infrared_Time6_Init(void)
{
    RCC->APB1ENR|=1<<4;
        RCC->APB1RSTR|=1<<4;
        RCC->APB1RSTR&=~(1<<4);
    TIM6->PSC=72-1; //預分頻器
    TIM6->ARR=65535;   //重裝載寄存器
    TIM6->CR1|=1<<7; //開啟緩存功能
      //TIMx->CR1|=1<<0; //開啟定時器
}
 
/*
函數功能: 測量高電平持續的時間
*/
u32 Infrared_GetTime_H(void)
{
    TIM6->CNT=0;
    TIM6->CR1|=1<<0;    //開啟定時器
    while(NEC_IR){}     //等待高電平結束
    TIM6->CR1&=~(1<<0); //關閉定時器
    return TIM6->CNT;
}
 
/*
函數功能: 測量低電平持續的時間
*/
u32 Infrared_GetTime_L(void)
{
    TIM6->CNT=0;
    TIM6->CR1|=1<<0;    //開啟定時器
    while(!NEC_IR){}     //等待低電平結束
    TIM6->CR1&=~(1<<0); //關閉定時器
    return TIM6->CNT;
}
 
 
/*
函數功能:  外部中斷線9_5服務函數
*/
void EXTI9_5_IRQHandler(void)
{
    u32 time;
    u8 i,j,data=0;
  
    //清除中斷線9上的中斷請求
        EXTI->PR|=1<<9;
  
    time=Infrared_GetTime_L();       //得到低電平時間
    if(time<7000||time>10000)return; //标準時間: 9000us
    time=Infrared_GetTime_H();       //得到高電平時間
    if(time<3000||time>5500)return;  //标準時間4500us
    
    //正式解碼NEC協定
    for(i=0;i<4;i++)
    {
        for(j=0;j<8;j++)
        {
             time=Infrared_GetTime_L();       //得到低電平時間
             if(time<400||time>700)return;    //标準時間: 560us
             
             time=Infrared_GetTime_H();       //得到高電平時間
             if(time>1400&&time<1800)         //資料1 1680us
             {
                data>>=1;
                data|=0x80;
             }
             else if(time>400&&time<700)   //資料0 560us
             {
                data>>=1;
             }
             else return;
        }
        InfraredRecvData[i]=data; //存放解碼成功的值
      }
    
      //解碼成功
      InfraredRecvState=1;
}      

3.2  主函數.c

#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include "at24c02.h"
#include "W25Q64.h"
#include "spi.h"
#include "nec_Infrared.h"
 
int main()
{
    LED_Init();
    BEEP_Init();
    KeyInit();
  USARTx_Init(USART1,72,115200);
  IIC_Init();
  
  W25Q64_Init();
  
  printf("晶片ID号:0x%X\n",W25Q64_ReadID());
  
  Infrared_RecvInit();
  
    while(1)
    {
        if(InfraredRecvState)
    {
        InfraredRecvState=0;
        printf("使用者碼:%d,按鍵碼:%d\n",InfraredRecvData[0],InfraredRecvData[2]);
        printf("user反碼:%d,key反碼:%d\n",(~InfraredRecvData[1])&0xFF,(~InfraredRecvData[3])&0xFF);
        BEEP=!BEEP;
        LED0=!LED0;
    }
    }
}
       

四、擴充提高

如果上面的NEC的解碼思路已經看到,程式已經可以自己編寫,就可以試着使用STM32的輸入捕獲+定時器方式寫一版解碼代碼,既能更加熟悉NEC協定、也可以學習STM32定時器捕獲捕獲的用法;也可以做一些小東西來鍛煉,比如:紅外線遙控小車、音樂播放器支援紅外線遙控器切歌,電機的開關、燈的開關等等。

搞定協定解碼之後,我們下一步就是完成自定義的NEC協定紅外線制作,采用STM32模拟一個萬能紅外線遙控器。

繼續閱讀