提示:文章寫完後,目錄可以自動生成,如何生成可參考右邊的幫助文檔
文章目錄
- 前言
- 一、超音波子產品的工作原理
- 二、超音波的時序圖
- 三、利用STM32F407進行驅動編寫
-
- 1.定時器配置
- 總結
前言
今天我們針對超音波測距子產品進行學習,HC-SR04超音波測距子產品可提供2cm-400cm的非接觸式距離感測功能,測距精度可達高到3mm;子產品包括超音波發射器、接收器與控制電路。像智能小車的測距以及轉向,或是一些項目中,常常會用到。智能小車測距可以及時發現前方的障礙物,使智能小車可以及時轉向,避開障礙物。在後續各種比賽中超音波子產品必不可少。
一、超音波子產品的工作原理
針對HC-SR04子產品,依據其資料手冊我們可以了解到它的工作原理: I、給超音波子產品接入電源和地。 II、給脈沖觸發引腳(trig)輸入一個長為20us的高電平方波。 III、輸入方波後,子產品會自動發射8個40KHz的聲波,與此同時回波引腳(echo)端的電平會由0變為1;(此時應該啟動定時器計時)。 IV、當超音波傳回被子產品接收到時,回波引 腳端的電平會由1變為0;(此時應該停止定時器計數),定時器記下的這個時間即為超音波由發射到傳回的總時長。 V、根據聲音在空氣中的速度為344米/秒,即可計算出所測的距離。
二、超音波的時序圖
![在這裡插入圖檔描述](https://img-blog.csdnimg.cn/bf13dcbdcac544d4a6171b07a53b767f.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NDQ4MDg4,size_16,color_FFFFFF,t_70#pic_center) 從時序圖中,我們可以看到HC-SR04屬于電平觸發型子產品,利用單片機對其輸入10us的高電平觸發其測距功能,然後利用定時器捕獲得到回響電平的高電平時間,利用公式計算得出所測的距離,這裡需要注意測出的距離需要除以2,因為超神波是一來一回的。
三、利用STM32F407進行驅動編寫
1.定時器配置
因為考慮到F4的開發闆的定時器捕獲通道最多有4路,是以如果一個定時器隻對一個超音波子產品進行捕獲,太浪費資源,因而如何讓利用一個定時器對多個子產品進行采內建為了我接下來主要解決的問題。 相關配置如下:
void TIM5_Cap_Init(u32 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE); //TIM5時鐘使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTA時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //GPIOA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//複用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽複用輸出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA0
GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM5); //PA0複用位定時器5
GPIO_PinAFConfig(GPIOA,GPIO_PinSource1,GPIO_AF_TIM5);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_TIM5);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_TIM5);
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定時器分頻
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上計數模式
TIM_TimeBaseStructure.TIM_Period=arr; //自動重裝載值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);
//初始化TIM5輸入捕獲參數
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 選擇輸入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置輸入濾波器 不濾波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;//CC1S=01 選擇輸入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置輸入濾波器 不濾波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_3; //CC1S=01 選擇輸入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置輸入濾波器 不濾波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_4; //CC1S=01 選擇輸入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置輸入濾波器 不濾波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);//允許更新中斷 ,允許CC1IE捕獲中斷
TIM_ITConfig(TIM5,TIM_IT_CC1,ENABLE);
TIM_ITConfig(TIM5,TIM_IT_CC2,ENABLE);
TIM_ITConfig(TIM5,TIM_IT_CC3,ENABLE);
TIM_ITConfig(TIM5,TIM_IT_CC4,ENABLE);
TIM_Cmd(TIM5,ENABLE ); //使能定時器5
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//搶占優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器、
}
## 2.捕獲中斷編寫 相關配置如下:
//捕獲狀态
//[7]:0,沒有成功的捕獲;1,成功捕獲到一次.
//[6]:0,還沒捕獲到低電平;1,已經捕獲到低電平了.
//[5:0]:捕獲低電平後溢出的次數(對于32位定時器來說,1us計數器加1,溢出時間:4294秒)
u8 TIM5CH1_CAPTURE_STA=0; //輸入捕獲狀态
u32 TIM5CH1_CAPTURE_VAL; //輸入捕獲值(TIM2/TIM5是32位)
u8 TIM5CH2_CAPTURE_STA=0; //輸入捕獲狀态
u32 TIM5CH2_CAPTURE_VAL; //輸入捕獲值(TIM2/TIM5是32位)
u8 TIM5CH3_CAPTURE_STA=0; //輸入捕獲狀态
u32 TIM5CH3_CAPTURE_VAL; //輸入捕獲值(TIM2/TIM5是32位)
u8 TIM5CH4_CAPTURE_STA=0; //輸入捕獲狀态
u32 TIM5CH4_CAPTURE_VAL; //輸入捕獲值(TIM2/TIM5是32位)
//定時器5中斷服務程式
void TIM5_IRQHandler(void)
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//還未成功捕獲
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)//溢出
{
if(TIM5CH1_CAPTURE_STA&0X40)//已經捕獲到高電平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高電平太長了
{
TIM5CH1_CAPTURE_STA|=0X80; //标記成功捕獲了一次
TIM5CH1_CAPTURE_VAL=0XFFFFFFFF;
}else TIM5CH1_CAPTURE_STA++;
}
}
if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕獲1發生捕獲事件
{
if(TIM5CH1_CAPTURE_STA&0X40) //捕獲到一個下降沿
{
TIM5CH1_CAPTURE_STA|=0X80; //标記成功捕獲到一次高電平脈寬
TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);//擷取目前的捕獲值.
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 設定為上升沿捕獲
}else //還未開始,第一次捕獲上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM5CH1_CAPTURE_STA|=0X40; //标記捕獲到了上升沿
TIM_Cmd(TIM5,DISABLE ); //關閉定時器5
TIM_SetCounter(TIM5,0);
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 設定為下降沿捕獲
TIM_Cmd(TIM5,ENABLE ); //使能定時器5
}
}
}
if((TIM5CH2_CAPTURE_STA&0X80)==0)//還未成功捕獲
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)//溢出
{
if(TIM5CH2_CAPTURE_STA&0X40)//已經捕獲到高電平了
{
if((TIM5CH2_CAPTURE_STA&0X3F)==0X3F)//高電平太長了
{
TIM5CH2_CAPTURE_STA|=0X80; //标記成功捕獲了一次
TIM5CH2_CAPTURE_VAL=0XFFFFFFFF;
}else TIM5CH2_CAPTURE_STA++;
}
}
if(TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕獲1發生捕獲事件
{
if(TIM5CH2_CAPTURE_STA&0X40) //捕獲到一個下降沿
{
TIM5CH2_CAPTURE_STA|=0X80; //标記成功捕獲到一次高電平脈寬
TIM5CH2_CAPTURE_VAL=TIM_GetCapture2(TIM5);//擷取目前的捕獲值.
TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 設定為上升沿捕獲
}else //還未開始,第一次捕獲上升沿
{
TIM5CH2_CAPTURE_STA=0; //清空
TIM5CH2_CAPTURE_VAL=0;
TIM5CH2_CAPTURE_STA|=0X40; //标記捕獲到了上升沿
TIM_Cmd(TIM5,DISABLE ); //關閉定時器5
TIM_SetCounter(TIM5,0);
TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 設定為下降沿捕獲
TIM_Cmd(TIM5,ENABLE ); //使能定時器5
}
}
}
if((TIM5CH3_CAPTURE_STA&0X80)==0)//還未成功捕獲
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)//溢出
{
if(TIM5CH3_CAPTURE_STA&0X40)//已經捕獲到高電平了
{
if((TIM5CH3_CAPTURE_STA&0X3F)==0X3F)//高電平太長了
{
TIM5CH3_CAPTURE_STA|=0X80; //标記成功捕獲了一次
TIM5CH3_CAPTURE_VAL=0XFFFFFFFF;
}else TIM5CH3_CAPTURE_STA++;
}
}
if(TIM_GetITStatus(TIM5, TIM_IT_CC3) != RESET)//捕獲1發生捕獲事件
{
if(TIM5CH3_CAPTURE_STA&0X40) //捕獲到一個下降沿
{
TIM5CH3_CAPTURE_STA|=0X80; //标記成功捕獲到一次高電平脈寬
TIM5CH3_CAPTURE_VAL=TIM_GetCapture3(TIM5);//擷取目前的捕獲值.
TIM_OC3PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 設定為上升沿捕獲
}else //還未開始,第一次捕獲上升沿
{
TIM5CH3_CAPTURE_STA=0; //清空
TIM5CH3_CAPTURE_VAL=0;
TIM5CH3_CAPTURE_STA|=0X40; //标記捕獲到了上升沿
TIM_Cmd(TIM5,DISABLE ); //關閉定時器5
TIM_SetCounter(TIM5,0);
TIM_OC3PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 設定為下降沿捕獲
TIM_Cmd(TIM5,ENABLE ); //使能定時器5
}
}
}
if((TIM5CH4_CAPTURE_STA&0X80)==0)//還未成功捕獲
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)//溢出
{
if(TIM5CH4_CAPTURE_STA&0X40)//已經捕獲到高電平了
{
if((TIM5CH4_CAPTURE_STA&0X3F)==0X3F)//高電平太長了
{
TIM5CH4_CAPTURE_STA|=0X80; //标記成功捕獲了一次
TIM5CH4_CAPTURE_VAL=0XFFFFFFFF;
}else TIM5CH4_CAPTURE_STA++;
}
}
if(TIM_GetITStatus(TIM5, TIM_IT_CC4) != RESET)//捕獲1發生捕獲事件
{
if(TIM5CH4_CAPTURE_STA&0X40) //捕獲到一個下降沿
{
TIM5CH4_CAPTURE_STA|=0X80; //标記成功捕獲到一次高電平脈寬
TIM5CH4_CAPTURE_VAL=TIM_GetCapture4(TIM5);//擷取目前的捕獲值.
TIM_OC4PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 設定為上升沿捕獲
}else //還未開始,第一次捕獲上升沿
{
TIM5CH4_CAPTURE_STA=0; //清空
TIM5CH4_CAPTURE_VAL=0;
TIM5CH4_CAPTURE_STA|=0X40; //标記捕獲到了上升沿
TIM_Cmd(TIM5,DISABLE ); //關閉定時器5
TIM_SetCounter(TIM5,0);
TIM_OC4PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 設定為下降沿捕獲
TIM_Cmd(TIM5,ENABLE ); //使能定時器5
}
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4); //清除中斷标志位
}
總結
利用序列槽進行仿真,誤差和真實距離相差不大,但由于其器件特性精度無法太高,同時在使用中,我們還需要注意,超音波的探頭需要與被測物體的接觸面平行,不然會出現較大的誤差。是以,在使用過程中經常和濾波算法一起使用。文章僅供參考,歡迎斧正。一些原理借鑒了這位部落客:https://blog.csdn.net/m0_37655357/article/details/72934643