物联网学习——IIC协议、MPU6050芯片

IIC简介

IIC硬件电路

添加上拉电阻,防止总线出现主设备高电平,从设备低电平,发生短路现象

·只要有一个或多个设备输出低电平,总线就处于低电平;所有从设备输出高电平,总线才高电平

基本时序单元


如果在传输过程中发生中断,则SCL和SDA的电平状态保持不变,暂停传输,等待中断处理完毕再继续传输数据
SCL全程由主设备控制,在主设备接收数据时,释放SDA控制权给从设备

完整时序单元

指定地址写

当前地址读

当前地址指的是,存在一个指针变量指向寄存器组的某一个寄存器(该指针变量会自增),程序根据当前地址进行读操作

指定地址读

发送应答、接收应答

MPU6050简介

加速度计具有静态稳定性,不具有动态稳定性,因为受物体运动时的角度影响
陀螺仪动态稳定,静态不稳定

相关参数

从机地址分为:纯十六进制(0x68)、融入了读写位(0xD0、0xD1,1101000先左移一位,然后或操作0x01或0x00,表示读或者写)

硬件电路

在这里插入图片描述

芯片框图

接线图

My_I2C测试源码

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "MyDelay.h"   //自定义延时函数
#include "Delay.h"     //官方延迟函数
#include "Button.h"   //按键Led驱动
#include "stdio.h"
#include "OLED.h"
#include "Button.h"
#include "MyI2C.h"   //IIC驱动

int main(void){
  //环境配置
   OLED_Init();
   //测试IIC完整时序
   MyI2C_Init();
  //起始
  MyI2C_Start();
  //AD0引脚可以改变电平 ,从而改变MPU6050地址, 最后一位代表读写位
  MyI2C_SendByte(0xD0);  //指定从机地址(MPU6050当前地址):1101 0000 (配合循环,可以实现从机地址扫描)
  uint8_t ACK = MyI2C_ReceiveAck(); //接收数据
  MyI2C_Stop();  //终止
  
  //展示应答信号
  OLED_ShowNum(1,1,ACK,3);
   return 0;
}              
c 复制代码
#include "stm32f10x.h"   //Device header 
#include "Delay.h"      
#include "OLED.h"

//封装操作,方便移植
//修改scl电平
void myi2c_write_scl(uint8_t BitValue){
    //BitAction: 枚举变量(Reset、set)
     GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
     Delay_us(10) ;   // 延时,确保SDA与SCL时序吻合 ,具体时间看手册
}

//修改sda电平
void myi2c_write_sda(uint8_t BitValue){
    //BitAction: 枚举变量(Reset、set)
     GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
     Delay_us(10) ;   // 延时,确保SDA与SCL时序吻合 ,具体时间看手册
}

//读取sda电平
uint8_t myi2c_read_sda(){
     uint8_t BitValue; 
    //BitAction: 枚举变量(Reset、set)
     BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
     Delay_us(10) ;   // 延时,确保SDA与SCL时序吻合 ,具体时间看手册
     return BitValue; 
}


void MyI2C_Init(void){
   //GPIO初始化,SCL接PB10, SDA接PB11 ,开漏输出(能够输入输出)
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	// SCL和SDA端口置高电平,模仿上拉电阻 ,I2C总线处于空闲状态
  GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);
  
}

//  iic时序: 起始、终止、发送字节、接收字节、发送应答、接收应答
void MyI2C_Start(void){
    //SDA先高电平转低电平、SCL后高转低,表示起始时序
  //SDA、SCL先释放总线,先释放sda是为了兼容:在新的周期开启之前,
  // SCL可能为低电平暂时未释放总线 ,所以先释放sda可以兼容该情况
    myi2c_write_sda(1);
    myi2c_write_scl(1);
    
    //先拉低sda,再拉低scl ,表示开启新周期
    myi2c_write_sda(0);
    myi2c_write_scl(0);  
  
  
}

//先释放SDA,再释放SCL,表示终止一个周期
//但是释放时SDA时未必是低电平,所以先拉低SDA
//再先后释放SDA、SCL
void MyI2C_Stop(void){
     myi2c_write_sda(0);
     myi2c_write_sda(1);
     myi2c_write_scl(1);
}

// 主机发送一个字节的数据,高位先行
// scl低电平切换发送下一位数据,SCL高电平发送
void MyI2C_SendByte(uint8_t data){
  OLED_Init(); 
  int times = 8;
  uint8_t pos = 0x80;
//  OLED_ShowString(3,1,"data:");    
//  OLED_ShowBinNum(3,5,data,8);
  while(times--){
      //通过按位与的操作,取出数据的某一位或几位
      myi2c_write_sda(data & pos);    //主机发送数据位
      myi2c_write_scl(1);  //发送sda的数据
      myi2c_write_scl(0);  //切换到下一个数据
//     
//      OLED_ShowString(2,1,"pos:");    
//      OLED_ShowBinNum(2,5,pos,8);
//      Delay_ms(500);
      pos >>= 1;
  }
} 

// 接收字节,SCL拉低电平(从机放数据),释放SCL保持高阻态,
// 主机释放SDA,从机通过切换SDA高低电平发送数据(主机读数据)
// 读写分离:SCL低电平写SDA,SCL高电平读SDA
uint8_t MyI2C_ReceiveByte(){
    uint8_t r_data= 0x00, times=8;
    while(times--){
        myi2c_write_scl(0);  //确保scl低电平,开启接收数据的流程
        myi2c_write_sda(1);  //主机释放sda,由从机通过sda发送数据
        myi2c_write_scl(1);  //拔高scl,开始读从机发送的数据       
        r_data |= myi2c_read_sda(); //读取从机发送的数据
        myi2c_write_scl(0);  //拉低scl,开启下一位数据读取     
//      OLED_ShowString(2,1,"r_data:");    
//      OLED_ShowBinNum(3,1,r_data,8);
//      Delay_ms(500);
    }
    return r_data;
} 

//发送应答: 在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
void MyI2C_SendAck(uint8_t AckBit){
     myi2c_write_scl(0); 
     myi2c_write_scl(0);
     myi2c_write_sda(AckBit);  //在scl高电平时期,将应答数据写入sda
}

//接收应答: 在下一个时钟接收一位数据,数据0表示应答,数据1表示非应答
uint8_t MyI2C_ReceiveAck(void){
     myi2c_write_scl(0);
     myi2c_write_scl(1);
     //主机释放sda控制权,从机读取sda
     myi2c_write_sda(1);
     uint8_t r_ack = myi2c_read_sda();
     myi2c_write_scl(0);   //拉低scl,开启下一个周期
     return r_ack;
}
c 复制代码
#ifndef  My_I2C_H
#define  My_I2C_H

//封装操作,方便移植
//修改scl电平
void myi2c_write_scl(uint8_t BitValue);

//修改sda电平
void myi2c_write_sda(uint8_t BitValue);

//读取sda电平
uint8_t myi2c_read_sda();


void MyI2C_Init(void);

//  iic时序: 起始、终止、发送字节、接收字节、发送应答、接收应答
void MyI2C_Start(void);

//先释放SDA,再释放SCL,表示终止一个周期
//但是释放时SDA时未必是低电平,所以先拉低SDA
//再先后释放SDA、SCL
void MyI2C_Stop(void);

// 主机发送一个字节的数据,高位先行
// scl低电平切换发送下一位数据,SCL高电平发送
void MyI2C_SendByte(uint8_t data);

// 接收字节,SCL拉低电平(从机放数据),释放SCL保持高阻态,
// 主机释放SDA,从机通过切换SDA高低电平发送数据(主机读数据)
// 读写分离:SCL低电平写SDA,SCL高电平读SDA
uint8_t MyI2C_ReceiveByte();

//发送应答: 在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
void MyI2C_SendAck(uint8_t AckBit);

//接收应答: 在下一个时钟接收一位数据,数据0表示应答,数据1表示非应答
uint8_t MyI2C_ReceiveAck(void);

#endif

MPU6050测量偏转角及加速度

c 复制代码
#include "stm32f10x.h"
#include "MyI2C.h"
#include "MPU6050.h"
#include "MPU6050Reg.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();
}

//指定地址读(先指定MPU6050 + 寄存器,再读取sda传送的数据)
uint8_t MPU6050_ReadReg(uint8_t RegAddress){
       uint8_t Data;
      //sendbyte 、 receiveack、 sendbyte/start/stop/recebyte/sendack
       //先指定当前地址
       MyI2C_Start();
       MyI2C_SendByte(MPU6050_ADDRESS); //写操作,从机地址最后一位为0
       MyI2C_ReceiveAck();
       MyI2C_SendByte(RegAddress); 
       MyI2C_ReceiveAck();     
  
       //进入读当前地址的时序
       MyI2C_Start();
       MyI2C_SendByte(MPU6050_ADDRESS | 0x01); //从机地址,读操作,最后一位改为1
       MyI2C_ReceiveAck();   
       //主机移交sda权限,从机发送数据,主机读数据
       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);  //采样分频为10
  MPU6050_WriteReg(MPU6050_CONFIG,0x06);    //滤波参数最大
  MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);   //陀螺仪数值最大
  MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);   //加速器,数值最大
}


//获取MPU6050当前的xyz加速度值和陀螺仪值
void MPU6050_XYZ_RPY_Data(struct MPU_Data * md){
     //缓存数据的高8位、低8位
     uint16_t data_h, data_l;
     //加速器
     data_h = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
     data_l = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
     md->x = (data_h << 8) | data_l;   //高8位左移8位,再或操作低8位,得16位数据

     data_h = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
     data_l = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
     md->y = (data_h << 8) | data_l;
    
     data_h = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
     data_l = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
     md->z = (data_h << 8) | data_l;
    
     //偏转角: Roll(滚转角),Pitch(俯仰角),Yaw(偏航角)
     data_h = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
     data_l = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
     md->P = (data_h << 8) | data_l;   //高8位左移8位,再或操作低8位,得16位数据

     data_h = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
     data_l = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
     md->Y = (data_h << 8) | data_l;
    
     data_h = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
     data_l = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
     md->R = (data_h << 8) | data_l;
}

/**
  * 函    数: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;						//数据拼接,通过输出参数返回
}
c 复制代码
#ifndef MPU6050_H
#define MPU6050_H

//加速器和偏转角数据
typedef struct MPU_Data{
   int16_t x;
   int16_t y;
   int16_t z;
   int16_t R;
   int16_t P;
   int16_t Y;
}; 
 

//指定寄存器写
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);

//指定地址读(先指定MPU6050 + 寄存器,再读取sda传送的数据)
uint8_t MPU6050_ReadReg(uint8_t RegAddress);

void MPU6050_Init(void);

//获取MPU6050当前的xyz加速度值和陀螺仪值
void MPU6050_XYZ_RPY_Data(struct MPU_Data * md);

void MPU6050_GetData(int16_t *AccX, int16_t *AccY, 
            int16_t *AccZ,int16_t *GyroX,
            int16_t *GyroY, int16_t *GyroZ);
#endif


#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
c 复制代码
#include "stm32f10x.h"                  // Device header
#include "MyDelay.h"   //自定义延时函数
#include "Delay.h"     //官方延迟函数
#include "Button.h"   //按键Led驱动
#include "stdio.h"
#include "OLED.h"
#include "Button.h"
#include "MPU6050.h"   //IIC驱动

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_ReadReg(0x75);				//获取MPU6050的ID号
	OLED_ShowHexNum(1, 4, ID, 2);		//OLED显示ID号
  
  MPU6050_WriteReg(0x69,0xA8);
  OLED_ShowString(2, 1, "Reg:");		//显示静态字符串
  uint8_t Reg_data = MPU6050_ReadReg(0x69);
	OLED_ShowHexNum(2, 5, Reg_data, 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);
	}
  
}              
相关推荐
并不会2 小时前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
龙鸣丿2 小时前
Linux基础学习笔记
linux·笔记·学习
Nu11PointerException4 小时前
JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习
笔记·学习
vfbox33987 小时前
C-DTL698电表 转 IEC61850 协议项目案例
物联网·iec61850·vfbox·协议转换工业网关·c-dtl698
@小博的博客8 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
南宫生8 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
易云码9 小时前
信息安全建设方案,网络安全等保测评方案,等保技术解决方案,等保总体实施方案(Word原件)
数据库·物联网·安全·web安全·低代码
懒惰才能让科技进步9 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
love_and_hope9 小时前
Pytorch学习--神经网络--搭建小实战(手撕CIFAR 10 model structure)和 Sequential 的使用
人工智能·pytorch·python·深度学习·学习
Chef_Chen9 小时前
从0开始学习机器学习--Day14--如何优化神经网络的代价函数
神经网络·学习·机器学习