tips:随手记一些写程序时遇到的小问题,主要内容看纸质笔记。
目标:将MPU6050挂载到I2C上实现控制。
一、I2C模块
1.硬件电路
为了避免总线没协调好,导致电源短路,I2C禁止所有设备输出强上拉的高电平。因此使用外置弱上拉电阻(通常4.7K)加开漏输出结构(指SCL、SDA)也正是因为通信线高电平驱动能力比较弱,导致由上升沿耗时比较长,限制了I2C的最大通信速度。
2.软件时序
六个模块:起始、终止、发送一个字节、接收一个字节、发送应答、接收应答。
需要注意的地方:
(1)发送一个字节:SCL低时,主机将数据位依次放到SDA上,高位先行。释放SCL,从机在SCL高时读数据位,此时SDA不允许变。放一位读一位。控制权都在主机,从机只能被动读取。
(2)接收一个字节:SCL低时,从机将数据为依次放到SDA上,高位先行。释放SCL,主机在SCL高时读数据位,此时SDA不允许变。放一位读一位。主机在接收前,需要释放SDA,此时控制权在从机
(3)接收应答:接收前要释放SDA
3.程序实现
(1)发送一个字节/发送应答:
强制转换类型BitAction
GPIO_WriteBit(GPIOB, MyI2C_SDA, (BitAction)(Byte & (0x80>>i)));
(2)取某位/写某位
写程序的时候站的是主机视角,GPIO口的配置、输入输出都是以主机的态度写。
if(GPIO_ReadInputDataBit(GPIOB, MyI2C_SDA) == 1) //Input
{
Byte |= (0x80 >> i); //接收一位,用或写
}
/////////
GPIO_WriteBit(GPIOB, MyI2C_SDA, (BitAction)(Byte & (0x80>>i))); //写一位,用与
二、MPU6050模块
1.初始化
(1)首先另存一个文件MPU6050_Reg.h用来放各种功能的寄存器以及对应的地址
//采样率分频寄存器
#define MPU6050_SMPLRT_DIV 0x19
//配置寄存器,是否平滑滤波
#define MPU6050_CONFIG 0x1A
//陀螺仪配置寄存器,前面三位是自测使能
#define MPU6050_GYRO_CONFIG 0x1B
//加速度计配置寄存器,前面三位是自测使能
#define MPU6050_ACCEL_CONFIG 0x1C
//配置电源管理寄存器
#define MPU6050_PWR_MGMT_1 0x6B
#define MPU6050_PWR_MGMT_2 0x6C
//加速度计
#define MPU6050_ACCEL_XOUT_H 0x3B
#define MPU6050_ACCEL_XOUT_L 0x3C
//轴
#define MPU6050_GYRO_XOUT_H 0x43
#define MPU6050_GYRO_XOUT_L 0x44
(2)编写读寄存器和写寄存器MPU6050_WriteReg()和MPU6050_ReadReg()
使用MyI2C时序。流程都是:起始 -> 发送MPU6050地址 -> 接收应答 -> 发寄存器地址 ->接收应答 -> (发数据 -> 接收应答 -> 终止)(起始 -> MPU6050地址最低位置1后发送字节 -> 接收应答 -> 接收字节 -> 发送应答 -> 终止)
划线处因为此时总线控制权交到从机手中,而程序是主机视角,所以是接收。
(3)用写好的读写寄存器函数进行配置
#define MPU6050_ADDRESS 0xD0
void MPU6050_Init(void)
{
MyI2C_Init();
//基础配置,配置好以后,获取到的数据存在对应寄存器里
//配置电源管理寄存器
MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);
MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
//配置采样率分频寄存器
MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);
//配置寄存器,平滑滤波
MPU6050_WriteReg(MPU6050_CONFIG, 0x06);
//陀螺仪配置寄存器,选择最大量程
MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);
//加速度计配置寄存器,选择最大量程
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);
}
(4)读取存在寄存器里的数据
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
uint16_t DataH, DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
*AccX = (DataH<<8) | DataL;
//......剩下的复制粘贴复制粘贴贴贴贴
}
用MPU6050_Reg.h里define好的代号读数据。MPU6050里分高低位存储,需要分别读出来,然后高位左移八位或上低位,得到完整的数据。
有多个需要返回的数据,可以用指针分别传递,也可以写一个结构体传送,或者定义一个数组传送。