【STM32-学习笔记-8-】I2C通信

文章目录

  • I2C通信
    • Ⅰ、硬件电路
    • Ⅱ、IIC时序基本单元
      • ① 起始条件
      • ② 终止条件
      • ③ 发送一个字节
      • ④ 接收一个字节
      • ⑤ 发送应答
      • ⑥ 接收应答
    • Ⅲ、IIC时序
      • ① 指定地址写
      • ② 当前地址读
      • ③ 指定地址读
    • Ⅳ、MPU6050---6轴姿态传感器(软件I2C)
      • 1、模块内部电路
      • 2、寄存器地址
      • 3、软件模拟IIC
        • ①、MPU6050.c
        • ②、MPU6050.h
        • ③、MPU6050_Reg.h
    • Ⅴ、硬件IIC(I2C外设)
      • 1、I2C硬件外设
      • 2、I2C框图
      • 3、I2C基本结构
      • 4、主机发送
      • 5、主机接收
    • Ⅵ、配置I2C外设
      • 1、I2C函数
      • 2、I2C_InitTypeDef结构体参数
        • ①、I2C_ClockSpeed
        • ②、I2C_Mode
        • ③、I2C_DutyCycle
        • ④、I2C_OwnAddress1
        • ⑤、I2C_Ack
        • ⑥、I2C_AcknowledgedAddress
      • 3、I2C外设
        • ①、MPU6050.c

I2C通信

名称引脚双工时钟电平设备
I2CSCL、SDA半双工同步单端多设备
  • I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线

  • 两根通信线:SCL(Serial Clock)、SDA(Serial Data)

  • 同步,半双工

  • 带数据应答

  • 支持总线挂载多设备(一主多从、多主多从)

Ⅰ、硬件电路

  1. 所有I2C设备的SCL连在一起,SDA连在一起

  2. 设备的SCL和SDA均要配置成开漏输出模式

  3. SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右,高电平驱动能力弱

image-20250104142118180

Ⅱ、IIC时序基本单元

① 起始条件

  • SCL高电平期间,SDA从高电平切换到低电平

image-20250104142341367

② 终止条件

  • SCL高电平期间,SDA从低电平切换到高电平

image-20250104142446909

③ 发送一个字节

  • SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节

image-20250104142940845

发送期间SCL与SDA只能由主机(STM32)控制

  • 最后一位为读写标志位(0:写;1:读)

④ 接收一个字节

  • SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA

image-20250104143340792

⑤ 发送应答

  • 主机在接收完一个字节之后,在下一个时钟发送一位数据,(SDA)数据0表示应答,数据1表示非应答

image-20250104145540041

⑥ 接收应答

  • 主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA

image-20250104145556162

Ⅲ、IIC时序

指定地址单字节

image-20250104185532597

指定地址多字节

image-20250104185611104

指定地址单字节

image-20250104185646816

指定地址多字节

image-20250104185723329

① 指定地址写

  • 对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)

image-20250104162847176

② 当前地址读

  • 对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
    • 每一次的读写数据都会造成地址指针+1

image-20250104164032199

③ 指定地址读

  • 对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)

image-20250104164540158

  • Sr:重复起始条件(另启时序)

Ⅳ、MPU6050—6轴姿态传感器(软件I2C)

  • MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景

  • 3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度

    • 静态稳定,动态不稳定
  • 3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度

    • 动态稳定,静态不稳定
  • 16位ADC采集传感器的模拟信号,量化范围:-32768~32767

  • 加速度计满量程选择:±2、±4、±8、±16(g)

  • 陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)

  • 可配置的数字低通滤波器

  • 可配置的时钟源

  • 可配置的采样分频

  • I2C从机地址:

    • 1101000AD0=0
    • 1101001AD0=1

1、模块内部电路

image-20250104172907586

引脚功能
VCC、GND电源
SCL、SDAI2C通信引脚
XCL、XDA主机I2C通信引脚 (与扩展设备通信)
AD0从机地址最低位
INT中断信号输出

image-20250104173210431

2、寄存器地址

image-20250105164048416

请添加图片描述

