/**
* @brief I2C去初始化
*
* 将指定的I2C外设复位到默认状态。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
*/
void I2C_DeInit(I2C_TypeDef* I2Cx);
/**
* @brief I2C初始化
*
* 根据指定的参数初始化I2C外设。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param I2C_InitStruct 指向I2C初始化结构的指针,包含初始化参数
*/
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
/**
* @brief I2C结构初始化
*
* 将指定的I2C初始化结构复位到默认状态。
*
* @param I2C_InitStruct 指向I2C初始化结构的指针,用于存储初始化参数
*/
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);
/**
* @brief I2C命令使能或失能
*
* 使能或失能指定的I2C外设。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能,DISABLE表示失能
*/
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C DMA命令使能或失能
*
* 使能或失能指定的I2C外设的DMA功能。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能,DISABLE表示失能
*/
void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C DMA最后传输命令使能或失能
*
* 使能或失能指定的I2C外设的DMA最后传输功能。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能,DISABLE表示失能
*/
void I2C_DMALastTransferCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C生成START条件
*
* 在指定的I2C外设上生成START条件。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示生成START条件,DISABLE表示不生成
*/
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C生成STOP条件
*
* 在指定的I2C外设上生成STOP条件。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示生成STOP条件,DISABLE表示不生成
*/
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C应答配置
*
* 配置指定的I2C外设是否响应从设备地址匹配时的应答信号。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示响应应答信号,DISABLE表示不响应
*/
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C自有地址2配置
*
* 配置指定的I2C外设的自有地址2。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param Address 新的自有地址2值
*/
void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address);
/**
* @brief I2C双地址模式命令使能或失能
*
* 使能或失能指定的I2C外设的双地址模式。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能双地址模式,DISABLE表示失能
*/
void I2C_DualAddressCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C全局调用命令使能或失能
*
* 使能或失能指定的I2C外设的全局调用功能。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能全局调用功能,DISABLE表示失能
*/
void I2C_GeneralCallCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C中断配置
*
* 配置指定的I2C外设的中断。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param I2C_IT I2C中断源,指定要配置的中断
* @param NewState 新的状态,ENABLE表示使能中断,DISABLE表示失能
*/
void I2C_ITConfig(I2C_TypeDef* I2Cx, uint16_t I2C_IT, FunctionalState NewState);
/**
* @brief I2C发送数据
*
* 通过指定的I2C外设发送一个字节的数据。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param Data 要发送的数据
*/
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);
/**
* @brief I2C接收数据
*
* 从指定的I2C外设读取一个字节的数据。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @return 接收到的数据
*/
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
/**
* @brief I2C发送7位地址
*
* 通过指定的I2C外设发送一个7位的设备地址,并指定传输方向。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param Address 目标设备的7位地址
* @param I2C_Direction 传输方向,通常指定为I2C_Direction_Transmitter或I2C_Direction_Receiver
*/
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
/**
* @brief I2C读取寄存器
*
* 从指定的I2C外设的寄存器中读取数据。
*
* 注意:这个函数通常用于特定设备或库扩展,标准的I2C库中可能不包含这个函数。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param I2C_Register 要读取的寄存器地址
* @return 读取到的寄存器数据
*/
uint16_t I2C_ReadRegister(I2C_TypeDef* I2Cx, uint8_t I2C_Register);
/**
* @brief I2C软件复位命令使能或失能
*
* 使能或失能指定的I2C外设的软件复位功能。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能软件复位,DISABLE表示失能
*/
void I2C_SoftwareResetCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C NACK位置配置
*
* 配置指定的I2C外设的NACK(Not Acknowledge)响应的位置。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param I2C_NACKPosition NACK位置,通常指定为I2C_NACKPosition_Next或I2C_NACKPosition_Current
*/
void I2C_NACKPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_NACKPosition);
/**
* @brief I2C NACK位置配置
*
* 配置指定的I2C外设的NACK(Not Acknowledge)响应的位置。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param I2C_NACKPosition NACK位置配置,例如I2C_NACKPosition_Next或I2C_NACKPosition_Current
*/
void I2C_NACKPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_NACKPosition);
/**
* @brief I2C SMBus警报配置
*
* 配置指定的I2C外设的SMBus警报模式。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param I2C_SMBusAlert SMBus警报配置
*/
void I2C_SMBusAlertConfig(I2C_TypeDef* I2Cx, uint16_t I2C_SMBusAlert);
/**
* @brief I2C 传输PEC
*
* 使能或失能指定的I2C外设的PEC(Packet Error Checking)传输。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能PEC传输,DISABLE表示失能
*/
void I2C_TransmitPEC(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C PEC位置配置
*
* 配置指定的I2C外设的PEC(Packet Error Checking)位置。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param I2C_PECPosition PEC位置配置
*/
void I2C_PECPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_PECPosition);
/**
* @brief I2C 计算PEC
*
* 使能或失能指定的I2C外设的PEC(Packet Error Checking)计算功能。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能PEC计算,DISABLE表示失能
*/
void I2C_CalculatePEC(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C 获取PEC
*
* 读取指定的I2C外设的PEC(Packet Error Checking)计算结果。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @return PEC计算结果
*/
uint8_t I2C_GetPEC(I2C_TypeDef* I2Cx);
/**
* @brief I2C 自动重发命令
*
* 使能或失能指定的I2C外设的自动重发功能。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能自动重发,DISABLE表示失能
*/
void I2C_ARPCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C 时钟拉伸命令
*
* 使能或失能指定的I2C外设的时钟拉伸功能。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param NewState 新的状态,ENABLE表示使能时钟拉伸,DISABLE表示失能
*/
void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
/**
* @brief I2C 快速模式占空比配置
*
* 配置指定的I2C外设在快速模式下的占空比。
*
* @param I2Cx I2C结构体指针,指定要操作的I2C实例
* @param I2C_DutyCycle 占空比配置
*/
void I2C_FastModeDutyCycleConfig(I2C_TypeDef* I2Cx, uint16_t I2C_DutyCycle);
I²C(Inter-Integrated Circuit)是一种串行通信协议,通常用于连接微控制器和外部设备,例如传感器、存储器、显示器等。以下是I²C通信的基本原理:
- 总线结构:
- I²C通信使用两根线,即串行数据线(SDA)和串行时钟线(SCL)。
- SDA线用于传输数据,而SCL线用于传输时钟信号。
- 多个设备可以连接到同一条I²C总线上。
- 主从结构:
- I²C通信中的设备分为主设备(Master)和从设备(Slave)。
- 主设备负责发起通信并控制总线上的数据传输,而从设备则被动地响应主设备的指令。
- 起始和停止条件:
- 通信开始时,主设备发送起始条件(Start Condition),它包括将SDA线从高电平切换到低电平后,再将SCL线从高电平切换到低电平。
- 通信结束时,主设备发送停止条件(Stop Condition),它包括将SDA线从低电平切换到高电平后,再将SCL线从低电平切换到高电平。
- 在通信过程中,起始和停止条件的发送标志着数据传输的开始和结束。
- 数据传输:
- 每个数据传输包含8位数据字节,以及一个读/写位,用于指示数据的方向。
- 数据传输的开始时,主设备发送一个7位的设备地址,用于指示通信的目标设备。
- 如果目标设备存在并且可以响应,它会发送应答位(Acknowledge)以示确认。
- 主设备继续发送或接收数据字节,每个字节后都会有一个应答位。
- 数据传输完成后,主设备发送停止条件。
- 时钟同步:
- 数据传输的时钟由主设备生成和控制,从设备在SCL线上同步主设备的时钟信号以接收或发送数据。
- 速率和频率:
- I²C通信的速率可以根据需要进行调整,通常有标准速率和快速速率两种模式,分别为100 kHz和400 kHz。
- 除此之外,还有更高速率的扩展模式,如快速模式加速(Fast Mode Plus,最高达1 MHz)和超快速模式(High-Speed Mode,最高达3.4 MHz)。
- 通讯流程:
I²C(Inter-Integrated Circuit)通信流程遵循特定的序列,包括启动、设备地址、数据传输和停止等步骤。以下是典型的I²C通信流程:
- 启动条件(Start Condition):
- 通信开始时,主设备发出启动条件,即在时钟信号(SCL)为高电平时,数据信号(SDA)从高电平切换到低电平。
- 这个信号告诉所有从设备一个新的通信周期即将开始。
- 发送设备地址:
- 主设备发送一个7位的目标设备地址,同时附带读/写位。目标设备地址用于指示要进行通信的从设备。
- 如果通信是要向设备写入数据,则读/写位为0;如果是要从设备读取数据,则读/写位为1。
- 在发送地址后,主设备等待从设备的应答。
- 应答位(Acknowledge):
- 从设备在接收到其地址后会发送一个应答位。如果从设备存在且准备好接收数据,则应答位为低电平(0);如果从设备不可用或出现错误,则应答位为高电平(1)。
- 应答位的发送允许主设备确定通信是否可以继续。
- 数据传输:
- 主设备根据通信的类型(读或写)发送或接收数据字节。
- 每个数据字节都由8位组成,先发送最高位,然后是次高位,依此类推,直到发送最低位。
- 每发送一个字节后,主设备等待从设备的应答。
- 重复启动(Repeated Start)(可选):
- 如果主设备需要与另一个设备进行通信而不断开总线,则可以发送重复启动条件,而不是发送停止条件。
- 重复启动条件与启动条件相同,但在通信过程中不会释放总线。
- 停止条件(Stop Condition):
- 通信结束时,主设备发送停止条件。停止条件是在时钟信号为高电平时,数据信号从低电平切换到高电平。
- 发送停止条件后,所有设备释放总线,并将其状态重置为等待下一个通信周期。
以上是典型的I²C通信流程,通过这些步骤,主设备可以与一个或多个从设备进行可靠的数据交换。
I²C通信协议灵活且简单,适用于连接多种类型的设备,并且由于其使用的线路较少,可以节省系统的硬件成本。
S:起始条件
2:设备地址 + 读写位 1表示读 0表示写
RA:接收从机的应答位,主机释放SDA,SDA应该为高电平,只是从机会拉低SDA,此时SDA如果为高电平代表没有从应答
4:一般为寄存器地址
RA:接收从机的应答位,主机释放SDA,SDA应该为高电平,只是从机会拉低SDA,此时SDA如果为高电平代表没有从应答
6:写入数据
RA:接收从机的应答位,主机释放SDA,SDA应该为高电平,只是从机会拉低SDA,此时SDA如果为高电平代表没有从应答
P:终止条件
S:起始条件
2:主机发送一个字节:设备地址 + 读写位 1表示读 0表示写
RA:接收从机的应答位,主机释放SDA,SDA应该为高电平,只是从机会拉低SDA,此时SDA如果为高电平代表没有从应答
S:起始条件
4:主机交出SDA控制权,依照当前地址指示的地址读取SDA数据,从机将数据存入SDA,
RA:接收从机的应答位,主机释放SDA,SDA应该为高电平,只是从机会拉低SDA,此时SDA如果为高电平代表没有从应答
P:终止条件
S:起始条件
2:设备地址 + 读写位 1表示读 0表示写
RA:接收从机的应答位,主机释放SDA,SDA应该为高电平,只是从机会拉低SDA,此时SDA如果为高电平代表没有从应答
4:指定寄存器地址
RA:接收从机的应答位,主机释放SDA,SDA应该为高电平,只是从机会拉低SDA,此时SDA如果为高电平代表没有从应答
SR:另起一个时序,起始条件
6:主机发送一个字节:设备地址 + 读写位 1表示读 0表示写
RA:接收从机的应答位,主机释放SDA,SDA应该为高电平,只是从机会拉低SDA,此时SDA如果为高电平代表没有从应答
S:起始条件
8:主机交出SDA控制权,依照当前地址指示的地址读取SDA数据,从机将数据存入SDA,
RA:接收从机的应答位,主机释放SDA,SDA应该为高电平,只是从机会拉低SDA,此时SDA如果为高电平代表没有从应答
P:终止条件
一. 软实现
- I2C写SCL引脚电平
/**
* 函 数: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引脚电平
/**
* 函 数: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引脚电平
/**
* 函 数: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初始化
/**
* 函 数: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起始
/**
* 函 数: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终止
/**
* 函 数:I2C终止
* 参 数:无
* 返 回 值:无
*/
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0); //拉低SDA,确保SDA为低电平
MyI2C_W_SCL(1); //释放SCL,使SCL呈现高电平
MyI2C_W_SDA(1); //在SCL高电平期间,释放SDA,产生终止信号
}
- I2C发送一个字节
/**
* 函 数: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接收一个字节
/**
* 函 数: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发送应答位
/**
* 函 数: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接收应答位
/**
* 函 数: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写寄存器
/**
* 函 数: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读寄存器
/**
* 函 数: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号
* 参 数:无
* 返 回 值:MPU6050的ID号
*/
uint8_t MPU6050_GetID(void)
{
return MPU6050_ReadReg(MPU6050_WHO_AM_I); //返回WHO_AM_I寄存器的值
}
- MPU6050获取数据
/**
* 函 数: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; //数据拼接,通过输出参数返回
}
二. 硬件I2C
- 开启时钟
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); //开启I2C2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
- GPIO初始化
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_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引脚初始化为复用开漏输出
- I2C初始化
/*I2C初始化*/
I2C_InitTypeDef I2C_InitStructure; //定义结构体变量
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //模式,选择为I2C模式
I2C_InitStructure.I2C_ClockSpeed = 50000; //时钟速度,选择为50KHz
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //时钟占空比,选择Tlow/Thigh = 2
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //应答,选择使能
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //应答地址,选择7位,从机模式下才有效
I2C_InitStructure.I2C_OwnAddress1 = 0x00; //自身地址,从机模式下才有效
I2C_Init(I2C2, &I2C_InitStructure); //将结构体变量交给I2C_Init,配置I2C2
typedef struct
{
// 指定I2C的时钟频率
// 此参数必须设置为低于400kHz的值
uint32_t I2C_ClockSpeed; /*!< Specifies the clock frequency.
This parameter must be set to a value lower than 400kHz */
// 指定I2C的工作模式
// 此参数可以是@ref I2C_mode中定义的值
uint16_t I2C_Mode; /*!< Specifies the I2C mode.
This parameter can be a value of @ref I2C_mode */
// 指定I2C快速模式的占空比
// 此参数可以是@ref I2C_duty_cycle_in_fast_mode中定义的值
uint16_t I2C_DutyCycle; /*!< Specifies the I2C fast mode duty cycle.
This parameter can be a value of @ref I2C_duty_cycle_in_fast_mode */
// 指定第一个设备的自有地址
// 此参数可以是7位或10位地址
uint16_t I2C_OwnAddress1; /*!< Specifies the first device own address.
This parameter can be a 7-bit or 10-bit address. */
// 启用或禁用应答
// 此参数可以是@ref I2C_acknowledgement中定义的值
uint16_t I2C_Ack; /*!< Enables or disables the acknowledgement.
This parameter can be a value of @ref I2C_acknowledgement */
// 指定是否应答7位或10位地址
// 此参数可以是@ref I2C_acknowledged_address中定义的值
uint16_t I2C_AcknowledgedAddress; /*!< Specifies if 7-bit or 10-bit address is acknowledged.
This parameter can be a value of @ref I2C_acknowledged_address */
} I2C_InitTypeDef;
/* 定义I2C的标准模式,即普通的I2C通信模式 */
#define I2C_Mode_I2C ((uint16_t)0x0000)
/* 定义SMBus(System Management Bus,系统管理总线)设备模式
* SMBus 是一种基于I2C的简化总线,通常用于系统内部的电源管理和设备监控
* 这里的值可能是特定硬件或库实现所定义的
*/
#define I2C_Mode_SMBusDevice ((uint16_t)0x0002)
/* 定义SMBus主机模式
* SMBus主机可以发起与SMBus设备之间的通信
* 这里的值也是特定硬件或库实现所定义的
*/
#define I2C_Mode_SMBusHost ((uint16_t)0x000A)
/* 定义I2C快速模式的占空比,Tlow/Thigh = 16/9
* 这意味着在低电平(SCL = 0)时的时间长度与在高电平(SCL = 1)时的时间长度的比率为16:9
*/
#define I2C_DutyCycle_16_9 ((uint16_t)0x4000)
/* 定义I2C快速模式的占空比,Tlow/Thigh = 2
* 这意味着在低电平(SCL = 0)时的时间长度与在高电平(SCL = 1)时的时间长度的比率为2:1
* 注意:这里的值0xBFFF可能是一个位掩码或特定于某个硬件或库的表示,因为通常的比率值不会直接用16位整数的形式来表示
* 在实际应用中,这个值可能会被用来配置SCL线的时钟占空比
*/
#define I2C_DutyCycle_2 ((uint16_t)0xBFFF)
/* 定义I2C通信中确认的7位设备地址的位掩码
* 在I2C通信中,设备地址通常通过起始条件(START condition)后跟一个或多个字节来发送
* 对于7位地址,最高位(MSB)是读/写位,接下来的7位是设备地址
* 这个宏通常用于检查或设置I2C通信的某个状态或寄存器,以表示接收到了一个有效的7位设备地址的响应(ACK)
*/
#define I2C_AcknowledgedAddress_7bit ((uint16_t)0x4000)
/* 定义I2C通信中确认的10位设备地址的位掩码
* 10位地址模式在I2C通信中不太常见,但在某些应用中可能需要支持
* 10位地址模式使用两个字节来发送设备地址:第一个字节的高7位是地址的高位部分,最低位是地址模式位(设置为1表示使用10位地址)
* 第二个字节包含地址的低10位中的剩余部分
* 这个宏通常用于检查或设置I2C通信的某个状态或寄存器,以表示接收到了一个有效的10位设备地址的响应(ACK)
*/
#define I2C_AcknowledgedAddress_10bit ((uint16_t)0xC000)
- I2C使能
/*I2C使能*/
I2C_Cmd(I2C2, ENABLE); //使能I2C2,开始运行
- MPU6050等待事件
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t Timeout;
Timeout = 10000; //给定超时计数时间
while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) //循环等待指定事件
{
Timeout --; //等待时,计数值自减
if (Timeout == 0) //自减到0后,等待超时
{
/*超时的错误处理代码,可以添加到此处*/
break; //跳出等待,不等了
}
}
}
- MPU6050写寄存器
/**
* 函 数:MPU6050写寄存器
* 参 数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
* 参 数:Data 要写入寄存器的数据,范围:0x00~0xFF
* 返 回 值:无
*/
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成起始条件
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5
I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6
I2C_SendData(I2C2, RegAddress); //硬件I2C发送寄存器地址
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); //等待EV8
I2C_SendData(I2C2, Data); //硬件I2C发送数据
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2
I2C_GenerateSTOP(I2C2, ENABLE); //硬件I2C生成终止条件
}
- MPU6050读寄存器
/**
* 函 数:MPU6050读寄存器
* 参 数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
* 返 回 值:读取寄存器的数据,范围:0x00~0xFF
*/
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
uint8_t Data;
I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成起始条件
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5
I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6
I2C_SendData(I2C2, RegAddress); //硬件I2C发送寄存器地址
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2
I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成重复起始条件
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5
I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver); //硬件I2C发送从机地址,方向为接收
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //等待EV6
I2C_AcknowledgeConfig(I2C2, DISABLE); //在接收最后一个字节之前提前将应答失能
I2C_GenerateSTOP(I2C2, ENABLE); //在接收最后一个字节之前提前申请停止条件
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); //等待EV7
Data = I2C_ReceiveData(I2C2); //接收数据寄存器
I2C_AcknowledgeConfig(I2C2, ENABLE); //将应答恢复为使能,为了不影响后续可能产生的读取多字节操作
return Data;
}
/* 定义了主模式被选中的事件,包括BUSY、MSL和SB标志 */
#define I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */
/* --EV6 事件定义 --*/
/* 定义了主发送模式被选中的事件,包括BUSY、MSL、ADDR、TXE和TRA标志 */
#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */
/* 定义了主接收模式被选中的事件,包括BUSY、MSL和ADDR标志 */
#define I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ((uint32_t)0x00030002) /* BUSY, MSL and ADDR flags */
/* --EV9 事件定义 --*/
/* 定义了使用10位地址的主模式事件,包括BUSY、MSL和ADD10标志 */
#define I2C_EVENT_MASTER_MODE_ADDRESS10 ((uint32_t)0x00030008) /* BUSY, MSL and ADD10 flags */
/* 主接收模式相关的事件定义 -----------------------------*/
/* --EV7 事件定义 --*/
/* 定义了主模式接收到一个字节的事件,包括BUSY、MSL和RXNE标志 */
#define I2C_EVENT_MASTER_BYTE_RECEIVED ((uint32_t)0x00030040) /* BUSY, MSL and RXNE flags */
/* 主发送模式相关的事件定义 --------------------------*/
/* --EV8 事件定义 --*/
/* 定义了主模式正在发送一个字节的事件,包括TRA、BUSY、MSL和TXE标志 */
#define I2C_EVENT_MASTER_BYTE_TRANSMITTING ((uint32_t)0x00070080) /* TRA, BUSY, MSL, TXE flags */
/* --EV8_2 事件定义 --*/
/* 定义了主模式发送一个字节完成的事件,包括TRA、BUSY、MSL、TXE和BTF标志 */
#define I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */