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

STM32F446 USART DMA模式仅单次发送 连续传输配置咨询

问题根因

代码存在4个核心配置/逻辑错误,导致第二次及之后的传输无法正常执行:

  • DMA状态标志清除逻辑错误:DMA1->HISR是只读状态寄存器,无法通过写操作清除标志位。循环中对HISR写0的操作完全无效,第一次传输完成后TCIF6标志会一直置位。
  • 标志位判断逻辑错误:等待传输完成的判断使用了逻辑与&&而非按位与&。原判断!(DMA1->HISR && DMA_HISR_TCIF6)等价于判断HISR寄存器整体是否为0,只要HISR任意位置位就会直接跳出等待,根本没有检测TCIF6位的真实状态。
  • DMA流配置时序错误:对DMA流写0禁用后,没有等待硬件将EN位清零就修改配置参数。STM32F4的DMA流关闭存在硬件延迟,EN位清零前写入的配置寄存器、地址寄存器、传输长度寄存器都会被硬件忽略。
  • USART DMA模式使能时序错误:USART_CR3_DMAT位在DMA启动之后才被设置,第一次传输后重复操作该位容易引发USART DMA请求时序异常。
修复方案

按照以下步骤调整代码即可实现轮询模式下的多次连续DMA发送,无需开启任何中断:

  1. 将USART的DMA发送模式(DMAT)使能放到USART初始化阶段,不要在主循环中重复配置。
  2. 禁用DMA流后增加轮询逻辑,等待硬件将EN位清零后再修改DMA配置。
  3. 修正DMA标志清除逻辑:对DMA1->HIFCR寄存器的对应清除位写1,完成Stream6的标志清除。
  4. 将所有标志位判断中的逻辑与&&替换为按位与&,确保正确检测对应位状态。
  5. 显式配置DMA的外设/内存数据宽度为8位,避免默认值异常导致的传输错误。
修正后的核心代码

初始化部分调整

// 原USART初始化代码后增加DMAT配置,不需要放到循环里
USART2->BRR = ( ( ( uartdiv / 16 ) << USART_BRR_DIV_Mantissa_Pos ) | ( ( uartdiv % 16 ) << USART_BRR_DIV_Fraction_Pos ) );
USART2->CR1 |= ( USART_CR1_RE | USART_CR1_TE | USART_CR1_UE );
USART2->CR3 |= USART_CR3_DMAT; // 初始化阶段一次性开启USART DMA发送模式

主循环调整

while ( 1 ) {
    // 禁用DMA流,等待硬件确认关闭后再修改配置
    DMA1_Stream6->CR &= ~(DMA_SxCR_EN);
    while( (DMA1_Stream6->CR & DMA_SxCR_EN) != 0 );

    // 清空DMA配置位
    DMA1_Stream6->CR &= ~(DMA_SxCR_CHSEL | DMA_SxCR_PL | DMA_SxCR_PSIZE | DMA_SxCR_MSIZE | DMA_SxCR_DIR | DMA_SxCR_MINC);
    // 配置DMA参数
    DMA1_Stream6->CR |=  (DMA_SxCR_CHSEL_2); // 选择通道4(对应USART2_TX)
    DMA1_Stream6->CR |=  (0x0 << DMA_SxCR_PL_Pos);    // 配置为低优先级
    DMA1_Stream6->CR |=  (DMA_SxCR_MINC);    // 内存地址自增
    DMA1_Stream6->CR |=  (DMA_SxCR_DIR_0);   // 传输方向:内存到外设
    DMA1_Stream6->CR |=  (0x0 << DMA_SxCR_PSIZE_Pos); // 外设数据宽度:8位
    DMA1_Stream6->CR |=  (0x0 << DMA_SxCR_MSIZE_Pos); // 内存数据宽度:8位

    // 配置地址和传输长度
    DMA1_Stream6->PAR = (uint32_t)&USART2->DR;
    DMA1_Stream6->M0AR = (uint32_t)TX_buffer;
    DMA1_Stream6->NDTR = 2; // 每次传输2字节

    // 清除所有残留DMA标志:写HIFCR寄存器清除,不要写只读的HISR
    DMA1->HIFCR |= (DMA_HIFCR_CTCIF6 | DMA_HIFCR_CHTIF6 | DMA_HIFCR_CTEIF6 | DMA_HIFCR_CDMEIF6 | DMA_HIFCR_CFEIF6);
    // 清除USART传输完成标志
    USART2->SR &= ~USART_SR_TC;

    // 启动DMA传输
    DMA1_Stream6->CR |= DMA_SxCR_EN;

    // 等待传输完成:使用按位与判断标志位
    while( (DMA1->HISR & DMA_HISR_TCIF6) == 0 );
    while( (USART2->SR & USART_SR_TC) == 0 );

    // 此处添加运算逻辑,可修改TX_buffer内容、调整下一次传输长度
    for(volatile uint32_t i=0; i<10000; i++); // 模拟运算延时,方便观察串口输出
}
补充说明
  • 该实现完全基于轮询,不需要配置任何DMA或USART中断,符合“计算-发送-计算-发送”的使用场景,不需要开启DMA循环模式。
  • 每次传输前可根据需求修改NDTR值(传输字节数)和M0AR值(发送缓冲区首地址),适配不同长度的数据发送。
  • 传输完成后不要关闭USART_CR3_DMAT位,保持初始化时的开启状态即可,不会影响正常运算逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 18:06:07