江协科技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);
	}
}
相关推荐
千千道3 小时前
STM32 的 CAN 通讯全攻略
stm32·单片机·物联网
百里与司空4 小时前
STM32——看门狗通俗解析
stm32·单片机·嵌入式硬件·门控循环单元
学术头条5 小时前
【直播预告】从人工智能到类脑与量子计算:数学与新计算范式
人工智能·科技·安全·语言模型·量子计算
OH五星上将5 小时前
鸿蒙轻内核A核源码分析系列七 进程管理 (2)
嵌入式硬件·移动开发·openharmony·鸿蒙开发·鸿蒙内核·鸿蒙源码·liteos_a
数科星球5 小时前
可解耦的多模态机器人来了,第一个场景是酒店
嵌入式硬件·fpga开发·车载系统·硬件工程·基带工程
北京搜维尔科技有限公司6 小时前
搜维尔科技:AcuMap - 针灸模拟VR训练解决方案
科技·vr
小郭爱学习@7 小时前
反激电路中TL431光耦反馈参数的计算,环路设计思路
嵌入式硬件·硬件架构·硬件工程
云卓科技8 小时前
无人机之悬停精度篇
科技·安全·机器人·无人机·制造
python_use9 小时前
充电管理芯片
单片机·嵌入式硬件