串口工作在DMA模式下有时接收异常

本文分析了STM32F205在使用DMA模式接收串口数据时遇到的问题,即有时能正常接收数据,有时则完全无法接收。通过对代码及现象的详细分析,找到了问题根源在于串口初始化后DMA未及时启用导致的数据溢出,并提供了两种解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 前言

客户反馈在使用STM32F205的串口工作在DMA模式时,有时能够接收数据,有时完全没有数据,但如果换成中断模式来接收又能100%正常收到数据。

2 复现现象

2.1 问题背景

与客户沟通,客户使用的是STM32F2标准库V1.1.0,串口波特率为1.408Mbps,不经过串口RS232,直接连接主CPU和从MCU(STM32F205)的串口发送和接收引脚,如下图所示:

图1

图1

2.2 尝试重现问题

由于客户使用的是主从架构,实验采用两块STM3220G-EVAL评估板来重现现象。一块用来不间断发送串口数据,另一块采用串口DMA进行接收,直接通过杜邦线连接串口PIN脚并共地,不使用评估板上的RS232收发器。接收端使用STM32F2xx_StdPeriph_Examples\ USART\USART_TwoBoards的示例代码。代码片段如下:

int main(void)
{
  ...
  USART_Config();
  ...
  while (1)
  {
    /* Clear Buffers */
    Fill_Buffer(RxBuffer, TXBUFFERSIZE);
    Fill_Buffer(CmdBuffer, 2);

    DMA_DeInit(USARTx_RX_DMA_STREAM);
    DMA_InitStructure.DMA_Channel = USARTx_RX_DMA_CHANNEL;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    /************* USART will receive the the transaction data ****************/
    /* Transaction data (length defined by CmdBuffer[1] variable) */       
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RxBuffer;
    DMA_InitStructure.DMA_BufferSize =10;// (uint16_t)CmdBuffer[1];
    DMA_InitStructure.DMA_Mode =DMA_Mode_Normal;//DMA_Mode_Circular;
    DMA_Init(USARTx_RX_DMA_STREAM, &DMA_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure); 
    /* Enable DMA Stream Transfer Complete interrupt */
    DMA_ITConfig(USARTx_RX_DMA_STREAM, DMA_IT_TE|DMA_IT_DME|DMA_IT_FE, ENABLE);

    /* Enable the DMA Stream */
    DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
    /* Enable the USART Rx DMA requests */
    USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);

//  USART_Cmd(USARTx, ENABLE);
//  while(SET ==USART_GetFlagStatus(USARTx,USART_FLAG_ORE))
//  {
//    Tmp =USART_ReceiveData(USARTx);
//  }


    while ((DMA_GetFlagStatus(USARTx_RX_DMA_STREAM, USARTx_RX_DMA_FLAG_TCIF) ==     RESET)
    {
    }   
    /* Clear all DMA Streams flags */
    DMA_ClearFlag(USARTx_RX_DMA_STREAM, USARTx_RX_DMA_FLAG_HTIF | USARTx_RX_DMA_FLAG_TCIF);

    /* Disable the DMA Stream */
    DMA_Cmd(USARTx_RX_DMA_STREAM, DISABLE);

    /* Disable the USART Rx DMA requests */
    USART_DMACmd(USARTx, USART_DMAReq_Rx, DISABLE);

//handle the RxBuffer data...
    //...
  }
}

USART_Config()函数如下:

