1. 程式人生 > 實用技巧 >【鴻蒙開發】俄羅斯方塊 += 遙控器

【鴻蒙開發】俄羅斯方塊 += 遙控器

目錄:

一、紅外遙控器原理(簡述)

二、解析原理

三、同時也實現了紅外編碼

四、原始碼包

Hi3861解碼紅外遙控器

紅外遙控器是個很實用的鍵盤擴充套件,即能擴充套件鍵盤還能遠端操作!

首先在我的小遊戲上試一下!

先前釋出的小遊戲:https://harmonyos.51cto.com/posts/1995#kyzg

先上圖:

視訊:因為需要稽核稍後再上!

一、紅外遙控器原理(簡述)

紅外遙控器是通過940nm-950nm的紅外線傳輸的,載波頻率是38K,傳輸協議也比較簡單:

首先發送一個9ms的引導碼,引起接收方注意,我要傳送資料了!
停止4.5ms;
開始傳送資料(發560us停560us代表一個bit 0,發560us停1680us代表傳送一個bit 1);
每次傳輸傳送4個位元組 0-15是使用者碼(一個控制器這個碼是固定的)16-23是命令碼,24-31是命令碼的反碼,以上都是低位在前。

上圖左是紅外發光二極體

上圖右是紅外接收器(HS0038B),會自動過濾掉38K的載波留下資料資訊

我壓上了杜邦頭可以直接插在開發板的引腳上。

二、解析原理

接收器有三個管腳(左:輸出,中:電源負,右:電源正)

配置該管腳為普通輸入、啟用內部上拉電阻、並註冊中斷函式;

    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_6, WIFI_IOT_IO_FUNC_GPIO_6_GPIO);
    GpioSetDir(WIFI_IOT_IO_NAME_GPIO_6, WIFI_IOT_GPIO_DIR_IN);
    IoSetPull(WIFI_IOT_IO_NAME_GPIO_6, WIFI_IOT_IO_PULL_UP);
    GpioRegisterIsrFunc(WIFI_IOT_IO_NAME_GPIO_6, WIFI_IOT_INT_TYPE_EDGE, WIFI_IOT_GPIO_EDGE_FALL_LEVEL_LOW, rc_decode, NULL);

有輸出(下降沿)的時候觸發中斷,中斷裡讀取us時鐘;

然後判斷本次中斷與上一次中斷的時間間隔;
如果在13500(9000+4500)左右,說明接收到了一個引導訊號,準備接收資料;
如果在1120(560+560)左右,說明接收到一個 bit 0,接收資料不變,接收序號++;
如果在2240(560+1680)左右,說明接收到一個 bit 1,接收資料與上接收序號所在的位為1;
如果接收序號=32說明該次接收結束
判斷第3個位元組與第4個位元組是否剛好是互補的,成功可執行命令解析執行相關操作。

static void rc_decode(char *arg)
{
    (void) arg;
    time_r = hi_get_us();
    // t = 13500
    if(time_r - time_c > 13000 && time_r - time_c < 14000)
    {
        n = 0;
        data.Int = 0;
    }    
    // t = 1120
    if(time_r - time_c > 920 && time_r - time_c < 1320)
    {
        ++n;
    } 
    // t = 2250
    if(time_r - time_c > 2050 && time_r - time_c < 2450)
    {
        data.Int |= 1<<n;
        ++n;
    }
    if(n == 32)
    {
        if ((data.Char[2] ^ data.Char[3]) == 0xff)
        {
//printf("user_code:%x\tcom_code:%x\n", data.Short[0], data.Char[2]);
            switch_key(data.Char[2]);
        }
        data.Int = 0;
    }
    time_c = time_r;
}

要獲取每個按鍵的命令碼是什麼,可以直接列印到串列埠

printf("user_code:%x\tcom_code:%x\n", data.Short[0], data.Char[2]);

然後對不同的鍵碼進行一個switch操作就OK了!

void switch_key(unsigned char key)
{
    switch(key)
    {
        case 0x99: block_left();break;
        case 0xc1: block_right();break;
        case 0xca: game_stop();break;
        case 0xd2: block_down();break;
        case 0xce: block_turn();break;
    }
}

三、同時也實現了紅外編碼

void rc_encode(unsigned user_code, unsigned com_code)
{
    PwmInit(PWM);
    PwmStart(PWM, 1404, 4212);
    hi_udelay(9000);
    PwmStop(PWM);
    hi_udelay(4500);
    unsigned int data = user_code | com_code<<16 | ~com_code<<24;
    for(unsigned char i=0;i<32;++i)
    {
        PwmStart(PWM, 1404, 4212);
        hi_udelay(560);
        PwmStop(PWM);
        hi_udelay((data&0x0001)==0x0001?1680:560);
        data >>= 1;
    }  
    PwmStart(PWM, 1404, 4212);
    hi_udelay(560);
    PwmStop(PWM);
    hi_udelay(3000);
    PwmStart(PWM, 1404, 4212);
    hi_udelay(560);
    PwmStop(PWM);
}

編碼就是解碼的反操作,相關簡單

函式接收使用者碼和命令碼;
傳送9000us的引導碼,停4500us
將使用者碼與命令碼整理成一個32位的資料,方便傳送;
依次按位進行開關PWM進行傳送;38k = (160M/4212), 1/3的佔空比(4212/3=1404)
32位傳送完後,再發送一個結束碼

傳送間隔本應該用定時器進行操作,但Hi3861的定時器都是ms級的,無法完成us級延時;

開始我用usleep延時操作,發現誤差有一兩個數量級,根本無法使用,還好我找到了hi_udelay(),位於hi_time.h可以滿足需求!

以上只是介紹最常見的紅外遙控器的解碼及編碼!有些廠家自己定義了請多非標編碼就不一一介紹了!

最近必須付上程式碼!!!

作者:Hallym6

想了解更多內容,請訪問: 51CTO和華為官方戰略合作共建的鴻蒙技術社群https://harmonyos.51cto.com

【免費直播公開課- 讓鴻蒙智慧家居開發板與AWS IoT雲完美連通 】