cortex_m3_stm32嵌入式學習筆記(二十三):紅外遙控實驗(輸入捕捉+解碼)

ARM 518瀏覽

? 紅外遙控是一種無線、非接觸控制技術,具有抗干擾能力強,信息傳輸可靠,功耗低,成本低,易實現等顯著優點,被諸多電子設備特別是家用電器廣泛采用,并越來越多的應用到計算機系統中。
??紅外遙控的編碼方式目前廣泛使用的是: PWM(脈沖寬度調制)的 NEC 協議和 PhilipsPPM(脈沖位置調制) 的 RC-5 協議的.ALIENTEK MiniSTM32 開發板配套的遙控器使用的是NEC 協議,其特征如下:
1、 8 位地址和 8 位指令長度;
2、地址和命令 2 次傳輸(確保可靠性)
3、 PWM 脈沖位置調制,以發射紅外載波的占空比代表“0”和“ 1”;
4、載波頻率為 38Khz;
5、位時間為 1.125ms 或 2.25ms;

? NEC 碼的位定義:一個脈沖對應 560us 的連續載波,一個邏輯 1 傳輸需要 2.25ms( 560us脈沖+1680us 低電平),一個邏輯 0 的傳輸需要 1.125ms( 560us 脈沖+560us 低電平)。而遙控接收頭在收到脈沖的時候為低電平,在沒有脈沖的時候為高電平,這樣,我們在接收頭端收到的信號為:邏輯 1 應該是 560us 低+1680us 高,邏輯 0 應該是 560us
低+560us 高。

??紅外遙控接收頭通過 P2 與 P3,連接在 STM32 的PA1( TIM5_CH2)上。硬件上,我們只需要拿一個跳線帽把 RMT 和 PA1 短接即可(默認已經短接)。然后, 程序將 TIM5_CH2 設計為輸入捕獲,然后將收到的脈沖信號解碼就可以了。

?
捕獲信號思路和輸入捕捉思路很像 詳見:點擊打開鏈接

?
紅外遙控初始化文件 remote.c

#include "remote.h"
#include "delay.h"
#include "usart.h"
//紅外遙控初始化
//設置IO以及定時器5的輸入捕獲
void Remote_Init(void)    			  
{  

	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_ICInitTypeDef  TIM_ICInitStructure;  
 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能PORTB時鐘 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);	//TIM5 時鐘使能 

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;				 //PA1 輸入 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 		//上拉輸入 
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOA,GPIO_Pin_1);	//初始化GPIOA1
	
						  
 	TIM_TimeBaseStructure.TIM_Period = 10000; //設定計數器自動重裝值 最大10ms溢出  
	TIM_TimeBaseStructure.TIM_Prescaler =(72-1); 	//預分頻器,1M的計數頻率,1us加1.	   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設置時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式

	TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根據指定的參數初始化TIMx

  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;  // 選擇輸入端 IC2映射到TI5上
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕獲
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置輸入分頻,不分頻 
  TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置輸入濾波器 8個定時器時鐘周期濾波
  TIM_ICInit(TIM5, &TIM_ICInitStructure);//初始化定時器輸入捕獲通道

  TIM_Cmd(TIM5,ENABLE ); 	//使能定時器5
 
 						
	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM5中斷
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占優先級0級
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //從優先級3級
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根據NVIC_InitStruct中指定的參數初始化外設NVIC寄存器	
 
 	TIM_ITConfig( TIM5,TIM_IT_Update|TIM_IT_CC2,ENABLE);//允許更新中斷 ,允許CC2IE捕獲中斷	
}

 
//遙控器接收狀態
//[7]:收到了引導碼標志
//[6]:得到了一個按鍵的所有信息
//[5]:保留	
//[4]:標記上升沿是否已經被捕獲								   
//[3:0]:溢出計時器
u8 	RmtSta=0;	  	  
u16 Dval;		//下降沿時計數器的值
u32 RmtRec=0;	//紅外接收到的數據	   		    
u8  RmtCnt=0;	//按鍵按下的次數	  
//定時器5中斷服務程序	 
void TIM5_IRQHandler(void)
{ 		    	 
    if(TIM_GetITStatus(TIM5,TIM_IT_Update)!=RESET)
	{
		if(RmtSta&0x80)//上次有數據被接收到了
		{	
			RmtSta&=~0X10;						//取消上升沿已經被捕獲標記
			if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;//標記已經完成一次按鍵的鍵值信息采集
			if((RmtSta&0X0F)<14)RmtSta++;
			else
			{
				RmtSta&=~(1<<7);//清空引導標識
				RmtSta&=0XF0;	//清空計數器	
			}						 	   	
		}							    
	}
 	if(TIM_GetITStatus(TIM5,TIM_IT_CC2)!=RESET)
	{	  
		if(RDATA)//上升沿捕獲
		{

			TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Falling);		//CC1P=1 設置為下降沿捕獲				
	    	TIM_SetCounter(TIM5,0);	   	//清空定時器值
			RmtSta|=0X10;					//標記上升沿已經被捕獲
		}else //下降沿捕獲
		{			
  			 Dval=TIM_GetCapture2(TIM5);//讀取CCR1也可以清CC1IF標志位
			 TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC4P=0	設置為上升沿捕獲
 			
			if(RmtSta&0X10)					//完成一次高電平捕獲 
			{
 				if(RmtSta&0X80)//接收到了引導碼
				{
					
					if(Dval>300&&Dval<800)			//560為標準值,560us
					{
						RmtRec<<=1;	//左移一位.
						RmtRec|=0;	//接收到0	   
					}else if(Dval>1400&&Dval<1800)	//1680為標準值,1680us
					{
						RmtRec<<=1;	//左移一位.
						RmtRec|=1;	//接收到1
					}else if(Dval>2200&&Dval<2600)	//得到按鍵鍵值增加的信息 2500為標準值2.5ms
					{
						RmtCnt++; 		//按鍵次數增加1次
						RmtSta&=0XF0;	//清空計時器		
					}
 				}else if(Dval>4200&&Dval<4700)		//4500為標準值4.5ms
				{
					RmtSta|=1<<7;	//標記成功接收到了引導碼
					RmtCnt=0;		//清除按鍵次數計數器
				}						 
			}
			RmtSta&=~(1<<4);
		}				 		     	    					   
	}
 TIM_ClearFlag(TIM5,TIM_IT_Update|TIM_IT_CC2);	    
}

