1、串口使用发送和接收DMA(会出现运行一段时间后无法接收,只有重新初始化串口)
原因:产生了ORE中断却没有清除
STM32 的 UART 外设,触发 ORE 的唯一硬件条件是:
接收数据寄存器(RDR)或 RX FIFO 里已经有数据了,新的一帧数据又接收完成,旧数据还没被读走。此时旧数据会被新数据覆盖,硬件置位ORE标志,如果中断使能了就会触发错误中断。
解决:(我选择了方式二)
**方式一:**开启串口全局中断,并且该中断优先级低于串口DMA
初始化串口时:
// 使能串口错误中断:溢出错误、帧错误、噪声错误、奇偶错误
// LL_USART_EnableIT_ERROR(UART8);
// NVIC_SetPriority(UART8_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),4, 0));
// NVIC_EnableIRQ(UART8_IRQn);
中断里面处理ORE:
c
if(LL_USART_IsActiveFlag_ORE(UART8) == SET)
{
LL_USART_ClearFlag_ORE(UART8); //在重新配置串口接收时,总线上有数据,则会出现该错误
// 1. 关闭 DMA Stream 和串口 DMA 请求
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
while(LL_DMA_IsEnabledStream(DMA1, LL_DMA_STREAM_1));
UART8->CR3 &= ~USART_CR3_DMAR;
// 2. 重新配置 DMA 传输长度
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, MAX485LEN);
// 3. 重新使能 DMA Stream 和串口 DMA 请求
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
UART8->CR3 |= USART_CR3_DMAR;
// 4. 重置状态机和标志位
RS485.RxDMAFlag = 0x1;
RS485.DMA_RecTick = SYS_TICK;
RS485.Status = RS485_WAIT_REC_FIRST_BYTE;
}
方式二:
串口初始化时,直接关闭ORE功能

