STM32定時器輸入捕獲功能應用——超聲波模塊


一、工作原理

輸入捕獲是STM32單片機定時器的一項重要的功能,應用很廣泛,常用於測量脈沖寬度,周期等。

超聲波模塊測距的原理是:單片機給超聲波模塊(我用到的超聲波模塊型號是HC-SR04,下面簡稱HC-SR04)發送一個大於10us的高電平,觸發HC-SR04發出8個40kHz的方波,並自動檢測是否有信號返回,如果有信號返回,就會通過Echo對單片機輸出一個高電平,高電平的持續時間就是超聲波從發射到返回的時間。

換而言之,單片機的工作就是給HC-SR04的Trig端發送一個一個大於10us的高電平,觸發HC-SR04工作,然后利用輸入捕獲功能計算出HC-SR04的Echo端輸入的高電平持續時間就可以測出超聲波發出到返回的時間,聲音在空氣中的傳播速度是340m/s,因此利用公式:測試距離=(高電平時間 * 聲速)/2,就可以算出超聲波模塊與前方的物體之間的距離是多少。原理圖如下:

 

用一個簡圖來說明輸入捕獲測量高電平延續時間的實現原理:

 

 

二、利用CubeMX生成驅動代碼

HC-SR04上有4個引腳:VCC(5V)、GND、Trig(控制端)、Echo(接收端),所以需要配置一個GPIO作為控制HC-SR04的引腳,Echo這個引腳在配置定時器的時候就會自動配置好,不需要單獨配置。另外還需要配置一個串口作為打印口方便調試。

1、時鍾源配置:

 

 

 2、定時器

 

 

 

 

 3、控制引腳的配置

 4、開啟串口

 

 

 配置完成后生成代碼。

三、修改代碼

1、寫一個us級別的延時:

/********************************************
*函數名稱:void delay_us(__IO uint32_t delay)
*函數形參:__IO uint32_t delay--延時時間
*函數返回值:無
*函數功能:微秒級別延時
*********************************************/
void delay_us(__IO uint32_t delay)
{
    int last, curr, val;
    int temp;

    while (delay != 0)
    {
        temp = delay > 900 ? 900 : delay;
        last = SysTick->VAL;
        curr = last - CPU_FREQUENCY_MHZ * temp;
        if (curr >= 0)
        {
            do
            {
                val = SysTick->VAL;
            }
            while ((val < last) && (val >= curr));
        }
        else
        {
            curr += CPU_FREQUENCY_MHZ * 1000; 
            do
            {
                val = SysTick->VAL;
            }
            while ((val <= last) || (val > curr));
        }
        delay -= temp;
    }
}

 

2、重定向

/********************************************
*函數名稱:int fputc(int ch, FILE* stream)
*函數功能:重定向
*********************************************/
int fputc(int ch, FILE* stream)
{
    
   HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
    return ch;
}

3、觸發信號

/********************************************
*函數名稱:void Trigger_Signal(__IO uint32_t us)
*函數形參:__IO uint32_t us--觸發信號保持時間
*函數返回值:無
*函數功能:觸發信號
*********************************************/
void Trigger_Signal(__IO uint32_t us)
{
	HAL_GPIO_WritePin(Trigger_GPIO_Port,Trigger_Pin,GPIO_PIN_SET);
	delay_us(us);
	HAL_GPIO_WritePin(Trigger_GPIO_Port,Trigger_Pin,GPIO_PIN_RESET);
	printf("發送觸發信號\r\n");
}

4、 輸入捕獲中斷回調函數:

/********************************************
*函數名稱:void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
*函數形參:TIM_HandleTypeDef *htim--定時器句柄
*函數返回值:無
*函數功能:捕獲到高電平后,計數器清零,配置為低電平捕獲,
	當捕獲到低電平時,讀出CCR的值
*備注:TIM2_CH2_CAPTURE_STA這個是uint8_t數據類型的全局變量,
   它的每一位數據可以自定義為某些狀態,在這里,第7位為1表示捕獲完成,為0表示未完成,
   第6位為1表示捕獲到上升沿,為0表示未捕獲到高電平,0~5bit保留;
   TIM2_CH2_ELAPSED_CNT也是一個全局變量,表示從捕獲到高電平起,計數器溢出的次數,
   TIM2_CH2_CAPTURE_VAL也是一個全局變量,當捕獲到下降沿后把CCR2的值讀取到這個變量里
* *********************************************/ void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if((TIM2_CH2_CAPTURE_STA&0x80) != 0x80)//還未完成捕獲 { if((TIM2_CH2_CAPTURE_STA&0x40) == 0)  //此前尚未捕獲到上升沿,那么這次捕獲到的就是上升沿 { TIM2_CH2_CAPTURE_VAL = 0;    //清零,防止干擾 TIM2_CH2_ELAPSED_CNT = 0;    //清零,捕獲到上升沿后重新計算周期溢出次數 TIM2_CH2_CAPTURE_STA |= 0x40;  //捕獲到一個上升沿 __HAL_TIM_DISABLE(&htim2);    //停止TIM2 __HAL_TIM_SET_COUNTER(&htim2,0);  //把TIM2的計數器清零 TIM_RESET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_2);  //清除原來的設置 TIM_SET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_2,TIM_ICPOLARITY_FALLING);  //將TIM2的通道2輸入捕獲設置為下降沿捕獲 __HAL_TIM_ENABLE(&htim2);  //使能TIM2 } else { TIM2_CH2_CAPTURE_STA |= 0x80;  //捕獲到一個下降沿,代表捕獲完成 TIM2_CH2_CAPTURE_VAL = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2);  //把此時CCR2的值讀到變量TIM2_CH2_CAPTURE_VAL TIM_RESET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_2);  //清除原來的設置 TIM_SET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_2,TIM_ICPOLARITY_RISING);  //設置為上升沿捕獲 } } }

5、計數周期溢出中斷回調函數:

/********************************************
*函數名稱:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
*函數形參:TIM_HandleTypeDef *htim--定時器句柄
*函數返回值:無
*函數功能:定時器溢出次數計算
*********************************************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	TIM2_CH2_ELAPSED_CNT++;    //每次溢出,該變量增加1
}

6、計算高電平的持續時間:

/********************************************
*函數名稱:uint32_t CalculatePulseWide(void)
*函數形參:無
*函數返回值:無
*函數功能:計算出高電平的寬度
*********************************************/
uint32_t CalculatePulseWide(void)
{
	uint32_t PulseWide = 0;
	if((TIM2_CH2_CAPTURE_STA&0x80) == 0x80)
	{
		PulseWide = 0xffff*TIM2_CH2_ELAPSED_CNT+TIM2_CH2_CAPTURE_VAL;
		TIM2_CH2_CAPTURE_STA = 0;   //計算完將該變量清零,其實即使不清零應該也沒關系,每次捕獲到上升沿也會清零
		TIM2_CH2_ELAPSED_CNT = 0;   //計算完將該變量清零,其實即使不清零應該也沒關系,每次捕獲到上升沿也會清零

     }
     return PulseWide;
}

7、計算距離:

/********************************************
*函數名稱:void GetDistance(void)
*函數形參:無
*函數返回值:無
*函數功能:獲取距離
*********************************************/
void GetDistance(void)
{
	float temp = 0;
	float distance = 0;
	Trigger_Signal(20);  //發送觸發信號,因為要大於10us,這里就設置為20us
	while(!temp)      //等待計算出高電平的時間,如果temp為0,說明還未計算出來,繼續等待
	{
		temp = CalculatePulseWide();		
	}
	printf("temp:%f\r\n",temp);  
	distance = (float)(temp*0.034)/2;  //計算出距離
	printf("distant:%.2f CM\r\n",distance);

	HAL_Delay(100);    //手冊中說明兩次測量的時間間距最好大於60ms,避免引起干擾,這里取100ms

}

 8、主函數 

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	printf("********** HC-RS04 *********\r\n");
	HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);  //開啟輸入捕獲中斷
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		GetDistance();  //計算出距離
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

  

 完成代碼修改后,燒錄到單片機上,進行了測量,發現測得的距離跟實際距離之間的誤差不大,當實際距離等於1米的時候,誤差大概在1~5cm左右波動。

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM