江协科技stm32————10-3 软件I2C读写MPU6050
目录
整体架构
错误现象
MyI2C.c
MPU6050.c
MPU6050_Reg.h
main.c
整体架构
一、建立I2C通信层的.c和.h模块,写好I2C底层的GPIO初始化和六个时序单元起始、终止、发送一个字节、接收一个字、发送应答和接收应答
二、建立MUP6050的.c和.h模块,基于I2C通信模块来实现指定地址读、指定地址写再实现写寄存器对芯片进行配置,读寄存器得到传感器数据
三、在main.c里,调用MPU6050模块,初始化,拿到数据,显示数据
uint8_t MyI2C_ReceiveAck(void)//接收应答
{
uint8_t AckBit;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckBit;
}
一、 MyI2C_W_SDA(1);这里主机将SDA置1,并不是强制SDA为高电平(开漏输出+弱上拉),而是释放SDA
二、I2C在进行通信,主机释放SDA后,从机也可能将SDA拉低
通过把AD0接高低电平,可以将更改设备名字,避免从机地址的重复
错误现象
在读取寄存器地址0x75时,ID显示7F错误(应该是68)
经检查是MyI2C接收字节代码写错了,MyI2C_W_SCL(1);要放到循环里面
uint8_t MyI2C_ReceiveByte(void)//接收一个字节
{
uint8_t Byte = 0x00;
uint8_t i;
MyI2C_W_SDA(1);//两个都释放开始接收数据
for(i = 0 ; i < 8 ;i++)
{
MyI2C_W_SCL(1);//释放SCL要放到循环里面,每次判断完后拉低,进入循环后再次释放
if(MyI2C_R_SDA() == 1)//如果接收数据为1,那一位置1
{
Byte |= (0x80 >> i);
}
MyI2C_W_SCL(0);//接收完后拉低SCL,接收下一个数据
}
return Byte;
}
MyI2C.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
void MyI2C_W_SCL(uint8_t BitValue)//写SCL
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
Delay_us(10);
}
void MyI2C_W_SDA(uint8_t BitValue)//写SDA
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
Delay_us(10);
}
uint8_t MyI2C_R_SDA(void)//读SDA参数
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
Delay_us(10);
return BitValue;
}
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
void MyI2C_Start(void)//起始条件:SCL高电平期间,SDA从高电平切换到低电平
{
MyI2C_W_SDA(1);//兼容重复起始条件
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
void MyI2C_Stop(void)//终止条件:SCL高电平期间,SDA从低电平切换到高电平
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);//只有Stop的SCl以高电平结束
MyI2C_W_SDA(1);
}
void MyI2C_SendByte(uint8_t Byte)//发送一个字节,SDA高位先行
{
uint8_t i;
for(i = 0 ; i<8 ; i++)
{
MyI2C_W_SDA(Byte & (0x80 >> i));
MyI2C_W_SCL(1);//读取SDA数据
MyI2C_W_SCL(0);//拉低SCL,随后可以继续放SDA
}
}
uint8_t MyI2C_ReceiveByte(void)//接收一个字节
{
uint8_t Byte = 0x00;
uint8_t i;
MyI2C_W_SDA(1);//两个都释放开始接收数据
for(i = 0 ; i < 8 ;i++)
{
MyI2C_W_SCL(1);//释放SCL要放到循环里面,每次判断完后拉低,进入循环后再次释放
if(MyI2C_R_SDA() == 1)//如果接收数据为1,那一位置1
{
Byte |= (0x80 >> i);
}
MyI2C_W_SCL(0);//接收完后拉低SCL,接收下一个数据
}
return Byte;
}
void MyI2C_SendAck(uint8_t AckBit)//发送应答
{
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
uint8_t MyI2C_ReceiveAck(void)//接收应答
{
uint8_t AckBit;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckBit;
}
MPU6050.c
#include "stm32f10x.h" // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"
#define MPU6050_ADDRESS 0xD0
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_SendByte(Data);
MyI2C_ReceiveAck();
MyI2C_Stop();
}
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
uint8_t Data;
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS | 0x01);
MyI2C_ReceiveAck();
Data = MyI2C_ReceiveByte();
MyI2C_SendAck(1);
MyI2C_Stop();
return Data;
}
void MPU6050_Init(void)
{
MyI2C_Init();
MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//解除睡眠、选择陀螺仪时钟
MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//6个轴均不待机
MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//10分频
MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//配置寄存器
MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//陀螺仪配置寄存器
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//加速度配置寄存器
}
uint8_t MPU6050_GetID(void)
{
return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
//分别读取6个轴数据寄存器的高位和低位,拼接为16位数据再通过指针变量返回
{
uint8_t DataH, DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
*AccX = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
*AccY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
*AccZ = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
*GyroX = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
*GyroY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
*GyroZ = (DataH << 8) | DataL;
}
MPU6050_Reg.h
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H
#define MPU6050_SMPLRT_DIV 0x19
#define MPU6050_CONFIG 0x1A
#define MPU6050_GYRO_CONFIG 0x1B
#define MPU6050_ACCEL_CONFIG 0x1C
#define MPU6050_ACCEL_XOUT_H 0x3B
#define MPU6050_ACCEL_XOUT_L 0x3C
#define MPU6050_ACCEL_YOUT_H 0x3D
#define MPU6050_ACCEL_YOUT_L 0x3E
#define MPU6050_ACCEL_ZOUT_H 0x3F
#define MPU6050_ACCEL_ZOUT_L 0x40
#define MPU6050_TEMP_OUT_H 0x41
#define MPU6050_TEMP_OUT_L 0x42
#define MPU6050_GYRO_XOUT_H 0x43
#define MPU6050_GYRO_XOUT_L 0x44
#define MPU6050_GYRO_YOUT_H 0x45
#define MPU6050_GYRO_YOUT_L 0x46
#define MPU6050_GYRO_ZOUT_H 0x47
#define MPU6050_GYRO_ZOUT_L 0x48
#define MPU6050_PWR_MGMT_1 0x6B
#define MPU6050_PWR_MGMT_2 0x6C
#define MPU6050_WHO_AM_I 0x75
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID; //定义用于存放ID号的变量
int16_t AX, AY, AZ, GX, GY, GZ; //定义用于存放加速度值和陀螺仪值
int main(void)
{
OLED_Init();
MPU6050_Init();
OLED_ShowString(1, 1, "ID:");
ID = MPU6050_GetID();
OLED_ShowHexNum(1, 4, ID, 2);
while (1) // 进入主循环,程序将在这里无限循环执行
{
MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
OLED_ShowSignedNum(2, 1, AX, 5);
OLED_ShowSignedNum(3, 1, AY, 5);
OLED_ShowSignedNum(4, 1, AZ, 5);
OLED_ShowSignedNum(2, 8, GX, 5);
OLED_ShowSignedNum(3, 8, GY, 5);
OLED_ShowSignedNum(4, 8, GZ, 5);
}
}