開發(fā)板鴻蒙Hi3861之俄羅斯方塊 += 遙控器
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz
Hi3861解碼紅外遙控器
紅外遙控器是個很實用的鍵盤擴展,即能擴展鍵盤還能遠程操作!
首先在我的小游戲上試一下!
先前發(fā)布的小游戲:https://harmonyos.51cto.com/posts/1995
視頻:https://harmonyos.51cto.com/show/2063
先上圖:

一、紅外遙控器原理(簡述)
紅外遙控器是通過940nm-950nm的紅外線傳輸?shù)?,載波頻率是38K,傳輸協(xié)議也比較簡單:

- 首先發(fā)送一個9ms的引導(dǎo)碼,引起接收方注意,我要發(fā)送數(shù)據(jù)了!
- 停止4.5ms;
- 開始發(fā)送數(shù)據(jù)(發(fā)560us停560us代表一個bit 0,發(fā)560us停1680us代表發(fā)送一個bit 1);
- 每次傳輸發(fā)送4個字節(jié) 0-15是用戶碼(一個控制器這個碼是固定的)16-23是命令碼,24-31是命令碼的反碼,以上都是低位在前。
上圖左是紅外發(fā)光二極管
上圖右是紅外接收器(HS0038B),會自動過濾掉38K的載波留下數(shù)據(jù)信息
我壓上了杜邦頭可以直接插在開發(fā)板的引腳上。
二、解析原理
接收器有三個管腳(左:輸出,中:電源負,右:電源正)
配置該管腳為普通輸入、啟用內(nèi)部上拉電阻、并注冊中斷函數(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);
有輸出(下降沿)的時候觸發(fā)中斷,中斷里讀取us時鐘;
- 然后判斷本次中斷與上一次中斷的時間間隔;
- 如果在13500(9000+4500)左右,說明接收到了一個引導(dǎo)信號,準備接收數(shù)據(jù);
- 如果在1120(560+560)左右,說明接收到一個 bit 0,接收數(shù)據(jù)不變,接收序號++;
- 如果在2240(560+1680)左右,說明接收到一個 bit 1,接收數(shù)據(jù)與上接收序號所在的位為1;
- 如果接收序號=32說明該次接收結(jié)束
- 判斷第3個字節(jié)與第4個字節(jié)是否剛好是互補的,成功可執(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;
- }
- 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;
- }
- }
三、同時也實現(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)簡單
- 函數(shù)接收用戶碼和命令碼;
- 發(fā)送9000us的引導(dǎo)碼,停4500us
- 將用戶碼與命令碼整理成一個32位的數(shù)據(jù),方便發(fā)送;
- 依次按位進行開關(guān)PWM進行發(fā)送;38k = (160M/4212), 1/3的占空比(4212/3=1404)
- 32位發(fā)送完后,再發(fā)送一個結(jié)束碼
發(fā)送間隔本應(yīng)該用定時器進行操作,但Hi3861的定時器都是ms級的,無法完成us級延時;
開始我用usleep延時操作,發(fā)現(xiàn)誤差有一兩個數(shù)量級,根本無法使用,還好我找到了hi_udelay(),位于hi_time.h可以滿足需求!
以上只是介紹最常見的紅外遙控器的解碼及編碼!有些廠家自己定義了請多非標編碼就不一一介紹了!
最近必須付上代碼!!!
©著作權(quán)歸作者和HarmonyOS技術(shù)社區(qū)共同所有,如需轉(zhuǎn)載,請注明出處,否則將追究法律責任
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz