【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程序

cpp 复制代码
#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程序

cpp 复制代码
#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程序

cpp 复制代码
#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程序

cpp 复制代码
#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

main.c程序

cpp 复制代码
#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;
uint32_t light;

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

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

相关推荐
d111111111d7 小时前
STM32外设学习--TIM定时器--输入捕获---测频方法。
stm32·单片机·学习
光子物联单片机7 小时前
C语言基础开发入门系列(八)C语言指针的理解与实战
c语言·开发语言·stm32·单片机·mcu
Tracy9738 小时前
XMAU7118_VC1:16通道PDM到I²S/TDM音频转换器产品介绍
嵌入式硬件·音视频·智能硬件·xmos模组固件
聚能芯罗180380464768 小时前
HLD3350AL深圳智芯一级代理聚能芯半导体CMOS 技术耐压 50V低压差线性稳压器原厂技术支持
嵌入式硬件
Shylock_Mister9 小时前
ESP32堆栈空间优化全攻略
c语言·嵌入式硬件·物联网
点灯小铭9 小时前
基于单片机的超声波自动泥浆回收系统
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
Tracy9739 小时前
OT83211_VC1:4通道 ASRC OTG(44.1kHz~192kHz)音频采样率转换器产品介绍
嵌入式硬件·音视频·xmos 模组·xmos模组固件
电鱼智能的电小鱼14 小时前
基于电鱼 AI 工控机的智慧工地视频智能分析方案——边缘端AI检测,实现无人值守下的实时安全预警
网络·人工智能·嵌入式硬件·算法·安全·音视频
电院工程师19 小时前
SIMON64/128算法Verilog流水线实现(附Python实现)
python·嵌入式硬件·算法·密码学
Shang1809893572620 小时前
T41LQ 一款高性能、低功耗的系统级芯片(SoC) 适用于各种AIoT应用智能安防、智能家居方案优选T41L
人工智能·驱动开发·嵌入式硬件·fpga开发·信息与通信·信号处理·t41lq