3、软件模拟IIC

可任意指定两个引脚,分别作为IICSCLSDA,本示例中使用的引脚为PB10、PB11

  • SCL:PB10
  • SDA:PB11

MyI2C.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/*
SCL:>PB10
SDA:>PB11
*/

void MyI2C_W_SCL(char BitValue) {
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
	Delay_us(10);
}
void MyI2C_W_SDA(char BitValue) {
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
	Delay_us(10);
}
uint8_t MyI2C_R_SDA(void) {
	uint8_t BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
	Delay_us(10);
	return BitValue;
}

//初始化IIC
void MyI2C_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;//配置成开漏输出模式
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);//初始化为高电平(释放总线)
}

//I2C开始函数(SCL高电平期间,SDA从高电平切换到低电平,最后拉低SCL)
//为了兼容重复起始条件,应将SDA置高放在SCL置高之前
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

//I2C终止函数(SCL高电平期间,SDA从低电平切换到高电平)
void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

//I2C发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i = 0;
	for(i = 0;i < 8;i++) {
		MyI2C_W_SDA(Byte & (0x80 >> i));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}

//I2C接收一个字节
uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t Byte = 0x00;
	uint8_t i = 0;
	MyI2C_W_SDA(1);//主机释放SDA
	for(i = 0;i < 8;i++) {
		MyI2C_W_SCL(1);
		if(MyI2C_R_SDA() == 1) {//主机读取SDA		
			Byte |= (0x80 >> i);
		}
		MyI2C_W_SCL(0);
	}
	return Byte;
}

//发送应答
void MyI2C_SendAck(char AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}

//接收应答
uint8_t MyI2C_ReceiveAck(void)
{
	MyI2C_W_SDA(1);//松手
	MyI2C_W_SCL(1);
	uint8_t Ack = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return Ack;
}


①、MPU6050.c
#include "stm32f10x.h"                  // Device header
#include "MyIIC.h"
#include "MPU6050_Reg.h"
#include "MPU6050.h"

#define MPU6050_ADDR 0xD0//从机地址+读写位


//指定地址写
void MPU6050_WriteReg(uint8_t RegAddr, uint8_t Data)
{
	MyI2C_Start();					//开始
	MyI2C_SendByte(MPU6050_ADDR);	//指定从机
	MyI2C_ReceiveAck();				//接收应答(可处理)
	MyI2C_SendByte(RegAddr);		//指定地址
	MyI2C_ReceiveAck();				//接收应答(可处理)
	MyI2C_SendByte(Data);			//发送数据
	MyI2C_ReceiveAck();				//接收应答(可处理)
	MyI2C_Stop();					//停止
}

//指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddr)
{
	MyI2C_Start();					//开始
	MyI2C_SendByte(MPU6050_ADDR);	//指定从机--写
	MyI2C_ReceiveAck();	
	MyI2C_SendByte(RegAddr);		//指定地址
	MyI2C_ReceiveAck();
	
	MyI2C_Start();					//Sr重复起始
	MyI2C_SendByte(MPU6050_ADDR | 0x01);//指定从机--读
	MyI2C_ReceiveAck();
	uint8_t Data = MyI2C_ReceiveByte();
	MyI2C_SendAck(1);				//不给从机应答
	MyI2C_Stop();					//停止
	return Data;
}

void MPU6050_Init(void)
{
	MyI2C_Init();
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//配置电源管理寄存器1,解除睡眠,选择x轴的时钟
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//配置电源管理寄存器2,六轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//采样率分频器:10分频
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//滤波参数
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//陀螺仪最大量程
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//加速度计最大量程
}

