stm32——I2C协议
I2C协议概述
-
I2C协议详解
概述
I2C(Inter-Integrated Circuit)协议是由Philips(现NXP)开发的串行通信总线,用于连接微控制器和外围设备(如EEPROM、传感器、ADC/DAC等)。它是一种同步、半双工、多主多从的串行通信协议。
主要特点
- 两线制:仅使用两根线进行通信:
- SCL (Serial Clock Line):串行时钟线(由主设备生成)
- SDA (Serial Data Line):串行数据线(双向传输)
- 多主多从:总线上可连接多个主设备和多个从设备
- 半双工:同一时刻只能单向传输数据
- 地址寻址:每个从设备有唯一7位或10位地址
- 开漏输出:SCL和SDA均为开漏输出,需外接上拉电阻(4.7kΩ或10kΩ)
- 线与逻辑:支持多主设备仲裁
连接拓扑
核心原理
1. 起始条件(Start Condition)
- SCL高电平时,SDA从高→低跳变
- 主设备获取总线控制权
SDA: -----|________ | SCL: _____|
2. 停止条件(Stop Condition)
- SCL高电平时,SDA从低→高跳变
- 主设备释放总线
SDA: ________|----- | SCL: _______|
3. 数据传输
- 数据在SCL高电平期间保持稳定
- 数据变化发生在SCL低电平期间
- 8位数据,MSB(最高位)优先
SDA: --Bit7--Bit6--...--Bit0-- _ _ _ _ _ _ _ _ SCL: | |_| |_| |_| |_| |_| |_| |_| |
4. 应答机制(ACK/NACK)
每字节传输后的第9个时钟周期:
- ACK:接收方拉低SDA(成功接收)
- NACK:接收方保持SDA高电平(接收失败)
SDA: ... Bit0 | ACK/NACK _ SCL: ... Bit0|_|
5. 地址帧
- 起始条件后发送
- 7位地址 + 1位R/W方向位:
- R/W=0:主→从写操作
- R/W=1:主←从读操作
- 地址匹配的从设备回复ACK
通信时序
写操作时序(主→从)
主设备: [START] → [Addr+W] → [Data1] → [Data2] → [STOP] 从设备: [ACK] → [ACK] → [ACK]
读操作时序(主←从)
主设备: [START] → [Addr+R] → [NACK] → [STOP] 从设备: [ACK] → [Data1] → [Data2]
STM32实现(HAL库)
初始化流程
int main(void) { HAL_Init(); // 1. HAL库初始化 SystemClock_Config(); // 2. 系统时钟配置 MX_GPIO_Init(); // 3. GPIO初始化 MX_I2C1_Init(); // 4. I2C外设初始化 while(1) { // 数据传输操作 } }
GPIO配置示例
static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟 // 配置PB6(SCL), PB7(SDA) GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // 开漏复用 GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉电阻 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); }
I2C外设配置
static void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.Timing = 0x00702999; // 时序配置(根据时钟计算) hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress1 = 0; // 主模式设为0 hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } }
数据传输操作
// 写操作示例 #define SLAVE_ADDR 0x68 0x00, 0x01, 0x02}; HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDR, txData, 3, 100); // 读操作示例(先写寄存器地址,再读数据) uint8_t regAddr = 0x32; uint8_t rxData[2]; HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDR, ®Addr, 1, 100); HAL_I2C_Master_Receive(&hi2c1, SLAVE_ADDR, rxData, 2, 100); // 传输完成处理 } /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); // 1. HAL库初始化 /* Configure the system clock */ SystemClock_Config(); // 2. 系统时钟配置 /* Initialize all configured peripherals */ MX_GPIO_Init(); // 3. GPIO初始化 MX_I2C1_Init(); // 4. I2C外设初始化 /* Infinite loop */ while (1) { /* Add user code here */ } } GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOB_CLK_ENABLE(); // Enable GPIOB clock (assuming I2C1 uses PB6, PB7) /*Configure GPIO pins : PB6 (SCL), PB7 (SDA) */ GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; // SCL, SDA GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // Alternate Function Open-Drain (开漏) GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉电阻 (很重要!) GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // High speed for I2C GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; // Set alternate function to I2C1 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } /* I2C1 parameter configuration*/ hi2c1.Instance = I2C1; // 选择I2C1外设 // !!! 对于STM32G0/G4/H7等新的I2C,需要配置Timing Register !!! // 这个值需要根据你的系统时钟和I2C速度(如100kHz, 400kHz)通过STM32CubeIDE计算或手册查找 // 例如,对于48MHz系统时钟,100kHz I2C,可能是0x10901021UL hi2c1.Init.Timing = 0x00702999; // 这是一个示例值,请根据实际情况配置 // 如果是较旧的STM32F系列,可能需要配置以下参数而不是Timing // hi2c1.Init.ClockSpeed = 100000; // 100kHz // hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 占空比 // hi2c1.Init.OwnAddress1 = 0; // 主设备模式下为0 // hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 7位地址 // hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 禁用双地址 // hi2c1.Init.OwnAddress2 = 0; // hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 禁用通用呼叫 // hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 禁用时钟延长 hi2c1.Init.OwnAddress1 = 0; // 主设备模式下,自身地址通常不重要 hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 7位地址模式 hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 调用HAL库函数进行I2C初始化 if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); // 初始化失败,调用错误处理函数 } // 使能模拟降噪滤波器(可选,CubeIDE默认开启) if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) { Error_Handler(); } // 使能数字降噪滤波器(可选,CubeIDE默认开启) if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) { Error_Handler(); } } 0x00, 0x01, 0x02}; // 第一个字节通常是内部寄存器地址,后面是要写入的数据 HAL_StatusTypeDef status; // 1. 向从设备写入数据 // HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) status = HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_WRITE, txData_write, sizeof(txData_write), HAL_MAX_DELAY); if (status != HAL_OK) { // 错误处理,例如: if (status == HAL_ERROR) { // 总线错误,如ACK未收到 } else if (status == HAL_TIMEOUT) { // 超时 } Error_Handler(); } HAL_Delay(10); // 延时等待从设备处理 Error_Handler(); } HAL_Delay(10); Error_Handler(); } // 2. 紧接着,从该从设备读取数据 (HAL库内部会处理重复起始条件) status = HAL_I2C_Master_Receive(&hi2c1, SLAVE_ADDRESS, rxData_reg, sizeof(rxData_reg), HAL_MAX_DELAY); if (status != HAL_OK) { Error_Handler(); } HAL_Delay(10);
- 两线制:仅使用两根线进行通信:
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。