参考教程:【STM32入门教程-2023版 细致讲解 中文字幕】
I2C通信
- I2C(Inter IC Bus)是由Philips公司八十年代推出的一种通用数据总线
- 两根通信线:SCL(Serial Clock)、SDA(Serial Data) 同步,半双工 带数据应答
- 支持总线挂载多设备(一主多从、多主多从)
硬件电路
- 所有I2C设备的SCL连在一起,SDA连在一起
- 设备的SCL和SDA均要配置成开漏输出模式
- SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
I2C时序基本单元
- 起始条件:SCL高电平期间,SDA从高电平切换到低电平
- 终止条件:SCL高电平期间,SDA从低电平切换到高电平
- 发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
- 接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
- 发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
- 接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
I2C时序
- 指定地址写
- 对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
- 当前地址读
- 对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
- 指定地址读
- 对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
Sr为重复起始
代码实现软件I2C通信
#include "stm32f10x.h" // Device header
#include "Delay.h"
//标准库I2C函数以I2C开头,这里自己实现用IIC作为前缀
#define IIC_PORT GPIOB
#define IIC_SCL GPIO_Pin_10
#define IIC_SDA GPIO_Pin_11
void IIC_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = IIC_SCL | IIC_SDA;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//默认状态给高电平
GPIO_SetBits(IIC_PORT,IIC_SCL | IIC_SDA);
}
void IIC_W_SCL(uint8_t BitVal)
{
GPIO_WriteBit(IIC_PORT,IIC_SCL,(BitAction)BitVal);
Delay_us(10);
}
void IIC_W_SDA(uint8_t BitVal)
{
GPIO_WriteBit(IIC_PORT,IIC_SDA,(BitAction)BitVal);
Delay_us(10);
}
uint8_t IIC_R_SDA(void)
{
uint8_t Bitval = GPIO_ReadInputDataBit(IIC_PORT,IIC_SDA);
Delay_us(10);
return Bitval;
}
void IIC_Start(void)
{
IIC_W_SDA(1);
IIC_W_SCL(1);
IIC_W_SDA(0);
IIC_W_SCL(0);
}
void IIC_End(void)
{
//其余的函数保证了SCL结束操作时是0
IIC_W_SDA(0);
IIC_W_SCL(1);
IIC_W_SDA(1);
}
void IIC_SendByte(uint8_t Byte)
{
uint8_t i;
for(i = 0;i < 8;i++)
{
//00000001
IIC_W_SDA(Byte & (0x80 >> i));
IIC_W_SCL(1);
IIC_W_SCL(0);
}
}
void IIC_SendAck(BitAction Ack)
{
IIC_W_SDA(Ack);
IIC_W_SCL(1);
IIC_W_SCL(0);
}
uint8_t IIC_ReceiveByte(void)
{
uint8_t i;
uint8_t Byte = 0x00;
IIC_W_SDA(1);
for(i = 0;i < 8;i++)
{
IIC_W_SCL(1);
if(IIC_R_SDA())
{
Byte |= (0x80 >> i);
}
IIC_W_SCL(0);
}
return Byte;
}
uint8_t IIC_ReceiveAck(void)
{
uint8_t Ack;
IIC_W_SDA(1); //主机释放SDA,以接受从机Ack
IIC_W_SCL(1);
Ack = IIC_R_SDA();
IIC_W_SCL(0);
return Ack;
}
MPU6050使用软件I2C通信
#include "stm32f10x.h" // Device header
#include "IIC_SOFTWARE.h"
#include "MPU6050.h"
#include "MPU6050_Regs.h"
void MPU6050_WriteRegData(uint8_t RegAddr,uint8_t Data)
{
IIC_Start();
IIC_SendByte(MPU6050_DEVICE_ADDRESS);
IIC_ReceiveAck();
IIC_SendByte(RegAddr);
IIC_ReceiveAck();
IIC_SendByte(Data);
IIC_ReceiveAck();
IIC_End();
}
uint8_t MPU6050_ReadRegData(uint8_t RegAddr)
{
uint8_t RegData;
IIC_Start();
IIC_SendByte(MPU6050_DEVICE_ADDRESS);
IIC_ReceiveAck();
IIC_SendByte(RegAddr);
IIC_ReceiveAck();
IIC_Start();
IIC_SendByte(MPU6050_DEVICE_ADDRESS | 0x01);
IIC_ReceiveAck();
RegData = IIC_ReceiveByte();
IIC_SendAck(1);
IIC_End();
return RegData;
}
uint8_t MPU6050_ReadID(void)
{
return MPU6050_ReadRegData(MPU6050_WHO_AM_I);
}
void MPU6050_GetData(MPU6050_GYRO_TypeDef *MPU6050_GYRO_Structure,MPU6050_ACCEL_TypeDef *MPU6050_ACCEL_Structure)
{
uint16_t Reg_H,Reg_L;
//MPU6050_GYRO_OUT
Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_XOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_XOUT_L);
MPU6050_GYRO_Structure->GYRO_XOUT = (Reg_H << 8) | Reg_L;
Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_YOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_YOUT_L);
MPU6050_GYRO_Structure->GYRO_YOUT = (Reg_H << 8) | Reg_L;
Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_ZOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_ZOUT_L);
MPU6050_GYRO_Structure->GYRO_ZOUT = (Reg_H << 8) | Reg_L;
//MPU6050_ACCEL_OUT
Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_XOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_XOUT_H);
MPU6050_ACCEL_Structure->ACCEL_XOUT = (Reg_H << 8) | Reg_L;
Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_YOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_YOUT_H);
MPU6050_ACCEL_Structure->ACCEL_YOUT = (Reg_H << 8) | Reg_L;
Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_ZOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_ZOUT_H);
MPU6050_ACCEL_Structure->ACCEL_ZOUT = (Reg_H << 8) | Reg_L;
}
void MPU6050_MeasureData(MPU6050_GYRO_TypeDef *MPU6050_GYRO_Structure,MPU6050_ACCEL_TypeDef *MPU6050_ACCEL_Structure,MPU6050_Measure_TypeDef *MPU6050_Measure_Structure)
{
MPU6050_Measure_Structure->Gx = ((float)MPU6050_GYRO_Structure->GYRO_XOUT)/32768.0*2000.0;
MPU6050_Measure_Structure->Gy = ((float)MPU6050_GYRO_Structure->GYRO_YOUT)/32768.0*2000.0;
MPU6050_Measure_Structure->Gz = ((float)MPU6050_GYRO_Structure->GYRO_ZOUT)/32768.0*2000.0;
MPU6050_Measure_Structure->Ax = ((float)MPU6050_ACCEL_Structure->ACCEL_XOUT)/32768.0*16.0;
MPU6050_Measure_Structure->Ay = ((float)MPU6050_ACCEL_Structure->ACCEL_YOUT)/32768.0*16.0;
MPU6050_Measure_Structure->Az = ((float)MPU6050_ACCEL_Structure->ACCEL_ZOUT)/32768.0*16.0;
}
void MPU6050_Init(void)
{
IIC_Init();
MPU6050_WriteRegData(MPU6050_PWR_MGMT_1,0x00);
MPU6050_WriteRegData(MPU6050_PWR_MGMT_2,0x00);
MPU6050_WriteRegData(MPU6050_SMPRT_DIV,0x00);
MPU6050_WriteRegData(MPU6050_CONFIG,0x06);
MPU6050_WriteRegData(MPU6050_GYRO_CONFIG,0x18);
MPU6050_WriteRegData(MPU6050_ACCEL_CONFIG,0x18);
}
float MPU6050_Abs(float num)
{
return num < 0 ? -num:num;
}
I2C外设简介
- STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
- 支持多主机模型
- 支持7位/10位地址模式
- 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
- 支持DMA
- 兼容SMBus协议
- STM32F103C8T6 硬件I2C资源:I2C1、I2C2
I2C外设基本结构
I2C主机发送数据
I2C主机接收数据
MPU6050使用I2C外设通信
#include "stm32f10x.h" // Device header
#include "MPU6050.h"
#include "MPU6050_Regs.h"
#define IIC_PORT GPIOB
#define IIC_SCL GPIO_Pin_10
#define IIC_SDA GPIO_Pin_11
void MPU6050_WriteRegData(uint8_t RegAddr,uint8_t Data)
{
// IIC_Start();
// IIC_SendByte(MPU6050_DEVICE_ADDRESS);
// IIC_ReceiveAck();
// IIC_SendByte(RegAddr);
// IIC_ReceiveAck();
// IIC_SendByte(Data);
// IIC_ReceiveAck();
// IIC_End();
I2C_GenerateSTART(I2C2,ENABLE);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
I2C_Send7bitAddress(I2C2,MPU6050_DEVICE_ADDRESS,I2C_Direction_Transmitter);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
I2C_SendData(I2C2,RegAddr);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);
I2C_SendData(I2C2,Data);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);
I2C_GenerateSTOP(I2C2,ENABLE);
}
uint8_t MPU6050_ReadRegData(uint8_t RegAddr)
{
uint8_t RegData;
// IIC_Start();
// IIC_SendByte(MPU6050_DEVICE_ADDRESS);
// IIC_ReceiveAck();
// IIC_SendByte(RegAddr);
// IIC_ReceiveAck();
//
// IIC_Start();
// IIC_SendByte(MPU6050_DEVICE_ADDRESS | 0x01);
// IIC_ReceiveAck();
// RegData = IIC_ReceiveByte();
// IIC_SendAck(1);
// IIC_End();
I2C_GenerateSTART(I2C2,ENABLE);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
I2C_Send7bitAddress(I2C2,MPU6050_DEVICE_ADDRESS,I2C_Direction_Transmitter);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
I2C_SendData(I2C2,RegAddr);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);
I2C_GenerateSTART(I2C2,ENABLE);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
I2C_Send7bitAddress(I2C2,MPU6050_DEVICE_ADDRESS,I2C_Direction_Receiver);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);
I2C_AcknowledgeConfig(I2C2,DISABLE);
I2C_GenerateSTOP(I2C2,ENABLE);
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);
RegData = I2C_ReceiveData(I2C2);
I2C_AcknowledgeConfig(I2C2,ENABLE);
return RegData;
}
uint8_t MPU6050_ReadID(void)
{
return MPU6050_ReadRegData(MPU6050_WHO_AM_I);
}
void MPU6050_GetData(MPU6050_GYRO_TypeDef *MPU6050_GYRO_Structure,MPU6050_ACCEL_TypeDef *MPU6050_ACCEL_Structure)
{
uint16_t Reg_H,Reg_L;
//MPU6050_GYRO_OUT
Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_XOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_XOUT_L);
MPU6050_GYRO_Structure->GYRO_XOUT = (Reg_H << 8) | Reg_L;
Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_YOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_YOUT_L);
MPU6050_GYRO_Structure->GYRO_YOUT = (Reg_H << 8) | Reg_L;
Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_ZOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_ZOUT_L);
MPU6050_GYRO_Structure->GYRO_ZOUT = (Reg_H << 8) | Reg_L;
//MPU6050_ACCEL_OUT
Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_XOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_XOUT_H);
MPU6050_ACCEL_Structure->ACCEL_XOUT = (Reg_H << 8) | Reg_L;
Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_YOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_YOUT_H);
MPU6050_ACCEL_Structure->ACCEL_YOUT = (Reg_H << 8) | Reg_L;
Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_ZOUT_H);
Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_ZOUT_H);
MPU6050_ACCEL_Structure->ACCEL_ZOUT = (Reg_H << 8) | Reg_L;
}
void MPU6050_MeasureData(MPU6050_GYRO_TypeDef *MPU6050_GYRO_Structure,MPU6050_ACCEL_TypeDef *MPU6050_ACCEL_Structure,MPU6050_Measure_TypeDef *MPU6050_Measure_Structure)
{
MPU6050_Measure_Structure->Gx = ((float)MPU6050_GYRO_Structure->GYRO_XOUT)/32768.0*2000.0;
MPU6050_Measure_Structure->Gy = ((float)MPU6050_GYRO_Structure->GYRO_YOUT)/32768.0*2000.0;
MPU6050_Measure_Structure->Gz = ((float)MPU6050_GYRO_Structure->GYRO_ZOUT)/32768.0*2000.0;
MPU6050_Measure_Structure->Ax = ((float)MPU6050_ACCEL_Structure->ACCEL_XOUT)/32768.0*16.0;
MPU6050_Measure_Structure->Ay = ((float)MPU6050_ACCEL_Structure->ACCEL_YOUT)/32768.0*16.0;
MPU6050_Measure_Structure->Az = ((float)MPU6050_ACCEL_Structure->ACCEL_ZOUT)/32768.0*16.0;
}
void MPU6050_Init(void)
{
// IIC_Init();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = IIC_SCL | IIC_SDA;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 50000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_Init(I2C2,&I2C_InitStructure);
I2C_Cmd(I2C2,ENABLE);
MPU6050_WriteRegData(MPU6050_PWR_MGMT_1,0x00);
MPU6050_WriteRegData(MPU6050_PWR_MGMT_2,0x00);
MPU6050_WriteRegData(MPU6050_SMPRT_DIV,0x00);
MPU6050_WriteRegData(MPU6050_CONFIG,0x06);
MPU6050_WriteRegData(MPU6050_GYRO_CONFIG,0x18);
MPU6050_WriteRegData(MPU6050_ACCEL_CONFIG,0x18);
}
float MPU6050_Abs(float num)
{
return num < 0 ? -num:num;
}
I2C通信只需要SDA和SCL两根通信线,节省IO口,一般常用软件实现。