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);
}
相关推荐
linx29512 分钟前
单元五 · 对称认知·下:编译与链接
linux·c语言·开发语言·c++·嵌入式硬件
西城微科方案开发14 分钟前
雾化器方案开发:低功耗精密雾化技术的全场景落地
单片机·嵌入式硬件
Jason_zhao_MR19 分钟前
新国标下DTUFTU设计最优解——米尔基于全志T153核心板_排版优化
linux·单片机·架构·t113i·双处理器·mcu+linux·电力采集
老当益壮梁奶奶36 分钟前
51单片机入门总结(一):基本概念、最小系统、流水灯与按键实验(附完整代码)
笔记·stm32·单片机·嵌入式硬件·51单片机
M78佐菲37 分钟前
51单片机学习笔记(2)
linux·笔记·嵌入式硬件·学习·51单片机
ouynagda1 小时前
51 单片机 LED 流水灯 + 数码管动态扫描学习笔记(STC89C52)
笔记·单片机·学习
祖力551 小时前
51单片机(中断、定时器、PWM)
单片机·嵌入式硬件·51单片机
扎陵半导体1 小时前
电子系统中的隔离芯片:关键作用与性能指标
单片机·嵌入式硬件·国产芯片·隔离器·隔离芯片·光隔
jianqiang.xue2 小时前
ESP-IDF保姆级入门38|电源管理与锂电池充电全解:充电拓扑/电源路径控制/充放电保护/电量检测/低功耗策略,掌握嵌入式电池供电系统设计
单片机·嵌入式·esp32
abc light3 小时前
单片机(2)
stm32·单片机·嵌入式硬件