第七章-V1.5STM32超聲波測(cè)距STM32F103C8t6超聲波避障小車 超聲波避障模塊 STM32超聲波測(cè)距 HCSR04超聲波測(cè)距定時(shí)器測(cè)量超聲波測(cè)距超聲波測(cè)距系統(tǒng)設(shè)計(jì)
當(dāng)前項(xiàng)目V1.5版本,一共兩個(gè)版本
V1.5.0
[stm32入門(mén)教程][STM32實(shí)戰(zhàn)項(xiàng)目]STM32智能小車V1.5-stm32f103c8t6-stm32最小系統(tǒng)-手把手入門(mén)教程-嵌入式開(kāi)發(fā)-嵌入式學(xué)習(xí)
V3-STM32智能小車
視頻
介紹:
V1.5.0:庫(kù)函數(shù)開(kāi)發(fā)。功能:循跡、避障、跟隨、遙控、電池電壓顯示等。
V3:HAL庫(kù)開(kāi)發(fā)、功能:PID速度控制、PID循跡、PID跟隨、遙控、避障、PID角度控制、openmv視覺(jué)等。
超聲波測(cè)距
通過(guò)超聲波的硬件介紹我們知道
MCU給Trig腳一個(gè)大于10us的高電平脈沖;然后讀取Echo腳的高電平信號(hào)時(shí)間,通過(guò)公式:距離 = T聲速/2 就可以算出來(lái)距離。*
軟件方面:10us高電平脈沖通過(guò)GPIO輸出實(shí)現(xiàn),高電平信號(hào)時(shí)間我們通過(guò)定時(shí)器的輸入捕獲來(lái)計(jì)算的。
初始化脈沖引腳PA0
在led.c中的SR04初始化函數(shù)
void SR04_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
}
led.h有關(guān)宏定義和聲明
#define SR04 PAout(0) // PA0
void SR04_GPIO_Init(void);
初始化PA1輸入捕獲
查看數(shù)據(jù)手冊(cè)
初始化定時(shí)器2 通道2 輸入捕獲相關(guān)功能
//定時(shí)器2通道2輸入捕獲配置
TIM_ICInitTypeDef TIM2_ICInitStructure;
void TIM2_Cap_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA時(shí)鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PA1 清除之前設(shè)置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA1 輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_1); //PA1 下拉
//初始化定時(shí)器5 TIM5
TIM_TimeBaseStructure.TIM_Period = arr; //設(shè)定計(jì)數(shù)器自動(dòng)重裝值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //預(yù)分頻器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設(shè)置時(shí)鐘分割:TDTS =
Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計(jì)數(shù)模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根據(jù)TIM_TimeBaseInitStruct中
指定的參數(shù)初始化TIMx的時(shí)間基數(shù)單位
//初始化TIM5輸入捕獲參數(shù)
TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2; // 選擇輸入端 IC2映射到TI2
上
TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲
TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI2上
TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻
TIM2_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置輸入濾波器 不濾波
TIM_ICInit(TIM2, &TIM2_ICInitStructure);
//中斷分組初始化
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占優(yōu)先級(jí)2級(jí)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //從優(yōu)先級(jí)0級(jí)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根據(jù)NVIC_InitStruct中指定的參數(shù)初始化外設(shè)NVIC
寄存器
TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC2,ENABLE);//允許更新中斷 ,允許CC2IE捕獲
中斷
TIM_Cmd(TIM2,ENABLE ); //使能定時(shí)器2
}
u8 TIM5CH1_CAPTURE_STA=0; //輸入捕獲狀態(tài)
u16 TIM5CH1_CAPTURE_VAL; //輸入捕獲值
//定時(shí)器2中斷服務(wù)程序
void TIM2_IRQHandler(void)
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//還未成功捕獲
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
if(TIM5CH1_CAPTURE_STA&0X40)//已經(jīng)捕獲到高電平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高電平太長(zhǎng)了
{
TIM5CH1_CAPTURE_STA|=0X80;//標(biāo)記成功捕獲了一次
TIM5CH1_CAPTURE_VAL=0XFFFF;
}else TIM5CH1_CAPTURE_STA++;
}
}
if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕獲2發(fā)生捕獲事件
{
if(TIM5CH1_CAPTURE_STA&0X40) //捕獲到一個(gè)下降沿
{
TIM5CH1_CAPTURE_STA|=0X80; //標(biāo)記成功捕獲到一次上升沿
TIM5CH1_CAPTURE_VAL=TIM_GetCapture2(TIM2);
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC2P=0 設(shè)置為
上升沿捕獲
}else //還未開(kāi)始,第一次捕獲上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM2,0);
TIM5CH1_CAPTURE_STA|=0X40; //標(biāo)記捕獲到了上升沿
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling); //CC2P=1
設(shè)置為下降沿捕獲
}
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中斷標(biāo)志位
}
在time 中聲明初始化函數(shù)
void TIM2_Cap_Init(u16 arr,u16 psc);
計(jì)算輸出距離
在main.c聲明變量
extern u8 TIM5CH1_CAPTURE_STA; //輸入捕獲狀態(tài)
extern u16 TIM5CH1_CAPTURE_VAL; //輸入捕獲值
定義變量
int Distance =0;
int time=0;
調(diào)用初始化函數(shù)
SR04_GPIO_Init();
TIM2_Cap_Init(0XFFFF,72-1); //以1Mhz的頻率計(jì)數(shù)
完成測(cè)距的函數(shù)
delay_ms(500);//加入延時(shí)
HC_SR04 =0;
delay_us(10);
HC_SR04 = 1;
if(TIM5CH1_CAPTURE_STA&0X80)//成功捕獲到了一次上升沿
{
time=TIM5CH1_CAPTURE_STA&0X3F;
time*=65536;//溢出時(shí)間總和
time+=TIM5CH1_CAPTURE_VAL;//得到總的高電平時(shí)間
printf("rnHIGH:%d usrn",time);//打印總的高點(diǎn)平時(shí)間
Distance = time*0.033/2;
printf("cm:%drn",Distance);
TIM5CH1_CAPTURE_STA=0;//開(kāi)啟下一次捕獲
}
封裝一下方便調(diào)用
int TCRT5000_Dist(void)
{
HC_SR04 = 1;
delay_us(13);
HC_SR04=0;
if(TIM5CH1_CAPTURE_STA&0X80)//成功捕獲到了一次上升沿
{
time=TIM5CH1_CAPTURE_STA&0X3F;
time*=65536;//溢出時(shí)間總和
time+=TIM5CH1_CAPTURE_VAL;//得到總的高電平時(shí)間
printf("rnHIGH:%d usrn",time);//打印總的高點(diǎn)平時(shí)間
Distance = time*0.033/2;
printf("cm:%drn",Distance);
TIM5CH1_CAPTURE_STA=0;//開(kāi)啟下一次捕獲
}
return Distance;
}
使用串口助手查看結(jié)果
練一練–編寫(xiě)定距離跟隨功能
功能:根據(jù)根據(jù)超聲波測(cè)量距離跟隨前方物體
while(1){
HC_SR04 = 1;
delay_us(13);
HC_SR04=0;
if(TIM5CH1_CAPTURE_STA&0X80)//成功捕獲到了一次上
升沿
{
time=TIM5CH1_CAPTURE_STA&0X3F;
time*=65536;//溢出時(shí)間總和
time+=TIM5CH1_CAPTURE_VAL;//得到總的高電平時(shí)間
printf("rnHIGH:%d usrn",time);//打印總的高
點(diǎn)平時(shí)間
Distance = time*0.033/2;
printf("cm:%drn",Distance);
TIM5CH1_CAPTURE_STA=0;//開(kāi)啟下一次捕獲
}
if(Distance>20)
{
Forward();
delay_ms(50);
}
if(Distance<15)
{
Backward();
delay_ms(50);
}
AIN1 =0;
AIN2 = 0;
BIN1 = 0;
BIN2 =0;
}
練一練–結(jié)合舵機(jī)完成避障功能
功能:通過(guò)舵機(jī)旋轉(zhuǎn)不同角度,超聲波測(cè)量左右是否存在障礙物,控制小車運(yùn)動(dòng)。
測(cè)試舵機(jī)的轉(zhuǎn)角,不同占空比小車舵機(jī)的角度
TIM_SetCompare1(TIM3,80);
TIM_SetCompare1(TIM3,50);
TIM_SetCompare1(TIM3,110);
整體邏輯
if(Mode == 3)
{
//超聲波避障
TIM_SetCompare1(TIM3,80); //舵機(jī)向前 使超聲波朝前方
delay_ms(200);
if(TCRT5000_Dist()>25)// 前方無(wú)障礙物
{
Forward();
delay_ms(500);
}
if(TCRT5000_Dist()<25) //向前有障礙物
{
TIM_SetCompare1(TIM3,50); //舵機(jī)向右邊轉(zhuǎn)大約30度
delay_ms(200);
if(TCRT5000_Dist()>25)//右側(cè)無(wú)障礙物判斷
{
Rightward();
delay_ms(700);
}
else { //右邊有障礙物
TIM_SetCompare1(TIM3,100); //舵機(jī)向左邊轉(zhuǎn)大
約30度
delay_ms(200);
if(TCRT5000_Dist()>25)//左側(cè)無(wú)障礙物
{
Leftward();
delay_ms(700);
}
else{
Backward();//后退
delay_ms(700);
Rightward(); //右轉(zhuǎn)
delay_ms(700);
}
}
}
} }
綜合一下-縫合上面練一練的功能
功能:
- 小車具有紅外對(duì)管循跡、藍(lán)牙遙控、定距離跟隨、避障運(yùn)動(dòng)模式
- 可以通過(guò)小車按鍵和APP進(jìn)行切換小車的運(yùn)動(dòng)模式。
- APP與OLED顯示小車所處模式和超聲波測(cè)量值、電池電壓。
實(shí)現(xiàn)切換功能必須
main中的循環(huán)
while(1)
{
sprintf((char *)string,"Distance:%d ",TCRT5000_Dist());// 顯示距離信息
這里的 %d 需要幾個(gè)空格
OLED_ShowString(6,3,string,16);
sprintf((char *)string,"Mode:%d",Mode);//顯示小車模式
OLED_ShowString(6,6,string,16);
if(Mode == 1)
{
//定距離跟隨
TIM_SetCompare1(TIM3,80); //超聲波舵機(jī)向前
if(TCRT5000_Dist()>25)// 距離太遠(yuǎn)
{
Forward();
delay_ms(200);
}
if(TCRT5000_Dist() <20)//距離太近
{
Backward();
delay_ms(200);
}
AIN1 =0;//車輛暫定 如果不加 小車就會(huì)一直往前或者一直往后
AIN2 =0;
BIN1 =0;
BIN2 =0;
}
if(Mode == 2)
{
//藍(lán)牙控制小車
TIM_SetCompare1(TIM3,80); //超聲波舵機(jī)向前
if(g_USART3_FLAG1 == 1) //前進(jìn)
{
g_USART3_FLAG1=0;
Forward();
delay_ms(500);
}
if(g_USART3_FLAG1 == 2) //向右
{
g_USART3_FLAG1=0;
Rightward();
delay_ms(500);
}
if(g_USART3_FLAG1 ==3) //向左
{
g_USART3_FLAG1=0;
Leftward();
delay_ms(500);
}
if(g_USART3_FLAG1 ==4) //向后
{
g_USART3_FLAG1=0;
Backward();
delay_ms(500);
}
if(g_USART3_FLAG1 ==5) //停止
{
g_USART3_FLAG1=0;
AIN1=0;
AIN2=0;
BIN1=0;
BIN2=0;
delay_ms(500);
}
}
if(Mode == 3)
{
//超聲波避障
TIM_SetCompare1(TIM3,80); //舵機(jī)向前
delay_ms(200);
if(TCRT5000_Dist()>25)// 前方無(wú)障礙物
{
Forward();
delay_ms(500);
}
if(TCRT5000_Dist()<25) //向前有障礙物
{
TIM_SetCompare1(TIM3,50); //舵機(jī)右轉(zhuǎn)
delay_ms(200);
if(TCRT5000_Dist()>25)//右側(cè)無(wú)障礙物判斷
{
Rightward();
delay_ms(700);
}
else {
TIM_SetCompare1(TIM3,100); //舵機(jī)向左
delay_ms(200);
if(TCRT5000_Dist()>25)//左側(cè)無(wú)障礙物
{
Leftward();
delay_ms(700);
}
else{
Backward();
delay_ms(700);//后退
Rightward();
delay_ms(700);
}
}
}
}
if(Mode == 4)
{
//紅外循跡
TIM_SetCompare1(TIM3,80); //超聲波舵機(jī)向前
if(HW_1 == 0&&HW_2 == 0&&HW_3 == 0&&HW_4 == 0)//應(yīng)該
前進(jìn)
{
Forward();
delay_ms(20);
}
if(HW_1 == 0&&HW_2 == 1&&HW_3 == 0&&HW_4 == 0)//應(yīng)該右邊
{
Rightward();
delay_ms(150);
}
if(HW_1 == 1&&HW_2 == 0&&HW_3 == 0&&HW_4 == 0)//應(yīng)該右邊
{
Rightward();
delay_ms(270);
}
if(HW_1 == 1&&HW_2 == 1&&HW_3 == 0&&HW_4 == 0)//應(yīng)該右邊
{
Rightward();
delay_ms(370);
}
if(HW_1 == 0&&HW_2 == 0&&HW_3 == 1&&HW_4 == 0)//應(yīng)該左邊
{
Leftward();
delay_ms(150);
}
if(HW_1 == 0&&HW_2 == 0&&HW_3 == 0&&HW_4 == 1)//應(yīng)該左邊
{
Leftward();
delay_ms(270);
}
if(HW_1 == 0&&HW_2 == 0&&HW_3 == 1&&HW_4 == 1)//應(yīng)該左邊
{
Leftward();
delay_ms(370);
}
}
if(Mode ==0)
{
delay_ms(1);
AIN1=0;
AIN2=0;
BIN1=0;
BIN2=0;
}
}
串口三中斷服務(wù)函數(shù)
//串口3 中斷處理函數(shù)
void USART3_IRQHandler (void)
{
u8 Res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART3); //讀取接收到的數(shù)據(jù)
if(Res == 'A') g_USART3_FLAG1 = 1 ; //根據(jù)接受的數(shù)據(jù) 置為標(biāo)志位
if(Res == 'B')g_USART3_FLAG1 = 2 ;
if(Res == 'C') g_USART3_FLAG1 = 3 ;
if(Res == 'D')g_USART3_FLAG1 = 4 ;
if(Res == 'E')g_USART3_FLAG1 = 5;
if(Res == 'F') Mode =1;
if(Res == 'G') Mode =2;
if(Res == 'H') Mode =3;
if(Res == 'I') Mode =4;
if(Res == 'J') Mode =0;
}
}
按鍵處理函數(shù)
void EXTI9_5_IRQHandler(void)//按鍵KEY_1 和KEY_2的中斷服務(wù)函數(shù)
{
delay_ms(10);//消抖
if(KEY_1 == 1) //判斷按鍵KEY_1 是否被按下
{
if(Mode == 4) Mode =1;
else
{
Mode = Mode + 1;
}
LED =! LED;
EXTI_ClearITPendingBit(EXTI_Line7); //清除LINE7上的中斷標(biāo)志位
}
}
void EXTI15_10_IRQHandler(void)//按鍵KEY_SW1 和KEY_SW2的中斷服務(wù)函數(shù)
{
delay_ms(10);//消抖
if(KEY_2 == 0) //判斷按鍵KEY_2 是否被按下
{
Mode = 0 ;
LED =! LED;
EXTI_ClearITPendingBit(EXTI_Line12); //清除LINE12上的中斷標(biāo)志位
}
}
手機(jī)APP-藍(lán)牙調(diào)試助手設(shè)置