這個(gè)是全網(wǎng)最詳細(xì)的STM32項(xiàng)目教學(xué)視頻。
第一篇CSDN文章在這里在這里:
75
V3:HAL庫(kù)開發(fā)、手把手教學(xué)下面功能:PID速度控制、PID循跡、PID跟隨、遙控、避障、PID角度控制、openmv視覺控制、電磁循跡、FreeRTOS、K210視覺智能車(更新中)、K230視覺智能車(更新中)、MSPM0G3507視覺智能車(更新中)
22.6-實(shí)現(xiàn)裸機(jī)的功能-(通過全局變量同步)
開始調(diào)試實(shí)現(xiàn)之前裸機(jī)時(shí)候的六個(gè)模式,我們先使用全局變量同步和通信,再下面的章節(jié)講解消息隊(duì)列。
復(fù)制一份22-5_LED_FreeRTOS的代碼,命名22-6_LED_FreeRTOS
為了方便理解我們需要一個(gè)執(zhí)行模式0的任務(wù),就是用來停止電機(jī)的任務(wù),、
我們給之前的默認(rèn)任務(wù)修改優(yōu)先級(jí)和任務(wù)名字
然后設(shè)置新的任務(wù)名字和優(yōu)先級(jí)為高,重新生成代碼
把OLED刷新任務(wù)的電機(jī)停止函數(shù)注釋掉
在停止任務(wù)中增加 電機(jī)停止和串口輸出信息
/**
* @brief Function implementing the StopTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartStopTask */
void StartStopTask(void const * argument)
{
/* USER CODE BEGIN StartStopTask */
/* Infinite loop */
for(;;)
{
UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 獲取當(dāng)前任務(wù)的棧高水位值
printf("StartStopTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
size_t freeHeapSize = xPortGetFreeHeapSize();// 獲取系統(tǒng)的可用堆空間
printf("StartStopTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
if(g_ucMode == 0)
{
motorPidSetSpeed(0,0); //設(shè)置小車速度為0
}
osDelay(10);
}
/* USER CODE END StartStopTask */
}
發(fā)現(xiàn)模式6的顯示openmv發(fā)送過來數(shù)據(jù)的顯示位置,被之前顯示位置占用,所以我們換個(gè),顯示在更下面的一行,
sprintf((char*)OledString, "lHW:%d ", g_lHW_State);//視覺識(shí)別結(jié)果
OLED_ShowString(0,7,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
調(diào)試發(fā)現(xiàn)OLED刷新有時(shí)候比較亂,因?yàn)镾tartOledTask任務(wù)里面的OLED刷新合串口輸出都比較費(fèi)時(shí)間,為避免影響到其他任務(wù)執(zhí)行,我們降低任務(wù)執(zhí)行頻率,設(shè)置延時(shí)時(shí)間osDelay(100)
并且把OLED刷新的操作的放到一個(gè)任務(wù)中執(zhí)行,把openmv視覺識(shí)別結(jié)果的值放到OLED刷新任務(wù)中執(zhí)行,把模式6里面的視覺識(shí)別結(jié)果OLED刷新的注釋掉。
OLED刷新任務(wù)中加入 視覺結(jié)果刷新。
sprintf((char*)OledString, "lHW:%d ", g_lHW_State);//視覺識(shí)別結(jié)果
OLED_ShowString(0,7,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
因?yàn)槲覀円呀?jīng)通過串口輸出測(cè)試了,每個(gè)任務(wù)的棧高水位值,還有獲取了系統(tǒng)可用的??臻g都是可用正常執(zhí)行的,所以為了提高系統(tǒng)執(zhí)行效率就把四個(gè)任務(wù)里面的相關(guān)任務(wù)??臻g和系統(tǒng)堆空間注釋掉。
這樣修改后四個(gè)任務(wù)內(nèi)容如下:
void StartStopTask(void const * argument)
{
/* USER CODE BEGIN StartStopTask */
/* Infinite loop */
for(;;)
{
// UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 獲取當(dāng)前任務(wù)的棧高水位值
// printf("StartLedTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
// size_t freeHeapSize = xPortGetFreeHeapSize();// 獲取系統(tǒng)的可用堆空間
// printf("StartLedTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
if(g_ucMode == 0)
{
motorPidSetSpeed(0,0); //設(shè)置小車速度為0
}
osDelay(10);
}
/* USER CODE END StartStopTask */
}
void StartLedTask(void const * argument)
{
/* USER CODE BEGIN StartLedTask */
/* Infinite loop */
for(;;)
{
// UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 獲取當(dāng)前任務(wù)的棧高水位值
// printf("StartLedTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
// size_t freeHeapSize = xPortGetFreeHeapSize();// 獲取系統(tǒng)的可用堆空間
// printf("StartLedTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);//切換LED GPIO狀態(tài)
osDelay(300);
}
/* USER CODE END StartLedTask */
}
void StartOledTask(void const * argument)
{
/* USER CODE BEGIN StartOledTask */
/* Infinite loop */
for(;;)
{
// UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 獲取當(dāng)前任務(wù)的棧高水位值
// printf("StartOledTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
// size_t freeHeapSize = xPortGetFreeHeapSize();// 獲取系統(tǒng)的可用堆空間
// printf("StartOledTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
sprintf((char *)OledString," g_ucMode:%d",g_ucMode);//顯示g_ucMode 當(dāng)前模式
OLED_ShowString(0,6,OledString,12); //顯示在OLED上
sprintf((char*)OledString, "lHW:%d ", g_lHW_State);//視覺識(shí)別結(jié)果
OLED_ShowString(0,7,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
sprintf((char *)Usart3String," g_ucMode:%d",g_ucMode);//藍(lán)牙APP顯示
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式發(fā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
//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ā)送通過串口三輸出字符 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ā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
sprintf((char*)Usart3String, "U:%.2fV", adcGetBatteryVoltage());//顯示電池電壓
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式發(fā)送通過串口三輸出字符 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ā)送通過串口三輸出字符 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ā)送通過串口三輸出字符 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ā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
//獲得6050數(shù)據(jù)
// while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){} //這個(gè)可以解決經(jīng)常讀不出數(shù)據(jù)的問題
//在顯示模式電機(jī)停轉(zhuǎn) 設(shè)置小車速度為0
//motorPidSetSpeed(0,0);
osDelay(100);
}
/* USER CODE END StartOledTask */
}
void StartMultiModTask(void const * argument)
{
/* USER CODE BEGIN StartMultiModTask */
/* Infinite loop */
for(;;)
{
// UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 獲取當(dāng)前任務(wù)的棧高水位值
// printf("StartMultiModTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
// size_t freeHeapSize = xPortGetFreeHeapSize();// 獲取系統(tǒng)的可用堆空間
// printf("StartMultiModTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
if(g_ucMode == 1)
{
///**** 紅外PID循跡功能******************/
g_ucaHW_Read[0] = READ_HW_OUT_1;//讀取紅外對(duì)管狀態(tài)、這樣相比于寫在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");//注釋掉更加高效,減少無必要程序執(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;//進(jìn)行限幅 限幅速度在0-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);//通過計(jì)算的速度控制電機(jī)
}
g_cLastState = g_cThisState;//保存上次紅外對(duì)管狀態(tài)
}
if(g_ucMode == 2)
{
//***************遙控模式***********************//
//遙控模式的控制在串口三的中斷里面
}
if(g_ucMode == 3)
{
//******超聲波避障模式*********************//
避障邏輯
if(HC_SR04_Read() > 25)//前方無障礙物
{
motorPidSetSpeed(1,1);//前運(yùn)動(dòng)
osDelay(100);
}
else{ //前方有障礙物
motorPidSetSpeed(-1,1);//右邊運(yùn)動(dòng) 原地
osDelay(500);
if(HC_SR04_Read() > 25)//右邊無障礙物
{
motorPidSetSpeed(1,1);//前運(yùn)動(dòng)
osDelay(100);
}
else{//右邊有障礙物
motorPidSetSpeed(1,-1);//左邊運(yùn)動(dòng) 原地
osDelay(1000);
if(HC_SR04_Read() >25)//左邊無障礙物
{
motorPidSetSpeed(1,1);//前運(yùn)動(dòng)
osDelay(100);
}
else{
motorPidSetSpeed(-1,-1);//后運(yùn)動(dòng)
osDelay(1000);
motorPidSetSpeed(-1,1);//右邊運(yùn)動(dòng)
osDelay(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 沒有東西就停止
osDelay(10);//讀取超聲波傳感器不能過快
}
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);//通過串口三輸出字符 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ù)的問題
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;//進(jìn)行限幅
if(g_fMPU6050YawMovePidOut2 <0) g_fMPU6050YawMovePidOut2 =0;
motorPidSetSpeed(g_fMPU6050YawMovePidOut1,g_fMPU6050YawMovePidOut2);//將最后計(jì)算的目標(biāo)速度 通過motorPidSetSpeed控制電機(jī)
}
if(g_ucMode == 6)
{
// sprintf((char*)OledString, "lHW:%d ", g_lHW_State);//視覺識(shí)別結(jié)果
// OLED_ShowString(0,7,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
g_fHW_PID_Out = PID_realize(&pidOpenmv_Tracking,g_cThisState);//PID計(jì)算輸出目標(biāo)速度 這個(gè)速度,會(huì)和基礎(chǔ)速度加減
g_fHW_PID_Out1 = 0.5 + g_fHW_PID_Out;//電機(jī)1速度=基礎(chǔ)速度+循跡PID輸出速度
g_fHW_PID_Out2 = 0.5 - g_fHW_PID_Out;//電機(jī)1速度=基礎(chǔ)速度-循跡PID輸出速度
if(g_fHW_PID_Out1 >1.2) g_fHW_PID_Out1 =1.2;//進(jìn)行限幅 限幅速度在0-1.2之間
if(g_fHW_PID_Out1 <0) g_fHW_PID_Out1 =0;
if(g_fHW_PID_Out2 >1.2) g_fHW_PID_Out2 =1.2;//進(jìn)行限幅 限幅速度在0-1.2之間
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);//通過計(jì)算的速度控制電機(jī)
}
g_cLastState = g_cThisState;//保存上次紅外對(duì)管狀態(tài)
}
osDelay(1);
}
/* USER CODE END StartMultiModTask */
}
22.6.1-實(shí)際測(cè)試幾個(gè)功能是否正常
先燒錄一下 "22-6_LED_FreeRTOS"的代碼
然后進(jìn)行測(cè)試,測(cè)試結(jié)果如下
模式0-可以正常刷新屏幕(除了6050數(shù)據(jù)外)
模式1-可以完成紅外對(duì)管PID循跡
模式2-可以進(jìn)行藍(lán)牙遙控
模式3-可以進(jìn)行超聲波避障 偶爾出現(xiàn)超聲波測(cè)距值錯(cuò)誤
模式4-可以進(jìn)行PID跟隨 但是有些抖動(dòng)
模式5-不能進(jìn)行MPU6050角度控制
模式6-可以進(jìn)行openmv視覺循跡