OskarBot小车驱动(二)、直流电机驱动

OskarBot小车驱动(二)、直流电机驱动

【目录】

    - 1、硬件资源与控制信号

        - 1.1 信号与引脚

        - 1.2 电机驱动:H桥电路

        - 1.3 TB6612芯片及其控制

            - 1 TB6612芯片介绍

            - 2)接线

    - 2、转向控制信号

        - 2.1 定义变量与引脚对应关系

        - 2.2 定义电机运转函数

        - 3.3 调用电机运转函数

    - 3PWM信号

        - 3.1 定时器参数计算

            - 1F429 定时器周期计算

            - 2F103 输出PWM定时器参数计算

        - 3.2定时器初始化

    - 4、改变PWM信号占空比

 

1、硬件资源与控制信号

1.1 信号与引脚

电机转向控制信号:PC0~PC3。高低电平控制,正转与反转。

PWM输出信号:PB8与PB9。通用定时器Timer4CH3和CH4通道

PWM信号占空比,调节电机驱动电压,控制转速。

1.2 电机驱动:H桥电路

H桥是一种电子电路,可使其连接的负载或输出端两端电压反相/电流反向。

用于机器人领域:直流电动机的顺反向控制及转速控制、步进电机控制;

用于电能变换:大部分直流-交流变换器(如逆变器及变频器)、部分直流-直流变换器(推挽式变换器)等。

OskarBot小车驱动(二)、直流电机驱动_第1张图片

 

使能信号与方向信号的使用:

OskarBot小车驱动(二)、直流电机驱动_第2张图片

参考:【扫盲贴】H桥式电机驱动电路原理

来自 <https://www.amobbs.com/thread-513251-1-1.html>

1.3 TB6612芯片及其控制

(1) TB6612芯片介绍

TB6612FNG,大电流MOSFET-H 桥结构,双通道电路输出,可同时驱动 2 个电机。

相比 L298N 的热耗性和外围二极管续流电路,它无需外加散热片,外围电路简单,只需外接电源滤波电容就可以直接驱动电机,利于减小系统尺寸。

Power supply voltage: VM = 15 V(Max);Output current: IOUT = 1.2 A(ave)/3.2 A (peak)

PWM信号输入频率<100KHz

(2)接线

接线示意图:

OskarBot小车驱动(二)、直流电机驱动_第3张图片

OskarBot小车驱动(二)、直流电机驱动_第4张图片

2、转向控制信号

2.1 定义变量与引脚对应关系

#define MOTOR_L_IN1_LOW         (GPIO_ResetBits(GPIOC, GPIO_Pin_0))

#define MOTOR_L_IN1_HIGH        (GPIO_SetBits(GPIOC, GPIO_Pin_0))

#define MOTOR_L_IN2_LOW         (GPIO_ResetBits(GPIOC, GPIO_Pin_1))

#define MOTOR_L_IN2_HIGH        (GPIO_SetBits(GPIOC, GPIO_Pin_1))

 

#define MOTOR_R_IN1_LOW         (GPIO_ResetBits(GPIOC, GPIO_Pin_2))

#define MOTOR_R_IN1_HIGH        (GPIO_SetBits(GPIOC, GPIO_Pin_2))

#define MOTOR_R_IN2_LOW         (GPIO_ResetBits(GPIOC, GPIO_Pin_3))

#define MOTOR_R_IN2_HIGH        (GPIO_SetBits(GPIOC, GPIO_Pin_3))

2.2 定义电机运转函数

/***   @brief      左轮电机正转

    *   @param      none

    *   @retval     none

    */

void    MotorDriver_L_Turn_Forward(void)

{

    MOTOR_L_IN1_LOW;

    MOTOR_L_IN2_HIGH;

}

3.3 调用电机运转函数

void Set_Pwm(int moto1, int moto2)

{

    if(moto1<0)

    MotorDriver_L_Turn_Reverse();

    else

    MotorDriver_L_Turn_Forward();

    TIM_SetCompare3(TIM4, myabs(moto1)); //更新定时器4,通道3CCR值,改变占空比

 

   

    if(moto2<0)

    MotorDriver_R_Turn_Reverse();

    else

    MotorDriver_R_Turn_Forward();

    TIM_SetCompare4(TIM4, myabs(moto2)); //更新定时器4,通道4CCR值,改变占空比

}

3、PWM信号

定时器输出PWM信号。PWM输出信号来自PB8与PB9,通用定时器Timer4(CH3和CH4通道)。

PWM 输出就是对外输出脉宽(即占空比)可调的方波信号,信号频率由自动重装寄存器 ARR 的值决定,占空比由比较寄存器 CCR 的值决定。

 

3.1 定时器参数计算

自动重载寄存器 TIMx_ARR 用来存放于计数器值比较的数值。

当 TIMx_CNT 值与 TIMx_ARR 的设定值相等时就自动生成事件并 TIMx_CNT 自动清零,然后自动重新开始计数。

(1)F429 定时器周期计算

时钟源:AHB 总线时钟 HCLK=系统时钟SYSCLK=180M;

4 分频,APB1 总线时钟 PCLK1 = HCLK/4 = 45M;( 2 分频,APB2 总线时钟 PCLK2 = HCLK /2= 90M);

定时器时钟源:TIMxCLK = 2 * PCLK1=90MHz=输入时钟 fCK_PSC

定时器时钟预分频:输出时钟 fCK_CNT =输入时钟 fCK_PSC/预分频(PSC[15:0]+1);

 

1)产生1s的定时周期 =计数(10k次)/频率(10KHz)

定时器输出时钟频率 fCK_CNT =90MHz/9k=10KHz(100us); 预分频 Prescaler= 9000-1;

计数次数:Period = 10000-1,从0~9999,共计10K次。

/* 累计 TIM_Period 个后产生一个更新或者中断*/

TIM_TimeBaseStructure.TIM_Period = 5000-1;  //当定时器从 0 计数到 4999,即为 5000 次,为一个定时周期

 

2)0.5s的定时中断,0.5s高电平,0.5s低电平;5k/10k

/* 累计 TIM_Period 个后产生一个更新或者中断*/

TIM_TimeBaseStructure.TIM_Period = 5000-1;  //当定时器从 0 计数到 4999,即为 5000 次,为一个定时周期

 

//定时器时钟源 TIMxCLK = 2 * PCLK1,PCLK1 = HCLK / 4,=> TIMxCLK=HCLK/2=SystemCoreClock/2=90MHz

// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=90M/9k=10kHz

TIM_TimeBaseStructure.TIM_Prescaler = 9000-1;

 

3)100kHz的PWM信号,30%占空比

//累计 TIM_Period 个后产生一个更新或者中断

//当定时器从 0 计数到 8999,即为 9000 次,为一个定时周期

TIM_TimeBaseStructure.TIM_Period = 10000-1;

 

// 通用定时器 2 时钟源 TIMxCLK = HCLK/2=90MHz

// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=100KHz

TIM_TimeBaseStructure.TIM_Prescaler = 900-1;

// 采样时钟分频,这里不需要用到

TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;

//PWM 脉冲宽度

TIM_OCInitStructure.TIM_Pulse = 3000-1;

 

占空比为:(Pulse+1)/(Period+1) = 30%。

 

(2)F103 输出PWM定时器参数计算

PWM信号频率选择:10KHz(0.1ms)(根据电机特性而定)。

1、没有统一的标准,其实PWM的频率和你的电机感抗和你需要的速度响应时间有很大的关系。一般的电机用14K就足够了。

当然自需要简单的调速可以随便选。 如果电机转速比较高,感抗比较小,可以使用比较高的频率。

