不得不吐槽一下,我真的好挫,真的真的非常差勁兒。。。
一、紅外遙控解碼部分從昨天開始整,一直到現在才解碼成功!中途遇到了不少問題,結果出來後還是覺得有必要總結一下,唉!
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) //紅外資料輸入腳
當然,這隻是個人感覺!
就弄到這裡吧!!!