void MPU6050_GetData(MPU_Data* pData)
{
	uint16_t Data_H, Data_L;
	//加速度计X
	Data_H = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);//读高位
	Data_L = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);//读低位
	pData->AccX = (Data_H << 8) | Data_L;//拼接
	//加速度计Y
	Data_H = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	pData->AccY = (Data_H << 8) | Data_L;
	//加速度计Z
	Data_H = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	pData->AccZ = (Data_H << 8) | Data_L;
	
	//陀螺仪X
	Data_H = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	pData->GyrX = (Data_H << 8) | Data_L;
	//陀螺仪Y
	Data_H = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	pData->GyrY = (Data_H << 8) | Data_L;
	//陀螺仪Z
	Data_H = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	pData->GyrZ = (Data_H << 8) | Data_L;
	
	//温度
	Data_H = MPU6050_ReadReg(MPU6050_TEMP_OUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_TEMP_OUT_L);
	pData->TEMP = (Data_H << 8) | Data_L;	
}

uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}


②、MPU6050.h
#ifndef __MPU6050_H__
#define __MPU6050_H__
#include "stdint.h"
typedef struct {
	int16_t AccX;
	int16_t AccY;
	int16_t AccZ;
	int16_t GyrX;
	int16_t GyrY;
	int16_t GyrZ;
	int16_t TEMP;
}MPU_Data;

void MPU6050_WriteReg(uint8_t RegAddr, uint8_t Data);//指定地址写
uint8_t MPU6050_ReadReg(uint8_t RegAddr);//指定地址读
void MPU6050_Init(void);
void MPU6050_GetData(MPU_Data* pData);
uint8_t MPU6050_GetID(void);


#endif

实际数据 = 转换后的值 32768 ∗ 选择的量程 实际数据 = \frac{转换后的值 }{32768}*选择的量程 实际数据=32768转换后的值选择的量程

③、MPU6050_Reg.h

常用寄存器

#ifndef __MPU6050_REG_H__
#define __MPU6050_REG_H__
//#define	寄存器名称	地址

#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	//加速度计X轴高字节
#define	MPU6050_ACCEL_XOUT_L	0x3C	//加速度计X轴低字节
#define	MPU6050_ACCEL_YOUT_H	0x3D	//加速度计Y轴高字节
#define	MPU6050_ACCEL_YOUT_L	0x3E	//加速度计Y轴低字节
#define	MPU6050_ACCEL_ZOUT_H	0x3F	//加速度计Z轴高字节
#define	MPU6050_ACCEL_ZOUT_L	0x40	//加速度计Z轴低字节
#define	MPU6050_TEMP_OUT_H		0x41	//温度高字节
#define	MPU6050_TEMP_OUT_L		0x42	//温度低字节
#define	MPU6050_GYRO_XOUT_H		0x43	//陀螺仪X轴高字节
#define	MPU6050_GYRO_XOUT_L		0x44	//陀螺仪X轴低字节
#define	MPU6050_GYRO_YOUT_H		0x45	//陀螺仪Y轴高字节
#define	MPU6050_GYRO_YOUT_L		0x46	//陀螺仪Y轴低字节
#define	MPU6050_GYRO_ZOUT_H		0x47	//陀螺仪Z轴高字节
#define	MPU6050_GYRO_ZOUT_L		0x48	//陀螺仪Z轴低字节

#define	MPU6050_PWR_MGMT_1		0x6B	//电源管理1
#define	MPU6050_PWR_MGMT_2		0x6C	//电源管理2
#define	MPU6050_WHO_AM_I		0x75	//设备ID


#endif

Ⅴ、硬件IIC(I2C外设)

1、I2C硬件外设

  • STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担

  • 支持多主机模型

  • 支持7位/10位地址模式

  • 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)

  • 支持DMA

  • 兼容SMBus协议

  • STM32F103C8T6 硬件I2C资源:I2C1、I2C2

2、I2C框图

image-20250107202309431

image-20250107203426140

3、I2C基本结构

image-20250107203509030

4、主机发送

image-20250107205015755

5、主机接收

image-20250107205024938

Ⅵ、配置I2C外设

  1. 开启时钟:I2C外设和GPIO口的时钟
  2. 把I2C外设对应的GPIO口设置成复用开漏模式
  3. 配置I2C:结构体
  4. 使能I2C

1、I2C函数

// 重置指定的I2C为默认值
void I2C_DeInit(I2C_TypeDef* I2Cx);

// 初始化指定的I2C,根据初始化结构体配置参数
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
// 初始化I2C初始化结构体的默认值
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);

