串口配置
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);
}