STM32F407 包含 3个I2C控制器(I2C1/2/3),支持:
标准模式(100 kHz)
快速模式(400 kHz)
超快速模式(1 MHz,需特定配置)
通过DMA可实现高效数据传输,减少CPU轮询或中断开销。
I2C_HandleTypeDef hi2c1;
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000; // 400 kHz(快速模式)
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 占空比(仅快速模式有效)
hi2c1.Init.OwnAddress1 = 0x00; // 本机地址(主模式时可忽略)
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
// 发送DMA配置(以I2C1_TX为例)
DMA_HandleTypeDef hdma_i2c1_tx;
hdma_i2c1_tx.Instance = DMA1_Stream6; // 根据数据手册选择DMA流
hdma_i2c1_tx.Init.Channel = DMA_CHANNEL_1; // I2C1_TX对应通道1
hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址固定(I2C数据寄存器)
hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE; // 内存地址递增
hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 字节对齐
hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c1_tx.Init.Mode = DMA_NORMAL; // 单次传输模式
hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
HAL_DMA_Init(&hdma_i2c1_tx);
__HAL_LINKDMA(&hi2c1, hdmatx, hdma_i2c1_tx); // 绑定DMA到I2C句柄
// 接收DMA配置类似(方向改为PERIPH_TO_MEMORY)
uint8_t tx_buffer[] = {0x01, 0x02, 0x03};
uint16_t dev_address = 0xA0; // 从设备地址(左移1位后为0xA0 << 1 = 0x140)
HAL_I2C_Master_Transmit_DMA(&hi2c1, dev_address, tx_buffer, sizeof(tx_buffer));
主模式接收(Master Receive)
c
uint8_t rx_buffer[3];
HAL_I2C_Master_Receive_DMA(&hi2c1, dev_address, rx_buffer, sizeof(rx_buffer));
从模式接收(Slave Receive)
c
// 启用从模式地址监听
HAL_I2C_EnableListen_IT(&hi2c1);
// 启动DMA接收
HAL_I2C_Slave_Receive_DMA(&hi2c1, rx_buffer, sizeof(rx_buffer));
// DMA传输完成回调
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {
// 发送完成处理
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) {
// 接收完成处理
}
// 错误处理回调
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) {
uint32_t error = HAL_I2C_GetError(hi2c);
// 处理I2C错误(如总线错误、ACK失败等)
}
优点:
减少CPU干预
在长数据帧传输(如EEPROM读写)中,DMA自动搬运数据,CPU可执行其他任务或进入低功耗模式。
避免总线超时
I2C协议要求严格时序,DMA传输可避免因中断延迟导致的ACK超时或总线错误。
高吞吐量支持
适合连续传输场景(如传感器批量读取),尤其在高时钟频率(400 kHz+)下效率显著。
缺点:
配置复杂度高
DMA流/通道需严格匹配I2C外设(参考《STM32F4xx参考手册》表“DMA请求映射”)。
需处理内存对齐(如字节对齐)和缓冲区溢出风险。
调试难度大
DMA传输错误(如外设未就绪)可能引发总线锁死,需结合硬件调试器(如ST-Link)分析。
时序问题(如SCL/SDA信号干扰)难以通过纯软件排查。
硬件限制
STM32F407的DMA流资源有限,多外设共用时需优先级协调。
I2C从模式下的DMA支持较弱,需依赖中断辅助。
潜在的协议冲突
DMA传输无法自动处理I2C协议的重复起始条件(Repeated Start),需手动控制时序。
在多主模式下,总线仲裁失败可能导致DMA传输异常终止。
DMA通道与流的选择
I2C1_TX → DMA1 Stream6/Channel1
I2C1_RX → DMA1 Stream0/Channel1
I2C2/3的DMA映射需查阅手册,避免与其他外设冲突。
时钟配置
I2C时钟源必须使能(默认APB1时钟),超快速模式需配置I2C_TIMINGR寄存器。
缓冲区管理
使用__ALIGNED(4)确保缓冲区4字节对齐(防止DMA访问越界)。
循环DMA模式需配合双缓冲区策略,避免数据覆盖。
错误恢复机制
实现HAL_I2C_ErrorCallback,检测到错误时调用HAL_I2C_Init()重新初始化总线。
总线锁死时可尝试GPIO模拟I2C释放总线。
大数据块传输:如外部存储器(24LC256 EEPROM)读写。
实时性要求高的系统:如多传感器数据采集(避免CPU阻塞)。
低功耗应用:CPU在DMA传输期间进入Sleep模式。
DMA传输未启动
检查DMA流是否被其他外设占用。
确认HAL_I2C_Init()后调用了HAL_I2C_Start()(部分HAL版本需要)。
总线锁死(SCL拉低)
在错误回调中复位I2C外设:
c
HAL_I2C_DeInit(&hi2c1);
HAL_I2C_Init(&hi2c1);
数据错位或丢失
确保发送/接收缓冲区地址和长度正确,避免内存越界。
在DMA传输前后禁用中断(__disable_irq()/__enable_irq()),防止优先级反转。
通过合理配置,I2C+DMA可显著提升系统效率,但其复杂性要求开发者深入理解硬件特性与协议细节。建议在资源充足且对性能要求苛刻的场景中使用此方案。