// 开启或关闭指定的I2C
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 开启或关闭I2C的DMA功能
void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 配置I2C DMA传输的最后一个数据
void I2C_DMALastTransferCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 生成I2C的启动信号
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
// 生成I2C的停止信号
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 配置I2C的应答使能
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 配置I2C的第二个地址
void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address);
// 开启或关闭I2C的双地址模式
void I2C_DualAddressCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
// 开启或关闭I2C的通用呼叫模式
void I2C_GeneralCallCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 开启或关闭I2C的中断
void I2C_ITConfig(I2C_TypeDef* I2Cx, uint16_t I2C_IT, FunctionalState NewState);

// 通过I2C发送数据
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);
// 通过I2C接收数据
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);

// 发送I2C的7位地址和方向
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);

// 读取I2C寄存器的值
uint16_t I2C_ReadRegister(I2C_TypeDef* I2Cx, uint8_t I2C_Register);

// 发送I2C软件复位信号
void I2C_SoftwareResetCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 配置I2C的NACK位置
void I2C_NACKPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_NACKPosition);

// 配置I2C的SMBus警报
void I2C_SMBusAlertConfig(I2C_TypeDef* I2Cx, uint16_t I2C_SMBusAlert);

// 开启或关闭I2C的PEC传输
void I2C_TransmitPEC(I2C_TypeDef* I2Cx, FunctionalState NewState);
// 配置I2C的PEC位置
void I2C_PECPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_PECPosition);
// 开启或关闭I2C的PEC计算
void I2C_CalculatePEC(I2C_TypeDef* I2Cx, FunctionalState NewState);
// 获取I2C的PEC值
uint8_t I2C_GetPEC(I2C_TypeDef* I2Cx);

// 开启或关闭I2C的ARP功能
void I2C_ARPCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 开启或关闭I2C的时钟拉伸功能
void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 配置I2C的快速模式占空比
void I2C_FastModeDutyCycleConfig(I2C_TypeDef* I2Cx, uint16_t I2C_DutyCycle);

//检查I2C事件是否发生
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
//获取I2C最后一个发生的事件
uint32_t I2C_GetLastEvent(I2C_TypeDef* I2Cx);

//获取I2C标志状态
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
//清除I2C标志。
void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);

//获取I2C中断状态。
ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT);
//清除I2C中断待处理位。
void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT);

2、I2C_InitTypeDef结构体参数

①、I2C_ClockSpeed

时钟频率—该参数必须设置为低于400kHz的值

②、I2C_Mode

I2C模式

  • 该参数可以是@ref I2C_mode

  • @ref I2C_mode

    • 宏定义解释

      1. I2C_Mode_I2C
        • 描述:定义了标准的I2C模式。在这种模式下,I2C接口按照I2C协议的标准规范进行通信
      2. I2C_Mode_SMBusDevice
        • 描述:定义了SMBus(System Management Bus)设备模式。SMBus是一种基于I2C的协议,主要用于系统管理通信。在这种模式下,I2C接口按照SMBus协议进行通信,作为设备端
      3. I2C_Mode_SMBusHost
        • 描述:定义了SMBus主机模式。在这种模式下,I2C接口按照SMBus协议进行通信,作为主机端,可以控制通信过程

      宏函数

      1. IS_I2C_MODE(MODE)
        • 描述:这是一个宏函数,用于检查给定的工作模式设置是否有效
        • 参数MODE,代表I2C的工作模式设置
        • 功能:检查MODE是否等于I2C_Mode_I2CI2C_Mode_SMBusDeviceI2C_Mode_SMBusHost中的任一个
        • 返回值:如果MODE有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      I2C_Mode_I2C0x0000标准I2C模式
      I2C_Mode_SMBusDevice0x0002SMBus设备模式
      I2C_Mode_SMBusHost0x000ASMBus主机模式
      宏函数描述
      IS_I2C_MODE(MODE)检查MODE是否为有效的I2C工作模式设置
③、I2C_DutyCycle

