本文是关于2023 3月-4月的总结,暂定标题为 “平衡小车”。
文章目录
硬件篇
1.硬件选择
-
- 主控选择:这个嘛,不多说,选择便宜的 STM32F103C8T6
-
- 降压:mp1584
-
- 显示:使用IIC协议驱动的0.96寸OLED
-
- 陀螺仪:MPU6050
-
- 电机驱动:tb6612
-
- 电机使用轮趣科技编码电机
2.硬件使用方法
2.1 主控配置(STM32F103C8T6)
2.1.1 时钟配置
主频使用72Mhz
2.1.2RCC & SYS
RCC选择外部高速时钟,SYS Debug模式使用SW模式
RCC
SYS
2.1.3 Timers
首先说明定时器的作用:
- TIM1 产生两路PWM(使用通道1与通道4)
- TIM2 用于定时10ms定时,当然也可使用mpu6050 INT 引脚 外中断进行10ms定时,可以省去一个定时器
- TIM3 & TIM4 使用编码器模式,记录电机的速度
cubemx配置 |
---|
![]() |
![]() |
2.1.4 Conectivity
I2C1 & I2C2 & USART2
- 使用硬件I2C1驱动OLED 使用快速模式,传输速率为400kbit/s
- I2C2 这里不再赘述,讲其MPU6050的硬件使用方法,在详细解说
- USART2使用串口2异步模式进行蓝牙通信
2.2 陀螺仪使用方法(MPU6050)
2.2.1 IIC协议
笔者这里不在赘述IIC协议,各位要是有兴趣去搜搜其他博客内容。这里笔者推荐几篇文章,也可以看看其他的文章,不一定笔者推荐的就是不错的文章。
IIC通信协议,搞懂这篇就够了
I²C(IIC)总线协议详解—完整版
关于STC15 IIC AT24C02实现
2.2.2 硬件IIC使用方法
使用硬件IIC时,配置与上述OLED配置一样,勾选I2C2,速度模式选择Fast Mode,笔者这里使用是正点原子所提供的库,主要包含以下文件:
将原始模拟IIC代码拷贝过来后,会出现IIC连续写的注释片段,将连续写下面代码片段,全部替换成以下代码片段。
//IIC连续写
//addr:器件地址
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
// 其他,错误代码
uint8_t MPU_Write_Len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf)
{
HAL_I2C_Mem_Write(&hi2c2, ((addr<<1)|0), reg, 1, (unsigned char *)buf, len, HAL_MAX_DELAY);
return 0;
}
//IIC连续读
//addr:器件地址
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值:0,正常
// 其他,错误代码
uint8_t MPU_Read_Len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf)
{
HAL_I2C_Mem_Read(&hi2c2, ((addr<<1)|1), reg, 1, (unsigned char *)buf, len, HAL_MAX_DELAY);
return 0;
}
//IIC写一个字节
//reg:寄存器地址
//data:数据
//返回值:0,正常
// 其他,错误代码
uint8_t MPU_Write_Byte(uint8_t reg,uint8_t data)
{
HAL_I2C_Mem_Write(&hi2c2, (MPU_ADDR<<1)|0, reg, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY);
return 0;
}
//IIC读一个字节
//reg:寄存器地址
//返回值:读到的数据
uint8_t MPU_Read_Byte(uint8_t reg)
{
uint8_t res;
HAL_I2C_Mem_Read(&hi2c2, (MPU_ADDR<<1)|1, reg, I2C_MEMADD_SIZE_8BIT, &res, 1, HAL_MAX_DELAY);
return res;
}
这里需要将MPU_Init初始化也进行更改,对于头文件的包含,因该包含cubemx生成的I2C.h。需将软件初始化
//初始化MPU6050
//返回值:0,成功
// 其他,错误代码
uint8_t MPU_Init(void)
{
uint8_t res;
HAL_I2C_Init(&hi2c1);//初始化IIC总线
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80); //复位MPU6050
delay_ms(50);
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00); //唤醒MPU6050
MPU_Set_Gyro_Fsr(3); //陀螺仪传感器,±2000dps
MPU_Set_Accel_Fsr(0); //加速度传感器,±2g
MPU_Set_Rate(50); //设置采样率50Hz
MPU_Write_Byte(MPU_INT_EN_REG,0X00); //关闭所有中断
MPU_Write_Byte(MPU_USER_CTRL_REG,0X00); //I2C主模式关闭
MPU_Write_Byte(MPU_FIFO_EN_REG,0X00); //关闭FIFO
MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80); //INT引脚低电平有效
res=MPU_Read_Byte(MPU_DEVICE_ID_REG);
print(&huart1, "\r\nMPU6050:0x%2x\r\n", res);
if(res==MPU_ADDR)//器件ID正确
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01); //设置CLKSEL,PLL X轴为参考
MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00); //加速度与陀螺仪都工作
MPU_Set_Rate(50); //设置采样率为50Hz
} else return 1;
return 0;
}
2.2.3 软件模拟IIC使用方法
软件使用,保持粘贴过来的模拟IIC原样就行。
//IIC连续写
//addr:器件地址
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
// 其他,错误代码
uint8_t MPU_Write_Len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf)
{
uint8_t i;
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令
if(MPU_IIC_Wait_Ack()) //等待应答
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg); //写寄存器地址
MPU_IIC_Wait_Ack(); //等待应答
for(i=0; i<len; i++)
{
MPU_IIC_Send_Byte(buf[i]); //发送数据
if(MPU_IIC_Wait_Ack()) //等待ACK
{
MPU_IIC_Stop();
return 1;
}
}
MPU_IIC_Stop();
return 0;
}
//IIC连续读
//addr:器件地址
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值:0,正常
// 其他,错误代码
uint8_t MPU_Read_Len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令
if(MPU_IIC_Wait_Ack()) //等待应答
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg); //写寄存器地址
MPU_IIC_Wait_Ack(); //等待应答
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|1);//发送器件地址+读命令
MPU_IIC_Wait_Ack(); //等待应答
while(len)
{
if(len==1)*buf=MPU_IIC_Read_Byte(0);//读数据,发送nACK
else *buf=MPU_IIC_Read_Byte(1); //读数据,发送ACK
len--;
buf++;
}
MPU_IIC_Stop(); //产生一个停止条件
return 0;
}
//IIC写一个字节
//reg:寄存器地址
//data:数据
//返回值:0,正常
// 其他,错误代码
uint8_t MPU_Write_Byte(uint8_t reg,uint8_t data)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令
if(MPU_IIC_Wait_Ack()) //等待应答
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg); //写寄存器地址
MPU_IIC_Wait_Ack(); //等待应答
MPU_IIC_Send_Byte(data);//发送数据
if(MPU_IIC_Wait_Ack()) //等待ACK
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Stop();
return 0;
}
//IIC读一个字节
//reg:寄存器地址
//返回值:读到的数据
uint8_t MPU_Read_Byte(uint8_t reg)
{
uint8_t res;
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令
MPU_IIC_Wait_Ack(); //等待应答
MPU_IIC_Send_Byte(reg); //写寄存器地址
MPU_IIC_Wait_Ack(); //等待应答
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|1);//发送器件地址+读命令
MPU_IIC_Wait_Ack(); //等待应答
res=MPU_IIC_Read_Byte(0);//读取数据,发送nACK
MPU_IIC_Stop(); //产生一个停止条件
return res;
}
下面MPU_Init初始化
//初始化MPU6050
//返回值:0,成功
// 其他,错误代码
uint8_t MPU_Init(void)
{
uint8_t res;
MPU_IIC_Init();//初始化IIC总线
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80); //复位MPU6050
delay_ms(100);
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00); //唤醒MPU6050
MPU_Set_Gyro_Fsr(3); //陀螺仪传感器,±2000dps
MPU_Set_Accel_Fsr(0); //加速度传感器,±2g
MPU_Set_Rate(50); //设置采样率50Hz
MPU_Write_Byte(MPU_INT_EN_REG,0X00); //关闭所有中断
MPU_Write_Byte(MPU_USER_CTRL_REG,0X00); //I2C主模式关闭
MPU_Write_Byte(MPU_FIFO_EN_REG,0X00); //关闭FIFO
MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80); //INT引脚低电平有效
res=MPU_Read_Byte(MPU_DEVICE_ID_REG);
if(res==MPU_ADDR)//器件ID正确
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01); //设置CLKSEL,PLL X轴为参考
MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00); //加速度与陀螺仪都工作
MPU_Set_Rate(50); //设置采样率为50Hz
} else return 1;
return 0;
}
笔者在这里使用的是软件IIC方案,最开始,笔者使用的是硬件IIC方案,但是在采集数据后,没过一会就出现死机,我也不知道是为什么。(对这方面感兴趣大佬可以指出指出)所以后续使用软件IIC方案,死机问题得到了解决。如果要使用外中断的话,需要将采集频率改成100Hz
MPU_Set_Rate(100); //设置采样率为50Hz
这样可以保证,没触发一次外中断的时间是10ms。
2.3 电机驱动使用(tb6612)
tb6612使用网上最多红色的版本。当然我们这里只讲使用方法,对于网络上有许多讲tb6612的工作原理,有兴趣的朋友可以搜搜看,这里不做赘述。
上图是tb6612的引脚分布图
接线,AIN1,AIN2,BIN1,BIN2接单片机IO,STBY你若不想使用程序控制,可以接3v3或者是5v,这里笔者选择接单片机IO。PWMA与PWMB上述所说定时器1开启的两通道,这里笔者开的是定时器1的通道1(PA8)与通道4(PA11),AIN1,AIN2,BIN1,BIN2这几个口俩俩给相反电平,给相同电平会导致电机不转。这里我们只需要控制通道1与通道4的占空比,使用单极性控制,就可以进行电机调速。
上图是cubemx画出tb6612配置引脚,这里笔者将STBY重新定义一个名字,这个名字声明过后会在main.h中进行宏定义(下面描述为宏方法)。如下图:
这样会给我们编程提供极大便利,且代码易于移植。这里我们只需要写
HAL_GPIO_WritePin(STBY_GPIO_Port, STBY_Pin, GPIO_PIN_SET);
就可以使能STBY。
HAL库中有许多这样宏方法,可能这显得过于累赘复杂,或者是多余。下面是HAL中在设置编码器值的一些宏方法
这里笔者希望读者体会这种写法与思想。
3.直立环速度环公式推导
软件篇