這個(gè)是全網(wǎng)最詳細(xì)的STM32項(xiàng)目教學(xué)視頻。
第一篇在這里:
視頻在這里
STM32智能小車(chē)V3-STM32入門(mén)教程-openmv與STM32循跡小車(chē)-stm32f103c8t6-電賽 嵌入式學(xué)習(xí) PID控制算法 編碼器電機(jī) 跟隨
第18章-綜合以上功能
18-按鍵和app按鈕切換功能
根據(jù)上面介紹,我們的模式可以有:
**OLED顯示模式: 速度、里程、電壓、超聲波數(shù)據(jù)、MPU6050俯仰角、橫滾角、航向角 數(shù)據(jù)顯示在OLED上和通過(guò)串口發(fā)送藍(lán)牙APP **
PID循跡模式:紅外對(duì)管PID循跡
手機(jī)遙控普通運(yùn)動(dòng)模式:遙控前、后、左、右加速運(yùn)動(dòng)
超聲波避障模式
PID跟隨模式:超聲波PID定距離跟隨
手機(jī)遙控角度閉環(huán)模式:MPU6050角度PID控制
可以設(shè)置標(biāo)志位通過(guò)按鍵改變標(biāo)志位,以實(shí)現(xiàn)功能切換。
定義一個(gè)全局變量,
uint8_t g_ucMode = 0;
//小車(chē)運(yùn)動(dòng)模式標(biāo)志位 0:顯示功能、1:PID循跡模式、2:手機(jī)遙控普通運(yùn)動(dòng)模式、3.超聲波避障模式、4:PID跟隨模式、5:遙控角度閉環(huán)
uint8_t g_ucMode = 0; //小車(chē)運(yùn)動(dòng)模式標(biāo)志位
在gpio.h聲明一下
extern uint8_t g_ucMode ; //小車(chē)運(yùn)動(dòng)模式標(biāo)志位
按鍵中斷回調(diào)函數(shù)里面補(bǔ)充按下按鍵后的處理
先不進(jìn)行消抖,如果后面KEY1 KEY2效果不好再消抖
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY1_Pin) //判斷一下那個(gè)引腳觸發(fā)中斷
{
//這里編寫(xiě)觸發(fā)中斷后要執(zhí)行的程序
if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5
else
{
g_ucMode+=1;
}
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}
if(GPIO_Pin == KEY2_Pin) //判斷一下那個(gè)引腳觸發(fā)中斷
{
//這里編寫(xiě)觸發(fā)中斷后要執(zhí)行的程序
g_ucMode=0;
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}
}
然后主函數(shù)顯示當(dāng)前處于的模式
然后判斷當(dāng)前模式 執(zhí)行不同代碼
方法:一個(gè)功能一個(gè)功能的添加代碼,添加好一個(gè)調(diào)試測(cè)試一下,然后再添加下一個(gè)
下面這個(gè)就是我們主函數(shù)的代碼。
sprintf((char *)OledString," g_ucMode:%d",g_ucMode);//顯示g_ucMode 當(dāng)前模式
OLED_ShowString(0,6,OledString,12); //顯示在OLED上
sprintf((char *)Usart3String," g_ucMode:%d",g_ucMode);//藍(lán)牙APP顯示
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式發(fā)送通過(guò)串口三輸出字符 strlen:計(jì)算字符串大小
if(g_ucMode == 0)
{
//0LED顯示功能
sprintf((char*)OledString, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//顯示速度
OLED_ShowString(0,0,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
sprintf((char*)OledString, "Mileage:%.2f", Mileage);//顯示里程
OLED_ShowString(0,1,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
sprintf((char*)OledString, "U:%.2fV", adcGetBatteryVoltage());//顯示電池電壓
OLED_ShowString(0,2,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
sprintf((char *)OledString,"HC_SR04:%.2fcmrn",HC_SR04_Read());//顯示超聲波數(shù)據(jù)
OLED_ShowString(0,3,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
sprintf((char *)OledString,"p:%.2f r:%.2f rn",pitch,roll);//顯示6050數(shù)據(jù) 俯仰角 橫滾角
OLED_ShowString(0,4,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
sprintf((char *)OledString,"y:%.2f rn",yaw);//顯示6050數(shù)據(jù) 航向角
OLED_ShowString(0,5,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
//藍(lán)牙APP顯示
sprintf((char*)Usart3String, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//顯示速度
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式發(fā)送通過(guò)串口三輸出字符 strlen:計(jì)算字符串大小
//阻塞方式發(fā)送可以保證數(shù)據(jù)發(fā)送完畢,中斷發(fā)送不一定可以保證數(shù)據(jù)已經(jīng)發(fā)送完畢才啟動(dòng)下一次發(fā)送
sprintf((char*)Usart3String, "Mileage:%.2f", Mileage);//顯示里程
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式發(fā)送通過(guò)串口三輸出字符 strlen:計(jì)算字符串大小
sprintf((char*)Usart3String, "U:%.2fV", adcGetBatteryVoltage());//顯示電池電壓
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式發(fā)送通過(guò)串口三輸出字符 strlen:計(jì)算字符串大小
sprintf((char *)Usart3String,"HC_SR04:%.2fcmrn",HC_SR04_Read());//顯示超聲波數(shù)據(jù)
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式發(fā)送通過(guò)串口三輸出字符 strlen:計(jì)算字符串大小
sprintf((char *)Usart3String,"p:%.2f r:%.2f rn",pitch,roll);//顯示6050數(shù)據(jù) 俯仰角 橫滾角
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式發(fā)送通過(guò)串口三輸出字符 strlen:計(jì)算字符串大小
sprintf((char *)Usart3String,"y:%.2f rn",yaw);//顯示6050數(shù)據(jù) 航向角
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式發(fā)送通過(guò)串口三輸出字符 strlen:計(jì)算字符串大小
//獲得6050數(shù)據(jù)
while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){} //這個(gè)可以解決經(jīng)常讀不出數(shù)據(jù)的問(wèn)題
//顯示模式電機(jī)停轉(zhuǎn)
motorPidSetSpeed(0,0);
}
if(g_ucMode == 1)
{
///**** 紅外PID循跡功能******************/
g_ucaHW_Read[0] = READ_HW_OUT_1;//讀取紅外對(duì)管狀態(tài)、這樣相比于寫(xiě)在if里面更高效
g_ucaHW_Read[1] = READ_HW_OUT_2;
g_ucaHW_Read[2] = READ_HW_OUT_3;
g_ucaHW_Read[3] = READ_HW_OUT_4;
if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )
{
// printf("應(yīng)該前進(jìn)rn");//注釋掉更加高效,減少無(wú)必要程序執(zhí)行
g_cThisState = 0;//前進(jìn)
}
else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )//使用else if更加合理高效
{
// printf("應(yīng)該右轉(zhuǎn)rn");
g_cThisState = -1;//應(yīng)該右轉(zhuǎn)
}
else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )
{
// printf("快速右轉(zhuǎn)rn");
g_cThisState = -2;//快速右轉(zhuǎn)
}
else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0)
{
// printf("快速右轉(zhuǎn)rn");
g_cThisState = -3;//快速右轉(zhuǎn)
}
else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 0 )
{
// printf("應(yīng)該左轉(zhuǎn)rn");
g_cThisState = 1;//應(yīng)該左轉(zhuǎn)
}
else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 1 )
{
// printf("快速左轉(zhuǎn)rn");
g_cThisState = 2;//快速左轉(zhuǎn)
}
else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 1)
{
// printf("快速左轉(zhuǎn)rn");
g_cThisState = 3;//快速左轉(zhuǎn)
}
g_fHW_PID_Out = PID_realize(&pidHW_Tracking,g_cThisState);//PID計(jì)算輸出目標(biāo)速度 這個(gè)速度,會(huì)和基礎(chǔ)速度加減
g_fHW_PID_Out1 = 3 + g_fHW_PID_Out;//電機(jī)1速度=基礎(chǔ)速度+循跡PID輸出速度
g_fHW_PID_Out2 = 3 - g_fHW_PID_Out;//電機(jī)1速度=基礎(chǔ)速度-循跡PID輸出速度
if(g_fHW_PID_Out1 >5) g_fHW_PID_Out1 =5;//進(jìn)行限幅 限幅速度在0-5之間
if(g_fHW_PID_Out1 <0) g_fHW_PID_Out1 =0;
if(g_fHW_PID_Out2 >5) g_fHW_PID_Out2 =5;
if(g_fHW_PID_Out2 <0) g_fHW_PID_Out2 =0;
if(g_cThisState != g_cLastState)//如何這次狀態(tài)不等于上次狀態(tài)、就進(jìn)行改變目標(biāo)速度和控制電機(jī)、在定時(shí)器中依舊定時(shí)控制電機(jī)
{
motorPidSetSpeed(g_fHW_PID_Out1,g_fHW_PID_Out2);//通過(guò)計(jì)算的速度控制電機(jī)
}
g_cLastState = g_cThisState;//保存上次紅外對(duì)管狀態(tài)
}
if(g_ucMode == 2)
{
//***************遙控模式***********************//
//遙控模式的控制在串口三的中斷里面
}
if(g_ucMode == 3)
{
//******超聲波避障模式*********************//
避障邏輯
if(HC_SR04_Read() > 25)//前方無(wú)障礙物
{
motorPidSetSpeed(1,1);//前運(yùn)動(dòng)
HAL_Delay(100);
}
else{ //前方有障礙物
motorPidSetSpeed(-1,1);//右邊運(yùn)動(dòng) 原地
HAL_Delay(500);
if(HC_SR04_Read() > 25)//右邊無(wú)障礙物
{
motorPidSetSpeed(1,1);//前運(yùn)動(dòng)
HAL_Delay(100);
}
else{//右邊有障礙物
motorPidSetSpeed(1,-1);//左邊運(yùn)動(dòng) 原地
HAL_Delay(1000);
if(HC_SR04_Read() >25)//左邊無(wú)障礙物
{
motorPidSetSpeed(1,1);//前運(yùn)動(dòng)
HAL_Delay(100);
}
else{
motorPidSetSpeed(-1,-1);//后運(yùn)動(dòng)
HAL_Delay(1000);
motorPidSetSpeed(-1,1);//右邊運(yùn)動(dòng)
HAL_Delay(50);
}
}
}
}
if(g_ucMode == 4)
{
//**********PID跟隨功能***********//
g_fHC_SR04_Read=HC_SR04_Read();//讀取前方障礙物距離
if(g_fHC_SR04_Read < 60){ //如果前60cm 有東西就啟動(dòng)跟隨
g_fFollow_PID_Out = PID_realize(&pidFollow,g_fHC_SR04_Read);//PID計(jì)算輸出目標(biāo)速度 這個(gè)速度,會(huì)和基礎(chǔ)速度加減
if(g_fFollow_PID_Out > 6) g_fFollow_PID_Out = 6;//對(duì)輸出速度限幅
if(g_fFollow_PID_Out < -6) g_fFollow_PID_Out = -6;
motorPidSetSpeed(g_fFollow_PID_Out,g_fFollow_PID_Out);//速度作用與電機(jī)上
}
else motorPidSetSpeed(0,0);//如果前面60cm 沒(méi)有東西就停止
HAL_Delay(10);//讀取超聲波傳感器不能過(guò)快
}
if(g_ucMode == 5)
{
//*************MPU6050航向角 PID轉(zhuǎn)向控制*****************//
sprintf((char *)Usart3String,"pitch:%.2f roll:%.2f yaw:%.2frn",pitch,roll,yaw);//顯示6050數(shù)據(jù) 俯仰角 橫滾角 航向角
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),0xFFFF);//通過(guò)串口三輸出字符 strlen:計(jì)算字符串大小
//mpu_dmp_get_data(&pitch,&roll,&yaw);//返回值:0,DMP成功解出歐拉角
while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){} //這個(gè)可以解決經(jīng)常讀不出數(shù)據(jù)的問(wèn)題
g_fMPU6050YawMovePidOut = PID_realize(&pidMPU6050YawMovement,yaw);//PID計(jì)算輸出目標(biāo)速度 這個(gè)速度,會(huì)和基礎(chǔ)速度加減
g_fMPU6050YawMovePidOut1 = 1.5 + g_fMPU6050YawMovePidOut;//基礎(chǔ)速度加減PID輸出速度
g_fMPU6050YawMovePidOut2 = 1.5 - g_fMPU6050YawMovePidOut;
if(g_fMPU6050YawMovePidOut1 >3.5) g_fMPU6050YawMovePidOut1 =3.5;//進(jìn)行限幅
if(g_fMPU6050YawMovePidOut1 <0) g_fMPU6050YawMovePidOut1 =0;
if(g_fMPU6050YawMovePidOut2 >3.5) g_fMPU6050YawMovePidOut2 =3.5;
if(g_fMPU6050YawMovePidOut2 <0) g_fMPU6050YawMovePidOut2 =0;
motorPidSetSpeed(g_fMPU6050YawMovePidOut1,g_fMPU6050YawMovePidOut2);
}
可以測(cè)試上面的代碼 然后沒(méi)有問(wèn)題后,我們添加一個(gè)通過(guò)藍(lán)牙APP按鈕切換模式代碼
if(g_ucUsart3ReceiveData == 'J') //改變模式
{
if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5
else
{
g_ucMode+=1;
}
}
if(g_ucUsart3ReceiveData == 'K') g_ucMode=0;//設(shè)置為顯示模式
然后對(duì)應(yīng)APP也要添加 按鈕設(shè)置
我們
按鍵沒(méi)有消抖效果不好,我們消抖一下
我們?cè)黾恿?HAL延時(shí)和再次判斷電平
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY1_Pin) //判斷一下那個(gè)引腳觸發(fā)中斷
{
HAL_Delay(10);//延時(shí)消抖 主要
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET)//判斷KEY1引腳仍為高電平
{
//這里編寫(xiě)觸發(fā)中斷后要執(zhí)行的程序
if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5
else
{
g_ucMode+=1;
}
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}
}
if(GPIO_Pin == KEY2_Pin) //判斷一下那個(gè)引腳觸發(fā)中斷
{
HAL_Delay(10);//延時(shí)消抖
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET)//判斷KEY2引腳仍為低電平
{
//這里編寫(xiě)觸發(fā)中斷后要執(zhí)行的程序
g_ucMode=0;
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}
}
}
但是測(cè)試不能執(zhí)行中斷,程序異??ㄋ懒?/p>
原因是HAL_Delay使用的是sysTick 中斷優(yōu)先級(jí)在軟件初始化是默認(rèn)最低的,比外部中斷優(yōu)先級(jí)低,所以HAL_Delay不能在外部中斷服務(wù)函數(shù)中調(diào)用。
所以我們可以通過(guò)提高sysTick 中斷的優(yōu)先級(jí),提高的比HAL_Delay高。
然后我們提高至 如下圖
然后編譯燒錄測(cè)試按鍵是否更加穩(wěn)定。
下面的章節(jié)我們講解視覺(jué),RTOS系統(tǒng),電磁循跡等功能
聯(lián)系:Q,1930299709