天天看點

基于STM32F4的紅外控制基于STM32F4的紅外控制紅外控制原理按鍵擡起檢測定時器的捕獲定時器的配置和紅外解碼源碼總結

基于STM32F4的紅外控制

紅外控制原理

從紅外接收頭接收到的編碼可以看出,-幀編碼以引導碼開始,然後跟随32位的系統和資料碼。引導碼是以下降沿開始,按照輸入捕獲的原理,在引導碼的第一個下降沿到來時,将此時的計數值捕獲,第二個下降沿到來時,再次捕獲計數值,計算第二次 的計數值和第一次計數值的內插補點,如果內插補點時間在13.5ms附近(編碼時間上會有一定誤差),則認為是引導碼,例如時間差Δt在13ms-14ms之間,則認為是引導碼。同理,如果時間差Δt在1ms-1.3ms之間,則認為時編碼‘0’,如果時間差Δt在2ms-2.5ms之間,則認為時編碼‘1’。 還要注意的是,計數內插補點機關是個數,而兩次紅外編碼兩次下降沿時間間隔機關為ms,它們相比較需要機關統一,是以要将計數內插補點轉換為ms或者将ms轉換成內插補點個數,轉換時需要用到定時器驅動時鐘的周期,如果将ms轉換為個數,則隻需要将兩次下降沿時間差除以時鐘周期即可得到個數,如果将個數轉換為ms,則隻需将內插補點個數乘以時鐘周期即可得到ms的機關時間差。

接收端接收到碼除引導碼外有32位二進制碼組成,其中前16位為使用者識别碼,能差別不同的電器裝置,防止不同機種遙控碼互相幹擾。後16位為8位操作碼(功能碼)及其反碼,可以進行編碼的校驗。發射端發射的碼和接收端的碼波形是相反的。遙控器在按鍵按下後,周期性地發出同一種32位二進制碼,周期約為108ms。一組碼本身的持續時間随它包含的二進制“0”和“1”的個數不同而不同,大約在45-63ms之間, 下圖為接收波形圖。

按鍵擡起檢測

當一個鍵按下超過36ms,振蕩器使晶片激活,将發射一組108ms的編碼脈沖,這108ms發射代碼由一個引導碼(9ms) ,一個結果碼(4. 5ms) ,低8位位址碼(9ms-18ms) ,高8位位址碼(9ms-18ms) , 8位資料碼(9ms-18ms)和這8位資料的反碼(9ms-18ms) 組成。如果鍵按下超過108ms仍未松開,接下來發射的代碼(連發碼)将僅由起始碼(9ms) 和結束碼(2. 25ms)組成。

基于STM32F4的紅外控制基于STM32F4的紅外控制紅外控制原理按鍵擡起檢測定時器的捕獲定時器的配置和紅外解碼源碼總結

定時器的捕獲

STM32F4除了定時5和6不能捕獲外,其他的定時器都可一進行捕獲,即捕獲脈沖波形的下降沿或者是上升沿。

下圖是定時器捕獲紅外編碼原理,其中定時器先進行(168-1)的分頻,時鐘頻率為84MHz,即2us定時器加1,定時器自動重裝為0xFFFF,即溢出時間約為65536*2us = 131ms,以使得定時器可以在1-2個定時器溢出周期獲得紅外編碼。

定時器捕獲共有兩種情況,第一種情況是在一個定時器溢出周期能夠采集完,即兩次捕獲的下降沿時間為t2 - t1,第二種情況是在第二個定時器溢出周期獲得紅外編碼,那麼他的捕獲下降沿的時間差為0xFFFF - t3 + t4。

基于STM32F4的紅外控制基于STM32F4的紅外控制紅外控制原理按鍵擡起檢測定時器的捕獲定時器的配置和紅外解碼源碼總結

定時器的配置和紅外解碼源碼

下面是定時器的的配置函數

Remote_Init

.采用定時器3的channel3進行捕獲,并對IO口進行複用,系統時鐘為84MHz,溢出時間為2us。

//紅外遙控初始化
//設定IO以及TIM3_CH3的輸入捕獲
void Remote_Init(void)    			  
{		
    GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_ICInitTypeDef  TIM_ICInitStructure;  
 
 	RCC_APB2PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能PORTB時鐘 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);	//TIM3 時鐘使能 

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;			// PB0 輸入 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; 		// 上拉輸入 
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
 	GPIO_ResetBits(GPIOB, GPIO_Pin_0);	//初始化GPIOB0
	
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3); //GPIOB0複用為TIM3
						  
 	TIM_TimeBaseStructure.TIM_Period = 0xffff; 			//設定計數器自動重裝值 最大131ms溢出  
	TIM_TimeBaseStructure.TIM_Prescaler = (176 - 1); 	//預分頻器,2us加1.t = (arr+1)*(psc+1)/84000000	   	
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設定時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據指定的參數初始化TIMx

    TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;  // 選擇輸入端 IC3映射到TI3上
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置輸入分頻,不分頻 
    TIM_ICInitStructure.TIM_ICFilter = 0x00;	//IC4F=0011 配置輸入濾波器 8個定時器時鐘周期濾波
    TIM_ICInit(TIM3, &TIM_ICInitStructure);		//初始化定時器輸入捕獲通道
	
	TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允許更新中斷 ,允許CC4IE捕獲中斷
    TIM_Cmd(TIM3, ENABLE ); 	//使能定時器3
	
	TIM_ClearFlag(TIM3, TIM_IT_CC3|TIM_IT_Update);
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中斷
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占優先級1級
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //從優先級3級
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根據NVIC_InitStruct中指定的參數初始化外設NVIC寄存器	
}
           

下面是定時器3的回調函數

TIM3_IRQHandler

.分别進入定時器3的溢出中斷和捕獲中斷對紅外遙控器進行解碼。

void TIM3_IRQHandler(void)
{ 		    	 
 
	if(TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET)
	{
		TIM_ClearITPendingBit(TIM3, TIM_IT_CC3);
		IR_Up = 0;								// 每次進入捕獲中斷就清除溢出次數計數值
		IR_ThisPulse = TIM_GetCapture3(TIM3);	// 擷取通道3的捕獲值
		
		if(IR_ThisPulse > IR_LastPulse)			// 這次捕獲的值大于上次捕獲的值
		{
			IR_PulseSub = IR_ThisPulse - IR_LastPulse;	// 得到時間差
		}
		else									// 小于時要加上0xffff
		{
			IR_PulseSub = 0xffff - IR_LastPulse + IR_ThisPulse;	// 得到時間差
		}
		IR_LastPulse = IR_ThisPulse;			// 将本次得到值作為下一次編碼的前一個值
		IR_PulseCnt++;					// 解碼位數加1
		if(IR_PulseCnt == 2)
		{
			if((IR_PulseSub > 6000) && (IR_PulseSub < 8000))	// 引導碼範圍,13.5ms 13.5ms/2us = 6750
			{
				IR_Sta = 0x01;			// 标志位為1, 表示引導碼已經收到
			}
			else
				IR_PulseCnt = 0;		// 如果時間差沒有在引導碼範圍内,中斷次數清零,重新編碼
		}
		if((IR_PulseCnt > 2) && (IR_Sta == 0x01))	// 中斷次數大于2,并且引導碼已經解完
		{
			IR_Code <<= 1;				// 存儲紅外編碼的寄存器向左移一位, 以便于存儲下一位放到最低位
			if((IR_PulseSub > 450) && (IR_PulseSub < 700))	// 編碼‘0’範圍, 1.125ms	1.125/2us = 562.5
			{
				IR_Code |= 0x00;		// 存儲0
			}
			else if((IR_PulseSub > 800) && (IR_PulseSub) < 1300) // 編碼‘1’範圍, 2.25ms	2.25/2us = 1125
			{
				IR_Code |= 0x01;		// 存儲1
			}
			else				// 如果不是0碼也不是1碼, 變量清零,重新解碼
			{
				IR_Sta = 0;
				IR_Code = 0;
				IR_PulseCnt = 0;
			}
			
		}
		if(IR_PulseCnt == 34)		// 如果解完碼, 第34次進入中斷正好解完一幀碼
		{
			IR_Key = IR_Code;		// 存放解出的碼
			IR_Sta = 0x02;			// 進入連發碼的狀态
			flag = 1;				// 解完碼标志位1,進行編碼處理
		}
		if((IR_PulseCnt == 36) && (IR_Sta == 0x02)) // 2位引導碼,32位資料碼
		{
			IR_PulseCnt = 34;			// 按鍵不松手便認為他在34
			if((IR_PulseSub > 4500) && (IR_PulseSub < 6000))	// 進入連發狀态, 11.5ms/2us = 5525
			{
				LianfaCnt++;
				IR_Key = IR_Code;
				flag = 1;
			}
		}		
	}
	if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)
	{
		TIM_ClearITPendingBit(TIM3,TIM_IT_Update);	
		if(RDATA == 1)
		{
			IR_Up++;
			if(IR_Up >= 2)
			{
				IR_Code = 0;
				IR_Sta = 0;
				IR_PulseCnt = 0;
				LianfaCnt = 0;
			}
		}
	}
	
}
           

總結

解碼的關鍵是如何識别“0”和“1”,解碼方法也有很多種方法,第一種使用讀取GPIO端口的輸入電平的方法:從位的定義我們可以發現“0”“1”均以0. 56ms的低電平開始。不同的是高電平的寬度不同,“0”為0. 56ms,“1”為1. 68ms,是以必須根據高電平的寬度差別“0”和“1”。如果從0.56ms低電平過後,開始延時,0. 56ms以後,若讀到的電平為低,說明該位為“0”,反之則為“1”,為了可靠起見,延時必須比0. 56ms長些,但又不能超過1.12ms,否則如果該位為“0”,讀到的已是下一位的高電平,是以取(1. 12ms+0. 56ms )/2=0. 84ms最為可靠,一般取0. 84ms左右均可;第二種采用單片機的外部中斷功能:由于編碼0和編碼1的周期不同,隻要計算兩次下降沿之間的時間差,即可判斷是編碼0還是編碼1;第三種采用單片機的輸入捕獲功能,在紅外編碼的下降沿捕捉計數器的值,記錄兩次下降沿捕捉到的計數器的值,然後後- -次減前一次即可得到時間差,進而判斷是編碼0還是編碼1.

源碼如下:

連結: 紅外遙控.