書接上回的硬件篇STM32控制四自由度機械臂(SG90舵機)(硬件篇)(簡單易復刻)-CSDN博客
此時硬件平臺已經搭建完畢,軟件總共設計了三種模式,分別為
模式1:搖桿&藍牙模式,此模式下可用搖桿或手機操作機械臂
模式2:示教器模式,此模式下由電位器控制機械臂
模式3:執行記憶動作,此模式下機械臂重復數組/鏈表中存儲的動作
三種模式的切換以及存儲動作可由按鍵或者手機藍牙切換。
代碼使用了FREERTOS操作系統,接下來我會將代碼分開解析,這樣大家看完后再結合起來回顧整體,就會更加容易理解了,請大家不要著急,慢慢看下去,一定能夠復刻成功。
先說模式1(搖桿&藍牙)的代碼,其中cmd_BLE是藍牙的數據,adc_dma[0-3]是搖桿的數據。
void check_sg_cmd()//搖桿&藍牙模式控制舵機函數
{check_A();//舵機Acheck_B();//舵機Bcheck_C();//舵機Ccheck_D();//舵機D
}
//舵機A,夾爪 CH4_B11-D1;adc4_A3
void check_A()
{if(Mode == 1){if((cmd_BLE == 'c' || adc_dma[3] > 4000) && angle[3] < 90)//合{angle[3]++;}else if((cmd_BLE == 'o' || adc_dma[3] <1000) && angle[3] > 0)//開{angle[3]--;}}
}
//舵機B,上下 CH3_B10-D2;adc3_A2
void check_B()
{if(Mode == 1){if((cmd_BLE == 'u' || adc_dma[2] <1000) && angle[2] < 135)//上{angle[2]++;}else if((cmd_BLE == 'd' || adc_dma[2] > 4000) && angle[2] > 45)//下{angle[2]--;}}
}
//舵機C,前后 CH2_B3-D3;adc2_A1
void check_C()
{if(Mode == 1){if((cmd_BLE == 'f' || adc_dma[1] <1000) && angle[1] < 135)//前{angle[1]++;}else if((cmd_BLE == 'b' || adc_dma[1] > 4000) && angle[1] > 45)//后{angle[1]--;}}
}
//舵機D,底座 CH1_A15-D0;adc1_A0
void check_D()
{if(Mode == 1){if((cmd_BLE == 'l' || adc_dma[0] <1000) && angle[0] < 180)//左{angle[0]++;}else if((cmd_BLE == 'r' || adc_dma[0] > 4000) && angle[0] > 0)//右{angle[0]--;}}
}
再看模式2(示教器)的代碼,先通過translate()獲取舵機目標角度,再通過reach_target()控制舵機達到目標角度,還有什么不懂的代碼里的注釋也很詳細。
void reach_target()//控制舵機到達目標角度
{for(j = 0;j <4;j++){if(angle[j] > angle_target[j]){angle[j]--;}else if(angle[j] < angle_target[j]){angle[j]++;}}
}
void translate()//示教器獲取舵機目標角度函數
{//adc_dma[4-7]表示四個電位器,將電位器數據轉換成目標角度angle_target[3] = (uint8_t)((double)adc_dma[7] / 22.75)/2;angle_target[2] = (uint8_t)((double)adc_dma[6] / 22.75);angle_target[1] = (uint8_t)((double)adc_dma[5] / 22.75) - 10;angle_target[0] = 180 - (uint8_t)((double)adc_dma[4] / 22.75);//180減只是改變一下電位器方向,不影響總體//限幅if(angle_target[1]<45) angle_target[1]=45;else if(angle_target[1]>135) angle_target[1]=135;if(angle_target[2]<45) angle_target[1]=45;else if(angle_target[2]>135) angle_target[1]=135;
}
再看模式3(記憶動作)的代碼,memory[i][j]數組里存儲的就是記憶數據。
void get_target()//獲取記憶角度數據,這里用的是數組存儲記憶數據
{angle_target_flag = 0;for(j=0;j<4;j++){if(angle[j] == angle_target[j]) angle_target_flag++;}if(angle_target_flag == 4) i++;for(j=0;j<4;j++){if(memory[i][j] == '\0'){i = 0;}angle_target[j] = memory[i][j];}
}void reach_target()//控制舵機到達目標角度,這個函數和示教器模式下的是同一個函數
{for(j = 0;j <4;j++){if(angle[j] > angle_target[j]){angle[j]--;}else if(angle[j] < angle_target[j]){angle[j]++;}}
}
到這里上述說的三個模式的代碼都在代碼工程的PWM.c中可以找到。?
接下來給大家下說一下freertos.c里面主要的三個任務?,分別是舵機任務Start_check_angle(),主要用于在不同的模式下控制舵機,在舵機任務我們也能看到上述提到了三個模式下的函數,藍牙串口S任務tart_usart_show(),主要用于打印信息到手機app上,這里并沒有通過手機控制機械臂的函數,那個在串口函數里,后面會說,oled顯示屏任務Start_OLED_Task(),和藍牙串口任務類似,只不過它是打印信息到oled顯示屏上。
void Start_check_angle(void const * argument)//舵機任務
{/* USER CODE BEGIN Start_check_angle *///開啟4路PWMHAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);/* Infinite loop */for(;;){if(Mode == 1){//搖桿&藍牙模式check_sg_cmd();}else if(Mode == 2){//示教器模式translate();reach_target();}else if(Mode == 3){//執行記憶動作get_target();reach_target();}if_BLE_cmd();//藍牙控制記憶動作模式//輸出PWM波控制舵機運動__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, Angle(angle[0]));__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, Angle(angle[1]));__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_3, Angle(angle[2]));__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_4, Angle(angle[3]));osDelay(15);//通過調整此延時可以改變機械臂運行速度}/* USER CODE END Start_check_angle */
}void Start_usart_show(void const * argument)//藍牙串口任務
{/* USER CODE BEGIN Start_usart_show *//* Infinite loop */for(;;){printf("Angle = {%d, %d, %d, %d}\r\n",angle[0],angle[1],angle[2],angle[3]);printf("adc_dma1 = {%d, %d, %d, %d}\r\n",adc_dma[0],adc_dma[1],adc_dma[2],adc_dma[3]);printf("adc_dma2 = {%d, %d, %d, %d}\r\n",adc_dma[4],adc_dma[5],adc_dma[6],adc_dma[7]);printf("\r\n");osDelay(1000);}/* USER CODE END Start_usart_show */
}void Start_OLED_Task(void const * argument)//oled顯示屏任務
{/* USER CODE BEGIN Start_OLED_Task */Oled_Init();Oled_Clear();/* Infinite loop */for(;;){//串口數據的字符串拼裝,speed是格子,每個格子1cmsprintf(speedMes,"A: %d ",angle[0]);sprintf(speedMes1,"B: %d ",angle[1]);sprintf(speedMes2,"C: %d ",angle[2]);sprintf(speedMes3,"D: %d ",angle[3]);sprintf(speedMes4,"Mode %d ",Mode);sprintf(speedMes5,"S %d ",i);Oled_Show_Str(1,5,speedMes);Oled_Show_Str(1,69,speedMes1);Oled_Show_Str(2,5,speedMes2);Oled_Show_Str(2,69,speedMes3);Oled_Show_Str(4,0,speedMes4);Oled_Show_Str(4,64,speedMes5);osDelay(500);}/* USER CODE END Start_OLED_Task */
}
接下來給大家說一下有關藍牙部分的相關代碼以及藍牙手機app的使用
首先是usart.c里的一段代碼,這里最重要的部分就是通過藍牙模式控制機械臂時候,切換模式只需發送M1,M2,M3就可切換相應模式,而執行相關動作時,必須在發送的指令前面加上A開頭,例如Ao就是夾爪開。
/*機械臂控制模式,默認為1
1:搖桿控制
2:示教器控制
3:執行記憶動作
*/
uint8_t Mode = 1;/*藍牙控制機械臂指令:
s/m 停/儲存當前動作
l/r 左右
u/d 上下
f/b 前后
o/c 開合*/
uint8_t cmd_BLE = 's';// 串口中斷:接收完成回調函數,收到一個數據后,在這里處理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{// 判斷中斷是由哪個串口觸發的if(huart->Instance == USART1){// 判斷接收是否完成(UART1_RX_STA bit15 位是否為1)if((UART1_RX_STA & 0x8000) == 0){// 如果已經收到了 0x0d (回車),if(UART1_RX_STA & 0x4000){// 則接著判斷是否收到 0x0a (換行)if(buf == 0x0a){// 如果 0x0a 和 0x0d 都收到,則將 bit15 位置為1UART1_RX_STA |= 0x8000;//=======中斷信息處理=======//模式切換在手機藍牙app中只需發送M1,M2,M3即可切換模式if (!strcmp((const char *)UART1_RX_Buffer, "M1")) { Mode = 1;printf("搖桿模式\r\n");}else if(!strcmp((const char *)UART1_RX_Buffer, "M2")) { Mode = 2;printf("示教模式\r\n");}else if(!strcmp((const char *)UART1_RX_Buffer, "M3")) { Mode = 3;printf("執行記憶動作\r\n");}//藍牙指令的切換則必須以A開頭,例:發送Ao就是夾爪開else if(UART1_RX_Buffer[0] == 'A'){cmd_BLE = UART1_RX_Buffer[1];}else {if(UART1_RX_Buffer[0] != '\0')printf("指令發送錯誤:%s\r\n", UART1_RX_Buffer);}//==========================memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer));// 重新開始下一次接收UART1_RX_STA = 0;//==========================}else// 否則認為接收錯誤,重新開始UART1_RX_STA = 0;}else // 如果沒有收到了 0x0d (回車){//則先判斷收到的這個字符是否是 0x0d (回車)if(buf == 0x0d){// 是的話則將 bit14 位置為1UART1_RX_STA |= 0x4000;}else{// 否則將接收到的數據保存在緩存數組里UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;UART1_RX_STA++;// 如果接收數據大于UART1_REC_LEN(200字節),則重新開始接收if(UART1_RX_STA > UART1_REC_LEN - 1)UART1_RX_STA = 0;}}}// 重新開啟中斷HAL_UART_Receive_IT(&huart1, &buf, 1);}
}
然后是PWM.c中的if_BLE_cmd(),這里藍牙控制記憶動作模式同樣需要在發送指令前面加上一個A開頭才可以。
//藍牙控制記憶動作模式
void if_BLE_cmd()
{switch(cmd_BLE){case 'm':if(i < location_cnt){for(j=0;j<4;j++){memory[i][j] = angle[j];}printf("儲存動作\r\n");cmd_BLE = 's';i++;}else{printf("動作已滿\r\n");cmd_BLE = 's';}break;case 'g':for(i=0;i < location_cnt;i++){for(j=0;j<4;j++){printf("%d ",memory[i][j]);}printf("\r\n");if(memory[i][j] == '\0') break;}cmd_BLE = 's';break;case 'D':for(i=0; i < location_cnt ;i++){memset(memory[i],'\0',4);}i = 0;printf("已清除動作");cmd_BLE = 's';break;}
}
最后介紹一下HC藍牙助手手機app(APP會在文末與代碼一起開源分享給大家)的使用方法 ,話不多說,直接上圖。
接下來再給大家介紹一下按鍵控制的代碼,A0按鍵切換模式1(搖桿&藍牙模式),A1按鍵切換模式2(示教器模式),B4按鍵切換模式3(執行記憶動作),B5按鍵用于存儲動作。
void Callback01(void const * argument)
{/* USER CODE BEGIN Callback01 */anti_shake = 0;/* USER CODE END Callback01 */
}void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//按鍵任務
{switch(GPIO_Pin){case GPIO_PIN_0://A0按鍵HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);//led亮HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);//led滅Mode = 1;printf("搖桿模式\r\n"); break;case GPIO_PIN_1://A1按鍵HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);//led滅HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);//led亮Mode = 2;printf("示教模式\r\n"); break;case GPIO_PIN_4://B4按鍵if(anti_shake == 0){anti_shake = 1;Mode = 3;HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);//led亮HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);//led亮printf("執行記憶動作\r\n");osTimerStart(myTimer01Handle,800);}break;case GPIO_PIN_5://B5按鍵if(anti_shake == 0){//軟件消抖 anti_shake = 1; osTimerStart(myTimer01Handle,800);//存儲動作 if(i<location_cnt){for(j=0;j<4;j++){memory[i][j] = angle[j];}printf("儲存動作\r\n");i++;}else if(i>=9){printf("動作已滿\r\n");} }break; }
}
到此為止,核心代碼基本上都介紹差不多了,還有一些例如i2c.c,OLED.c顯示屏引腳初始化用,gpio.c按鍵以及led初始化使用,adc.c,dma.c搖桿以及電位器初始化使用,tim.c舵機pwm初始化使用,這些代碼主要是初始化相關引腳,而相關引腳對應在硬件篇我已經介紹過了,當然對于這些代碼感興趣的也可以自己去代碼工程中看。
最后再帶大家簡單看一下main.c中的代碼:
int main(void)
{/* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init(); //GPIO初始化,四個按鍵和兩個LEDMX_DMA_Init(); //DMA初始化MX_ADC1_Init(); //ADC初始化,2個搖桿用and4個電位器用MX_TIM2_Init(); //定時器PWM初始化,舵機用MX_USART1_UART_Init();//串口初始化,藍牙用MX_I2C1_Init(); //I2C初始化,oled顯示屏用/* USER CODE BEGIN 2 */printf("Start\r\n");//程序開始運行HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_dma,8); //開啟ADC和DMAHAL_Delay(500);/* Call init function for freertos objects (in freertos.c) */MX_FREERTOS_Init();//freertos任務初始化/* Start scheduler */osKernelStart();//freertos開啟任務調度while (1){}
}
相信大家結合這篇軟件篇代碼解說來閱讀工程代碼看到這里就算不會freertos的人也已經復刻成功了,創造不易,感謝c友的一鍵三聯,球球了,這個對我真的很重要!?
工程文件和藍牙資料的鏈接給大家放這里了!!!http://通過網盤分享的文件:stm32機械臂源代碼.zip 鏈接: https://pan.baidu.com/s/1o1IBFdVu9Ybk9gXJIZTY8Q 提取碼: 0531
?
?
?
?
?
?
?