static void USART_Config(void)
{
  USART_InitTypeDef USART_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Peripheral Clock Enable -------------------------------------------------*/
  /* Enable GPIO clock */
  RCC_AHB1PeriphClockCmd(USARTx_TX_GPIO_CLK | USARTx_RX_GPIO_CLK, ENABLE);

  /* Enable USART clock */
  USARTx_CLK_INIT(USARTx_CLK, ENABLE);

  /* Enable the DMA clock */
  RCC_AHB1PeriphClockCmd(USARTx_DMAx_CLK, ENABLE);

  /* USARTx GPIO configuration -----------------------------------------------*/ 
  /* Connect USART pins to AF7 */
  GPIO_PinAFConfig(USARTx_TX_GPIO_PORT, USARTx_TX_SOURCE, USARTx_TX_AF);
  GPIO_PinAFConfig(USARTx_RX_GPIO_PORT, USARTx_RX_SOURCE, USARTx_RX_AF);

  /* Configure USART Tx and Rx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

  GPIO_InitStructure.GPIO_Pin = USARTx_TX_PIN;
  GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = USARTx_RX_PIN;
  GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStructure);

  /* USARTx configuration ----------------------------------------------------*/
  /* Enable the USART OverSampling by 8 */
  USART_OverSampling8Cmd(USARTx, ENABLE); 

  USART_InitStructure.USART_BaudRate = 1408000;//3750000;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  /* When using Parity the word length must be configured to 9 bits */
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  USART_Init(USARTx, &USART_InitStructure);

  /* Configure DMA controller to manage USART TX and RX DMA request ----------*/  
  DMA_InitStructure.DMA_PeripheralBaseAddr = USARTx_DR_ADDRESS;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  /* Here only the unchanged parameters of the DMA initialization structure are
     configured. During the program operation, the DMA will be configured with 
     different parameters according to the operation phase */

  /* Enable USART */
  USART_Cmd(USARTx, ENABLE);
}

按如上代码,有如下现象:
1. 代码不做修改,若先启动接收端MCU再启动发送端MCU,接收端MCU的串口能正常接收。
2. 代码不做修改,若先启动发送端MCU再启动接收端MCU,接收端MCU的串口100%接收异常。
3. 修改发送端代码,改为发送端MCU串口每1秒间隔发送一次,则无论启动顺序如何,接收端MCU的串口都能正常。

3 程序分析

由上述代码可知,程序是先在USART_Config()函数函数内初始化串口并使能,然后再在接下来的main函数的while循环内初始化DMA并使能。这个是标准库内附带的示例代码,咋一看没什么问题,但仔细一想,针对用户的使用场景,这里就会产生一个问题:由于用户的主CPU有可能在从MCU启动之前就已经有可能启动,那么在这种情况下,在初始化完串口并使能后,到DMA使能之前这段时间内,若主CPU向从MCU发送串口数据,从MCU是否能正确接收?

从上述测试代码的结果2可以得出,若在串口初始化并使能后到DMA使能之前有数据来,MCU是不能接收的,经进一步调试,发现此时数据寄存器USART_DR存在一个数据,且在状态寄存器USART_SR中ORE值1,由此可知,串口的接收寄存器中已经接收到一个数据,但是后面的数据又来了,由于数据寄存器中的数据没有及时转移走(此时DMA还没有开启),从而导致后面的数据无法存入,所以产生了上溢错误(ORE),而一旦产生上溢错误后,就无法再触发DAM请求,及时之后再启动DMA也不行,无法触发DMA请求就无法将数据寄存器内的数据及时转移走,如此陷入死锁,这就是串口无法正常接收的原因。这时反观一下代码的结果3,这又将做如何解释?

仔细查看测试结果3,发现这个发送端每1秒间隔发送一次,那么就会存在这个一个概率,这个发送的时间点是否刚好在接收端MCU的串口初始化并使能和DMA使能之间还是之后,这个时间窗口非常关键,如果刚好在时间窗,那么串口接收就不正常,如果在这个时间窗之后,串口接收就能正常。由于测试代码采用的是1秒间隔,对于MCU来说这个是非常大的时间长度,还是很小概率能碰中这个时间窗的,因此,测试结果看起来是都能正常,实际严格来说,还是存在刚好碰中的可能。如果间隔时间缩短,那个碰中的几率就增大。由此看来,这也就能解释测试结果3了,也能解释客户提到的有时正常有时不正常的现象了。

4 问题处理

处理有两种方法,第一种方法是在使能DMA后,及时将数据寄存器DR中的数据清除掉,如下代码所示:

//...
/* Enable the DMA Stream */
    DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
    /* Enable the USART Rx DMA requests */
    USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);

  while(SET ==USART_GetFlagStatus(USARTx,USART_FLAG_ORE))
  {
    Tmp =USART_ReceiveData(USARTx);
  }
  //...

这里是使用读DR的方法来清除的,从参考手册中也提到使用这种方法来清除ORE标志:

图2

图2

第一种方法类似于一种纠错措施,下面介绍另一种推荐的方法,如下代码所示:

//...
/* Enable the DMA Stream */
    DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
    /* Enable the USART Rx DMA requests */
    USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);

  USART_Cmd(USARTx, ENABLE);
  //...