SET_BIT(UART8->CR3, USART_CR3_OVRDIS); //串口使能前调用才生效
c
//UART8_TX DMA
void DMA1_Stream2_IRQHandler(void)
{
if(LL_DMA_IsActiveFlag_TC2(DMA1)) //传输完成中断
{
LL_DMA_ClearFlag_TC2(DMA1); //清除传输完成中断
while(LL_USART_IsActiveFlag_TC(UART8) != SET); //等待串口发送完成
LL_USART_DisableDMAReq_TX(UART8); //失能串口DMA控制器
RS485_CTR_L; //RS485设为接收模式
RS485.TxDMAFlag = 1;
RS485.Status = RS485_WAIT_REC_FIRST_BYTE; //等待接收第一个字节
//...配置串口接收DMA
if(!RS485.RxDMAFlag & 0x1)
{
LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1); //关闭接收DMA传输
while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1)); //确保DMA1_Stream1可以被设置
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN); //设置传输长度
LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1); //使能接收DMA数据流通道
UART8->CR1 |= (1 << 2); //串口接收使能 防止运行久了后串口接收死机
UART8->CR3 |= (1 << 6); //使能串口DMA接收
RS485.RxDMAFlag = 0x1; //标记已开启接收DMA
RS485.DMA_RecTick = SYS_TICK;
}
//...
}
if(LL_DMA_IsActiveFlag_TE2(DMA1)) //传输错误中断
{
LL_DMA_ClearFlag_TE2(DMA1);
while(LL_USART_IsActiveFlag_TC(UART8) != SET); //等待串口DMA发送完成
LL_USART_DisableDMAReq_TX(UART8); //关闭串口DMA发送
RS485.Status = RS485_INIT;
}
}
void UART8_IRQHandler(void)
{
//...产生以下错误中断
if(LL_USART_IsActiveFlag_PE(UART8) == SET)
{
LL_USART_ClearFlag_PE(UART8);
}
if(LL_USART_IsActiveFlag_FE(UART8) == SET)
{
LL_USART_ClearFlag_FE(UART8);
}
if(LL_USART_IsActiveFlag_NE(UART8) == SET)
{
LL_USART_ClearFlag_NE(UART8);
}
if(LL_USART_IsActiveFlag_ORE(UART8) == SET)
{
LL_USART_ClearFlag_ORE(UART8); //在重新配置串口接收时,总线上有数据,则会出现该错误
// 1. 关闭 DMA Stream 和串口 DMA 请求
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
while(LL_DMA_IsEnabledStream(DMA1, LL_DMA_STREAM_1));
UART8->CR3 &= ~USART_CR3_DMAR;
// 2. 重新配置 DMA 传输长度
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, MAX485LEN);
// 3. 重新使能 DMA Stream 和串口 DMA 请求
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
UART8->CR3 |= USART_CR3_DMAR;
// 4. 重置状态机和标志位
RS485.RxDMAFlag = 0x1;
RS485.DMA_RecTick = SYS_TICK;
RS485.Status = RS485_WAIT_REC_FIRST_BYTE;
}
}
/* UART8 init function */
void MX_UART8_Init(void)
{
/* USER CODE BEGIN UART8_Init 0 */
/* USER CODE END UART8_Init 0 */
LL_USART_InitTypeDef UART_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART8;
PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_D2PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_UART8);
//...新增复位串口时钟
/* Force reset of UART clock */
LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_UART8);
/* Release reset of UART clock */
LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_UART8);
//
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOE);
/**UART8 GPIO Configuration
PE0 ------> UART8_RX
PE1 ------> UART8_TX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_0|LL_GPIO_PIN_1;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* UART8 DMA Init */
/* UART8_RX Init */
//接收DMA配置
LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1); //关闭DMA传输
while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1)); //确保DMA1_Stream1可以被设置
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_1, (uint32_t)&UART8->RDR); //串口接收寄存器
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_1, (uint32_t)&RS485.RxBuf); //内存地址
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN); //传输长度
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_UART8_RX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
/* UART8_TX Init */
//发送DMA配置
LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_2); //关闭DMA传输
while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_2)); //确保DMA1_Stream2可以被设置
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_2, (uint32_t)&UART8->TDR); //串口发送寄存器
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_2, (uint32_t)&RS485.TxBuf); //内存地址
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_2, (uint32_t)MAX485LEN); //传输长度
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_2, LL_DMAMUX1_REQ_UART8_TX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_2, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_2, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_STREAM_2, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_2, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_2, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_2, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_2, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_2);
/* USER CODE BEGIN UART8_Init 1 */
//接收DMA中断配置
LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_1); //开启DMA传输完成中断
LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_1); //开启DMA传输错误中断
// LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1); //使能数据流
// LL_USART_EnableDMAReq_RX(UART8); //开启串口dma接收
//发送DMA中断配置
LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_2); //开启DMA传输完成中断
LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_2); //开启DMA传输错误中断
/* USER CODE END UART8_Init 1 */
UART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
UART_InitStruct.BaudRate = 4800;
UART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
UART_InitStruct.StopBits = LL_USART_STOPBITS_1;
UART_InitStruct.Parity = LL_USART_PARITY_NONE;
UART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
UART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
UART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(UART8, &UART_InitStruct);
LL_USART_DisableFIFO(UART8);
LL_USART_SetTXFIFOThreshold(UART8, LL_USART_FIFOTHRESHOLD_1_8);
LL_USART_SetRXFIFOThreshold(UART8, LL_USART_FIFOTHRESHOLD_1_8);
LL_USART_ConfigAsyncMode(UART8);
/* USER CODE BEGIN WKUPType UART8 */
/* USER CODE END WKUPType UART8 */
// 使能串口错误中断:溢出错误、帧错误、噪声错误、奇偶错误
// LL_USART_EnableIT_ERROR(UART8);
// NVIC_SetPriority(UART8_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),4, 0));
// NVIC_EnableIRQ(UART8_IRQn);
// 【终极预防】开启溢出禁用,溢出时仅丢弃新数据,不置位ORE标志、不卡死接收状态机
SET_BIT(UART8->CR3, USART_CR3_OVRDIS); //串口使能前调用才生效
LL_USART_Enable(UART8);
/* Polling UART8 initialisation */
while((!(LL_USART_IsActiveFlag_TEACK(UART8))) || (!(LL_USART_IsActiveFlag_REACK(UART8))))
{
}
/* USER CODE BEGIN UART8_Init 2 */
/* USER CODE END UART8_Init 2 */
}
c
//...DMA长度判断中断逻辑
void TIM15_IRQHandler(void)
{
if(LL_TIM_IsActiveFlag_UPDATE(TIM15) != RESET)
{
//...UART8 DMA接收长度判断
if(RS485.Status == RS485_WAIT_REC_FIRST_BYTE)
{
if(DMA1_Stream1->NDTR != MAX485LEN)//接收到了第一个字节
{
RS485.DMA_RecTick = SYS_TICK;
RS485.Status = RS485_REC; //跳转至接收状态
}
}
if(RS485.Status == RS485_REC)
{
if(RS485.DMA_RecCount >= 2)
{
RS485.DMA_RecCount = 0;
}
RS485.DMA_RecLen[RS485.DMA_RecCount++] = DMA1_Stream1->NDTR; //获取DMA接收的长度
if(RS485.DMA_RecLen[0] != RS485.DMA_RecLen[1])
{
RS485.DMA_RecTick = SYS_TICK;
}
}
//...UART4 DMA接收长度判断
}
LL_TIM_ClearFlag_UPDATE(TIM15);
}
//...
void RS485Pro(void)
{
uint8_t RxFlag;
uint16_t rec_len,k,DMARecTimeout = DMA_REC_TIMEOUT;
switch(RS485.Status)
{
case RS485_INIT:
MX_UART8_Init();
#ifdef DEBUG_WIND
RS485.TxDMAFlag = 1;
RS485.Status = RS485_DEBUG;
#else
RS485_CTR_L;
RS485.Status = RS485_WAIT_REC_FIRST_BYTE; //状态变更为等待接收第一字节
RS485.Tick = SYS_TICK;
RS485.RxDMAFlag = 0;
RS485.TxDMAFlag = 1;
//...打开DMA接收
if((RS485.RxDMAFlag & 0x1) == 0)
{
LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1); //关闭接收DMA传输
while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1)); //确保DMA1_Stream1可以被设置
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN); //设置传输长度
LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1); //使能接收DMA数据流通道
UART8->CR3 |= (1 << 6); //使能串口DMA接收
RS485.RxDMAFlag = 0x1; //标记已开启接收DMA
RS485.DMA_RecTick = SYS_TICK;
}
#endif
//...
break;
case RS485_WAIT_SEND_OVER:
if(MsTickDiff(RS485.DMA_TransTick) > 5000) //连续5s都没有发送完成 重新初始化串口
RS485.Status = RS485_INIT;
break;
case RS485_WAIT_REC_FIRST_BYTE:
if(MsTickDiff(RS485.DMA_RecTick) > 2000) //连续10s都没有收到第一个字节 重新初始化串口
RS485.Status = RS485_INIT;
// if(MsTickDiff(RS485.DMA_RecTick) > 500) //连续10s都没有收到第一个字节 重新初始化串口
// RS485.Status = RS485_INIT;
break;
case RS485_REC:
if(MsTickDiff(RS485.DMA_RecTick) > DMARecTimeout) //5ms未收到数据
{
RS485.RxDMAFlag = 0;
LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1); //关闭接收DMA传输
while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1)); //确保DMA1_Stream1可以被设置
UART8->CR3 &=~ (1 << 6); //关闭串口DMA接收
rec_len = DMA1_Stream1->NDTR;
RS485.RxLen = MAX485LEN - rec_len; //获取实际接收长度
RxFlag = HandleRS485Data();
for(k = 0;k < MAX485LEN;k++) //清除接收buf
{
RS485.RxBuf[k] = 0;
}
RS485.RxLen = 0;
//...接收到不符合要求的数据 符合要求的数据则会在发送完成DMA中重新开启接收DMA
if(RxFlag == 0)
{
LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1); //关闭接收DMA传输
while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1)); //确保DMA1_Stream1可以被设置
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN); //设置传输长度
LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1); //使能接收DMA传输
UART8->CR3 |= (1 << 6); //使能串口DMA接收
RS485.RxDMAFlag = 0x1; //标记已开启接收DMA
RS485.DMA_RecTick = SYS_TICK;
RS485.Status = RS485_WAIT_REC_FIRST_BYTE;
}
//...
}
break;
case RS485_DEBUG:
break;
case RS485_INIT_REC:
break;
default:
RS485.Status = RS485_INIT;
break;
}
}
c
//开启一次DMA传输 DMA1_Stream2
//ndtr:数据传输量
void Uart8_DMA_Send(uint16_t ndtr)
{
if(RS485.TxDMAFlag == 0)
return;
if((UART8->ISR & 0x40) == 0)
return;
RS485.TxDMAFlag = 0;
RS485_CTR_H;// 485发送模式
// DelayMS(2);
LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_2); //关闭发送DMA传输
while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_2)); //确保DMA1_Stream2可以被设置
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_2, (uint32_t)ndtr); //设置传输长度
LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_2); //使能发送DMA传输
UART8->CR3 |= 1 << 7; //使能串口DMA控制器
RS485.DMA_TransTick = SYS_TICK; //更新DMA接收长度Tick
RS485.Status = RS485_WAIT_SEND_OVER;
}
//UART8_RX DMA
void DMA1_Stream1_IRQHandler(void)
{
if(LL_DMA_IsActiveFlag_TC1(DMA1)) //传输完成中断
{
LL_DMA_ClearFlag_TC1(DMA1); //清除传输完成中断
}
if(LL_DMA_IsActiveFlag_TE1(DMA1)) //传输错误中断
{
LL_DMA_ClearFlag_TE1(DMA1);
RS485.Status = RS485_INIT;
}
}
//UART8_TX DMA
void DMA1_Stream2_IRQHandler(void)
{
if(LL_DMA_IsActiveFlag_TC2(DMA1)) //传输完成中断
{
LL_DMA_ClearFlag_TC2(DMA1); //清除传输完成中断
while(LL_USART_IsActiveFlag_TC(UART8) != SET); //等待串口发送完成
LL_USART_DisableDMAReq_TX(UART8); //失能串口DMA控制器
RS485_CTR_L; //RS485设为接收模式
RS485.TxDMAFlag = 1;
RS485.Status = RS485_WAIT_REC_FIRST_BYTE; //等待接收第一个字节
//...配置串口接收DMA
if(!RS485.RxDMAFlag & 0x1)
{
LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1); //关闭接收DMA传输
while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1)); //确保DMA1_Stream1可以被设置
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN); //设置传输长度
LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1); //使能接收DMA数据流通道
UART8->CR1 |= (1 << 2); //串口接收使能 防止运行久了后串口接收死机
UART8->CR3 |= (1 << 6); //使能串口DMA接收
RS485.RxDMAFlag = 0x1; //标记已开启接收DMA
RS485.DMA_RecTick = SYS_TICK;
}
//...
}
if(LL_DMA_IsActiveFlag_TE2(DMA1)) //传输错误中断
{
LL_DMA_ClearFlag_TE2(DMA1);
while(LL_USART_IsActiveFlag_TC(UART8) != SET); //等待串口DMA发送完成
LL_USART_DisableDMAReq_TX(UART8); //关闭串口DMA发送
RS485.Status = RS485_INIT;
}
}