STM32F10x中I2C DMA传输异常问题排查求助
STM32F10x I2C DMA传输异常排查与修复
核心问题定位
轮询模式正常说明I2C硬件连接、从设备通信逻辑无问题,异常根源集中在DMA配置细节、内存/外设地址合规性、I2C与DMA的联动逻辑这几个点。你遇到的地址后数据缺失、无关值输出,大概率是地址发送方式错误、DMA内存地址非法或传输参数不匹配导致的。
排查与修复方案
1. 修正I2C地址发送逻辑(核心误区)
你提到发送0x52地址后未输出0xC0,这里存在一个常见错误:I2C的7位地址不能通过DMA直接发送,必须使用I2C_Send7bitAddress函数触发地址匹配流程。如果把0x52放进DMA缓冲区,实际发送的是8位数据0x52,而非标准的7位地址+读写位组合(0x52作为7位写地址,实际发送的8位值是0x52 << 1 | 0 = 0xA4),这会导致从设备不响应ACK,后续数据传输直接异常。
2. 确保DMA内存地址有效性
调整内存地址后出现无关值0x68,说明原内存地址可能不在DMA可访问区域(STM32F1的DMA仅支持SRAM访问,Flash地址不可用),或未按字节对齐:
- 待发送数据需定义在SRAM中,避免用
const修饰(const变量默认存Flash),示例:uint8_t tx_buf[] = {0xC0}; // 全局变量默认存SRAM,无需额外修饰 - 内存地址必须是字节对齐(I2C为字节传输,F1 DMA要求地址对齐,否则会出现数据错位)。
3. 核对DMA传输参数
DMA的传输方向、缓冲区大小、地址增量模式必须与I2C传输匹配,以下是标准初始化代码:
// 以I2C1_TX对应DMA1通道6为例 DMA_InitTypeDef DMA_InitStructure; DMA_DeInit(DMA1_Channel6); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&I2C1->DR; // I2C数据寄存器地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tx_buf; // 确保是SRAM有效地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 内存→外设 DMA_InitStructure.DMA_BufferSize = 1; // 仅发送1字节数据0xC0 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址固定(I2C DR) DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 字节传输 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存字节对齐 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 单次传输,禁用循环模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel6, &DMA_InitStructure);
4. 开启I2C DMA请求
必须在I2C初始化中开启DMA发送请求,否则DMA不会触发传输:
I2C_InitTypeDef I2C_InitStructure; // ... 省略I2C基础配置代码 ... I2C_Init(I2C1, &I2C_InitStructure); I2C_DMACmd(I2C1, I2C_DMAReq_Tx, ENABLE); // 关键:开启I2C DMA发送请求 I2C_Cmd(I2C1, ENABLE);
5. 正确的传输流程
DMA启动前需完成I2C起始条件、地址发送,确保从设备进入接收状态后再触发DMA:
while(1) { // 等待总线空闲 while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // 发送起始条件 I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // 发送7位从设备地址(写方向) I2C_Send7bitAddress(I2C1, 0x52, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // 启动DMA传输 DMA_Cmd(DMA1_Channel6, ENABLE); // 等待DMA传输完成 while(!DMA_GetFlagStatus(DMA1_FLAG_TC6)); DMA_ClearFlag(DMA1_FLAG_TC6); DMA_Cmd(DMA1_Channel6, DISABLE); // 关闭DMA,下次传输重新开启 // 发送停止条件 I2C_GenerateSTOP(I2C1, ENABLE); // 延时循环 for(uint32_t i=0; i<1000000; i++); }
验证要点
- 示波器观测:起始条件后应输出8位地址
0xA4,从设备ACK后输出0xC0,最后出现停止条件。 - 检查DMA通道匹配:I2C1_TX对应DMA1通道6,I2C2_TX对应DMA1通道4,避免通道混淆。
- 打印内存地址确认:用
printf("%p", tx_buf)验证地址在0x20000000~0x2000FFFF(STM32F1 SRAM区间)内。
内容的提问来源于stack exchange,提问作者Tomertom
相关产品推荐
相关产品推荐

