STM32学习笔记(10_3)- 软件I2C读写MPU6050

无人问津也好,技不如人也罢,都应静下心来,去做该做的事。

最近在学STM32,所以也开贴记录一下主要内容,省的过目即忘。视频教程为江科大(改名江协科技),网站jiangxiekeji.com

本期开始学习 软件模拟I2C控制MPU6050,分为两部分,第一部分,我们完成软件I2C协议的时序;第二部分,基于软件I2C协议,读写寄存器, 来操控MPU6050。

软件I2C就是用普通的GPIO口,手动翻转电平实现的协议,它并不需要STM32内部的外设资源支持。所以这里的端口, 其实可以任意指定

I2C:高位先行,只有在SCL高电平时才会读取SDA的电平

MPU6050的读地址是0xD1,写地址是0xD0

本期的内容是软件I2C对MPU6050芯片内部的寄存器进行读写,写入到配置寄存器,就可以对外挂的这个模块进行配置;读出数据寄存器,就可以获取外挂模块的数据。最终,我们读出的数据会显示在这个OLED上。而STM32对MPU6050的读写是通过I2C来进行的。

 

 I2C的完整时序

主要有指定地址写、当前地址读和指定地址读这3种。I2C指定地址写简单来说就是SCL高电平时、SDA下降沿开始传输,传输的第一个字节为从机的7位地址+1位读写位(置0表示要写入),然后是1位应答位(RA,置0表示应答),传输的第二个字节为从机的寄存器地址或者是指令控制字,然后也是1位应答位,传输的第三个字节为主机想写入从机的数据,然后也是1位应答位,这时若不想传输了,则SCL高电平时、SDA上升沿停止传输。

当前地址读就是SCL高电平时、SDA下降沿作为起始条件开始传输,传输的第一个字节为从机的7位地址+1位读写位(这里置1表示读取),然后是1位应答位(RA,置0表示应答),传输的第二个字节为从机的数据,然后是1位非应答位(SA,置1),这时SCL高电平、SDA上升沿作为停止条件停止传输。

指定地址读就是指定地址写去掉写数据的部分+当前地址读,SCL高电平时、SDA下降沿作为起始条件开始传输,传输的第一个字节为从机的7位地址+1位读写位(置0表示要写入),然后是1位应答位(RA,置0表示应答),传输的第二个字节为从机的寄存器地址或者是指令控制字,然后也是1位应答位(置0),然后接重复起始条件Sr(即再来一次起始条件),传输的第三个字节为从机的7位地址+1位读写位(置1表示要读取),然后再接收1位应答位(置0),传输的第四个字节为从机的数据,然后1位非应答位(SA,置1),然后SCL高电平、SDA上升沿作为停止条件停止传输。

软件 I2C读写MPU6050程序

对MPU6050芯片内部的寄存器进行读写,写入到配置寄存器,就可以对外挂的这个模块进行配置;读出数据寄存器,就可以获取外挂模块的数据。最终,我们读出的数据会显示在这个OLED上。最上面的是ID号,图中这个MPU6050的ID号固定为0x68,可以读取这个ID号用来测试 I2C读取数据的功能是不是正常,不同批次的MPU6050,ID号会有所不同。然后下面,左边3个,是加速度传感器的输出数据,分别是X轴、Y轴和Z轴的加速度,右边3个,是陀螺仪传感器的输出数据,分别是X轴、Y轴和Z轴的角速度。

接线图

MPU6050的VCC、GND接供电,SCL接PB10,SDA接PB11。

软件I2C就是用普通的GPIO口,手动翻转电平实现的协议,它并不需要STM32内部的外设资源支持。所以这里的端口, 其实可以任意指定。

初始化步骤

我们首先建立I2C通信层的.c和.h模块,在通信层里,写好I2C底层的GPIO初始化和6个时序基本单元。也就是起始、终止、发送一个字节、接收一个字节、发送应答和接收应答。写好I2C通信层后,我们再建立MPU6050的.c和.h模块,基于I2C通信模块来实现指定地址读、指定地址写,再实现写寄存器对芯片进行配置,读寄存器得到传感器数据。最终在main.c里,调用MPU6050的模块,初始化,拿到数据,显示数据。

软件I2C,只需要用gpio的读写函数就行了,不需要用到库函数中的I2C函数。

一、I2C初始化

1、把SCL和SDA都初始化为开漏输出模式

2、把SCL和SDA置高电平

/**
  * 函    数:I2C初始化
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,实现SCL和SDA引脚的初始化
  */
void MyI2C_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//开启GPIOB的时钟
	
	/*GPIO初始化*/
	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);					//将PB10和PB11引脚初始化为开漏输出
	
	/*设置默认电平*/
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);			//设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}

二、I2C的6个时序基本单元

/*引脚配置层*/

/**
  * 函    数:I2C写SCL引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平
  */
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);		//根据BitValue,设置SCL引脚的电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C写SDA引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~0xFF
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue非0时,需要置SDA为高电平
  */
