電子產(chǎn)業(yè)一站式賦能平臺(tái)

PCB聯(lián)盟網(wǎng)

搜索
查看: 619|回復(fù): 0
收起左側(cè)

俄羅斯方塊 += 遙控器

[復(fù)制鏈接]

2607

主題

2607

帖子

7472

積分

高級(jí)會(huì)員

Rank: 5Rank: 5

積分
7472
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2020-12-20 23:56:50 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
俄羅斯方塊 += 遙控器,   
Hi3861解碼紅外遙控器

紅外遙控器是個(gè)很實(shí)用的鍵盤擴(kuò)展,即能擴(kuò)展鍵盤還能遠(yuǎn)程操作!

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

先上圖:






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

紅外遙控器是通過(guò)940nm-950nm的紅外線傳輸?shù)模d波頻率是38K,傳輸協(xié)議也比較簡(jiǎn)單:





  • 首先發(fā)送一個(gè)9ms的引導(dǎo)碼,引起接收方注意,我要發(fā)送數(shù)據(jù)了!
  • 停止4.5ms;
  • 開(kāi)始發(fā)送數(shù)據(jù)(發(fā)560us停560us代表一個(gè)bit 0,發(fā)560us停1680us代表發(fā)送一個(gè)bit 1);
  • 每次傳輸發(fā)送4個(gè)字節(jié) 0-15是用戶碼(一個(gè)控制器這個(gè)碼是固定的)16-23是命令碼,24-31是命令碼的反碼,以上都是低位在前。
      
上圖左是紅外發(fā)光二極管

上圖右是紅外接收器(HS0038B),會(huì)自動(dòng)過(guò)濾掉38K的載波留下數(shù)據(jù)信息

我壓上了杜邦頭可以直接插在開(kāi)發(fā)板的引腳上。


二、解析原理

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

配置該管腳為普通輸入、啟用內(nèi)部上拉電阻、并注冊(cè)中斷函數(shù);

     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);有輸出(下降沿)的時(shí)候觸發(fā)中斷,中斷里讀取us時(shí)鐘;



  • 然后判斷本次中斷與上一次中斷的時(shí)間間隔;
  • 如果在13500(9000+4500)左右,說(shuō)明接收到了一個(gè)引導(dǎo)信號(hào),準(zhǔn)備接收數(shù)據(jù);
  • 如果在1120(560+560)左右,說(shuō)明接收到一個(gè) bit 0,接收數(shù)據(jù)不變,接收序號(hào)++;
  • 如果在2240(560+1680)左右,說(shuō)明接收到一個(gè) bit 1,接收數(shù)據(jù)與上接收序號(hào)所在的位為1;
  • 如果接收序號(hào)=32說(shuō)明該次接收結(jié)束
  • 判斷第3個(gè)字節(jié)與第4個(gè)字節(jié)是否剛好是互補(bǔ)的,成功可執(zhí)行命令解析執(zhí)行相關(guān)操作。
      
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;}要獲取每個(gè)按鍵的命令碼是什么,可以直接打印到串口

printf(“user_code:%x\tcom_code:%x\n“, data.Short[0], data.Char[2]);然后對(duì)不同的鍵碼進(jìn)行一個(gè)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;    }}
三、同時(shí)也實(shí)現(xiàn)了紅外編碼

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);}編碼就是解碼的反操作,相關(guān)簡(jiǎn)單



  • 函數(shù)接收用戶碼和命令碼;
  • 發(fā)送9000us的引導(dǎo)碼,停4500us
  • 將用戶碼與命令碼整理成一個(gè)32位的數(shù)據(jù),方便發(fā)送;
  • 依次按位進(jìn)行開(kāi)關(guān)PWM進(jìn)行發(fā)送;38k = (160M/4212), 1/3的占空比(4212/3=1404)
  • 32位發(fā)送完后,再發(fā)送一個(gè)結(jié)束碼
      
發(fā)送間隔本應(yīng)該用定時(shí)器進(jìn)行操作,但Hi3861的定時(shí)器都是ms級(jí)的,無(wú)法完成us級(jí)延時(shí);

開(kāi)始我用usleep延時(shí)操作,發(fā)現(xiàn)誤差有一兩個(gè)數(shù)量級(jí),根本無(wú)法使用,還好我找到了hi_udelay(),位于hi_time.h可以滿足需求!



以上只是介紹最常見(jiàn)的紅外遙控器的解碼及編碼!有些廠家自己定義了請(qǐng)多非標(biāo)編碼就不一一介紹了!





注:本文章來(lái)自51CTO社區(qū)

發(fā)表回復(fù)

本版積分規(guī)則


聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表