STM32F407 串口3 DMA收发-标准库

串口配置

cpp 复制代码
void RS422_USART3_Init(uint32_t baudrate)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    USART_InitTypeDef USART_InitStruct;
    DMA_InitTypeDef DMA_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
    
    // ========== 1. 使能时钟 ==========
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);      // GPIOB时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);     // USART3时钟(在APB1上)
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);       // DMA1时钟
    
    // ========== 2. 配置GPIO ==========
    // PB10 - USART3_TX,复用推挽输出
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    // PB11 - USART3_RX,复用浮空输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    // 引脚复用映射到AF7(USART3)
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);  // PB10 -> AF7
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);  // PB11 -> AF7
    
    // ========== 3. 配置USART3 ==========
    USART_InitStruct.USART_BaudRate = baudrate;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART3, &USART_InitStruct);
    
    // ========== 4. 配置DMA发送(内存→外设) ==========
    DMA_DeInit(DMA1_Stream3);
    DMA_InitStruct.DMA_Channel = DMA_Channel_4;                 // USART3_TX使用通道4
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR;
//    DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)NULL;        // 发送时动态设置
    DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)usart3_tx_buf;        // 发送时动态设置
    DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_InitStruct.DMA_BufferSize = 0;                          // 发送时动态设置
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                  // 发送用普通模式
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;
    DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA1_Stream3, &DMA_InitStruct);
    
    // 使能DMA发送完成中断
    DMA_ITConfig(DMA1_Stream3, DMA_IT_TC, ENABLE);
    
    // 配置DMA发送中断优先级
    NVIC_InitStruct.NVIC_IRQChannel = DMA1_Stream3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    // ========== 5. 配置DMA接收(外设→内存,循环模式) ==========
    DMA_DeInit(DMA1_Stream1);
    DMA_InitStruct.DMA_Channel = DMA_Channel_4;                 // USART3_RX使用通道4
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR;
    DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)usart3_rx_buf;
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStruct.DMA_BufferSize = USART3_RX_BUF_MAX;
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;                // 接收用循环模式
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;
    DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA1_Stream1, &DMA_InitStruct);
    
    // ========== 6. 使能USART3的DMA请求 ==========
    USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE);
    USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);
    
    // ========== 7. 配置USART3空闲中断 ==========
    USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);
    
    // 配置USART3中断优先级
    NVIC_InitStruct.NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    // ========== 8. 使能DMA和USART3 ==========
    DMA_Cmd(DMA1_Stream1, ENABLE);      // 使能DMA接收(一直开着)
    DMA_Cmd(DMA1_Stream3, DISABLE);     // DMA发送默认关闭,发送时再开启
    USART_Cmd(USART3, ENABLE);
    
    // 清除可能存在的空闲中断标志
    USART3->SR;
    USART3->DR;
}

DMA接收中断

cpp 复制代码
void DMA1_Stream3_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_Stream3, DMA_IT_TCIF3) != RESET)
    {
        DMA_ClearITPendingBit(DMA1_Stream3, DMA_IT_TCIF3);
        usart3_tx_busy_flag = false;
			
			        // 关闭DMA发送流
        DMA_Cmd(DMA1_Stream3, DISABLE);
        while (DMA_GetCmdStatus(DMA1_Stream3) != DISABLE);
    }
}

空闲中断

cpp 复制代码
void USART3_IRQHandler(void)
{
    // 检查空闲中断标志
    if (USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) {
        // 先读SR再读DR以清除IDLE标志[reference:7]
        USART3->SR;
        USART3->DR;
        
        // 关闭DMA接收,获取已接收的数据长度
        DMA_Cmd(DMA1_Stream1, DISABLE);
        while (DMA_GetCmdStatus(DMA1_Stream1) != DISABLE);
        
        // 计算接收到的数据长度
        // 长度 = 缓冲区大小 - DMA当前剩余计数
        usart3_rx_len = USART3_RX_BUF_MAX - DMA_GetCurrDataCounter(DMA1_Stream1);
        
			
		if(usart3_rx_len > 0U) // DMA接收数据放入队列中
        {
					for(uint16_t i = 0; i < usart3_rx_len; i++)
					{
							//示例:usart3_rx_buf[i];
						que_push(&uart3_que, usart3_rx_buf[i]);
					}
					
					//清空接收缓存
					memset(usart3_rx_buf, 0x00, sizeof(usart3_rx_buf));
        }
        
        // 重新设置DMA接收缓冲区(从头开始)
        DMA_SetCurrDataCounter(DMA1_Stream1, USART3_RX_BUF_MAX);
        DMA1_Stream1->M0AR = (uint32_t)usart3_rx_buf;
        
        // 重新使能DMA接收
        DMA_Cmd(DMA1_Stream1, ENABLE);
    }
}

串口DMA发送

cpp 复制代码
uint8_t USART3_DMA_Send(uint8_t *send_data, uint16_t len)
{	
	    if((len == 0U) || (len > USART3_TX_BUF_MAX) || (usart3_tx_busy_flag == true))
    {
        return 1U;
    }

    memcpy(usart3_tx_buf, send_data, len);

    DMA_Cmd(DMA1_Stream3, DISABLE);
    DMA_ClearFlag(DMA1_Stream3, DMA_FLAG_TCIF3);  //移动到DISABLE之后
    DMA_SetCurrDataCounter(DMA1_Stream3, len);
    DMA_Cmd(DMA1_Stream3, ENABLE);

    usart3_tx_busy_flag = true;
    return 0U;
}

