当前位置: 首页 > article >正文

江协科技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);
	}
}

 


http://www.kler.cn/a/284164.html

相关文章:

  • python 同时控制多部手机
  • 机器学习在医疗健康领域的应用
  • K8资源之endpoint资源EP资源
  • Openstack7--安装消息队列服务RabbitMQ
  • 使用支付宝沙箱完成商品下单
  • JavaScript Cookie 与 服务器生成的 Cookie 的区别与应用
  • 2 Python开发工具:PyCharm的安装和使用
  • 【知识】对比Share mem/Pin mem/GPU mem之间的传输速度
  • TinTinLand Web3 + DePIN 共学月|挖掘 CESS 去中心化数据基础设施。
  • Jmeter进行http接口测试
  • 人工智能领域正经历模型规模变革,小型语言模型(SLM)崛起,挑战“规模至上”观念。
  • 算法设计与分析:实验1 排序算法性能分析
  • elasticsearch存入数据嵌入式数据解决扁平化查询问题
  • 后端微服务与分布式系统
  • LeetCode 3153.所有数对中数位差之和:计数
  • html2Canvas和jspdf导出长pdf
  • 绿色消费新动力:‘众店‘模式引领数字经济下的零售创新
  • TCP数据包——报文头部组成
  • x264 编码器 AArch64汇编系列:zigzag 扫描相关汇编函数
  • 互联网平台大模型网络架构设计
  • 美国硅谷多ip服务器用途广吗?
  • IOS开发 铃声制作(库乐队)
  • 叶子苏网络建站服务工作室
  • 【ceph学习】ceph如何进行数据的读写(3)
  • Rust语言实现图像编码转换
  • Linux提升篇-Linux虚拟网络介绍