基於stm32f103zet6之紅外遙控解碼的學習

來源:互聯網
上載者:User

不得不吐槽一下,我真的好挫,真的真的非常差勁兒。。。

一、紅外遙控解碼部分從昨天開始整,一直到現在才解碼成功!中途遇到了不少問題,結果出來後還是覺得有必要總結一下,唉!

1、首先我又是懷疑我硬體電平不相容德問題,後來給接上3.3V的電壓,還是不行,好吧,算失敗了,在網上查閱了比較多的文章,也找了比較多的資料,最終還是決定用原本那個生了鏽的遙控來解碼!

2、然後準備參照著原來51的思想來移植代碼,也確實找到類似的代碼貌似使用的2.0的庫寫的,單步調試了半天,總感覺在延時部分出了點問題,所以比較鬱悶,好吧,分析來分析去的,結果真的是沒有半點鐘現象啊!果斷網上求助,遊盪了一會,壓根沒人理,高手不屑一顧呀!!偶然間讓我遇到了原子哥的那段紅外的代碼,拖出來分析,所以就有了今晚解碼成功的結果!

3、我照著原子的移植,我用的是自己的延時,也就是系統定時器,MTD,單步調試的時候,發現居然死在了systick那裡,進不了中斷,一步步觀察,好像導致進不了中斷的原因就是:我已經進了外部中斷,心想,沒道理啊,系統定時器的優先順序不應該是高於外部中斷的麼,因為他是核決定的呀(至少我是這麼想的),然後又查了相關資料,據說系統定時器的中斷優先順序是最低的,這時候我才恍然大悟!

現在開始分析代碼,雖然說原子的代碼風格不怎麼樣,但是個人覺得他真的好牛逼,庫函數是人家ST公司搞出來的,我想,原子的這套代碼,應該基本上是他自己一個人整出來的吧!

二、所謂紅外遙控!(針對我手上的紅外遙控)

1、紅外解碼一直是單片機中應用較多的,需要裝置加裝專用解碼晶片,這就大大減輕了單片機的負擔。需要單片機範例使用延時做紅外解碼,比較容易理解,
下面通過TC9012和uPD6121晶片為例大致瞭解解碼原理:
先看一接收頭產生的波形圖,這是原子的一張圖


         % U, K" ?3 K2 _( j' a! e: K: o