串口禁用

复制代码
/*****************************************************************************
 * 函数名:USART3_Close
 * 功能:关闭USART3串口(停止收发,禁用DMA和中断)
 * 说明:调用后会等待当前发送完成,然后关闭外设、DMA流和中断。
 *       该函数用于任务切换前暂时关闭串口,以释放资源或避免干扰。
 *****************************************************************************/
void USART3_Disable(void)
{
    // 1. 等待当前DMA发送完成(防止强制关闭导致数据丢失)
    while (DMA_GetCmdStatus(DMA1_Stream3) != DISABLE) {
        // 若发送流正在运行,等待其自然结束
    }

    // 2. 禁用USART3外设
    USART_Cmd(USART3, DISABLE);

    // 3. 禁用DMA接收流(停止接收)
    DMA_Cmd(DMA1_Stream1, DISABLE);
    // 等待真正禁用
    while (DMA_GetCmdStatus(DMA1_Stream1) != DISABLE);

    // 4. 禁用DMA发送流(确保完全停止)
    DMA_Cmd(DMA1_Stream3, DISABLE);
    while (DMA_GetCmdStatus(DMA1_Stream3) != DISABLE);

    // 5. 禁用相关中断(可选,但强烈建议)
    NVIC_DisableIRQ(USART3_IRQn);
    NVIC_DisableIRQ(DMA1_Stream3_IRQn);
}

串口启用

cpp 复制代码
/*****************************************************************************
 * 函数名:USART3_Open
 * 功能:重新打开USART3串口,恢复所有功能
 * 说明:恢复外设、DMA接收和中断,并清空可能的历史标志。
 *       接收DMA会重新从缓冲区头部开始接收。
 *****************************************************************************/
void USART3_Enable(void)
{
    // 1. 重新配置DMA接收(重置缓冲区和计数器,确保从头开始)
    DMA_Cmd(DMA1_Stream1, DISABLE);
    while (DMA_GetCmdStatus(DMA1_Stream1) != DISABLE);

    // 恢复接收缓冲区基址和计数器
    DMA1_Stream1->M0AR = (uint32_t)usart3_rx_buf;
    DMA_SetCurrDataCounter(DMA1_Stream1, USART3_RX_BUF_MAX);

    // 清除接收DMA可能遗留的标志
    DMA_ClearFlag(DMA1_Stream1, DMA_FLAG_TCIF1 | DMA_FLAG_HTIF1 | DMA_FLAG_TEIF1 | DMA_FLAG_DMEIF1 | DMA_FLAG_FEIF1);
    DMA_Cmd(DMA1_Stream1, ENABLE);

    // 2. 清除USART3的残余中断标志(如空闲中断等)
    USART3->SR;   // 读SR
    USART3->DR;   // 读DR,组合操作清除IDLE标志

    // 3. 重新使能USART3的DMA请求(确保DMA通道有效)
    USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);
    USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE);

    // 4. 使能中断
    NVIC_EnableIRQ(USART3_IRQn);
    NVIC_EnableIRQ(DMA1_Stream3_IRQn);

    // 5. 最后使能USART3外设
    USART_Cmd(USART3, ENABLE);
}
相关推荐
greatriver0072 小时前
e2studio R7FA6M5 卡死 rtc 需要如何配置时钟
单片机·瑞萨单片机
恒锐丰小瑞2 小时前
率能SS6812T 40V/1A/双通道H桥驱动芯片,PWM电流控制与混合衰减模式(eTSSOP28),用于POS打印机/安防相机/机器人
单片机·嵌入式硬件·数码相机·机器人
深念Y3 小时前
JMS583 USB-NVMe 桥接芯片 LED 控制探索
嵌入式硬件·嵌入式·led·usb·电子·维修·硬盘盒
嵌入式阿蔡3 小时前
传感器_02:MPU6050 六轴 IMU 姿态入门——从一串看不懂的数,到一个不漂的角度
网络·stm32·单片机·嵌入式硬件·嵌入式实时数据库
恒锐丰科技韩生4 小时前
SS6951A|8.2‑36V 双通道 4A H 桥电机驱动 ETSSOP28 带散热焊盘 工业缝纫机打印机机器人专用
嵌入式硬件·硬件工程
恒锐丰小瑞4 小时前
率能SS6811H 38V/1.6A/双通道H桥驱动芯片,PWM控制接口与低功耗睡眠模式,用于舞台灯光/安防相机/机器人
单片机·嵌入式硬件·数码相机·机器人
怀民民民4 小时前
开篇:基于 Cortex-M7、OpenMV 与双频 GNSS 的智能派件 AGV 设计与实现
单片机·嵌入式硬件·gnss·openmv·智能车
爱吃米粥5 小时前
过流、过压、防反接、防浪涌于一体的输入电路
单片机·嵌入式硬件
江屿风5 小时前
【STM32基础篇】【嵌入式生态问题及历史追溯】流食般投喂
大数据·开发语言·人工智能·笔记·stm32·嵌入式硬件