如上所示,可以先使能DMA再使能串口,这样就彻底不存在那个时间窗了,不管数据何时过来能能被DAM及时转走。这个是推荐的解决方法。

5 结论

标准库中的示例代码一般来说只供参考,对于大部分情况来说都是能正常工作的,但偶尔也会出现不适用的情况,此时更需要我们针对问题进行思考分析,进一步找到原因才能解决问题。对于串口使用DMA来接收的情况,这里建议一定要先使能DMA,最后使能串口,这样就能避免类似问题出现了。

本文转自:http://www.stmcu.org/module/forum/thread-606799-1-1.html

/*************************************************************************** * @file hw_timer.h * @brief This file contains all the macros for bsp_timer.c & bsp_timer.h. * **************************************************************************** * @attention * * Created on: 2025-07-18 * Author: YL Monitor Software group * **************************************************************************** * @description * * 定时器的硬件资源映射 * 定时器中断服务,用于高频执行事件任务的服务入口 * ****************************************************************************/ #ifndef __HW_TIMER_H_ #define __HW_TIMER_H_ #ifdef __cplusplus extern "C" { #endif /************************ Includes *************************/ /************************ Exportd types ********************/ /************************ Exportd constants ****************/ /************************ Exportd macros *******************/ /* HMI CAN timer 硬件资源映射*/ #define HW_TIMER TIM4 /* APB1 Timer Clock 72MHz */ #define HW_TIMER_RCC_CLK RCC_APB1Periph_TIM4 #define HW_TIMER_ClockCmd RCC_APB1PeriphClockCmd #define HW_TIMER_PRESCALER 7199 //((uint16_t) ((SystemCoreClock / 1000) - 1))//1ms #define HW_TIMER_PERIOD 9 #define HW_TIMER_IRQn TIM4_IRQn #define HW_TIMER_PreemptionPriority 0 #define HW_TIMER_IRQHandler TIM4_IRQHandler /************************ Exportd variables ****************/ /************************ Exportd Functions ****************/ /************************************************************ * @funName : BSP_HwInitHwTimer * @Input : NULL * @Description : 定时器硬件资源初始化 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025-07-18 *************************************************************/ extern void BSP_HwInitHwTimer(void); extern void Main_Loop_Process(void); #ifdef __cplusplus } #endif #endif /* __HW_TIMER_H_ */ /************************ End of file *************************/ /*************************************************************************** * @file bsp_timer.c * @brief **************************************************************************** * @attention * Created on: 2025-07-18 * Author: YL Monitor Software group **************************************************************************** * @description ****************************************************************************/ /************************ Includes *************************/ #include "main.h" /************************ Private macros *************************/ /************************ Private types **************************/ /*********************** Private constants ***********************/ /************************ Private variables **********************/ /************************ Functions ******************************/ // 发送数据缓冲区(50字节,用于50ms发送阶段) uint8_t g_send_buffer[50] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31}; // 状态变量 typedef enum { SEND_PHASE, // 发送阶段(50ms) DELAY_PHASE // 延时阶段(200ms) } WorkPhase; static WorkPhase current_phase = SEND_PHASE; // 当前阶段,初始为发送阶段 static uint8_t send_index = 0; // 发送字节索引(0-49) static uint8_t delay_counter = 0; // 延时计数器(0-199,对应200ms) static uint8_t timer1ms_flag = 0; // 1ms定时标志 /************************************************************ * @funName : BSP_HwInitHwTimer * @Input : NULL * @Description : 定时器硬件资源初始化 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025-07-18 *************************************************************/ void BSP_HwInitHwTimer(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; /* HMI CAN TIM clock enable */ HW_TIMER_ClockCmd(HW_TIMER_RCC_CLK, ENABLE); TIM_InternalClockConfig(HW_TIMER); /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = HW_TIMER_PERIOD; TIM_TimeBaseStructure.TIM_Prescaler = HW_TIMER_PRESCALER; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(HW_TIMER, &TIM_TimeBaseStructure); /* TIM IT enable */ TIM_ITConfig(HW_TIMER, TIM_IT_Update, ENABLE); /* Enable the HMI CAN TIM global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = HW_TIMER_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;//HW_TIMER_PreemptionPriority; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* HMI CAN TIM enable counter */ TIM_Cmd(HW_TIMER, ENABLE); } /************************************************************ * @funName : HW_TIMER_IRQHandler * @Input : NULL * @Description : TIM7 中断函数 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025/3/22 *************************************************************/ void HW_TIMER_IRQHandler(void) { if(TIM_GetITStatus(HW_TIMER, TIM_IT_Update)==SET) { /* 清除标志位 */ TIM_ClearITPendingBit(HW_TIMER, TIM_IT_Update); timer1ms_flag = 1; } } /** * @brief 主循环处理函数,根据当前阶段执行对应逻辑 */ void Main_Loop_Process(void) { if (timer1ms_flag) { // 检测到1ms定时 timer1ms_flag = 0; // 清零标志 switch (current_phase) { case SEND_PHASE: // 发送当前索引的字节 BSP_Send2Serial1(&g_send_buffer[send_index], 1); send_index++; // 发送完50字节后,切换到延时阶段 if (send_index >= 50) { send_index = 0; // 重置发送索引 current_phase = DELAY_PHASE; // 切换到延时阶段 delay_counter = 0; // 重置延时计数器 } break; case DELAY_PHASE: delay_counter++; // 延时200ms后,切换回发送阶段 if (delay_counter >= 200) { current_phase = SEND_PHASE; // 切换到发送阶段 delay_counter = 0; // 重置延时计数器 } break; default: current_phase = SEND_PHASE; // 异常状态重置 delay_counter=0; send_index=0; break; } } } /************************ End of file *************************/ /*************************************************************************** * @file hw_serial1.h * @brief This file contains all the macros for bsp_serial1.c & bsp_serial1.h. * **************************************************************************** * @attention * * Created on: 2025-03-18 * Author: YL Monitor Software group * **************************************************************************** * @description * * 串行通信1(与功率部件1内部通信)的硬件资源映射 * ****************************************************************************/ #ifndef __HW_SERIAL1_H_ #define __HW_SERIAL1_H_ #ifdef __cplusplus extern "C" { #endif /************************ Includes *************************/ #include "main.h" /************************ Exportd types ********************/ /************************ Exportd constants ****************/ /************************ Exportd macros *******************/ /* 串行通信1 波特率 */ #define SERIAL1_BAUD_RATE 115200 /* 串口1硬件资源映射 */ #define SERIAL1 USART3 #define SERIAL1_IRQn USART3_IRQn #define SERIAL1_PreemptionPriority 6 #define SERIAL1_IRQHandler USART3_IRQHandler /****************Tx****************/ #define SERIAL1_TX_PORT GPIOB #define SERIAL1_TX_PIN GPIO_Pin_10 /****************Rx****************/ #define SERIAL1_RX_PORT GPIOB #define SERIAL1_RX_PIN GPIO_Pin_11 #define SERIAL1_CLK RCC_APB1Periph_USART3 //USART1 #define SERIAL1_ClockCmd RCC_APB1PeriphClockCmd //USART1时钟 #define SERIAL1_DMA_CLK RCC_AHBPeriph_DMA1 //DMA1 #define SERIAL1_DMA_ClockCmd RCC_AHBPeriphClockCmd //DMA1时钟 /* RM0008 Table 78. Summary of DMA1 requests for each channel */ #define SERIAL1_DMATx_Channel DMA1_Channel2 #define SERIAL1_DMATx_IT_TC DMA1_IT_TC2 #define SERIAL1_DMATx_FLAG DMA1_FLAG_GL2|DMA1_FLAG_TC2|DMA1_FLAG_HT2|DMA1_FLAG_TE2 #define SERIAL1_DMATx_PreemptionPriority 7 #define SERIAL1_DMATx_IRQn DMA1_Channel2_IRQn #define SERIAL1_DMATx_IRQHandler DMA1_Channel2_IRQHandler /* RM0008 Table 78. Summary of DMA1 requests for each channel -- RM0008 * 表78每个通道的DMA1请求摘要*/ #define SERIAL1_DMARx_Channel DMA1_Channel3 #define SERIAL1_DMARx_IT_TC DMA1_IT_TC3 #define SERIAL1_DMARx_FLAG DMA1_FLAG_GL3|DMA1_FLAG_TC3|DMA1_FLAG_HT3|DMA1_FLAG_TE3 #define SERIAL1_DMARx_PreemptionPriority 8 #define SERIAL1_DMARx_IRQn DMA1_Channel3_IRQn #endif /************************ Exportd variables ****************/ /************************ Exportd Functions ****************/ #ifdef __cplusplus } #endif /* __HW_SERIAL1_H_ */ /********************************************************* * @file bsp_serial1.c * Created on: 2025-07-22 * Author: YL Monitor Software group *********************************************************/ /************************ Includes *************************/ #include "main.h" /************************ Private macros *************************/ /************************ Private types **************************/ /*********************** Private constants ***********************/ /************************ Functions ******************************/ /********************* DMA收发缓冲区大小定义*********************/ /* 串行通信 DMA发送缓冲区大小 */ #define SERIAL_TX_BUFF_SIZE 64 /* 串行通信 DMA接收缓冲区大小 */ #define SERIAL_RX_BUFF_SIZE 64 /************************ Private variables **********************/ /* 串行通信 DMA发送缓存 */ static uint8_t DMA_TxBuff[SERIAL_TX_BUFF_SIZE] = {0}; /* 串行通信 DMA接收缓存 */ static uint8_t DMA_RxBuff[SERIAL_RX_BUFF_SIZE] = {0}; /* 串行通信 接收数据队列 */ static QueueHandle_t queueRecvData = NULL; static SemaphoreHandle_t mutexSerialSend = NULL; /* 串行通信1 数据接收离线状态时间计数器 * 在硬件定时器中进行累加 * DMA接收中断中清零 * 当离线超过5ms,接收结束,或者设备离线,可以进行数据转发至上位机*/ /************************ Functions ******************************/ /**************************************** * @funName : BSP_HwInitSerial1 * @Description : 串行通信口1 硬件资源初始化 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025-07-22 ********************************************/ void BSP_HwInitSerial1(void) { GPIO_InitTypeDef GPIO_InitStructure ={0}; USART_InitTypeDef USART_InitStructure ={0}; NVIC_InitTypeDef NVIC_InitStructure ={0}; DMA_InitTypeDef DMA_InitStructure ={0}; /* 开启所有GPIO 时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //USART1开时钟 SERIAL1_ClockCmd(SERIAL1_CLK, ENABLE); // 使能DMA1时钟 SERIAL1_DMA_ClockCmd(SERIAL1_DMA_CLK,ENABLE); /****************Tx****************/ GPIO_InitStructure.GPIO_Pin = SERIAL1_TX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(SERIAL1_TX_PORT, &GPIO_InitStructure); /****************Rx****************/ GPIO_InitStructure.GPIO_Pin = SERIAL1_RX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(SERIAL1_RX_PORT, &GPIO_InitStructure); /* 配置USART1 */ USART_DeInit(SERIAL1); USART_InitStructure.USART_BaudRate = SERIAL1_BAUD_RATE; //波特率 USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(SERIAL1, &USART_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = SERIAL1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = SERIAL1_PreemptionPriority; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // USART_ITConfig(SERIAL1, USART_IT_IDLE, ENABLE); //启用的USART1中断 USART_ITConfig(SERIAL1, USART_IT_RXNE, ENABLE); /* DMA发送初始化 */ DMA_DeInit(SERIAL1_DMATx_Channel); DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&SERIAL1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DMA_TxBuff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = SERIAL_TX_BUFF_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA_Mode_Circular DMA_Mode_Normal DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(SERIAL1_DMATx_Channel, &DMA_InitStructure); /* DMA发送中断优先级设置 */ NVIC_InitStructure.NVIC_IRQChannel = SERIAL1_DMATx_IRQn ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = SERIAL1_DMATx_PreemptionPriority; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(SERIAL1_DMATx_Channel, DMA_IT_TC, ENABLE); USART_DMACmd(SERIAL1, USART_DMAReq_Tx, ENABLE); DMA_Cmd(SERIAL1_DMATx_Channel, DISABLE); /* DMA接收初始化 */ DMA_DeInit(SERIAL1_DMARx_Channel); DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&SERIAL1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DMA_RxBuff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = SERIAL_RX_BUFF_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA_Mode_Circular DMA_Mode_Normal DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(SERIAL1_DMARx_Channel, &DMA_InitStructure); /* DMA接收中断优先级设置 */ NVIC_InitStructure.NVIC_IRQChannel = SERIAL1_DMARx_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = SERIAL1_DMARx_PreemptionPriority; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(SERIAL1_DMARx_Channel, DMA_IT_TC, DISABLE); DMA_Cmd(SERIAL1_DMARx_Channel, ENABLE); USART_DMACmd(SERIAL1, USART_DMAReq_Rx, ENABLE); //启用USAR1T1外设 USART_Cmd(SERIAL1, ENABLE); } /********************************************* * @funName : BSP_SwInitSerial1 * @Description : 串行通信口1 软件资源初始化 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025-07-22 ************************************************/ void BSP_SwInitSerial1(void) { // 创建接收队列(存储uint8_t类型数据) if(NULL == queueRecvData) { queueRecvData = xQueueCreate(SERIAL_DATA_QUEUE_SIZE, sizeof(uint8_t)); /* 数据接收队列创建失败 */ if(NULL == queueRecvData) { } } // 创建发送互斥锁 if(NULL == mutexSerialSend) { mutexSerialSend = xSemaphoreCreateBinary(); // mutexSerialSend = xSemaphoreCreateMutex(); /* 数据发送互斥量创建失败 */ if(NULL == mutexSerialSend) { } else { /* 释放数据发送互斥量 */ xSemaphoreGive(mutexSerialSend); } } } /****************************************** * @funName : SERIAL1_IRQHandler * @Description : 串行通信口1 中断,用于处理空闲接收中断, * 获取DMA接收的数据,并缓存至数据队列 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025-07-22 **************************************************/ void SERIAL1_IRQHandler(void) { if(USART_GetITStatus(SERIAL1, USART_IT_RXNE) != RESET)//接收数据寄存器不空中断 { uint8_t byte = 0; uint16_t i = 0, rlen = 0; portBASE_TYPE xRecvWoken = pdFALSE; byte = USART_ReceiveData(SERIAL1); rlen = SERIAL_RX_BUFF_SIZE - DMA_GetCurrDataCounter(SERIAL1_DMARx_Channel); DMA_Cmd(SERIAL1_DMARx_Channel, DISABLE); DMA_ClearFlag(SERIAL1_DMATx_FLAG); if (rlen > 0 && queueRecvData != NULL) { // 将DMA接收缓冲区数据存入队列 for (i = 0; i < rlen; i++) { // xQueueSendFromISR(queueRecvData, &DMA_RxBuff[i], NULL); if(errQUEUE_FULL == xQueueSendFromISR(queueRecvData, &DMA_RxBuff[i], &xRecvWoken)) { break; } } } USART_ClearITPendingBit(SERIAL1, USART_IT_IDLE); DMA_SetCurrDataCounter(SERIAL1_DMARx_Channel, SERIAL_RX_BUFF_SIZE); DMA_Cmd(SERIAL1_DMARx_Channel, ENABLE); } /* 全部数据发送完成,产生该标记 */ if (USART_GetITStatus(SERIAL1, USART_IT_TC) != RESET) //检查USART3中断是否发生 { USART_ClearITPendingBit(SERIAL1, USART_IT_TC); //清除USART3的中断挂起位-传输完全中断 } } /***************************************** * @funName : SERIAL1_DMATx_IRQHandler * @Description : 串行通信口1 DMA 发送中断 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025-07-22 *******************************************/ void SERIAL1_DMATx_IRQHandler(void) { portBASE_TYPE xSendWoken = pdFALSE; if(RESET != DMA_GetITStatus(SERIAL1_DMATx_IT_TC)) { /* 关闭 DMA */ DMA_Cmd(SERIAL1_DMATx_Channel, DISABLE); /* 清除完成标记 */ DMA_ClearITPendingBit(SERIAL1_DMATx_IT_TC); /* 清除数据长度 */ DMA_SetCurrDataCounter(SERIAL1_DMATx_Channel, 0); USART_ITConfig(SERIAL1, USART_IT_TC, DISABLE); if(NULL != mutexSerialSend) { /* 释放 OS 数据发送权限 */ xSemaphoreGiveFromISR(mutexSerialSend, &xSendWoken); portYIELD_FROM_ISR(xSendWoken); } } } /********************************************** * @funName : BSP_Send2Serial1 * @Description : 串行通信口1 DMA发送启动 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025-07-22 **********************************************/ void BSP_Send2Serial1(uint8_t* sbuf, const uint16_t slen) { uint16_t len = slen; if(len == 0 ||NULL == sbuf) { return; } /******************************************** *获取OS 数据发送权限 * <DMA发送完成后,在串行通信中断服务中释放> *********************************************/ if(NULL != mutexSerialSend) { xSemaphoreTake(mutexSerialSend, portMAX_DELAY); } else { //检查是否设置了DMA1通道标志 DMA1_IT_TC2 while(!DMA_GetFlagStatus(SERIAL1_DMATx_IT_TC)); } /*发送DMA流的地址不自增-不使用*/ //SERIAL1_DMATx_Channel->CCR |= (1 << 10); /*设置接收和发送的内存地址*/ SERIAL1_DMATx_Channel->CMAR = (uint32_t)sbuf; //设置当前DMA1通道传输中的数据单元数 DMA_SetCurrDataCounter(SERIAL1_DMATx_Channel, len);//DMA1_Channel4 //启用DMA1通道4 DMA_Cmd(SERIAL1_DMATx_Channel, ENABLE); //启用USART1的DMA接口 USART DMA传输请求 USART_DMACmd(SERIAL1, USART_DMAReq_Tx, ENABLE); } /******************************************** * @funName : BSP_Recv4Serial1 * @Description : 从串口1接收数据队列获取数据 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025-07-22 **********************************************/ void BSP_Recv4Serial1(uint8_t * rbuf, uint16_t* rlen) { uint16_t iCnt = 0; *rlen = 0; if(NULL == queueRecvData || NULL == rbuf) { return; } for(iCnt = 0; iCnt < SERIAL_DATA_QUEUE_SIZE; ++iCnt){ if(pdFALSE == xQueueReceive(queueRecvData, &rbuf[iCnt], 0)){ break; } } *rlen = iCnt; } /************************ End of file *************************/ /*************************************************************************** * @file main.c * @attention * Created on: 2025-03-18 * Author: YL Monitor Software group **************************************************************************** * @description ****************************************************************************/ /************************ Includes ********************************/ #include "main.h" /************************ Private types ***************************/ /************************ Private constants ***********************/ /************************ Private macros **************************/ /* 任务优先级定义 */ #define TASK_SCI1_RECV_PRIORITY (tskIDLE_PRIORITY + 7) //任务sci1接收优先级 #define TASK_SCI1_RECV_STACK_SIZE 128 //任务sci1接收堆栈大小 /************************ Private variables ***********************/ /************************ Functions *******************************/ /****************************** * @funName : main * @Description : 系统主函数,完成软硬件初始化 * 产品业务相关任务创建,并启动任务调度 * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025/3/27 * ***********************************/ int main( void ) { /* 功率部件1的串行通信驱动-DMA1 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); /* Flash1的spi驱动 */ BSP_HwInitFlash1SPI(); //Flash1 存储器 SPI通信接口初始化 /* 功率部件1的串行通信驱动-DMA1 */ BSP_HwInitSerial1(); //串行通信口1 硬件资源初始化-DMA1 BSP_HwInitHwTimer(); //定时器硬件资源初始化 //进入临界区时,首先关闭中断,当退出所有嵌套的临界区后再使能中断 taskENTER_CRITICAL(); /*********** 初始化各个模块使用到的OS相关资源 *********************/ BSP_SwInitSerial1(); /********************* 创建产品业务相关任务 ***********************/ //功率部件1(DSP SCI)通信信息流接收处理任务-USART3-DMA1 /* 功率部件1信息流监听任务 */ //任务创建(任务功能) xTaskCreate((TaskFunction_t ) vTask_SCI1RecvPro, (char* ) "vTask_SCI1RecvPro", (uint16_t ) TASK_SCI1_RECV_STACK_SIZE, (void * ) NULL, (UBaseType_t ) TASK_SCI1_RECV_PRIORITY, //任务控制块-空 (TaskHandle_t* ) NULL); /* 退出任务 */ taskEXIT_CRITICAL(); /* Start the scheduler. */ vTaskStartScheduler(); return 0; } /************************ End of file *************************/ 用keil530版本软件编写STM32F105VCT6芯片单片机中,APB2=72MHZ,APB1=36HZ,针对以上程序,当S-TLINK未连接时,BSP_Send2Serial1发送出来的数据乱码,深度分析程序,从keil5软件和代码中给出有效的解决办法,用标准库写出需要修改的代码和注释
最新发布
08-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值