STM32-i²c
I²C 协议简介I²C(Inter-Integrated Circuit,集成电路间通信)是一种广泛应用于微控制器和各种外部设备之间的串行通信协议。它由 Philips(现 NXP)公司开发,具有多种优点,如简化的硬件连接、支持多个设备(最多 127 个设备共享同一总线)等。SCL(时钟线)和SDA(数据线)。支持一主多从和多主多从的模式,主机与从机配合实现数据的相互交互.
STM32 I²C 通信协议介绍及项目操作
1. I²C 协议简介
I²C(Inter-Integrated Circuit,集成电路间通信)是一种广泛应用于微控制器和各种外部设备之间的串行通信协议。它由 Philips(现 NXP)公司开发,具有多种优点,如简化的硬件连接、支持多个设备(最多 127 个设备共享同一总线)等。I²C 是一种双线制协议,通常使用两根信号线:SCL(时钟线) 和 SDA(数据线)。支持一主多从和多主多从的模式,主机与从机配合实现数据的相互交互.

通信原理:SCL时钟控制线,高电平,SDA数据有效,低电平SDA数据无效可进行电平切换,其次主机从从机读取数据时,主机将SDA控制权转交给从机,在产生起始信号时产生EV5事件,SR1寄存器中SB位置1表示起始信号已经发送,SDA在SCL高电平期间寻转变成低电平,主机向从机发送寻址地址同时发送传输数据方向时产生EV6以及EV8事件,SR1寄存器中ADDR位及TXE位被置1表示地址已经发送,等待从机应答,从机再向数据写入一个字节八位的数据,然后可以多次发送相同类型的数据,当主机不想读取数据时,发送非应答信号产生EV8_2事件,SR1寄存器TxE位以及BTF位被置1,进入停止位,主机读取数据结束,当主机向从机写入数据时,从机将SDA控制权转交给主机,主机发送起始信号时产生EV5事件SR1寄存器中的SB位置1,表示起始信号已经发送,,SDA线在SCL高电平期间从高电平切换低电平,同时主机发送从机寻址基地后EV6事件SR1寄存器ADDR位被置1,传输发行位写入数据,等待从机应答,从机应答后,主机向从机发送数据,SCL高低电平切换采样数据,当发送完一个字节数据八位后是否继续发送等待从机应答也就是SDA在SCL转向高低电平时的切换状态产生EV7事件SR1寄存器中的RxNE位被置1表示数据接受寄存器非空,,如果不需要在接收数据,就产生非应答信号,然后进入停止位结束数据传输..
1.1 I²C 协议特点
- 双线通信:I²C 仅使用两根线:时钟线(SCL)和数据线(SDA)。
- 支持多主机和从机:I²C 支持多主机设备和多个从设备在同一总线上进行通信。
- 多速率支持:I²C 支持不同的通信速率,如标准模式(100kbps)、快速模式(400kbps)和高速模式(3.4Mbps)。
- 地址模式:每个 I²C 从设备有一个唯一的 7 位或 10 位地址。
1.2 I²C 通信的基本操作
- 启动信号(START):由主设备发出,用于开始通信。
- 停止信号(STOP):由主设备发出,表示通信结束。
- 应答信号(ACK):每接收到一个字节,接收方发送一个 ACK 信号,表示接收到数据。如果没有接收到数据,则发送 NACK(非应答信号)。
1.3 I²C 数据帧结构
- 起始信号(Start Condition):主设备发送一个特定信号标志着通信开始。
- 设备地址(Device Address):7 位或 10 位的设备地址,主设备通过地址来选择特定的从设备。
- 数据字节(Data Byte):每个字节的数据在 SDA 线上传输。
- 应答位(ACK/NACK):每接收一个字节后,接收方发送一个 ACK 位,表示确认接收。
2. STM32 I²C 外设介绍
STM32 微控制器提供了 I²C 外设,通过它可以方便地与支持 I²C 协议的外部设备(如 EEPROM、传感器、LCD 显示模块等)进行通信。STM32 的 I²C 外设具有以下特点:
- 支持多个主机和从设备:允许多个主设备和从设备在同一总线上进行数据交换。
- 低功耗:适合嵌入式设备和传感器应用。
- 灵活性强:支持标准模式、快速模式和高速模式的通信。
3. STM32 I²C 通信配置
3.1 硬件连接
I²C 通信需要以下硬件连接:
- SDA(数据线):数据线,通过这根线进行数据交换。
- SCL(时钟线):时钟线,由主设备生成时钟信号。
- VCC 和 GND:为设备提供电源和地线。
假设使用 STM32F4 系列,常见的引脚连接为:
- SCL(PB6):STM32 的时钟信号线
- SDA(PB7):STM32 的数据线
3.2 CubeMX 配置
在 STM32CubeMX 中配置 I²C 外设:
- 启用 I²C 外设:选择要使用的 I²C 通道(如 I²C1 或 I²C2)。
- 引脚配置:将 SDA 和 SCL 引脚配置为 I²C 模式。
- 配置 I²C 参数:选择波特率、工作模式(主机或从机)和其他相关设置(如设备地址、时钟频率等)。
- 生成代码:生成 STM32 HAL 库代码。
3.3 代码实现
main.c
#include "main.h"
#include "stm32f4xx_hal.h"
// I²C 句柄声明
I2C_HandleTypeDef hi2c1;
// I²C 初始化函数
void I2C1_Init(void) {
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 设置 I²C 波特率 100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 设置占空比
hi2c1.Init.OwnAddress1 = 0; // 作为主机时地址为 0
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 7 位地址模式
hi2c1.Init.Ack = I2C_ACK_ENABLE; // 启用应答功能
hi2c1.Init.PeripheralMode = I2C_MODE_MASTER; // 主机模式
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 禁止时钟拉伸
if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
// 初始化失败,错误处理
Error_Handler();
}
}
// 向 I²C 从设备写入数据
void I2C_Write(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
HAL_I2C_Mem_Write(&hi2c1, devAddr, regAddr, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY);
}
// 从 I²C 从设备读取数据
uint8_t I2C_Read(uint8_t devAddr, uint8_t regAddr) {
uint8_t receivedData = 0;
HAL_I2C_Mem_Read(&hi2c1, devAddr, regAddr, I2C_MEMADD_SIZE_8BIT, &receivedData, 1, HAL_MAX_DELAY);
return receivedData;
}
// 主函数
int main(void) {
HAL_Init();
SystemClock_Config();
// 初始化 I²C1
I2C1_Init();
// 向 I²C 从设备写入数据
uint8_t deviceAddress = 0xA0; // 假设从设备地址为 0xA0
uint8_t registerAddress = 0x01; // 假设寄存器地址为 0x01
uint8_t dataToSend = 0x55; // 要发送的数据
I2C_Write(deviceAddress, registerAddress, dataToSend);
// 从 I²C 从设备读取数据
uint8_t receivedData = I2C_Read(deviceAddress, registerAddress);
// 无限循环
while (1) {
}
}
// 错误处理函数
void Error_Handler(void) {
while (1) {
}
}
// 系统时钟配置函数(STM32CubeMX 自动生成)
void SystemClock_Config(void) {
// 省略时钟配置代码,STM32CubeMX 自动生成
}
4. 代码解释
- I2C1_Init:该函数初始化 I²C1 外设,并配置主机模式、时钟频率(100kHz)和其他相关参数。
- I2C_Write:向 I²C 从设备写入数据。使用
HAL_I2C_Mem_Write函数通过指定的寄存器地址发送一个字节的数据。 - I2C_Read:从 I²C 从设备读取数据。使用
HAL_I2C_Mem_Read函数从指定的寄存器地址读取一个字节的数据。 - main:在主函数中,初始化 I²C,向从设备写入数据并读取数据。
5. 调试和测试
- 使用 I²C 兼容设备(如 EEPROM、传感器等)与 STM32 进行连接。
- 通过串口输出调试信息,确认 I²C 数据传输是否成功。
- 使用示波器或逻辑分析仪查看 I²C 总线信号,检查时钟线(SCL)和数据线(SDA)是否正常。
6. 常见问题和解决方案
- 设备地址不正确:确认从设备的地址是否正确,I²C 地址通常需要左移一位。
- 数据传输失败:检查时钟频率、引脚配置以及硬件连接是否正确。
- I²C 总线冲突:如果使用多个主设备,可能会出现总线冲突。确保主设备和从设备的电气连接正确。
7. 扩展应用
- 温度传感器:使用 I²C 与温度传感器(如 LM75 或 DS18B20)进行通信。
- EEPROM 存储:使用 I²C 通信与 EEPROM 存
项目:利用i²c协议读取或写入MPU6050芯片内寄存器数据
1初始化关于IC协议的GPIO解结构体,结构体成员配置开漏输出,引脚分别对应i²c的SCL和SDA引脚,输出速度任意
2配置开始信号,停止信号,发送数据应答,接受数据应答,发送数据,接受数据六个时序单元函数
3在主函数里面操作时序单元写入MPU6050内部地址,写入数据,或者从内部寄存器中读取数据显示在OLED屏上
iIC通信程序代码
#include "stm32f10x.h" // Device header
#include "IIC.h"
#include "Delay.h"
#define SCL_PIN GPIO_Pin_10
#define SDA_PIN GPIO_Pin_11
#define IIC_PORT GPIOB
void Myiic_W_SCL(uint8_t BitVAlue) //控制SCL函数
{
GPIO_WriteBit(GPIOB, SCL_PIN, (BitAction)BitVAlue);
delay_us(10);
}
void Myiic_W_SDA(uint8_t BitVAlue)//写入SDA函数
{
GPIO_WriteBit(GPIOB, SDA_PIN , (BitAction)BitVAlue);
delay_us(10);
}
uint8_t Myiic_R_SDA(void )//读取SDA函数
{
uint8_t DataValue=GPIO_ReadInputDataBit( IIC_PORT , SDA_PIN );
delay_us(10);
return DataValue;
}
void iic_Init()//初始化关于iIC的GPIO引脚
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE );
GPIO_InitTypeDef iicGPIO_Initsturt;
iicGPIO_Initsturt.GPIO_Mode = GPIO_Mode_Out_OD ;
iicGPIO_Initsturt.GPIO_Speed = GPIO_Speed_50MHz;
iicGPIO_Initsturt.GPIO_Pin = SCL_PIN| SDA_PIN ;
GPIO_Init(IIC_PORT , &iicGPIO_Initsturt);
GPIO_SetBits(IIC_PORT , SCL_PIN | SDA_PIN );
}
void Myiic_Start(void)//开始时序单元
{
Myiic_W_SCL(1);
Myiic_W_SDA(1);
Myiic_W_SDA(0);
Myiic_W_SCL(0);
}
void Myiic_Stop(void)//结束时序单元
{
Myiic_W_SDA(0);
Myiic_W_SCL(1);
Myiic_W_SDA(1);
}
void Myiic_SendData(uint8_t Byte)//发送数据单元
{
uint8_t i;
for(i=0;i<8;i++){
Myiic_W_SDA(Byte&(0x80>>i));
Myiic_W_SCL(1);
Myiic_W_SCL(0);
}
}
uint8_t Myiic_ReceiveData(void)//接受数据单元
{
uint8_t DATA=0x00;
uint8_t i;
Myiic_W_SDA(1);
for(i=0;i<8;i++){
Myiic_W_SCL(1);
if(Myiic_R_SDA()==1)
{
DATA|=(0x80>>i);
}
Myiic_W_SCL(0);
}
return DATA;
}
void SendACk(uint8_t DataValue)//发送数据时序应答
{
Myiic_W_SDA(DataValue);
Myiic_W_SCL(1);
Myiic_W_SCL(0);
}
uint8_t ReceiveAck(void)//接受数据时序应答
{
uint8_t DATA;
Myiic_W_SDA(1);
Myiic_W_SCL(1);
DATA= Myiic_R_SDA();
Myiic_W_SCL(0);
return DATA;
}
//mpu6050代码通信嵌套iIC程序
#include "mpu6050.h"
#include "stm32f10x.h" // Device header
#include "IIC.h"
#define MPU6050_ADDRESS 0xD0
uint8_t mpu6050_WriteReg(uint8_t Regadress,uint8_t Data)//对mpu6050传感器地址寄存器数据写入
{
Myiic_Start();
Myiic_SendData(MPU6050_ADDRESS );
ReceiveAck();
Myiic_SendData(Regadress );
ReceiveAck();
Myiic_SendData(Data);
ReceiveAck();
Myiic_Stop();
}
uint8_t mpu6050_redReg(uint8_t RegADress)//对mpu6050传感器地址寄存器的数据读取
{
uint8_t data;
Myiic_Start();
Myiic_SendData(MPU6050_ADDRESS );
ReceiveAck();
Myiic_SendData(RegADress);
ReceiveAck();
Myiic_Start();
Myiic_SendData(MPU6050_ADDRESS |0x01);
ReceiveAck();
data=Myiic_ReceiveData();
SendACk(1);
Myiic_Stop();
return data;
}
//主函数实现单片机对mpu6050芯片的数据读写
void mpu6050_Init()
{
iic_Init();
}#include "stm32f10x.h"
#ifndef _MPU6050_H_
#define _MPU6050_H_
uint8_t mpu6050_WriteReg(uint8_t Regadress,uint8_t Data);
uint8_t mpu6050_redReg(uint8_t RegADress);
void mpu6050_Init(void);
void mpu6050_Init(void);
#endif
#include "oled.h"
#include "adc.h"
#include "dc.h"
#include "usart.h"
#include "mpu6050.h"
#include "IIC.h"
int main(void){
mpu6050_Init();
OLED_Init();
mpu6050_WriteReg(0x6B,0x00);
mpu6050_WriteReg(0x19,0xAA);
uint8_t IDnumber=mpu6050_redReg(0x19);
OLED_ShowNum (1,1,IDnumber,3);//在OLED屏上显示寄存器的应答信号
while(1){
}
}
更多推荐



所有评论(0)