void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);		//根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C读SDA引脚电平
  * 参    数:无
  * 返 回 值:协议层需要得到的当前SDA的电平,范围0~1
  * 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1
  */
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);		//读取SDA电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
	return BitValue;											//返回SDA电平
}


/*协议层*/

/**
  * 函    数:I2C起始
  * 参    数:无
  * 返 回 值:无
  */
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);							//释放SDA,确保SDA为高电平
	MyI2C_W_SCL(1);							//释放SCL,确保SCL为高电平
	MyI2C_W_SDA(0);							//在SCL高电平期间,拉低SDA,产生起始信号
	MyI2C_W_SCL(0);							//起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}

/**
  * 函    数:I2C终止
  * 参    数:无
  * 返 回 值:无
  */
void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);							//拉低SDA,确保SDA为低电平
	MyI2C_W_SCL(1);							//释放SCL,使SCL呈现高电平
	MyI2C_W_SDA(1);							//在SCL高电平期间,释放SDA,产生终止信号
}

/**
  * 函    数:I2C发送一个字节
  * 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)				//循环8次,主机依次发送数据的每一位
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));	//使用掩码的方式取出Byte的指定一位数据并写入到SDA线
		MyI2C_W_SCL(1);						//释放SCL,从机在SCL高电平期间读取SDA
		MyI2C_W_SCL(0);						//拉低SCL,主机开始发送下一位数据
	}
}

/**
  * 函    数:I2C接收一个字节
  * 参    数:无
  * 返 回 值:接收到的一个字节数据,范围:0x00~0xFF
  */
uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;					//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	for (i = 0; i < 8; i ++)				//循环8次,主机依次接收数据的每一位
	{
		MyI2C_W_SCL(1);						//释放SCL,主机机在SCL高电平期间读取SDA
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}	//读取SDA数据,并存储到Byte变量
														//当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0
		MyI2C_W_SCL(0);						//拉低SCL,从机在SCL低电平期间写入SDA
	}
	return Byte;							//返回接收到的一个字节数据
}

/**
  * 函    数:I2C发送应答位
  * 参    数:Byte 要发送的应答位,范围:0~1,0表示应答,1表示非应答
  * 返 回 值:无
  */
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);					//主机把应答位数据放到SDA线
	MyI2C_W_SCL(1);							//释放SCL,从机在SCL高电平期间,读取应答位
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
}

/**
  * 函    数:I2C接收应答位
  * 参    数:无
  * 返 回 值:接收到的应答位,范围:0~1,0表示应答,1表示非应答
  */
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;							//定义应答位变量
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	MyI2C_W_SCL(1);							//释放SCL,主机机在SCL高电平期间读取SDA
	AckBit = MyI2C_R_SDA();					//将应答位存储到变量里
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
	return AckBit;							//返回定义应答位变量
}

 三、MPU6050基于I2C通信模块来实现指定地址写

#define MPU6050_ADDRESS		0xD0		//MPU6050的I2C从机地址

/**
  * 函    数:MPU6050写寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	MyI2C_Start();						//I2C起始
	MyI2C_SendByte(MPU6050_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(Data);				//发送要写入寄存器的数据
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_Stop();						//I2C终止
}

四、MPU6050基于I2C通信模块来实现指定地址读

/**
  * 函    数:MPU6050读寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 返 回 值:读取寄存器的数据,范围:0x00~0xFF
  */
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	MyI2C_Start();						//I2C起始
	MyI2C_SendByte(MPU6050_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_ReceiveAck();					//接收应答
	
	MyI2C_Start();						//I2C重复起始
	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
	MyI2C_ReceiveAck();					//接收应答
	Data = MyI2C_ReceiveByte();			//接收指定寄存器的数据
	MyI2C_SendAck(1);					//发送应答,给从机非应答,终止从机的数据输出
	MyI2C_Stop();						//I2C终止
	
	return Data;
}

五、MPU6050初始化、读寄存器得到传感器数据

/**
  * 函    数:MPU6050初始化
  * 参    数:无
  * 返 回 值:无
  */
void MPU6050_Init(void)
{
	MyI2C_Init();									//先初始化底层的I2C
	
	/*MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);		//电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);		//电源管理寄存器2,保持默认值0,所有轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);		//采样率分频寄存器,配置采样率
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);			//配置寄存器,配置DLPF
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);	//陀螺仪配置寄存器,选择满量程为±2000°/s
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);	//加速度计配置寄存器,选择满量程为±16g
}

/**
  * 函    数:MPU6050获取ID号
  * 参    数:无
  * 返 回 值:MPU6050的ID号
  */
uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);		//返回WHO_AM_I寄存器的值
}

/**
  * 函    数:MPU6050获取数据
  * 参    数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 参    数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 返 回 值:无
  */
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;								//定义数据高8位和低8位的变量
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据
	*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据
	*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据
	*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据
	*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据
	*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据
	*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

 六、main函数

uint8_t ID;								//定义用于存放ID号的变量
int16_t AX, AY, AZ, GX, GY, GZ;			//定义用于存放各个数据的变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	MPU6050_Init();		//MPU6050初始化
	
	/*显示ID号*/
	OLED_ShowString(1, 1, "ID:");		//显示静态字符串
	ID = MPU6050_GetID();				//获取MPU6050的ID号
	OLED_ShowHexNum(1, 4, ID, 2);		//OLED显示ID号
	
	while (1)
	{
		MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);		//获取MPU6050的数据
		OLED_ShowSignedNum(2, 1, AX, 5);					//OLED显示数据
		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);
	}
}

  • 27
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32MPU6050OLCD指的是使用STM32系列微控制器和MPU6050惯性传感器及OLED屏幕的一种组合应用。STM32是意法半导体(STMicroelectronics)公司推出的一款32位微控制器,具有高性能、高集成度和低功耗的特点。MPU6050是一款六轴惯性传感器,内置了三轴陀螺仪和三轴加速度计,能够测量运动的加速度和角速度。OLED是一种有机发光二极管屏幕,具有高亮度、高对比度、低功耗和快速响应等特点。 使用STM32MPU6050OLCD可以实现许多应用,比如姿态检测、运动追踪、智能手势控制等。首先,通过MPU6050测量得到的角速度和加速度数据可以用来计算出物体的姿态,并进行相应的控制操作。例如,可以通过姿态检测实现无人机的稳定飞行控制。其次,可以通过MPU6050测量得到的加速度数据来实现运动追踪功能。利用这些数据可以制作计步器、运动手环等设备,对人体运动状态进行监测和分析。此外,可以利用MPU6050测量得到的角速度和加速度数据,通过算法判断用户的智能手势,从而实现手势识别和控制功能。最后,将数据显示在OLED屏幕上,用户可以直观地观察到相关数据和状态。 综上所述,STM32MPU6050OLCD是一种集成了STM32微控制器、MPU6050惯性传感器和OLED屏幕的组合应用,可用于姿态检测、运动追踪、智能手势控制等领域。 ### 回答2: STM32MPU6050OLCD是将STM32系列微控制器(MPU)与6050三轴陀螺仪和加速度计(MPU)以及OLED液晶显示屏(OLCD)组合在一起构成的一种开发板。STM32MPU6050OLCD具有多种功能和应用。 首先,STM32MPU6050OLCD使用STM32系列微控制器作为主控制单元,这些微控制器具有强大的处理能力和丰富的外设资源,可以实现各种功能。 其次,6050三轴陀螺仪和加速度计(MPU)是一种常用的传感器模块,可以实时测量物体的角加速度和线性加速度,广泛应用于无人机、智能车、机器人等领域。 另外,OLED液晶显示屏(OLCD)是一种具有自发光特点的显示技术,具有视角广、对比度高、响应速度快等优点,同时能耗低,适合嵌入式应用。 综上所述,STM32MPU6050OLCD集成了强大的微控制器、传感器和显示屏,可以用于各种应用场景。例如,可以利用6050传感器实时测量物体姿态,通过STM32微控制器进行数据处理和控制,并在OLED显示屏上展示相关信息,实现姿态跟踪和监测。此外,还可以将该开发板用于物联网、智能家居等应用,实现传感器与网络的连接,通过OLED显示屏展示相关数据。 总之,STM32MPU6050OLCD是一个功能强大且灵活应用的开发板,可满足各种项目的需求,并为开发者提供了方便快捷的开发环境。 ### 回答3: STM32MPU6050OLCD是指搭载STM32MPU6050芯片并配备OLED液晶屏的一款设备。STM32MPU6050是一款集成了三轴加速度计和三轴陀螺仪的MEMS传感器芯片,可以实现对物体的姿态、运动和加速度的检测。OLED液晶屏是一种新型的显示技术,具有高亮度、高对比度、快速响应和低功耗等特点。 通过将STM32MPU6050与OLED液晶屏相结合,可以实现对物体的姿态和动作的实时监测和显示。这种设备在航空航天、智能机器人和运动控制等领域具有广泛的应用前景。 在使用STM32MPU6050OLCD时,我们可以通过编程控制STM32MPU6050芯片读取加速度和角速度等数据,并将其显示在OLED液晶屏上。用户可以通过触摸屏或按钮等输入方式控制设备的工作模式或其他参数。 同时,STM32MPU6050OLCD还可以与其他传感器和执行器相连,通过串口或无线通信方式与其他设备进行数据交互和控制。这为用户提供了更多的灵活性和扩展性,使其适用于更多的应用场景。 总之,STM32MPU6050OLCD是一种基于STM32MPU6050和OLED液晶屏的设备,可以实现姿态和动作监测,并提供高亮度、高对比度和低功耗的显示效果。它具有广泛的应用前景,并且可以与其他设备进行数据交互和控制,扩展其功能和适用范围。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值