目錄
- PWM舵機控制原理
- STM32CubeMx主要配置
- TIMER
- 串口配置
- 中斷控制
- STM32CubeIDE代碼實作
- 通訊協議設計
- STM32代碼實作
- 測驗
、
通常的機械臂都是由多路舵機組成,我使用的是某寶上(并不)常見的五自由度機械臂,盡管商家稱它為六自由度,
這里使用STM32F407VGT6的6路PWM輸出通道來控制6個舵機的運動,樹莓派(上位機)通過USB轉TTL模塊與STM32進行串口通訊
PWM舵機控制原理
標準的 PWM 舵機有三條控制線,分別為:電源、地及信號線,

市面上大多數180°舵機需要的PWM波周期通常為20ms,高電平接收時間通常為0.5 ~ 2.5ms,對應舵機旋轉角度為0 ~ 180°,用PWM波控制舵機時,只需將時鐘的周期設定為20ms(50Hz),并且通過改變比較值pulse改變PWM波的高電平時間來控制舵機旋轉的角度.
STM32CubeMx主要配置
對于STM32,我使用的是STM32CubeIDE + Mx 進行開發,時鐘框圖為默認配置,如圖,

TIMER
由于需要控制6個舵機,我選擇了TIM3(4路PWM輸出)和TIM9(2路PWM輸出),
PWM輸出的頻率由時鐘APB2決定,由時鐘框圖可知此處的APB2頻率為16MHz;PWM波頻率計算公式為:
W = APB2 / (PSC + 1)(ARR + 1)
其中PSC為分頻系數,ARR為自動重裝載值,這里我將PSE設定為39,ARR設定為7999,

注意,這里的計數模式(Counter Mode)不同時,會導致接下來比較值值相同時舵機的旋轉方向不同,
由于PWM波高電平時間需控制在0.5 ~ 2.5ms內,所以各PWM通道的比較值(pulse)必須控制在200 ~ 1000之內; (8000 x 0.5 / 20 = 200)(8000 x 2.5 / 20 = 1000)
當舵機旋轉角度為90°時,pulse值應為600,這里我將各個PWM輸出通道的比較值均設定為600,

由于我手頭上的機械臂中的一個舵機用于控制機械爪,其為90°舵機,所以在配置控制該舵機的PWM通道時,比較值范圍僅能為200 ~ 600,中間值為400我使用的單片機為STM32F407VGT6,其TIM3和TIM9所對應的PWM輸出通道分別為PA6, PA7, PB0, PB1和PE5, PE6,
這里只需將這些IO口設定為復用推挽輸出以及上拉即可,

串口配置
這里我選擇USART_2用作串口通訊,其Tx與Rx分別對應為PA2、PA3,
串口的配置如圖所示,
這里使用異步通信模式,注意波特率等引數需與上位機相匹配,

PA2與PA3均設定為復用推挽輸出,上拉,
中斷控制
由于這里我只使用了串口接收中斷,所以NVIC這里可以不用配置;若STM32除了控制機械臂外還有其余任務,則可能需要配置NVIC,
STM32CubeIDE代碼實作
在STM32CubuMx配置完畢并生成工程之后,我們的主要任務只有設計上位機與單片機的通訊協議,以及完成串口接收中斷函式即可,
通訊協議設計
我初步的設計為串口發送一次資料,控制單個舵機的角度;所以發送的資料中需要包括舵機的編號以及舵機的目標角度(或者由角度轉換而成的比較值),
在這里我沒有設計通訊頭以及對舵機轉速的控制,因為這里我的串口只用于傳輸控制舵機的指令,以及我使用的是小型舵機,其轉速并不是很快,可以滿足我的要求,無需控制其速度,
設計思路如下:
上位機每次發送16位的資料(2個uint8_t型別,串口只能發送串口),舵機編號為0~5,發送的pulse值為 (200 ~ 1000) - 200,將舵機編號數值乘以1000加上pulse值減去200得到一個uint16_t型別的數值;再將其轉換為十六進制進行發送,
例:對3號舵機進行操作,角度為45°,
資料處理:
3 x 1000 + (45° / 180°) x 800 = 3200;
3200轉換為十六進制為:0x0C80,
STM32進行逆向操作得到原資料,
STM32代碼實作
由于串口發送的是兩個uint8_t型別的資料,所以在解碼之前還需將兩個uint8_t型別資料轉換為一個uint16_t型別的資料,
在打開USART2的接收中斷時,設定為接受到兩個uint8_t型別的資料進入接收中斷,以便于在接收中斷處理函式中進行資料處理,
uint8_t pulse[2];
HAL_UART_Receive_IT(&huart2, pulse, sizeof(pulse));
串口接受中斷函式代碼如下:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART2)
{
uint8_t rec0, rec1;
// 獲取接收到的兩個位元組的資料
rec0 = *((huart->pRxBuffPtr) - 2);
rec1 = *((huart->pRxBuffPtr) - 1);
uint16_t numPart[2], armControl, arm targetPulse;
// 通過移位與強制轉換得到uint16_t型別資料
numPart[0] = (uint16_t) rec0;
numPart[1] = (uint16_t) rec1;
armControl = ((numPart[0] << 8) | numPart[1]);
// 逆向解碼
arm = armControl / 1000; // 舵機編號
targetPulse = armControl % 1000 + 200; // 目標比較值
// 通過switch陳述句改變指定PWM通道的比較值
switch(arm)
{
case 1:
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, targetPulse);
break;
case 2:
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, targetPulse);
break;
case 3:
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, targetPulse);
break;
case 4:
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_4, targetPulse);
break;
case 5:
__HAL_TIM_SET_COMPARE(&htim9, TIM_CHANNEL_1, targetPulse);
break;
case 6:
__HAL_TIM_SET_COMPARE(&htim9, TIM_CHANNEL_2, targetPulse);
break;
}
}
}
在進入while回圈之前還需將PWM通道使能;
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);
HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_2);
并且在USART2_IRQHandler函式中使能串口接收中斷:
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
HAL_UART_Receive_IT(&huart2, pulse, sizeof(pulse));
/* USER CODE END USART2_IRQn 1 */
}
測驗
在測驗時我使用的環境是安裝有Ubuntu 18.04的樹莓派,串口除錯工具為CuteCom;由5V鋰電池為單片機與舵機(機械臂)供電,每次手動發送兩個位元組的資料,十進制轉十六進制在科學計算器上進行,機械臂的運動符合預期,
經過測驗,同時發送12個位元組的資料,機械臂也能夠正常運行,
這里需要注意,在接線時,舵機的地線必須與單片機共地,
之后就可以將上位機串口發送的代碼寫入C++或者Python程式,通過上位機控制機械臂,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/108699.html
標籤:其他
