本次實驗是用HAL庫編寫定時器捕獲程序,來獲得距離值,並通過串口助手顯示距離值。移植的是原子的實驗九輸入捕獲實驗的程序
實驗前準備:
- 阿波羅STM3F429開發板
- HC-SR04超聲波模塊
- 串口調試助手
- 四根杜邦線
關於HC-SR04超聲波測距模塊,需要注意看它的時序圖,如下:
可知使用該模塊我們需要給至少10us的觸發信號,在本實驗中我給了20us的觸發信號:
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);//打開PB6
delay_us(20);//向PB6輸入一個長爲20us的高電平方波
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);//關閉PB6
而模塊內部發出信號是傳感器自動迴應的,不需要代碼去實現。
輸出迴響信號是通過對輸入捕獲實現,在向上計數模式下,通過計算高電平的時間,即捕獲一次上升沿,清零計數器的值後再捕獲一次下降沿的值,根據定時器的計數頻率,就可以算出兩次捕獲的時間,從而得到高電平脈寬,也就得到了距離。(詳細請看正點原子STM32F429開發指南-HAL庫版本_V1.1——實驗九輸入捕獲實驗)
接線如下:
HC-SR04 STM32
VCC -> VCC
Trig -> PB6
Echo -> PA0
GND -> GND
下面貼出我覺得需要注意的地方的代碼:
//定時器5初始化
void ultrasound_init()
{
TIM5_Handler.Instance=TIM5;//定時器5
TIM5_Handler.Init.Prescaler=90-1;//預分屏係數
TIM5_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上計數
TIM5_Handler.Init.Period=0xFFFFFFFF;//自動裝載值
TIM5_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; //時鐘分頻因子
HAL_TIM_IC_Init(&TIM5_Handler);
TIM5_CH1Config.ICPolarity=TIM_ICPOLARITY_RISING;//上升沿捕獲
TIM5_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//映射到TI1上
TIM5_CH1Config.ICPrescaler=TIM_ICPSC_DIV1;//配置輸入分頻,不分頻
TIM5_CH1Config.ICFilter=0;//配置輸入濾波器,不濾波
HAL_TIM_IC_ConfigChannel(&TIM5_Handler,&TIM5_CH1Config,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&TIM5_Handler,TIM_CHANNEL_1); //開啓TIM5的捕獲通道1,並且開啓捕獲中斷
__HAL_TIM_ENABLE_IT(&TIM5_Handler,TIM_IT_UPDATE); //使能更新中斷
}
//定時器5回調函數
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM5)
{
__HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIOA時鐘
__HAL_RCC_TIM5_CLK_ENABLE();//使能TIM5時鐘
GPIO_Initure.Pin=GPIO_PIN_0;//Pin0
GPIO_Initure.Mode=GPIO_MODE_AF_PP;//複用推輓輸出
GPIO_Initure.Speed=GPIO_SPEED_HIGH;//高速
GPIO_Initure.Pull=GPIO_PULLDOWN;//下拉
GPIO_Initure.Alternate=GPIO_AF2_TIM5;//PA0 複用爲 TIM5 通道 1
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
HAL_NVIC_SetPriority(TIM5_IRQn,2,0); //設置中斷優先級,搶佔 2,子優先級 0
HAL_NVIC_EnableIRQ(TIM5_IRQn); //開啓 ITM5 中斷通道
}
__HAL_RCC_GPIOB_CLK_ENABLE(); //使能PB時鐘
GPIO_InitStructure.Pin= GPIO_PIN_6;
GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;//推輓輸出
GPIO_InitStructure.Pull=GPIO_PULLUP;//上拉
GPIO_InitStructure.Speed=GPIO_SPEED_HIGH;//高速
HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
}
一開始調試的時候我得到的距離是0,後來對比了一下別人的代碼,發現我GPIOA的管腳模式選擇錯了,應該要選擇GPIO_MODE_AF_PP(複用推完輸出),第二個是 HAL_TIM_IC_Start_IT(&TIM5_Handler,TIM_CHANNEL_1); 開啓中斷函數弄錯了,不知怎麼就少寫了_IT,導致後面檢查才發現沒開啓中斷。實在是太大意了。
接下來是主函數的測距實現:
int main(void)
{
long time=0;
float Distance;
HAL_Init(); //初始化HAL庫
Stm32_Clock_Init(360,25,2,8); //設置時鐘,180Mhz
delay_init(180); //初始化延時函數
uart_init(115200); //初始化USART
LED_Init(); //初始化LED
ultrasound_init(); //以1MHZ的頻率計數
while (1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);//打開PB6
delay_us(20);//向PB6輸入一個長爲20us的高電平方波
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);//關閉PB6
//delay_us(20);
if(TIM5CH1_CAPTURE_STA&0X80) //成功捕獲到了一次高電平
{
time=TIM5CH1_CAPTURE_STA&0X3F;
time*=0XFFFFFFFF; //溢出時間總和
time+=TIM5CH1_CAPTURE_VAL; //得到總的高電平時間
Distance=time*340/20000;
printf("the distance is %.2f cm\r\n",Distance);
delay_ms(1000);
TIM5CH1_CAPTURE_STA=0; //開啓下一次捕獲
}
}
}
Distance=time*340/20000;聲音在空氣中傳播的大約速度爲340m/s,而且是一個回傳,所以得到的距離還需要除以2纔是最後的實際距離。(計算的Distance單位是cm)
詳細解釋看mc.six的帖子
實驗測出距離如下: