stm32 HAL库 平衡小车 零基础 硬件篇

本文是关于2023 3月-4月的总结,暂定标题为 “平衡小车”。


硬件篇

1.硬件选择

    1. 主控选择:这个嘛,不多说,选择便宜的 STM32F103C8T6
    1. 降压:mp1584
    1. 显示:使用IIC协议驱动的0.96寸OLED
    1. 陀螺仪:MPU6050
    1. 电机驱动:tb6612
    1. 电机使用轮趣科技编码电机

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.直立环速度环公式推导

软件篇
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值