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);
}
相关推荐
HQB20267 小时前
EG1198|屹晶微电子 120V 内置 MOS 异步降压 DC‑DC(高性价比)
嵌入式硬件·硬件工程
果壳science8 小时前
张祥前实验如何获取正电荷
笔记·科技·嵌入式硬件·硬件工程·制造
南山电子nscn8 小时前
中科芯CETC汽车电池管理系统BMS进口芯片国产型号替代指南
单片机·汽车·bms
沉淀的.晴天9 小时前
can总线
stm32
桀人9 小时前
认识AT24C02
stm32·单片机·嵌入式硬件
国科安芯10 小时前
星间激光通信终端CANFD控制网络的时延确定性与辐射可靠性
网络·嵌入式硬件·信息与通信·canfd·低轨卫星·商业航天·抗辐射
一直C11 小时前
嵌入式 Linux 驱动开发学习笔记:内核定时器、platform 总线与 I²C 驱动
linux·驱动开发·嵌入式硬件·学习·imx6ull
传感器与混合集成电路11 小时前
150MHz主频、8路DAC与6路PWM:JLH221360伺服控制SIP解析
嵌入式硬件
comyccccc11 小时前
STM32PID控制板设计全解析学习笔记
单片机·嵌入式硬件
碧海银沙音频科技研究院12 小时前
实现炬芯ATS308X的usb cdc 串口IAP 固件升级功能实现
人工智能·嵌入式硬件·深度学习·算法