I2C通信外设
I2C外设简介
- STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
- 支持多主机模型
- 支持7位/10位地址模式
- 支持不同的通讯速度,标准速度(100kHz),快速(400kHz)
- 支持DMA
- 兼容SMBus协议
- STM32F103C8T6硬件I2C资源: I2C1、I2C2
I2C框图
SDA线控制
- 发送模式: 当发送寄存器空时(TXE), 单片机将数据写入数据寄存器(DATA REGISTER), 当数据移位寄存器为空时, 数据从数据寄存器转入数据移位寄存器, 再通过数据移位寄存器一位位输出
- 接收模式: 数据一位位被送到数据移位寄存器中, 移位寄存器再把数据一位位送到数据寄存器中(DATA REGISTER), 当接收寄存器非空时(RXNE), 单片机就可以将数据寄存器中的数据读出
单片机从模式控制(了解)
- 报错误校验(PEC) 用于提高通信的可靠性,使用CRC-8算法
- 当stm32主机属于从模式时,可以通过自身地址寄存器和双地址寄存器为stm32自定义地址
SCL线控制
-
控制寄存器用于配置硬件电路
- CR1
- CR2
-
状态寄存器可以读取电路的信息
- SR1
-
SR2
-
时钟控制寄存器用于配置时钟信号
- CCR
- 具体翻阅STM32F10xxx参考手册
I2C基本结构
时序图
主机发送
主机接收
软件硬件波形对比(了解)
程序设计
根据主机发送和主机接收的时序图重写 MPU6050_WriteReg()
和MPU6050_ReadReg()
I2C的配置结构体意义见注释
I2C常用函数介绍
-
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
- 用于配置I2C
-
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
- 开启对应的I2C外设
-
void I2C_GenerateSTART(I2C_TypeDef I2Cx, FunctionalState NewState);*
- 产生起始条件
-
void I2C_GenerateSTOP(I2C_TypeDef I2Cx, FunctionalState NewState);*
- 产生终止条件
-
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
- 控制是否有应答
-
void I2C_SendData(I2C_TypeDef I2Cx, uint8_t Data);*
- 发送数据
-
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
- 用于寻找从机地址
-
ErrorStatus I2C_CheckEvent(I2C_TypeDef I2Cx, uint32_t I2C_EVENT)*
- 状态检测函数,用于检查I2C通讯过程中产生的事件
-
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
- 状态检测函数,用于获取状态寄存器中的标志位
main.c
#include "stm32f10x.h"
#include "delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
int main(void)
{
OLED_Init();
MPU6050_Init();
OLED_ShowString(1, 1, "ID:");
ID = MPU6050_GetID();
OLED_ShowHexNum(1, 4, ID, 2);
MPU6050_DataTypeDef MPU6050_DataStructure;
while(1)
{
// 读取数据
MPU6050_GetData(&MPU6050_DataStructure);
// 显示数据
OLED_ShowSignedNum(2, 1, MPU6050_DataStructure.AccX, 5);
OLED_ShowSignedNum(3, 1, MPU6050_DataStructure.AccY, 5);
OLED_ShowSignedNum(4, 1, MPU6050_DataStructure.AccZ, 5);
OLED_ShowSignedNum(2, 8, MPU6050_DataStructure.GyroX, 5);
OLED_ShowSignedNum(3, 8, MPU6050_DataStructure.GyroY, 5);
OLED_ShowSignedNum(4, 8, MPU6050_DataStructure.GyroZ, 5);
}
}
MPU6050.c
#include "stm32f10x.h"
#include "MPU6050_SEG.h"
#define MPU_ADDRESS 0xD0
/*
* 等待事件 带超时退出机制
* 将while与I2C_CheckEvent封装在一起
*/
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t Timeout=50000;
while(Timeout && (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS))
{
Timeout --;
}
}
/*
* 往指定地址写入指定数据
*/
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
// 起始条件
I2C_GenerateSTART(I2C2, ENABLE);
// 等待 EV5
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
// 呼叫设备(MPU6050)
I2C_Send7bitAddress(I2C2, MPU_ADDRESS, I2C_Direction_Transmitter);
// 等待 EV6
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
// 寻找写入地址
I2C_SendData(I2C2, RegAddress);
// 等待EV8
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
// 写入数据
I2C_SendData(I2C2, Data);
// 等待EV8_2
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); // 等待 EV5
I2C_Send7bitAddress(I2C2, MPU_ADDRESS, I2C_Direction_Transmitter); //呼叫设备 写
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); // 等待 EV6
I2C_SendData(I2C2, RegAddress); // 寻找读取地址
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, MPU_ADDRESS, I2C_Direction_Receiver); // 呼叫设备 读
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;
}
/*
* 配置MPU6050
*/
void MPU6050_Init(void)
{
// I2C初始化
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 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);
// I2C
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Ack=I2C_Ack_Enable; // 有应答
I2C_InitStructure.I2C_ClockSpeed=50000; // 400 kHz以下 100 kHz以上为快速通讯
I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2; // 低电平比高电平2:1 仅在快速通讯生效
I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
// 从模式下 stm32作为从机的地址所占位数
I2C_InitStructure.I2C_OwnAddress1=0x00; // 从模式下stm32作为从机的地址
I2C_Init(I2C2, &I2C_InitStructure);
// 开启I2C2
I2C_Cmd(I2C2, ENABLE);
MPU6050_WriteReg(PWR_MGMT_1, 0x01);
MPU6050_WriteReg(PWR_MGMT_2, 0x00);
MPU6050_WriteReg(SMPLRT_DIV, 0x09);
MPU6050_WriteReg(CONFIG, 0x06);
MPU6050_WriteReg(GYRO_CONFIG, 0x18);
MPU6050_WriteReg(ACCEL_CONFIG, 0x18);
}
/*
* 获取ID号
*/
uint8_t MPU6050_GetID(void)
{
return MPU6050_ReadReg(WHO_AM_I);
}
/*
* 获取MPU6050数据
*/
void MPU6050_GetData(MPU6050_DataTypeDef * MPU_DataStructure)
{
uint16_t Data_H, Data_L; // 高位数据和低位数据
Data_H = MPU6050_ReadReg(ACCEL_XOUT_H);
Data_L = MPU6050_ReadReg(ACCEL_XOUT_L);
MPU_DataStructure->AccX=(Data_H<<8) | Data_L;
Data_H = MPU6050_ReadReg(ACCEL_YOUT_H);
Data_L = MPU6050_ReadReg(ACCEL_YOUT_L);
MPU_DataStructure->AccY=(Data_H<<8) | Data_L;
Data_H = MPU6050_ReadReg(ACCEL_ZOUT_H);
Data_L = MPU6050_ReadReg(ACCEL_ZOUT_L);
MPU_DataStructure->AccZ=(Data_H<<8) | Data_L;
Data_H = MPU6050_ReadReg(GYRO_XOUT_H);
Data_L = MPU6050_ReadReg(GYRO_XOUT_L);
MPU_DataStructure->GyroX=(Data_H<<8) | Data_L;
Data_H = MPU6050_ReadReg(GYRO_YOUT_H);
Data_L = MPU6050_ReadReg(GYRO_YOUT_L);
MPU_DataStructure->GyroY=(Data_H<<8) | Data_L;
Data_H = MPU6050_ReadReg(GYRO_ZOUT_H);
Data_L = MPU6050_ReadReg(GYRO_ZOUT_L);
MPU_DataStructure->GyroZ=(Data_H<<8) | Data_L;
}
MPU6050.h
#ifndef __MPU6050_H
#define __MPU6050_H
#include "MPU6050_REG.h"
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);
void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(MPU6050_DataTypeDef * MPU_DataStructure);
#endif
MPU6050_REG.h
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H
// 寄存器地址
#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B
#define PWR_MGMT_2 0x6C
#define WHO_AM_I 0x75
/*
* MPU6050数据结构体
*/
typedef struct
{
int16_t AccX;
int16_t AccY;
int16_t AccZ;
int16_t GyroX;
int16_t GyroY;
int16_t GyroZ;
} MPU6050_DataTypeDef;
#endif