1、平衡小车硬件选择
带编码器的直流减速电机(两个)、18650电池组、平衡车支架、MPU-6050陀螺仪、stm32f103c8t6、OLED,iic通信协议(0.96寸)、TB6612、
2、硬件准备阶段
根据电路原理,平衡小车电路板,单片机供电电路,模块供电电路,同时可以多加几个串口用于拓展。完成平衡小车基本架构的组装。同时向商家获取每个模块的数据手册便于使用。
3、单片机基础
在做平衡小车之前应该学会stm32的PWM输出,IO口模式的配置,IIC协议,熟练使用OLED显示屏、定时器中断,定时器编码器模式。单片机基础可以通过正点原子的视频完成。
4、MPU-6050模块OLED姿态角显示
首先完成两路IIC协议的编程整合,由于初学对寄存器的使用并不熟练,从网上找到的替代寄存器修改IO口模式的IIC代码
void MPU_SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
GPIO_InitStructer.GPIO_Pin= GPIO_Pin_9;
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructer);
}
void MPU_SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
GPIO_InitStructer.GPIO_Pin= GPIO_Pin_9;
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructer);
}
修改对应IO口后使用正点原子的MPU-6050课程代码进行初始化,一开始使用杜邦线进行MPU-6050的连接几乎无法初始化成功,尝试了很久最后才发现不能用杜邦线进行IIC通信,所以就直接将MPU-6050,插在排孔中最后成功通信,在用OLED对显示位置的调整最终完成MPU-6050的显示和数据获取
while(mpu_dmp_init())
{
b=mpu_dmp_init();
OLED_ShowString(0,6,"ASCII:",16);
OLED_ShowNum(64,6,b,3,16);
delay_ms(200);
}
MPU6050_EXTI_Init();
OLED_Clear(0);
while(1)
{
if(mpu_dmp_get_data(&pitch,&roll,&yaw)==0)
{
temp=MPU_Get_Temperature(); //µÃµ½Î¶ÈÖµ
MPU_Get_Accelerometer(&aacx,&aacy,&aacz); //µÃµ½¼ÓËٶȴ«¸ÐÆ÷Êý¾Ý
MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //µÃµ½ÍÓÂÝÒÇÊý¾Ý
}
p=pitch;
r=roll;
y=yaw;
if(pitch>=0)
{
OLED_ShowString(0,1,"pit",16);
OLED_ShowString(38,1,"+",16);
OLED_ShowNum(44,1,p,2,16);
OLED_ShowString(68,1,".",16);
delay_ms(10);
l=pitch;
l-=p;
l*=1000;
OLED_ShowNum(74,1,(int)l,3,16);
}
if(pitch<0)
{
p=-pitch;
OLED_ShowString(0,1,"pit",16);
OLED_ShowString(38,1,"-",16);
OLED_ShowNum(44,1,p,2,16);
OLED_ShowString(68,1,".",16);
delay_ms(10);
l=-pitch;
l-=p;
l*=1000;
OLED_ShowNum(74,1,(int)l,3,16);
}
if(roll>=0)
{
OLED_ShowString(0,3,"roll",16);
OLED_ShowString(38,3,"+",16);
OLED_ShowNum(44,3,r,2,16);
OLED_ShowString(68,3,".",16);
delay_ms(10);
l=roll;
l-=r;
l*=1000;
OLED_ShowNum(74,3,(int)l,3,16);
}
if(roll<0)
{
r=-roll;
OLED_ShowString(0,3,"roll",16);
OLED_ShowString(38,3,"-",16);
OLED_ShowNum(44,3,r,2,16);
OLED_ShowString(68,3,".",16);
delay_ms(10);
l=-roll;
l-=r;
l*=1000;
OLED_ShowNum(74,3,(int)l,3,16);
}
if(yaw>=0)
{
OLED_ShowString(0,6,"yaw",16);
OLED_ShowString(38,6,"+",16);
OLED_ShowNum(44,6,y,2,16);
OLED_ShowString(68,6,".",16);
delay_ms(10);
l=yaw;
l-=y;
l*=1000;
OLED_ShowNum(74,6,(int)l,3,16);
}
if(yaw<0)
{
y=-yaw;
OLED_ShowString(0,6,"yaw",16);
OLED_ShowString(38,6,"-",16);
OLED_ShowNum(44,6,y,2,16);
OLED_ShowString(68,6,".",16);
delay_ms(10);
l=-yaw;
l-=y;
l*=1000;
OLED_ShowNum(74,6,(int)l,3,16);
}
}
5、每个模块的整合
在进行整合时注意PWM,定时器编码器的时钟不能冲突,编码器获取到脉冲值就足够。因为我使用的是模拟IIC通信在初始化IIC协议发现PA15无法实现通信,原来在默认情况下PA13,PA14用作SW调试,PA15,PB4,PB3用作JLINK调试,所以无法将PA15直接用作普通IO口。要加入语句
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
6、平衡原理的学习
通过网上查资料可以了解到平衡小车的平衡原理,根据平衡原理写出对应的代码,通过MPU-6050,每5ms产生一次中断进行对小车位置的检测和电机PWM波以及转动方向的控制。在外部中断中实现
void EXTI9_5_IRQHandler(void)
{
if(PBin(5)==0)
{
EXTI->PR=1<<5;
mpu_dmp_get_data(&pitch,&roll,&yaw); //===µÃµ½Å·À½Ç£¨×Ë̬½Ç£©µÄÊý¾Ý
MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //===µÃµ½ÍÓÂÝÒÇÊý¾Ý
Encoder_Left=read_encoder_cnt(2);
Encoder_Right=read_encoder_cnt(4);
UltrasonicWave_Voltage_Counter++;
Balance_Pwm =balance_UP(roll,Mechanical_angle,gyroy);
Moto1=Balance_Pwm-Velocity_Pwm-Turn_Pwm; //===¼ÆËã×óÂÖµç»ú×îÖÕPWM
Moto2=Balance_Pwm-Velocity_Pwm+Turn_Pwm;
Set_Pwm(Moto1,Moto2);
}
}
直立环
int balance_UP(float Angle,float Mechanical_balance,float Gyro)
{
float Bias;
int balance;
Bias=Angle-Mechanical_balance;
balance=balance_UP_KP*Bias+balance_UP_KD*Gyro;
return balance;
}
将初次的程序烧录到单片机进行参数的调整,一直到平衡小车用外力推都不会倒下,就完成了平衡小车的直立。
文章使用正点原子代码仅为学习作用。