I2C快速模式占空比

  • 该参数可以是@ref I2C_duty_cycle_in_fast_mode

  • @ref I2C_duty_cycle_in_fast_mode

    • 宏定义解释

      1. I2C_DutyCycle_16_9
        • 描述:定义了I2C快速模式下的占空比为16:9。在这种配置下,I2C的时钟周期被分为16个低电平时间和9个高电平时间。这种配置适用于大多数I2C设备
      2. I2C_DutyCycle_2
        • 描述:定义了I2C快速模式下的占空比为2:1。在这种配置下,I2C的时钟周期被分为2个低电平时间和1个高电平时间。这种配置适用于需要更快数据传输速率的场景

      宏函数

      1. IS_I2C_DUTY_CYCLE(CYCLE)
        • 描述:这是一个宏函数,用于检查给定的占空比设置是否有效
        • 参数CYCLE,代表I2C的占空比设置
        • 功能:检查CYCLE是否等于I2C_DutyCycle_16_9I2C_DutyCycle_2中的任一个
        • 返回值:如果CYCLE有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      I2C_DutyCycle_16_90x4000I2C快速模式占空比为16:9
      I2C_DutyCycle_20xBFFFI2C快速模式占空比为2:1
      宏函数描述
      IS_I2C_DUTY_CYCLE(CYCLE)检查CYCLE是否为有效的I2C占空比设置
④、I2C_OwnAddress1

指定第一个设备自己的地址

  • 支持7位或10位地址
⑤、I2C_Ack

启用或禁用应答

  • 该参数可以是@ref I2C_acknowledgement

  • @ref I2C_acknowledgement

    • 宏定义解释

      1. I2C_Ack_Enable
        • 描述:定义了启用I2C应答的宏。当设置为启用应答时,I2C设备会在接收到数据后发送一个ACK信号,表示数据已被成功接收
      2. I2C_Ack_Disable
        • 描述:定义了禁用I2C应答的宏。当设置为禁用应答时,I2C设备不会在接收到数据后发送ACK信号。这在某些特定的应用场景中可能有用,例如在某些数据传输协议中不需要应答信号

      宏函数

      1. IS_I2C_ACK_STATE(STATE)
        • 描述:这是一个宏函数,用于检查给定的应答状态设置是否有效
        • 参数STATE,代表I2C的应答状态设置
        • 功能:检查STATE是否等于I2C_Ack_EnableI2C_Ack_Disable中的任一个
        • 返回值:如果STATE有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      I2C_Ack_Enable0x0400启用I2C应答
      I2C_Ack_Disable0x0000禁用I2C应答
      宏函数描述
      IS_I2C_ACK_STATE(STATE)检查STATE是否为有效的I2C应答状态设置
⑥、I2C_AcknowledgedAddress

指定确认7位或10位地址

  • 该参数可以是@ref i2c_cogndged_address

  • @ref i2c_cogndged_address

    • 宏定义解释

      1. I2C_AcknowledgedAddress_7bit
        • 描述:定义了7位地址的应答配置。在I2C通信中,7位地址是最常见的地址格式,适用于大多数I2C设备。当设置为7位地址应答时,I2C设备将识别并应答7位地址
      2. I2C_AcknowledgedAddress_10bit
        • 描述:定义了10位地址的应答配置。10位地址提供了更大的地址空间,适用于需要更多设备地址的应用场景。当设置为10位地址应答时,I2C设备将识别并应答10位地址

      宏函数

      1. IS_I2C_ACKNOWLEDGE_ADDRESS(ADDRESS)
        • 描述:这是一个宏函数,用于检查给定的应答地址设置是否有效
        • 参数ADDRESS,代表I2C的应答地址设置
        • 功能:检查ADDRESS是否等于I2C_AcknowledgedAddress_7bitI2C_AcknowledgedAddress_10bit中的任一个
        • 返回值:如果ADDRESS有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      I2C_AcknowledgedAddress_7bit0x40007位地址应答
      I2C_AcknowledgedAddress_10bit0xC00010位地址应答
      宏函数描述
      IS_I2C_ACKNOWLEDGE_ADDRESS(ADDRESS)检查ADDRESS是否为有效的I2C应答地址设置

3、I2C外设

①、MPU6050.c
#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"
#include "MPU6050.h"

#define MPU6050_ADDR 0xD0//从机地址+读写位


//指定地址写
void MPU6050_WriteReg(uint8_t RegAddr, uint8_t Data)
{
	I2C_GenerateSTART(I2C2, ENABLE);//起始条件
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) == !SUCCESS);//判断是否产生EV5事件
	I2C_Send7bitAddress(I2C2, MPU6050_ADDR, I2C_Direction_Transmitter);//指定从机地址
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == !SUCCESS);//等待事件EV6
	I2C_SendData(I2C2, RegAddr);//指定地址
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == !SUCCESS);//等待事件EV8
	I2C_SendData(I2C2, Data);//发送数据
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == !SUCCESS);//等待事件EV8_2
	I2C_GenerateSTOP(I2C2, ENABLE);//停止条件	
}

//指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddr)
{
	I2C_GenerateSTART(I2C2, ENABLE);//起始条件
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) == !SUCCESS);//判断是否产生EV5事件
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDR, I2C_Direction_Transmitter);//指定从机地址
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == !SUCCESS);//等待事件发送EV6
	
	I2C_SendData(I2C2, RegAddr);//指定地址
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == !SUCCESS);//等待事件EV8_2
	
	I2C_GenerateSTART(I2C2, ENABLE);//重复起始条件
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) == !SUCCESS);//判断是否产生EV5事件
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDR, I2C_Direction_Receiver);//指定从机--读
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == !SUCCESS);//等待事件接收EV6
	
	//接收之前要提前设置ACK=0和STOP请求
	I2C_AcknowledgeConfig(I2C2, DISABLE);//设置ACK=0
	I2C_GenerateSTOP(I2C2, ENABLE);//STOP请求
	
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED) == !SUCCESS);//等待事件接收EV7
	uint8_t Data = I2C_ReceiveData(I2C2);
	
	I2C_AcknowledgeConfig(I2C2, ENABLE);
	return Data;
}

void MPU6050_Init(void)
{
	//初始化I2C
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStruct.GPIO_Speed =GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	I2C_InitTypeDef I2C_InitStruct;
	I2C_InitStruct.I2C_ClockSpeed = 100000;//100kHz的时钟频率
	I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_16_9;//仅快速适用
	I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;//启用或禁用应答
	I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStruct.I2C_OwnAddress1 = 0x00;
	I2C_Init(I2C2, &I2C_InitStruct);
	
	I2C_Cmd(I2C2, ENABLE);
	
	
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//配置电源管理寄存器1,解除睡眠,选择x轴的时钟
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//配置电源管理寄存器2,六轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//采样率分频器:10分频
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//滤波参数
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//陀螺仪最大量程
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//加速度计最大量程
}

void MPU6050_GetData(MPU_Data* pData)
{
	...
}

uint8_t MPU6050_GetID(void)
{
	...
}


实现超时退出

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"
#include "MPU6050.h"

#define MPU6050_ADDR 0xD0//从机地址+读写位

//封装MPU6050_WaitEvent函数,实现超时退出
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout = 10000;
	while(I2C_CheckEvent(I2Cx, I2C_EVENT) == !SUCCESS)
	{
		Timeout--;
		if(Timeout == 0)
 {
   //错误处理
   break;
 }
	}
}

//指定地址写
void MPU6050_WriteReg(uint8_t RegAddr, uint8_t Data)
{
	I2C_GenerateSTART(I2C2, ENABLE);//起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//判断是否产生EV5事件

	I2C_Send7bitAddress(I2C2, MPU6050_ADDR, I2C_Direction_Transmitter);//指定从机地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待事件EV6

	I2C_SendData(I2C2, RegAddr);//指定地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);//等待事件EV8

	I2C_SendData(I2C2, Data);//发送数据
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待事件EV8_2

	I2C_GenerateSTOP(I2C2, ENABLE);//停止条件	
}

