stm32與紅外遙控器(NEC協議)

墨跡打鐵匠發表於2020-10-23

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中開啟輸入捕獲:

stm32與紅外遙控器(NEC協議)
  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 */
  }
main迴圈體
stm32與紅外遙控器(NEC協議)
/定時器輸入捕獲中斷回撥函式
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;
        }
    }
}
輸入捕獲中斷回撥
stm32與紅外遙控器(NEC協議)
//按鍵中斷回撥
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才可以。

感興趣的可以試一試,需要原工程的留言我發。

相關文章