一般最好不要超过20K  因为一般IGBT最高20K的开关频率。 MOS 的开关频率比较高, 但是过高的F 需要专用的驱动电路,不然MOS工作在放大区的时间比较长。

 如果电机转速比较低,感抗比较大, 而且又是在做伺服, 那开关频率就需要低一点。

2、对于电机应用,功率越大,PWM频率越低,最低有500Hz或者1KHz的,在兆瓦级的应用中。

普通中小功率的,5K20K常见,功率越低,电压等级越低,你所能使用的PWM频率越高。

因为低压的MOSFET开关频率可以做到很高,而高压的IGBT却很难快速开关。普通的马达,10K20K,都没问题。我们做KW级主变频器,开关频率10K

来自 <https://www.cnblogs.com/wangh0802PositiveANDupward/archive/2012/09/05/2671391.html>

 

定时器时钟 =输入时钟/(分频+1)=72MHz,计数次数7200次(Period-1=7199);Arr=7199;

定时时间 7.2K/72M=0.1ms;定时频率 =72M/7.2K=10kHz;p

 

主函数赋值初始化更新arr值,Motor_Init(7199, 0);

占空比的CCR值,初值为0,由函数更新 TIM_SetCompare3(TIM4, myabs(moto1)); //更新定时器4,通道3的CCR值,改变占空比

 

3.2定时器初始化

使用硬件资源:PB8和PB9引脚,对应TIM4的CH3和CH4通道,OC3和OC4输出;

PB9引脚,对应TIM4的CH3和CH4通道,OC3和OC4输出;

OskarBot小车驱动(二)、直流电机驱动_第5张图片

Motor.c部分

 

方法一、配置两个输出

Motor_PWM_Init(7200, 0, 7200, 0);        /*不分频,72000000/7200=10khz*/

//TIM4 PWM部分初始化 :CH1,PB6;CH2,PB7

//PWM输出初始化

//arr:自动重装值

//psc:时钟预分频数

/**

* Function       Motor_PWM_Init

* @author        liusen

* @date          2017.07.17    

* @brief         电机PWM口初始化

* @param[in]     左电机 arr:自动重装值  psc:时钟预分频数   右电机 arr2:自动重装值  psc2:时钟预分频数

* @param[out]    void

* @retval        void

* @par History  

*/

void Motor_PWM_Init(u16 arr, u16 psc, u16 arr2, u16 psc2 )

{  

    GPIO_InitTypeDef GPIO_InitStructure;

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    TIM_OCInitTypeDef  TIM_OCInitStructure;

    

    //PWMA  PB7 CH2

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);    //使能定时器4时钟

   //设置该引脚为复用输出功能,输出TIM4 CH2PWM脉冲波形 GPIOB.7

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM_CH2

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

   //初始化TIM4

    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值

    TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置用来作为TIMx时钟频率除数的预分频值

    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

    

    //初始化TIM4 Channel2 PWM模式    

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高

    TIM_OCInitStructure.TIM_Pulse = 0;

    TIM_OC2Init(TIM4, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM4 OC2

    TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);  //使能TIM4CCR2上的预装载寄存器

    TIM_Cmd(TIM4, ENABLE);  //使能TIM3

    

    //PWMB PB6 CH1

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);    //使能定时器4时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟

    //在使用Alternate Function时最好还是打开AFIO时钟(PB67作为I2CTIM4_CH1/2功能),配置结果取决于哪一个外设最后调用其使能函数

   //设置该引脚为复用输出功能,输出TIM4 CH1PWM脉冲波形 GPIOB.6

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //TIM4_CH2

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

 

   //初始化TIM4

    TIM_TimeBaseStructure.TIM_Period = arr2; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值

    TIM_TimeBaseStructure.TIM_Prescaler = psc2; //设置用来作为TIMx时钟频率除数的预分频值

    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

    

    //初始化TIM4 Channel1 PWM模式(OC1输出)    

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高

    TIM_OCInitStructure.TIM_Pulse = 0;

    TIM_OC1Init(TIM4, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM4 OC1

    TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);  //使能TIM4CCR1上的预装载寄存器

    TIM_Cmd(TIM4, ENABLE);  //使能TIM4

}

 