//指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddr)
{
	I2C_GenerateSTART(I2C2, ENABLE);//起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//判断是否产生EV5事件

	I2C_Send7bitAddress(I2C2, MPU6050_ADDR, I2C_Direction_Transmitter);//指定从机地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待事件发送EV6

	I2C_SendData(I2C2, RegAddr);//指定地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待事件EV8_2

	I2C_GenerateSTART(I2C2, ENABLE);//重复起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//判断是否产生EV5事件

	I2C_Send7bitAddress(I2C2, MPU6050_ADDR, I2C_Direction_Receiver);//指定从机--读
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//等待事件接收EV6

	//接收之前要提前设置ACK=0和STOP请求
	I2C_AcknowledgeConfig(I2C2, DISABLE);//设置ACK=0
	I2C_GenerateSTOP(I2C2, ENABLE);//STOP请求

	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);//等待事件接收EV7
	uint8_t Data = I2C_ReceiveData(I2C2);

	I2C_AcknowledgeConfig(I2C2, ENABLE);
	return Data;
}
... ...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/953937.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Angular-生命周期及钩子函数

什么是生命周期 Angular 创建和渲染组件及其子组件&#xff0c;当它们绑定的属性发生变化时检查它们&#xff0c;并在从 DOM 中移除它之前销毁它们。生命周期函数通俗的讲就是组件创建、组件更新、组件销毁的时候会触发的一系列的方法。当 Angular 使用构造函数新建一个组件或…

【计算机网络】深入浅出计算机网络

第一章 计算机网络在信息时代的作用 计算机网络已由一种通信基础设施发展成一种重要的信息服务基础设施 CNNIC 中国互联网网络信息中心 因特网概述 网络、互联网和因特网 网络&#xff08;Network&#xff09;由若干结点&#xff08;Node&#xff09;和连接这些结点的链路…

Golang——rune和byte

本文详细介绍Golang中的两种字符类型rune和byte&#xff0c;介绍他们的区别&#xff0c;编码方式和简单的使用。 文章目录 byte 类型rune 类型UTF-8 与 Unicode 的关系byte和rune的主要区别Go的默认编码方式遍历方式遍历 byte遍历 rune补充 字符还原从 byte 序列还原字符串从 r…

基于当前最前沿的前端(Vue3 + Vite + Antdv)和后台(Spring boot)实现的低代码开发平台

项目是一个基于当前最前沿的前端技术栈&#xff08;Vue3 Vite Ant Design Vue&#xff0c;简称Antdv&#xff09;和后台技术栈&#xff08;Spring Boot&#xff09;实现的低代码开发平台。以下是对该项目的详细介绍&#xff1a; 一、项目概述 项目名称&#xff1a;lowcode-s…

java springboot3.x jwt+spring security6.x实现用户登录认证

springboot3.x jwtspring security6.x实现用户登录认证 什么是JWT JWT&#xff08;JSON Web Token&#xff09;是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;它用于在网络应用环境中传递声明。通常&#xff0c;JWT用于身份验证和信息交换。JWT的一个典型用法是…

代码随想录刷题day07|(数组篇)58.区间和

目录 一、数组理论基础 二、前缀和 三、相关算法题目 四、总结 五、待解决问题 一、数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合。 代码随想录 (programmercarl.com) 特点&#xff1a; 1.下标从0开始&#xff0c;内存中地址空间是连续的 2.查询快&…

专用小软件,完全免费,非常丝滑

今天给大家介绍一个专门将PDF数电发票合并打印的软件&#xff0c;这个软件可以批量操作&#xff0c;完全免费没有任何的广告。 电子发票专用批量打印工具 免费批量使用 软件无需安装&#xff0c;解压之后双击这个图标就能直接使用了。 点击右上角的加号&#xff0c;选中需要打…

安装虚拟机VMware遇到的问题

问题1&#xff1a;进入如下界面&#xff0c;不知道如何操作 解决办法 键盘⬇️&#xff0c;选择“Reset the system”回车 问题2&#xff1a;系统存放位置我给放在了VMware安装目录&#xff0c;具体D:\software\VMware\Windows安装不行 解决办法&#xff1a;D:\software\virt…

