【stm32】软件I2C读写MPU6050

软件I2C读写MPU6050(文章最后附上源码)

编码

概况

  1. 首先建立通信层的.c和.h模块

  2. 在通信层里写好I2C底层的GPIO初始化

  3. 以及6个时序基本单元

    1. 起始、终值、发送一个字节、接收一个字节、发送应答、接收应答
  4. 写好I2C通信层之后,再建立MPU6050的.c和.h模块

    1. 基于I2C通信的模块,来实现指定地址读、指定地址写

    2. 再实现写寄存器对芯片进行配置

    3. 都寄存器得到传感器数据

  5. 最终在main.c里调用MPU6050的模块

    1. 初始化

    2. 拿到数据

    3. 显示数据

  6. 这就是程序的基本架构

步骤

  1. 初始化GPIO

    1. 引脚都要配置成开漏输出的模式

    2. 开漏输出模式仍然可以输入

    3. 输入时先输出1,再直接读取数据寄存器就行了

  2. 调用Setbits,把pin10和pin11都置高电平

  3. 这也初始化就完成了

  4. 调用MyI2C_Init函数

    1. pb10和pb11两个端口就被初始化为开漏输出模式

    2. 然后释放总线

    3. SCL和SDA处于高电平

    4. 此时I2C总线处于空闲状态

  5. 接下来就根据ppt时序波形来完成6个时序单元

  6. 初始化函数之前,定义函数,对操作端口的函数进行封装

    1. void MyI2C_w_SCL(uint8_t BitValue)

    2. 函数里面调用WriteBit函数

    3. 后面再调用MyI2C_w_SCL函数,参数给1或0

    4. 就可以释放或拉低SCL

  7. 复制函数,定义SDA函数

  8. 再写一个读SDA函数uint8_t MyI2C_R_SDA(void)

  9. 写六个时序单元

    1. 开始的函数

      1. 在前面最好先释放SDA,这样保险一些

      2. 如果起始条件之前,SCL和SDA已经是高电平了,先释放哪个都无所谓

      3. 但是在图示在这里插入图片描述
        还要兼容这里的重复起始条件Sr

      4. Sr开始,SCl是低电平,SDA电平不敢确定

      5. 所以保险起见,我们趁SCL是低电平,先确保释放SDA,再释放SCL,这是SDA和SCL都是高电平

      6. 然后再拉低SDA拉低SCl

      7. 这样这个Start可以兼容起始条件和重复起始条件

    2. 结束的函数

      1. 为了确保再SCL高电平期间,SDA产生上升沿,先把SDA拉低
    3. 发送一个字节数据

    4. 接收一个字节数据

      1. 防止主机干扰从机写入数据

      2. 主机需要先释放SDA,释放SDA也相当于切换为输入模式

      3. 再释放SCL

      4. 在SCL低电平时,从机会把数据已经放到SDA上

      5. 如果从机想发1,就释放SDA,如果从机想发0,就拉低SDA

      6. 主机释放SCL,在SCL高电平期间,读取SDA

      7. 再拉低SCL,从机把下一位数据放在SDA上

    5. 发送应答

    6. 接收应答

编写MPU6050模块

  1. 调用MyI2C.h函数

  2. 初始化MPU6050,调用I2C_Init

  3. 之后在上面 先封装指定地址写和指定地址读 的时序

    1. MPU6050_WriteReg指定地址写寄存器 参数是8位的指定地址(指定读写哪个寄存器,就是要读写寄存器的地址),另一个参数是要写入的数据

    2. 为了方便修改MyI2C_SendByte()的参数,并且突出它是从机地址,可以用宏定义替换一下这个数据

  4. MyI2C_ReceiveAck应答位是可以不处理的

  5. 在接收一个字节函数里uint8_t MPU6050_ReadReg(uint8_t RegAdress)

    1. 如果只接受一个字节,应答位给1(非应答)

    2. 如果想继续接收数据,就要给0(应答)

    3. 如果想进阶为指定地址读多个字节,可以用for循环套起来,重读读取多次,最后一个应答给非应答1

写寄存器注意事项
  • 首先解除芯片的睡眠模式

    • 睡眠模式是电源管理寄存器1的SLEEP位在这里插入图片描述

    • 直接写入0x00 这样就可以解除睡眠模式了

在MPU初始化函数里配置电源管理寄存器
  1. 先用宏定义把寄存器的地址用一个字符串来表示

  2. 寄存器比较少的话可以直接在上面进行宏定义

    1. 如果比较多的话,可以再新建一个单独的头文件进行存放

    2. 再添加一个.h文件 MPU6050_Reg 存放宏定义

  3. 配置电源管理寄存器1 0x01

  4. 配置电源管理寄存器2 0x00

  5. 配置头文件里上面四个寄存器

  6. 配置完之后陀螺仪内部就在连续不断的进行数据转换了

  7. 输出的数据就存放在数据寄存器里

    1. 接下来想获取数据的话

    2. 只需要再写一个获取数据寄存器的函数

  8. 在初始化下面编写一个获取数据寄存器数据的函数

    1. 根据任务要求,函数需要返回6个int16_t数据

    2. 分别表示xyz的加速度值和陀螺仪值

    3. 但是c语言中,函数的返回值只能有一个

      1. 使用指针,进行变量的地址传递来实现多返回值

      2. 高8位左移8位或上低8位

MyI2C.c程序

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_2, (BitAction)BitValue);
	Delay_us(10);
}

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_3, (BitAction)BitValue);
	Delay_us(10);
}

uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3);
	Delay_us(10);
	return BitValue;
}

void MyI2C_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
//	GPIO_SetBits(GPIOA, GPIO_Pin_2 | GPIO_Pin_3);
}

void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;
	MyI2C_W_SDA(1);
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SCL(1);
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
		MyI2C_W_SCL(0);
	}
	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;
}

MyI2C.h程序

#ifndef __MYI2C_H
#define __MYI2C_H

void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);

#endif

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);
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);
	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)
{
	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.h程序

#ifndef __MPU6050_H
#define __MPU6050_H

void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);

void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);

#endif

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;
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);
	}
}

如果发现错误或者需要改进的地方请私信或者评论

  • 32
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: STM32F407是一款强大的微控制器,可以通过软件模拟I2C总线来读写MPU6050传感器。首先,我们需要设置相关的GPIO引脚来模拟I2C的时钟(SCL)和数据线(SDA)。然后,我们可以通过相应的软件算法来模拟I2C的时序,并使用GPIO引脚来模拟信号的传输。 在进行软件模拟I2C读写MPU6050之前,我们需要先了解MPU6050的寄存器结构和通讯协议。MPU6050内部有多个寄存器,它们存储着各种传感器的原始数据和配置信息。通讯协议使用I2C,我们需要根据MPU6050的地址和寄存器地址来发送读写命令。 首先,发送开始信号。通过GPIO引脚模拟SCL和SDA,在SCL为高电平,SDA从高电平转为低电平时即可认为发送了开始信号。然后,发送MPU6050的设备地址,该地址为7位,由3位固定的值和一个可选择的读写位组成。接下来,发送要访问的寄存器地址,该地址也为7位。再发送读写命令位,0表示写入,1表示读取。 接下来,我们可以进行读写操作。对于写操作,我们发送要写入的数据到寄存器中。对于读操作,我们需要发送重复启动信号,然后再发送MPU6050的设备地址和寄存器地址,并设置读取命令位。此时,我们将把数据读取到SDA引脚上。 最后,发送停止信号。通过GPIO引脚模拟SCL和SDA,在SCL为高电平时将SDA从低电平转为高电平即可认为发送了停止信号。 以上是对于STM32F407软件模拟I2C读写MPU6050传感器的简要步骤介绍。具体实现过程中,需要根据实际需求进行相应的编码和调试。 ### 回答2: stm32f407是一款高性能的ARM Cortex-M4微控制器,可以通过软件模拟I2C总线来读写MPU6050传感器。 首先,我们需要了解MPU6050传感器的通信协议和寄存器地址。MPU6050使用I2C总线进行通信,需要通过开始信号、设备地址、寄存器地址、数据等步骤完成数据的读写。 在stm32f407上,我们可以使用GPIO引脚来模拟I2C总线的SCL(时钟线)和SDA(数据线)。我们需要配置SCL和SDA引脚为GPIO模式,并在代码中编写相应的功能实现软件模拟I2C总线的读写操作。 对于I2C总线的读操作,首先我们需要发送开始信号,然后发送设备地址加上写命令,接着发送寄存器地址,再发送一个重新开始信号,然后发送设备地址加上读命令,最后读取接收到的数据。 对于I2C总线的写操作,首先也是发送开始信号,然后发送设备地址加上写命令,接着发送寄存器地址,最后发送要写入的数据。 在代码中,我们可以按上述过程编写相应的函数来进行软件模拟的I2C读写操作,并将读取到的数据存储到相应的变量中。 需要注意的是,软件模拟I2C通信可能会导致一定的时序不准确。为了确保通信的稳定性和可靠性,我们可以使用延时函数来控制时序,并根据MPU6050传感器的参数手册调整延时时间。 总之,通过在stm32f407上进行软件模拟的I2C读写操作,我们可以实现对MPU6050传感器的数据读取和写入,从而实现与该传感器的通信和控制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值