從可以看出 9.0ms高電平+4.5ms低電平稱為頭碼,用於識別是否遙控碼開始,這是一張連續發射碼的波形圖(就是一直按下某一遙控器按鍵)。;
n5 [
+ z; ^4 d( T# L) h" Y6 B5 j3 T
頭碼過後會出現4個8位的資料,我們最終目的就是要把這個
32位(4x8)從一體化紅外接收頭提取出來,並轉換成16進位數,用於區分不同按鍵按下得出的不同數值。
在遙控器發射波形中,可以知道,8位元中的0或者1不是用高低電平表示,而是用不同的低電平的寬度表示,0.565ms表示0,1.69ms表示1,2個位中間還會有一個0.56ms的高電平
     看到如波形,表示單片機引腳可以接收到的波形,我們只要通過單片機讀取波形並分析波形的寬度,然後分辨出是頭碼,還是0或者1,最後整理出這組碼的16進位組合。正確的解碼結果是按同一個按鍵得出的16進位數值是不變化的。通過這個原理,我們可以分辨出每個按鍵的索引值。
! z7 B/ `2 Q: z
基本原理分析如下,如接收到頭碼是4.5ms低電平+4.5ms高電平,我們分析 第一個下降沿到第二個下降沿的寬度是 9ms,我們判斷這個頭碼可以給定一個範圍,只要資料在這個範圍內則認為頭碼是正確的,檢測頭碼正確後接著檢測剩下的32位元值。

2、用自己的話概括就是:平常是高電平 --->按鍵按下 --->產生引導碼(9+4.5)ms--->然後判斷是不是連續發送--->1還是0--->儲存碼值--->轉換碼值!簡單就是這樣!

3、首先是我的主程式,代碼注釋都非常詳細,不解釋了!注意碼值需要依據自己的遙控而定,我就是單步測試出來的!

#include "stm32f10x.h"    #include "Usart.h"#include "stdio.h"#include "Remote_Control.h"#include "Delay.h"/**************************PA1接紅外接收端************************************//************由於沒有做外設測試的程式是:按鍵PA0僅一個LED燈*******************//*******由於沒有做外設測試的程式是:串口採用的是PA9->(T<->T),PA9->(R<->R)*****/int main(void){u8 key;USART1_Config();delay_init(72);     //延時初始化    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設定NVIC中斷分組2:2位搶佔優先順序,2位響應優先順序printf("\r\n ("__DATE__ " - " __TIME__ ") \r\n");Remote_Init();while(1){  if(Remote_Rdy){key=Remote_Process();switch(key){ case 0x68:printf("0\n");break;//0    case 0x30:printf("1\n");break;//1  case 0x18:printf("2\n");break;//2 case 0x7a:printf("3\n");break;//3 case 0x10:printf("4\n");break;//4 case 0x38:printf("5\n");break;//5 case 0x5a:printf("6\n");break;//6 case 0x42:printf("7\n");break;//7 case 0x4a:printf("8\n");break;//8 case 0x52:printf("9\n");break;//9             default:break;}}}}

4、然後是驅動程式

/*-------------------------協議--------------------------開始拉低9ms,接著是一個4.5ms的高脈衝,通知器件開始傳送資料了接著是發送4個8位二進位碼,第一二個是遙控識別碼(REMOTE_ID),第一個為正碼(0),第二個為反碼(255),接著兩個資料是索引值,第一個為正碼第二個為反碼.發送完後40ms,遙控再發送一個9ms低,2ms高的脈衝,表示按鍵的次數,出現一次則證明只按下了一次,如果出現多次,則可以認為是持續按下該鍵.---------------------------------------------------------*/#include "Remote_Control.h"#include "Delay.h"    u32 Remote_Odr=0;    //命令暫存處 u8  Remote_Cnt=0;    //按鍵次數,此次按下鍵的次數u8  Remote_Rdy=0;    //紅外接收到資料    /************************初始化紅外接收引腳的設定**********************************//******************選擇PA1腳作為外部中斷,用於紅外輸入*****************************/void Remote_Init(void){ GPIO_InitTypeDef GPIO_InitStructure;//GPIONVIC_InitTypeDef NVIC_InitStructure;//中斷EXTI_InitTypeDef EXTI_InitStructure;//外部中斷線  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE );   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;  //注意需要上拉輸入GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);   GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);  //選擇PA1所在的GPIO管腳用作外部中斷線路EXIT1 EXTI_InitStructure.EXTI_Line = EXTI_Line1;//外部線路EXIT1EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//設外外部中斷模式:EXTI線路為插斷要求  EXTI_Mode_Event ;//設定 EXTI線路為事件請求 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  //外部中斷觸發沿選擇:設定輸入線路下降沿為插斷要求EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能外部中斷新狀態EXTI_Init(&EXTI_InitStructure);//根據EXTI_InitStruct中指定的參數初始化外設EXTI寄存器 NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //使能按鍵所在的外部中斷通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先佔優先順序2級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //從優先順序1級NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的參數初始化外設NVIC寄存器}   /*功能:檢測脈衝寬度*****************************************//*說明:最長脈寬為5ms/*傳回值: t代表脈寬為t*20us(T=1~250); *************************/u8 Pulse_Width_Check(void){    u8 t=0;     while(RDATA)    { t++;delay_us(20);         if(t==250)return t; //逾時溢出    }    return t;}     /*功能: 中斷函數*****************************************//*說明:檢測是否有中斷/*傳回值: 無************************************************/  void EXTI1_IRQHandler(void){       u8 res=0;    u8 OK=0;     u8 RODATA=0; EXTI_ClearITPendingBit(EXTI_Line1);  //清除EXTI1線路掛起位            while(1)    {                if(RDATA)//有高脈衝出現        {            res=Pulse_Width_Check();//獲得此次高脈衝寬度                   if(res==250)break;//非有用訊號            if(res>=200&&res<250)OK=1;  //獲得前置位(4.5ms)            else if(res>=85&&res<200)   //按鍵次數加一(2ms)            {                       Remote_Rdy=1;//接受到資料                Remote_Cnt++;//按鍵次數增加                break;            }            else if(res>=50&&res<85)RODATA=1;//1.5ms            else if(res>=10&&res<50)RODATA=0;//500us              if(OK)            {                Remote_Odr<<=1;                Remote_Odr+=RODATA; //這裡得到的是一個32位的碼值,//地址碼、地址反碼、控制碼、控制反碼                Remote_Cnt=0; //按鍵次數清零            }           }      }      }  /*功能: 處理紅外鍵盤*****************************************//*說明:無/*傳回值: 索引值************************************************/u8 Remote_Process(void){                   u8 t1,t2;       t1 = ((Remote_Odr >> 8)&(0xff)); //得到控制碼    t2=(Remote_Odr >> 0)&0xff;//得到控制反碼     Remote_Rdy=0;//清除標記       //    if(t1==(u8)~t2&&t1==REMOTE_ID)//檢驗遙控識別碼(ID)及地址 //    { //        t1=Remote_Odr>>8;//        t2=Remote_Odr; //    }         if(t1==(u8)~t2)return t1; //處理索引值 return 0xff; }

5、延時函數也採用用原子的!有時間自己改成定時器的!

//延時nus//nus為要延時的us數.       void delay_us(u32 nus){u32 temp;     SysTick->LOAD=nus*fac_us; //時間載入   SysTick->VAL=0x00;        //清空計數器SysTick->CTRL=0x01 ;      //開始倒數  do{temp=SysTick->CTRL;}while(temp&0x01&&!(temp&(1<<16)));//等待時間到達   SysTick->CTRL=0x00;       //關閉計數器SysTick->VAL =0X00;       //清空計數器 }

6.算是要注意的地方吧,有些地方還是不習慣原子的代碼風格,比如:

#define RDATA GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) //紅外資料輸入腳

當然,這隻是個人感覺!

就弄到這裡吧!!!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.