You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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++);
}

验证要点

  1. 示波器观测:起始条件后应输出8位地址0xA4,从设备ACK后输出0xC0,最后出现停止条件。
  2. 检查DMA通道匹配:I2C1_TX对应DMA1通道6,I2C2_TX对应DMA1通道4,避免通道混淆。
  3. 打印内存地址确认:用printf("%p", tx_buf)验证地址在0x20000000~0x2000FFFF(STM32F1 SRAM区间)内。

内容的提问来源于stack exchange,提问作者Tomertom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 17:24:51