STM32—I2C通信

1.I2C简介

两根通信线:SCL(串行时钟线)、SDA(串行数据线)

同步,半双工

带数据应答

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

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

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

SCL和SDS各添加一个上拉电阻,阻值一般为4.7kΩ左右

2.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)

 

3.I2C时序 

指定地址写

对于指定设备,在指定的地址下,写入指定数据

当前地址读

对于指定设备,在当前地址指针指示的地址下,读取从机数据

 

指定地址读

对于指定设备,在指定地址下,读取从机数据

 

4.I2C外设简介

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

支持多主机模型

支持7位/10位地址模式

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

支持DMA

兼容SMBus协议 

 

 5.I2C基本结构

 

 

6.I2C相关标准库函数简介

//恢复缺省配置
void I2C_DeInit(I2C_TypeDef* I2Cx);

//初始化
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);

//给结构体参数赋默认值
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);

//I2C使能
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
//生成起始条件
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);

//生成终止条件
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);
//配置CR1的ACK(应答使能)
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
//发送数据
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);

//接收数据
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
//发送7位地址
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);

 

7.案例

7.1软件I2C读写

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

void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);   //最后一位参数非0即1
	Delay_us(10);
}

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
	Delay_us(10);
}

uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
	Delay_us(10);
	return BitValue;
}

void MyI2C_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	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);
	
	GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);
}

void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);	
}

void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for(i = 0;i < 8;i++)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i,Byte = 0x00;
	MyI2C_W_SDA(1);
	for (i = 0;i < 8;i++)
	{
		MyI2C_W_SCL(1);
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
		MyI2C_W_SCL(0);		
	}
	return Byte;
}

void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}

uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);		
	return AckBit;
}

#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.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();
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveAck();
	
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);
	MyI2C_ReceiveAck();
	Data = MyI2C_ReceiveByte();
	MyI2C_SendAck(1);     //想读多个字节 此处给0
	MyI2C_Stop();
	
	return Data;
}

void MPU6050_Init(void)
{
	MyI2C_Init();
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);  //解除睡眠 选择陀螺仪内部时钟
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);  //6个轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);  //采样分频为10
	MPU6050_WriteReg(MPU6050_CONFIG,0x06);
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);	
}

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

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;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (DataH << 8) | DataL;

	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (DataH << 8) | DataL;

	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;

	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;

	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
}

 

7.2硬件I2C读写

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

#define MPU6050_ADDRESS			0xD0

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 RegAddress, uint8_t Data)
{	
	I2C_GenerateSTART(I2C2,ENABLE);
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter);
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2,RegAddress);
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING);
	
	I2C_SendData(I2C2,Data);
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	
	I2C_GenerateSTOP(I2C2,ENABLE);
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	I2C_GenerateSTART(I2C2,ENABLE);
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter);
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2,RegAddress);
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);

	I2C_GenerateSTART(I2C2,ENABLE);
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);

	I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver);
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
	
	I2C_AcknowledgeConfig(I2C2,DISABLE);
	I2C_GenerateSTOP(I2C2,ENABLE);
	
	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED);
	Data = I2C_ReceiveData(I2C2);
	
	I2C_AcknowledgeConfig(I2C2,ENABLE);

	return Data;
}

void MPU6050_Init(void)
{
	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 = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	I2C_InitTypeDef I2C_InitStructure;
	//模式选择
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	//时钟速度,数值越大,SCL频率越高,数据传输越快
	I2C_InitStructure.I2C_ClockSpeed = 50000;
	//时钟占空比,只有在时钟频率大于100kHz才有用,小于该值,固定约为1:1,即低电平时间:高电平时间
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	//应答位配置
	I2C_InitStructure.I2C_Ack = ENABLE;
	//指定STM32作为从机可以响应几位的从机地址,只有当STM32作为从机时才会用到
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	//自身地址1,也是只有当STM32作为从机时才会用到
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;
	I2C_Init(I2C2,&I2C_InitStructure);
	
	I2C_Cmd(I2C2,ENABLE);
	
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);  //解除睡眠 选择陀螺仪内部时钟
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);  //6个轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);  //采样分频为10
	MPU6050_WriteReg(MPU6050_CONFIG,0x06);
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);	
}

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

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;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (DataH << 8) | DataL;

	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (DataH << 8) | DataL;

	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;

	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;

	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
}
  • 16
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32可以通过I2C总线与其他设备进行通信I2C总线是一种串行通信协议,允许多个设备在同一个总线上通信。下面是STM32 I2C通信的基本步骤: 1. 配置I2C总线的GPIO管脚和时钟。 2. 初始化I2C外设,并设置传输速率和数据格式。 3. 发送起始信号和从设备地址。 4. 发送数据或读取数据。 5. 发送停止信号。 以下是一个简单的STM32 I2C通信示例代码,用于向从设备发送数据: ``` #include "stm32f4xx.h" #include "stm32f4xx_i2c.h" #define I2C_SPEED 100000 // I2C传输速率 #define I2C_TIMEOUT 1000 // I2C超时时间 void I2C_Configuration(void) { GPIO_InitTypeDef GPIO_InitStruct; I2C_InitTypeDef I2C_InitStruct; // 使能I2C和GPIO时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // 配置GPIO管脚 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStruct); // 配置GPIO复用功能 GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // 配置I2C参数 I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_OwnAddress1 = 0x00; I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStruct.I2C_ClockSpeed = I2C_SPEED; I2C_Init(I2C1, &I2C_InitStruct); // 使能I2C外设 I2C_Cmd(I2C1, ENABLE); } uint8_t I2C_SendData(uint8_t slaveAddr, uint8_t* buffer, uint8_t length) { uint32_t timeout = I2C_TIMEOUT; uint8_t status; // 发送起始信号 I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) { if (--timeout == 0) return I2C_ERROR_TIMEOUT; } // 发送从设备地址 I2C_Send7bitAddress(I2C1, slaveAddr, I2C_Direction_Transmitter); timeout = I2C_TIMEOUT; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if (--timeout == 0) return I2C_ERROR_TIMEOUT; } // 发送数据 for (int i = 0; i < length; i++) { I2C_SendData(I2C1, buffer[i]); timeout = I2C_TIMEOUT; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if (--timeout == 0) return I2C_ERROR_TIMEOUT; } } // 发送停止信号 I2C_GenerateSTOP(I2C1, ENABLE); return I2C_ERROR_NONE; } int main(void) { uint8_t data[] = {0x01, 0x02, 0x03}; uint8_t result = I2C_SendData(0x50, data, sizeof(data)); while (1); } ``` 需要注意的是,I2C通信中的从设备地址需要根据实际情况修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值