OskarBot小车驱动(二)、直流电机驱动
【目录】
- 1、硬件资源与控制信号
- 1.1 信号与引脚
- 1.2 电机驱动:H桥电路
- 1.3 TB6612芯片及其控制
- (1) TB6612芯片介绍
- (2)接线
- 2、转向控制信号
- 2.1 定义变量与引脚对应关系
- 2.2 定义电机运转函数
- 3.3 调用电机运转函数
- 3、PWM信号
- 3.1 定时器参数计算
- (1)F429 定时器周期计算
- (2)F103 输出PWM定时器参数计算
- 3.2定时器初始化
- 4、改变PWM信号占空比
1、硬件资源与控制信号
1.1 信号与引脚
电机转向控制信号:PC0~PC3。高低电平控制,正转与反转。
PWM输出信号:PB8与PB9。通用定时器Timer4(CH3和CH4通道);
PWM信号占空比,调节电机驱动电压,控制转速。
1.2 电机驱动:H桥电路
H桥是一种电子电路,可使其连接的负载或输出端两端电压反相/电流反向。
用于机器人领域:直流电动机的顺反向控制及转速控制、步进电机控制;
用于电能变换:大部分直流-交流变换器(如逆变器及变频器)、部分直流-直流变换器(推挽式变换器)等。
使能信号与方向信号的使用:
参考:【扫盲贴】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)接线
接线示意图:
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,通道3的CCR值,改变占空比
if(moto2<0) MotorDriver_R_Turn_Reverse(); else MotorDriver_R_Turn_Forward(); TIM_SetCompare4(TIM4, myabs(moto2)); //更新定时器4,通道4的CCR值,改变占空比 } |
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的,在兆瓦级的应用中。 普通中小功率的,5K到20K常见,功率越低,电压等级越低,你所能使用的PWM频率越高。 因为低压的MOSFET开关频率可以做到很高,而高压的IGBT却很难快速开关。普通的马达,10K到20K,都没问题。我们做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输出;
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 CH2的PWM脉冲波形 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); //使能TIM4在CCR2上的预装载寄存器 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时钟(PB6、7作为I2C和TIM4_CH1/2功能),配置结果取决于哪一个外设最后调用其使能函数 //设置该引脚为复用输出功能,输出TIM4 CH1的PWM脉冲波形 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); //使能TIM4在CCR1上的预装载寄存器 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)
TIM_SetCompare3(TIM4, myabs(moto1)); // 更新定时器4,通道3的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通道 int encoder_left, encoder_right; // 编码器目标值
int speed_x = 0x7f, speed_y = 0x7f, speed_w = 0x7f;
void Motion_PID(void)
joy_left_pwm += 3 * (encoder_left - Encoder_Left - last_bias_left) + 5 * (encoder_left - Encoder_Left); //编码器目标值-当前值-上次偏差
last_bias_left = encoder_left - Encoder_Left; //编码器目标值-当前值的上次偏差
Moto1 = joy_left_pwm; // 电机1PWM
Set_Pwm(Moto1, Moto2); } |