//處理紅外鍵盤
//返回值:
//	 0,沒有任何按鍵按下
//其他,按下的按鍵鍵值.
u8 Remote_Scan(void)
{        
	u8 sta=0;       
    u8 t1,t2;  
	if(RmtSta&(1<<6))//得到一個按鍵的所有信息了
	{ 
	    t1=RmtRec>>24;			//得到地址碼
	    t2=(RmtRec>>16)&0xff;	//得到地址反碼 
 	    if((t1==(u8)~t2)&&t1==REMOTE_ID)//檢驗遙控識別碼(ID)及地址 
	    { 
	        t1=RmtRec>>8;
	        t2=RmtRec; 	
	        if(t1==(u8)~t2)sta=t1;//鍵值正確	 
		}   
		if((sta==0)||((RmtSta&0X80)==0))//按鍵數據錯誤/遙控已經沒有按下了
		{
		 	RmtSta&=~(1<<6);//清除接收到有效按鍵標識
			RmtCnt=0;		//清除按鍵次數計數器
		}
	}  
    return sta;
}

remote.h

#ifndef __RED_H
#define __RED_H 
#include "sys.h"   
#define RDATA 	PAin(1)	 	//紅外數據輸入腳
//紅外遙控識別碼(ID),每款遙控器的該值基本都不一樣,但也有一樣的.
//我們選用的遙控器識別碼為0
#define REMOTE_ID 0      		   
extern u8 RmtCnt;			//按鍵按下的次數
void Remote_Init(void);    	//紅外傳感器接收頭引腳初始化
u8 Remote_Scan(void);	    
#endif

函數不多,只有3個,初始化完了之后主要看掃描函數就好

主函數(包括遙控器上的按鍵對應的解碼)

本實驗主要是顯示所按下按鍵的值,其實遙控器真正的作用應該是控制才對,按下按鍵后對應相應的反應應該才是終極目標

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "lcd.h"
#include "remote.h"

void init()
{
	NVIC_Configuration();
	delay_init();	    	 //延時函數初始化	  
	uart_init(9600);	 	//串口初始化為9600
	LED_Init();		  		//初始化與LED連接的硬件接口
 	LCD_Init();
	Remote_Init();
	POINT_COLOR=RED;//設置字體為紅色 
	LCD_ShowString(60,50,200,16,16,"Mini STM32");	
	LCD_ShowString(60,70,200,16,16,"REMOTE TEST");	
	LCD_ShowString(60,90,200,16,16,"  yh  ");
	LCD_ShowString(60,110,200,16,16,"2015/2/8");
  LCD_ShowString(60,130,200,16,16,"KEYVAL:");	
  LCD_ShowString(60,150,200,16,16,"KEYCNT:");	
  LCD_ShowString(60,170,200,16,16,"SYMBOL:");
	POINT_COLOR=BLUE;
}
 int main(void)
{ 
	u8 key,t=0;
	u8 *str=0;
	init();
	while(1)
	{
		key=Remote_Scan();
		if(key)
		{
			LCD_ShowNum(116,130,key,3,16);		//顯示鍵值
			LCD_ShowNum(116,150,RmtCnt,3,16);	//顯示按鍵次數
			switch(key)
			{
				case 0:str="ERROR";break;			   
				case 162:str="POWER";break;	    
				case 98:str="UP";break;	    
				case 2:str="PLAY";break;		 
				case 226:str="ALIENTEK";break;		  
				case 194:str="RIGHT";break;	   
				case 34:str="LEFT";break;		  
				case 224:str="VOL-";break;		  
				case 168:str="DOWN";break;		   
				case 144:str="VOL+";break;		    
				case 104:str="1";break;		  
				case 152:str="2";break;	   
				case 176:str="3";break;	    
				case 48:str="4";break;		    
				case 24:str="5";break;		    
				case 122:str="6";break;		  
				case 16:str="7";break;			   					
				case 56:str="8";break;	 
				case 90:str="9";break;
				case 66:str="0";break;
				case 82:str="DELETE";break;
			}
			LCD_Fill(116,170,116+8*8,170+16,WHITE);	//清楚之前的顯示
			LCD_ShowString(116,170,200,16,16,str);	//顯示SYMBOL
		}
		else
			delay_ms(10);
		t++;
		if(t==20)
		{
			t=0;
			LED0=!LED0;
		}
	}
}

PS:找紅外接受頭找了半天QAQ



七星彩走势图2元网官网