阿克曼转向小车是由两个减速直流电机和一个转向舵机构成,上一篇文章已经介绍了如何驱动舵机,这篇文章将会讲解怎么驱动两个动力电机。控制器的动力电机端口分配如下:
其中PE13是定时器1的CH3、PE11是定时器1的CH2;PE14是定时器1CH4、PE9是定时器1CH1。具体代码实在HwConfig/hw_STM32F40x.c中:
void MotorInit(uint8_t mName_t,uint32_t mPwm_Arr,uint32_t mPwm_Psc,uint8_t mMotorType){
GPIO_TypeDef* MOTOR_A_PORT[MOTORn] = {STARBOT_MOTOR1_A_GPIO_PORT, STARBOT_MOTOR2_A_GPIO_PORT,
STARBOT_MOTOR3_A_GPIO_PORT,STARBOT_MOTOR4_A_GPIO_PORT};
GPIO_TypeDef* MOTOR_B_PORT[MOTORn] = {STARBOT_MOTOR1_B_GPIO_PORT, STARBOT_MOTOR2_B_GPIO_PORT,
STARBOT_MOTOR3_B_GPIO_PORT,STARBOT_MOTOR4_B_GPIO_PORT};
TIM_TypeDef * MOTOR_TIM[MOTORn] = {STARBOT_MOTOR1_TIM, STARBOT_MOTOR2_TIM, \
STARBOT_MOTOR3_TIM, STARBOT_MOTOR4_TIM};
const uint32_t MOTOR_TIM_CLK[MOTORn] = {STARBOT_MOTOR1_TIM_CLK, STARBOT_MOTOR2_TIM_CLK, \
STARBOT_MOTOR3_TIM_CLK, STARBOT_MOTOR4_TIM_CLK};
const uint16_t MOTOR_A_CHANNEL[MOTORn] = {STARBOT_MOTOR1_A_CHANNEL, STARBOT_MOTOR2_A_CHANNEL, \
STARBOT_MOTOR3_A_CHANNEL, STARBOT_MOTOR4_A_CHANNEL};
//const uint16_t MOTOR_B_CHANNEL[MOTORn] = {STARBOT_MOTOR1_B_CHANNEL, STARBOT_MOTOR2_B_CHANNEL, \
// STARBOT_MOTOR3_B_CHANNEL, STARBOT_MOTOR4_B_CHANNEL};
const uint16_t MOTOR_A_PINSOU[MOTORn] = {STARBOT_MOTOR1_A_PINSOU,STARBOT_MOTOR2_A_PINSOU,\
STARBOT_MOTOR3_A_PINSOU,STARBOT_MOTOR4_A_PINSOU};
const uint16_t MOTOR_B_PINSOU[MOTORn] = {STARBOT_MOTOR1_B_PINSOU,STARBOT_MOTOR2_B_PINSOU,\
STARBOT_MOTOR3_B_PINSOU,STARBOT_MOTOR4_B_PINSOU};
const uint32_t MOTOR_A_PORT_CLK[MOTORn] = {STARBOT_MOTOR1_A_GPIO_CLK, STARBOT_MOTOR2_A_GPIO_CLK, \
STARBOT_MOTOR3_A_GPIO_CLK, STARBOT_MOTOR4_A_GPIO_CLK};
const uint32_t MOTOR_B_PORT_CLK[MOTORn] = {STARBOT_MOTOR1_B_GPIO_CLK, STARBOT_MOTOR2_B_GPIO_CLK, \
STARBOT_MOTOR3_B_GPIO_CLK, STARBOT_MOTOR4_B_GPIO_CLK};
const uint32_t MOTOR_AF_TIM[MOTORn] = {STARBOT_MOTOR1_AF_TIM,STARBOT_MOTOR2_AF_TIM,\
STARBOT_MOTOR3_AF_TIM,STARBOT_MOTOR4_AF_TIM};
const uint16_t MOTOR_A_PIN[MOTORn] = {STARBOT_MOTOR1_A_PIN, STARBOT_MOTOR2_A_PIN, \
STARBOT_MOTOR3_A_PIN, STARBOT_MOTOR4_A_PIN,};
const uint16_t MOTOR_B_PIN[MOTORn] = {STARBOT_MOTOR1_B_PIN, STARBOT_MOTOR2_B_PIN,\
STARBOT_MOTOR3_B_PIN, STARBOT_MOTOR4_B_PIN};
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(MOTOR_A_PORT_CLK[mName_t] | MOTOR_B_PORT_CLK[mName_t], ENABLE);
RCC_APB2PeriphClockCmd(MOTOR_TIM_CLK[mName_t], ENABLE);
if(1 == mMotorType)
{
/** init motor_ gpio **/
GPIO_InitStructure.GPIO_Pin = MOTOR_A_PIN[mName_t];
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(MOTOR_A_PORT[mName_t], &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = MOTOR_B_PIN[mName_t];
GPIO_Init(MOTOR_B_PORT[mName_t], &GPIO_InitStructure);
GPIO_PinAFConfig(MOTOR_A_PORT[mName_t],MOTOR_A_PINSOU[mName_t],MOTOR_AF_TIM[mName_t]); //GPIOA8复用为定时器1
GPIO_PinAFConfig(MOTOR_B_PORT[mName_t],MOTOR_B_PINSOU[mName_t],MOTOR_AF_TIM[mName_t]); //GPIOA7复用为定时器1
//init timx
//pwm value ((1 + psc)/168M)*(1+arr)
//eg: ((1+143)/168M)*(1+9999) = 0.02s --10000 count use 0.02s
//set arduino pwm value 490hz 255 count
//((1 + 575)/168M)(1 + 254) = (1 / 490)
//TIM_DeInit(MOTOR_TIM[mName_t]);
TIM_TimeBaseInitStructure.TIM_Period = mPwm_Arr-1; //arr 自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler = mPwm_Psc-1; //psc 定时器分频
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数次数然后进入中断
TIM_TimeBaseInit(MOTOR_TIM[mName_t], &TIM_TimeBaseInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable; //互补输出允许 TIM_OutputNState_Disable
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //High为占空比高极性,此时占空比为50%,Low则为反极性,占空比为50%
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; //指定空闲状态下的TIM输出比较的引脚状态。
if(mName_t == MOTOR1 )
{
TIM_OC3Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
TIM_OC3PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
TIM_OC2Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
TIM_OC2PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
TIM_SetCompare2(MOTOR_TIM[mName_t], 0);
TIM_SetCompare3(MOTOR_TIM[mName_t], 0);
}
else if(mName_t == MOTOR2)
{
TIM_OC1Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
TIM_OC1PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
TIM_OC4Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
TIM_OC4PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
TIM_SetCompare1(MOTOR_TIM[mName_t], 0);
TIM_SetCompare4(MOTOR_TIM[mName_t], 0);
}
else if( mName_t == MOTOR3)
{
TIM_OC1Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
TIM_OC1PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
TIM_OC2Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
TIM_OC2PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
TIM_SetCompare1(MOTOR_TIM[mName_t], 0);
TIM_SetCompare2(MOTOR_TIM[mName_t], 0);
}
else if(mName_t == MOTOR4)
{
TIM_OC3Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
TIM_OC3PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
TIM_OC4Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
TIM_OC4PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
TIM_SetCompare3(MOTOR_TIM[mName_t], 0);
TIM_SetCompare4(MOTOR_TIM[mName_t], 0);
}
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
//bit7~5 = 111,则deadtime = (32 + (bit4~bit0)* 16*1/fosc)ns = (32+31)*16*1/72000000 = 14us
死区时间 72:1us 172:3us 205:5us
TIM_BDTRInitStructure.TIM_DeadTime = 0x94;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(MOTOR_TIM[mName_t],&TIM_BDTRInitStructure);
TIM_ARRPreloadConfig(MOTOR_TIM[mName_t],ENABLE);
TIM_Cmd(MOTOR_TIM[mName_t], ENABLE);
TIM_CtrlPWMOutputs(MOTOR_TIM[mName_t], ENABLE);
}
}
其中Arr和Psc根据实际电机适配的频率进行设置, TIM_CtrlPWMOutputs(MOTOR_TIM[mName_t], ENABLE),该函数不能省略,否则PWM无法输出。假如让电机1正转则需要设置CH3的占空比为期望的输出,把CH2的占空比设置为0;假如让电机1反转则需要设置CH3的占空比为0,把CH2的占空比设置为期望的输出;电机2同理,具体代码实现如下:
void MotorSpin(const vMotorStr* Src){
int msDir_t = 0;
msDir_t = Src->dutyVal * Src->mDir;
if(1 == Src->mDirveType){
if(msDir_t>0){
TIM_SetCompareX(MOTOR_TIM[Src->mName], MOTOR_A_CHANNEL[Src->mName],0);
TIM_SetCompareX(MOTOR_TIM[Src->mName], MOTOR_B_CHANNEL[Src->mName],abs(Src->dutyVal));
}else if(msDir_t<0){
TIM_SetCompareX(MOTOR_TIM[Src->mName], MOTOR_A_CHANNEL[Src->mName],abs(Src->dutyVal));
TIM_SetCompareX(MOTOR_TIM[Src->mName], MOTOR_B_CHANNEL[Src->mName],0);
}else{
TIM_SetCompareX(MOTOR_TIM[Src->mName], MOTOR_A_CHANNEL[Src->mName],0);
TIM_SetCompareX(MOTOR_TIM[Src->mName], MOTOR_B_CHANNEL[Src->mName],0);
}
}
}
我们只需要调用MotorSpin函数然后给定转动的PWM期望值即可让电机转动起来。