方法二、参考,只配置一个OC4输出(貌似)

//定时器4,CH3和CH4:直流电机,PWM输出初始化

//PWM输出初始化,PB8和PB9,TIM4的CH3和CH4

//问题:只输出了OC4,不输出OC3? 转向由舵机提供,两轮子的速度是一样的,不需要分别配置?

//arr:自动重装值

//psc:时钟预分频数

void Motor_Init(u16 arr, u16 psc)

    GPIO_InitTypeDef GPIO_InitStructure;

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    TIM_OCInitTypeDef  TIM_OCInitStructure;

 

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);  //使能GPIO外设时钟使能,APB2时钟最高72MHz

                                                                           

                                                                       

   //设置该引脚为复用输出功能,输出TIM4定时器CH3和CH4的PWM脉冲波形

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9 ; //TIM4_CH3 TIM4_CH4

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOB, &GPIO_InitStructure);

 

     //初始化TIM4,定时器配置

    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值,arr=7199,定时频率10kHz

    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  0 不分频

    TIM_TimeBaseStructure.TIM_ClockDivision =0; //设置时钟分割:TDTS = Tck_tim,psc=0,不分割,定时器输出时钟=输入时钟/(分频+1)=72MHz

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

   

//初始化TIM4 Channel3 PWM模式  

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高

    TIM_OCInitStructure.TIM_Pulse = 0;   //设置待装入捕获比较寄存器的脉冲( CCR的初值为0,接收手柄摇杆数据,计算后,更新CCR值

//TIM_SetCompare3(TIM4, myabs(moto1)); //更新定时器4,通道3的CCR值,改变占空比  TIM_SetCompare4(TIM4, myabs(moto2));

    TIM_OC4Init(TIM4, &TIM_OCInitStructure);                     //输出极性:TIM输出比较极性高

    TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);            //根据TIM_OCInitStruct中指定的参数初始化外设TIM4

       

    TIM_CtrlPWMOutputs(TIM4,ENABLE);          //MOE 主输出使能,

    TIM_ARRPreloadConfig(TIM4, ENABLE);     //使能TIMx在ARR上的预装载寄存器 ,定时器周期,Period

   

    TIM_Cmd(TIM4, ENABLE);                  //使能TIM4

   

   

    /*初始化PA.07端口为Out_PP模式*/

    //电机转向引脚初始化,PC0~PC3,

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;/*选择要控制的GPIOB引脚*/

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;/*设置引脚模式为通用推挽输出*/

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/*设置引脚速率为50MHz */

    GPIO_Init(GPIOC, &GPIO_InitStructure);/*调用库函数,初始化GPIOC*/

       

    /* 低电平   */

    //GPIO_ResetBits(Motor_Port, Left_MotoA_Pin | Left_MotoB_Pin | Right_MotoA_Pin | Right_MotoB_Pin);

}

 

 

4、改变PWM信号占空比

库函数文件中,修改CCR值,更新定时器4,修改通道3和4的CCR值

TIM_SetCompare3(TIM4, myabs(moto1)); TIM_SetCompare4(TIM4, myabs(moto2));

void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3)

{

  /* Check the parameters */

  assert_param(IS_TIM_LIST3_PERIPH(TIMx));

  /* Set the Capture Compare3 Register value */

  TIMx->CCR3 = Compare3;

}

在航姿参考系统算法 (AHRS)中,ahrs.c文件

调用设置PWM的函数,

方法一、

void Set_Pwm(int moto1, int moto2)
{
        if(moto1<0)
        MotorDriver_L_Turn_Reverse();      // 小于0,电机反转
        else
        MotorDriver_L_Turn_Forward();

 

        TIM_SetCompare3(TIM4, myabs(moto1));      // 更新定时器4,通道3的CCR值,改变占空比
 
       
        if(moto2<0)
        MotorDriver_R_Turn_Reverse();
        else
        MotorDriver_R_Turn_Forward();
        TIM_SetCompare4(TIM4, myabs(moto2));       // 更新定时器4,通道4的CCR值,改变占空比
}

方法二、

#define  LeftMotorPWM(Speed)        TIM_SetCompare2(TIM4, Speed);
#define  RightMotorPWM(Speed)        TIM_SetCompare1(TIM4, Speed);        

 

占空比的数值,赋值来自运动PID处理函数。

根据得到的右摇杆数据进行处理。

右摇杆R3:数值增大,减去容错区间,与中心位置的偏差,乘以比例系数(Y轴2.5,X轴1.5),作为编码器的目标值。

(目标值-当前值-上次的偏差)X3;加上目标值与当前值的偏差X5,赋值给摇杆的pwm计算结果Moto1 和 Moto2。

Moto1大于0(摇杆数值增大),电机正转;Moto1小于0(摇杆数值减小),电机反转。

Moto1增大,CCR增大,占空比增大,电压升高,转速加快。

int Encoder_Left,Encoder_Right;             // 两个编码器当前值

int Moto1,Moto2;  

 

// 读取编码器当前值,因为两个电机的旋转了180度的,所以需要对其中一个取反

Encoder_Left = Read_Encoder(2); //定时器2,CH1与CH2通道
Encoder_Right = -Read_Encoder(8);  //定时器8,CH1与CH2通道

int encoder_left, encoder_right;            // 编码器目标值

 

int speed_x = 0x7f, speed_y = 0x7f, speed_w = 0x7f;
int last_bias_left, last_bias_right;
bool ahrs_task = false;

 

void Motion_PID(void)
{
        if((speed_y - 0x7f <= 15) && (speed_y - 0x7f >= -15))       // 摇杆数值0~255,判断是否偏离中心127
        {
                speed_y = 0x7f;
                encoder_left = 0;
        }
        else if(speed_y - 0x7f > 15)      // 摇杆数值,容错区间+-15
        {
                encoder_left = -(0x7f - (speed_y - 15)) * 2.5//右摇杆R3上下(Y轴),控制前进后退,Y轴比例大;摇杆模拟量数值增大,编码器为正,增大;
        }
        else if(speed_y - 0x7f < -15)
        {
                encoder_left = -(0x7f - (speed_y + 15)) * 2.5//摇杆数值减小,编码器为负
        }
       
        encoder_right = encoder_left;
       
       
        if((speed_x - 0x7f <= 15) && (speed_x - 0x7f >= -15))
        {
                speed_x = 0x7f;
        }
        else if(speed_x - 0x7f > 15)
        {
                encoder_left -= (speed_x - 0x7F - 15) * 1.5;//右摇杆R3左右(X轴),x轴比例小
                encoder_right += (speed_x - 0x7F - 15) * 1.5;
        }
        else if(speed_x - 0x7f < -15)
        {
                encoder_left -= (speed_x - 0x7F + 15) * 1.5;
                encoder_right += (speed_x - 0x7F + 15) * 1.5;
        }

       

        joy_left_pwm += 3 * (encoder_left - Encoder_Left - last_bias_left) + 5 * (encoder_left - Encoder_Left);   //编码器目标值-当前值-上次偏差
        joy_right_pwm += 3 * (encoder_right - Encoder_Right - last_bias_right) + 5 * (encoder_right - Encoder_Right);

 

last_bias_left = encoder_left - Encoder_Left;   //编码器目标值-当前值的上次偏差
last_bias_right = encoder_right - Encoder_Right;

 

Moto1 = joy_left_pwm;       // 电机1PWM
Moto2 = joy_right_pwm;      // 电机2PWM

 

Set_Pwm(Moto1, Moto2);

}

 

你可能感兴趣的:(小车,STM32)