Matlab 具有周期性分布的死角孔的饱和空气多孔材料的声学特性

本文对直主孔含侧空腔&#xff08;死角&#xff09;的饱和空气多孔介质中的声传播进行了理论和数值研究。侧腔位于沿每个主孔周期性间隔的“节点”上。研究了侧向空腔分布中周期性的影响&#xff0c;并单独考虑了紧间隔死角的低频极限。结果表明&#xff0c;吸附系数和透射损失…

Vue如何构建项目

目录 1.安装Node.js 2.换源(建议) 3.选择一个目录 4.创建一个vue项目 5.验证是否成功 1.安装Node.js 安装18.3或更⾼版本的 Nodejs 点击下载->Node.Js中文网 node -v npm -v 安装好后在windows的cmd窗口下运行 如果能运行出结果就说明安装好了。 2.换源(建议) //…

网络层协议-----IP协议

目录 1.认识IP地址 2.IP地址的分类 3.子网划分 4.公网IP和私网IP 5.IP协议 6.如何解决IP地址不够用 1.认识IP地址 IP 地址&#xff08;Internet Protocol Address&#xff09;是指互联网协议地址。 它是分配给连接到互联网的设备&#xff08;如计算机、服务器、智能手机…

MacOS 下 Memory Analyzer 启动报错

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的全栈工程师 欢迎分享 / 收藏 / 赞 / 在看…

sql模糊关联匹配

需求目标&#xff1a; 建立临时表 drop table grafana_bi.zbj_gift_2024;USE grafana_bi; CREATE TABLE zbj_gift_2024 (id INT AUTO_INCREMENT PRIMARY KEY,userName VARCHAR(255),giftName VARCHAR(255),giftNum INT,points INT,teacher VARCHAR(255),sendDate DATETIME,…

automake error: version mismatch

automake error: version mismatch REF:automake 编译提示版本报错 解决高版本不兼容低版本

C++----STL(string)

引言&#xff1a;STL简介 什么是STL STL(standard template libaray-标准模板库)&#xff1a; 是 C标准库的重要组成部分&#xff08;注意&#xff1a;STL只是C标准库里的一部分&#xff0c;cin和cout也是属于C标准库的&#xff09;&#xff0c;不仅是一个可复用的组件库&…

如何选择视频文件

文章目录 1. 概念介绍2. 方法与细节2.1 实现方法2.2 具体细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何选择多个图片文件"相关的内容&#xff0c;本章回中将介绍如何选择视频文件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在前…

C++中的STL

STL&#xff08;标准模板库&#xff09;在广义上分为&#xff1a;容器&#xff0c;算法&#xff0c;迭代器 容器和算法之间通过迭代器进行无缝衔接 STL大体上分为六大组件:分别为容器&#xff0c;算法&#xff0c;迭代器&#xff0c;仿函数&#xff0c;适配器&#xff0c;空间…

Windows下安装和配置Go开发环境

文章目录 1. 介绍了SDK2. 下载 SDK工具包3. windows 下配置 Golang 环境变量 1. 介绍了SDK SDK 的全称(Software Development Kit 软件开发工具包)SDK是提供给开发人员使用的&#xff0c;其中包含了对应开发语言的工具包 2. 下载 SDK工具包 Go语言的官网为&#xff1a;https…

riscv架构下linux4.15实现early打印

在高版本linux6.12.7源码中&#xff0c;early console介绍&#xff0c;可参考《riscv架构下linux6.12.7实现early打印》文章。 1 什么是early打印 适配内核到新的平台&#xff0c;基本环境搭建好之后&#xff0c;首要的就是要调通串口&#xff0c;方便后面的信息打印。 正常流…

HarmonyOS 鸿蒙 ArkTs(5.0.1 13)实现Scroll下拉到顶刷新/上拉触底加载,Scroll滚动到顶部

HarmonyOS 鸿蒙 ArkTs(5.0.1 13)实现Scroll下拉到顶刷新/上拉触底加载 效果展示 使用方法 import LoadingText from "../components/LoadingText" import PageToRefresh from "../components/PageToRefresh" import FooterBar from "../components/…