1.器件簡介
本次測試採用R903V1紅外接收頭與NEC協議的紅外遙控器,接收頭原理圖如下:
器件的供電電壓VCC在2.7V~5.5V之間,輸出電壓VOUT正常在0.2v ~(VCC-0.3±0.2)v,注意高低電平輸出脈衝寬度最小都在400us~800us之間。
NEC 碼的位定義:一個脈衝對應 560us 的連續載波,一個邏輯 1 傳輸需要 2.25ms(560us脈衝+1680us 低電平),一個邏輯 0 的傳輸需要 1.125ms(560us 脈衝+560us 低電平)。而遙控接收頭在收到脈衝的時候為低電平,在沒有脈衝的時候為高電平,這樣,我們在接收頭端收到的訊號為:邏輯 1 應該是 560us 低+1680us 高,邏輯 0 應該是 560us 低+560us 高。同時NEC碼還規定了連發碼由 9ms 低電平+2.5m 高電平+0.56ms 低電平+97.94ms 高電平組成。
NEC協議的資料格式:同步碼頭、地址碼、地址反碼、控制碼、控制反碼。同步碼由一個 9ms 的低電平和一個 4.5ms 的高電平組成,地址碼、地址反碼、控制碼、控制反碼均是8 位資料格式。正常是按照低位在前,高位在後的順序傳送,但是我測試是按照高位在前,低位在後的。採用反碼是為了增加傳輸的可靠性(可用於校驗)。
2.硬體連線
簡單說一下,紅外接收器電壓接3.3V,VOUT連線微控制器的輸入捕獲引腳即可。
3.軟體設計
使用stm32微控制器的輸入捕獲功能,用stm32cubemx進行設定:
這裡解釋主要引數部分:
prescaler:預分頻器,設定為72
counter mode:計數模式,設為向上計數
counter period:計數週期,設為65535
polarity selection:邊沿檢測方式,選擇下降沿檢測,程式裡還是改成了上升沿檢測
input filter:輸入濾波器,設為8,是指連續採集到8個一樣的高/低電平才計作高/低電平
在MAIN中開啟輸入捕獲:
while (1) { switch(cap_state) { case 0: __HAL_TIM_SET_CAPTUREPOLARITY(&htim5, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING);//設定為上升沿捕獲 HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_2); //啟動輸入捕獲 cap_state++; break; } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }
/定時器輸入捕獲中斷回撥函式 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕獲中斷髮生時執行 { if(htim->Instance==TIM5) { switch(cap_state) { case 1: rise_value[rise_i] = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_2);//取上升沿時刻 __HAL_TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2,TIM_ICPOLARITY_FALLING); //設定為下降沿捕獲 cap_state++; break; case 2: fall_value[fall_i] = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_2);//取下降沿時刻 if(fall_value[fall_i] > rise_value[rise_i]) { rising_time[t_i] = fall_value[fall_i] - rise_value[rise_i];//高電平時間獲取 }else { rising_time[t_i] = 65535 + fall_value[fall_i] - rise_value[rise_i];//高電平時間獲取 } //控制碼 if(same_i == 1) { cnt+=1; //地址碼1~8 if(t_i<8) { if(rising_time[t_i]>500&&rising_time[t_i]<600) { address <<= 1; address+=0; }else if(rising_time[t_i]>1500&&rising_time[t_i]<1800) { address <<= 1; address+=1; } }else if(t_i>16&&t_i<25)//控制碼17~24 { if(rising_time[t_i]>500&&rising_time[t_i]<600) { rec <<= 1; rec+=0; }else if(rising_time[t_i]>1500&&rising_time[t_i]<1800) { rec <<= 1; rec+=1; } } t_i++; rise_i++; fall_i++; } //同步碼 if(rising_time[t_i]>4300&&rising_time[t_i]<4700) { //同步碼正確 same_i=1; t_i++; rise_i++; fall_i++; } HAL_TIM_IC_Stop_IT(&htim5,TIM_CHANNEL_2); //停止捕獲 cap_state=0; break; } } }
//按鍵中斷回撥 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { //KEY0 if (GPIO_Pin==GPIO_PIN_5) { HAL_Delay(20);/* 延時一小段時間,消除抖動 */ if (HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)==0) { HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8); for(int i=0;i<=fall_i;i++) { if(fall_value[i] > rise_value[i]) { rising_time[i] = fall_value[i] - rise_value[i];//高電平時間獲取 }else { rising_time[i] = 65535 + fall_value[i] - rise_value[i];//高電平時間獲取 printf("hear:%d\r\n",i); } printf("高電平[%d]:%d us 下降沿時刻[%d]:%d us 上升沿時刻[%d]:%d us\r\n",i,rising_time[i],i,fall_value[i],i,rise_value[i]); } printf("地址:%d 控制碼:%lld\r\n",address,rec); rise_i=fall_i=same_i=t_i=cnt=address=rec=0; } __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_5); } }
大致說一下程式流程,在main中迴圈開啟上升沿檢測輸入捕獲功能(case 0),接著在輸入捕獲中斷回撥裡記下上升沿觸發時刻(rise_value),並改為下降沿檢測捕獲,再記下下降沿觸發時刻(fall_value)並關閉捕獲功能。
高電平持續時間(rising_time) = 下降沿觸發時刻(fall_value) - 上升沿觸發時刻(rise_value)
獲取到一系列高電平以後(看示波器圖),根據高電平持續時間進行數值移位換算,可以得到對應的地址碼和控制碼,串列埠列印如下:
列印結果中的hear是下降沿時刻溢位的情況,計算時需要在下降沿時刻基礎上再加65535才可以。
感興趣的可以試一試,需要原工程的留言我發。