本文主要是介绍STM32标准库学习笔记-11.I2C通信,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
参考教程:【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_11void 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结束操作时是0IIC_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++){//00000001IIC_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,以接受从机AckIIC_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_OUTReg_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_OUTReg_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_11void 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_OUTReg_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_OUTReg_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口,一般常用软件实现。
这篇关于STM32标准库学习笔记-11.I2C通信的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!