1. 程式人生 > >STM32學習筆記一一紅外遙控

STM32學習筆記一一紅外遙控

1. 簡述

紅外遙控是一種無線、非接觸控制技術,具有抗干擾能力強,資訊傳輸可靠,低功耗,低成本。

紅外遙控的編碼方式目前廣泛使用的是: PWM (脈衝寬度調製)的 NEC 協議和 Philips PPM(脈衝位置調製) 的 RC-5 協議的。

1.1 NEC 協議定義

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

發射端邏輯:
在這裡插入圖片描述

在這裡插入圖片描述

遙控接收頭邏輯:

在這裡插入圖片描述

在這裡插入圖片描述

1.2 NEC 協議特點

(1) 8 位地址和 8 位指令長度;
(2)地址和命令 2 次傳輸(確保可靠性);
(3) PWM 脈衝位置調製,以發射紅外載波的佔空比代表“0”和“1”;
(4)載波頻率為 38Khz;
(5)位時間為 1.125ms 或 2.25ms;

1.3 NEC 遙控指令的資料格式

在這裡插入圖片描述

採用反碼是為了增加傳輸的可靠性。NEC 碼規定的連發碼(由 9ms 低電平+2.5m 高電平+0.56ms 低電平+97.94ms 高電平組成),如果在一幀資料傳送完畢之後,按鍵仍然沒有放開,則發射重複碼,即連發碼。

2. 軟體實現

上面我們基本瞭解了 NEC 格式的紅外發碼和收碼的格式,就可以根據通訊的協議來完成對應的程式。

程式邏輯:

在這裡插入圖片描述

2.1 初始化

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);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,
ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; 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); TIM_TimeBaseStructure.TIM_Period = 10000;//設定自動重灌值,10ms溢位 TIM_TimeBaseStructure.TIM_Prescaler = (72-1);//預分頻器,1MHz的計數頻率,1us加一 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//時鐘分割 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上計數模式 TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure); 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;//搶佔優先順序1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//從優先順序3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC2,ENABLE);//允許更新中斷,允許CC2IE捕獲中斷 }

2.2 中斷捕獲

u8 RmtSta=0;
u16 Dval;
u32 RmtRec=0;
u8 RmtCnt=0;
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的值
			TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Rising);//設定為上升沿捕獲
			if(RmtSta&0x10)
			{
				if(RmtSta&0x80)//接收到引導碼
				{
					if(Dval>300&&Dval<800)//高電平為560us
					{
						RmtRec <<= 1;
						RmtRec |= 0;//接收到0碼
					}
					else if(Dval>1400&&Dval<1800)//高電平為1680us
					{
						RmtRec <<= 1;
						RmtRec |= 1;//接收到1碼
					}
					else if(Dval>2200&&Dval<2600)//連發碼判斷
					{
						RmtCnt++;
						RmtSta &= 0xF0;//清空計數器
					}
				}		
					else if(Dval>4200&&Dval<4700)
					{
						RmtSta |= 1<<7;//記錄接收到引導碼
						RmtCnt = 0;
					}
			}
			RmtSta &= ~(1<<4);
		}
	}
	TIM_ClearFlag(TIM5,TIM_IT_Update|TIM_IT_CC2);
}

2.3 遙控鍵值掃描

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)//校驗遙控識別碼以及遙控接收地址
		{
			t1 = RmtRec >> 8;//控制碼
			t2 = RmtRec;//控制反碼
			if(t1==(u8)~t2)
				sta = t1;
		}
		if((sta==0)||((RmtSta&0x80)==0))//接收錯誤或者按鍵沒有按下
		{
			RmtSta &= ~(1<<6);//清除接收按鍵有效標誌位
			RmtCnt = 0;
		}
	}
	return sta;
}

參考:

1.原子庫函式參考手冊