STM32之使用PWM控制多路舵機
前言
最近在玩一個6自由度的機械臂,我手上這臺機械臂的核心控制器件就是那六個能夠180度旋轉的舵機了。想想之前在學校還沒有系統性的把舵機給玩明白,所以就索性拿手上的STM32來自己寫驅動程式碼,將6個舵機給驅動起來。
舵機控制原理
舵機的控制原理還是比較簡單的,而且控制的角度和精度能夠比較好的按照開發者的意願來進行,因此經常被應用與一些控制類器械中,如機械手、雲臺、2自由度攝像頭等產品中。
舵機的外接線一般分為3根線,電源線、地線和訊號線,而控制舵機轉動,就是通過訊號線給舵機發送一系列的週期訊號(一般的舵機的能接收的訊號週期為20ms),然後通過控制週期訊號的高電平的持續時間來達到控制舵機轉動的目的。我手上的舵機就是根據高電平持續時間(0.5ms~2.5ms)來實現0~180的轉動的。下面附上一張舵機週期訊號控制和轉動角度的圖片說明。
當然了,週期訊號的產生可以使用很多方式,但是使用PWM來控制高電平的佔空比不失為一種最好的應用方式。在STM32中,STM32的定時器也都提供有PWM的功能。下面就說明一下STM32輸出PWM的具體實現方式。
使用STM32控制單個舵機
在STM32中控制舵機,實際上就是開發STM32上的PWM功能,這部分功能需要配置STM32的定時器和GPIO複用共功能,然後就是通過修改定時器計數器的比較暫存器的數值來達到控制PWM的高電平佔空比的目的。
這裡以STM32F767為例,說明一下具體的實現。
下面使用到了STM32F7中的定時器3作為主定時器,使用通道4來產生PWM。需要注意的定時器3的通道4是通過GPIOB1介面輸出的,因此還需要將GPIOB1配置為複用輸出功能。PWM的輸出比較極性設定為高。
TIM_HandleTypeDef TIM3_Handler;
TIM_OC_InitTypeDef TIM3_CH4Handler;
//arr 為自動重灌值
//psc 為時鐘預分頻數
void TIM3_PWM_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3;
TIM3_Handler.Init.Prescaler=psc;
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;
TIM3_Handler.Init.Period=arr;
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM3_Handler);
TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1;
TIM3_CH4Handler.Pulse=arr/2;
TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_HIGH; //設定輸出比較極性
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);
}
//htim 為定時器控制代碼
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_TIM3_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_Initure.Pin=GPIO_PIN_1;
GPIO_Initure.Mode=GPIO_MODE_AF_PP;
GPIO_Initure.Pull=GPIO_PULLUP;
GPIO_Initure.Speed=GPIO_SPEED_HIGH;
GPIO_Initure.Alternate= GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}
//設定定時器的比較暫存器的值
void TIM_SetTIM3Compare4(u32 compare)
{
TIM3->CCR4=compare;
}
以上的配置程式碼完成之後,就可以在主函式裡面進行呼叫,並且使用PWM功能了。
//配置定時器3的自動重灌值和時鐘預分頻數,定時器3使用的時鐘為108MHz,這裡進行108分頻,定時器的預分頻設定為1MHz
//自動重灌值設定為20000,既該定時器每計數20000次觸發一次中斷,預分頻為1MHz,則週期為20ms
TIM3_PWM_Init(20000-1,108-1)
定時器初始化配置完之後,在主迴圈之前要對舵機進行一次復位操作,從上面的圖中可以看出,在高電平持續時間為1.5ms的情況下,舵機能夠回中,因此需要呼叫以下函式,設定定時器的初始比較暫存器的值為1500,即可以使得定時器復位回中。
TIM_SetTIM3Compare4(1500);
需要說明一下,在修改定時器的比較暫存器的值的過程中,要有一些短暫的延時,以保證舵機將動作執行完成。
使用STM32控制多路舵機
單個舵機控制能夠完成之後,就可以嘗試使用STM32的定時器控制多路舵機了。STM32的定時器中通用定時器能夠產生多達4路PWM輸出,高階定時器TIM1和TIM8還能夠產生多達7路定時器。因此,我們完全可以使用STM32的一個定時器外設控制多路舵機,通過修改不同通道的比較暫存器的值,來達到控制舵機運動的目的。
下面就以STM32F7控制4路舵機為例,貼出來定時器配置相關程式碼。
TIM_HandleTypeDef TIM3_Handler; //定時器控制代碼
TIM_OC_InitTypeDef TIM3_CH1Handler; //定時器3通道1控制代碼
TIM_OC_InitTypeDef TIM3_CH2Handler; //定時器3通道2控制代碼
TIM_OC_InitTypeDef TIM3_CH3Handler; //定時器3通道3控制代碼
TIM_OC_InitTypeDef TIM3_CH4Handler; //定時器3通道4控制代碼
//arr:自動重灌值
//psc:時鐘預分頻數
void TIM3_PWM_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3; //定時器3
TIM3_Handler.Init.Prescaler=psc; //定時器分頻
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上計數模式
TIM3_Handler.Init.Period=arr; //自動重灌載值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM3_Handler); //初始化PWM
TIM3_CH1Handler.OCMode=TIM_OCMODE_PWM1;
TIM3_CH1Handler.Pulse=arr/2;
TIM3_CH1Handler.OCPolarity=TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH1Handler,TIM_CHANNEL_1);//配置TIM3通道1
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_1);//開啟PWM通道1
TIM3_CH2Handler.OCMode=TIM_OCMODE_PWM1;
TIM3_CH2Handler.Pulse=arr/2;
TIM3_CH2Handler.OCPolarity=TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH2Handler,TIM_CHANNEL_2);//配置TIM3通道2
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_2);//開啟PWM通道2
TIM3_CH3Handler.OCMode=TIM_OCMODE_PWM1; //模式選擇PWM1
TIM3_CH3Handler.Pulse=arr/2;
TIM3_CH3Handler.OCPolarity=TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH3Handler,TIM_CHANNEL_3);//配置TIM3通道3
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_3);//開啟PWM通道3
TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1; //模式選擇PWM1
TIM3_CH4Handler.Pulse=arr/2; //設定比較值,此值用來確定佔空比,
//預設比較值為自動重灌載值的一半,即佔空比為50%
TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);//配置TIM3通道4
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);//開啟PWM通道4
}
//此函式會被HAL_TIM_PWM_Init()呼叫
//htim:定時器控制代碼
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_TIM3_CLK_ENABLE(); //使能定時器3
__HAL_RCC_GPIOB_CLK_ENABLE(); //開啟GPIOB時鐘
__HAL_RCC_GPIOA_CLK_ENABLE(); //開啟GPIOA時鐘
GPIO_Initure.Pin=GPIO_PIN_6|GPIO_PIN_7; //PA6 PA7
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //複用推完輸出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate=GPIO_AF2_TIM3; //PB1複用為TIM3_CH4
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_1; //PB1
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //複用推完輸出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate=GPIO_AF2_TIM3; //PB1複用為TIM3_CH4
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_0 ; //PB0
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //複用推完輸出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate=GPIO_AF2_TIM3; //PB1複用為TIM3_CH3
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}//設定TIM3通道1的佔空比
//compare:比較值
void TIM_SetTIM3Compare1(u32 compare)
{
TIM3->CCR1=compare;
}//設定TIM3通道2的佔空比
//compare:比較值
void TIM_SetTIM3Compare2(u32 compare)
{
TIM3->CCR2=compare;
}
//設定TIM通道4的佔空比
//compare:比較值
void TIM_SetTIM3Compare4(u32 compare)
{
TIM3->CCR4=compare;
}//設定TIM通道3的佔空比
//compare:比較值
void TIM_SetTIM3Compare3(u32 compare)
{
TIM3->CCR3=compare;
}總結
善於利用STM32微控制器的外設資源,能夠讓開發效率大大提高。
後續就是要對這些函式進行封裝,用來作為舵機控制的統一介面,直接傳遞舵機控制的角度,來達到控制舵機的目的。