[APM32F0] G32M3101的UART驱动(轮询、中断和DMA均为接收不定长数据)

[复制链接]
24|2
口天土立口 发表于 2026-2-1 19:32 | 显示全部楼层 |阅读模式
本帖最后由 口天土立口 于 2026-2-1 19:42 编辑

#技术资源# #申请原创#
@21小跑堂

1. 外设介绍
79406697f37593338f.png
G32M3101支持有2个串口,最大通讯速率为2.5Mbit/s。实际在工业场景中,直接使用串口的TTL电平,通讯距离受限,因此串口搭配485电路是常用的使用场景,也因考虑到抗干扰问题,常用的通讯波特率一般为115200和9600。

2. 硬件
G32M3101K8T6

3. 驱动介绍
以下驱动分为三种方式:轮询、中断和DMA,三种方式均已实现接收不定长串口数据。

串口轮询通讯方式的初始化较为简单,初始化对应的IO和配置串口的通讯参数即可。
  1. /*
  2. * @brief       初始化
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void bsp_uart_init(void)
  10. {
  11.         DDL_GPIO_InitTypeDef GPIO_InitStruct;
  12.     DDL_USART_InitTypeDef USART_InitStruct;
  13.    
  14.     /* 使能时钟 */
  15.     DDL_SCU_Unlock();
  16.     DDL_SCU_EnableAHBPeripheralClock(DDL_SCU_AHB_PERIPHERAL_GPIOA);
  17.     DDL_SCU_EnableAPBPeripheralClock(DDL_SCU_APB_PERIPHERAL_UART0);
  18.     DDL_SCU_Lock();
  19.    
  20.     /* 配置GPIO */
  21.     /*  PA7 -> UART0_TX
  22.      *  PA8 -> UART0_RX
  23.      */
  24.     DDL_GPIO_StructInit(&GPIO_InitStruct);
  25.     GPIO_InitStruct.Pin        = DDL_GPIO_PIN_7 | DDL_GPIO_PIN_8;
  26.     GPIO_InitStruct.Mode       = DDL_GPIO_MODE_ALTERNATE;
  27.     GPIO_InitStruct.Drive      = DDL_GPIO_DRIVE_HIGH;
  28.     GPIO_InitStruct.OutputType = DDL_GPIO_OUTPUT_PUSHPULL;
  29.     GPIO_InitStruct.Pull       = DDL_GPIO_PULL_NO;
  30.     GPIO_InitStruct.Alternate  = DDL_GPIO_AF_0;
  31.     DDL_GPIO_LockKey(GPIOA, DDL_GPIO_LOCK_DISABLE);
  32.     DDL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  33.     DDL_GPIO_SetPinInputMode(GPIOA, DDL_GPIO_PIN_8, DDL_GPIO_INPUT_ENABLE);
  34.     DDL_GPIO_LockKey(GPIOA, DDL_GPIO_LOCK_ENABLE);
  35.      
  36.     /* USART */
  37.     DDL_USART_DeInit(UART_INS);
  38.     DDL_USART_StructInit(&USART_InitStruct);
  39.     USART_InitStruct.BaudRate            = 115200;
  40.     USART_InitStruct.DataWidth           = DDL_USART_DATAWIDTH_8B;
  41.     USART_InitStruct.StopBits            = DDL_USART_STOPBITS_1;
  42.     USART_InitStruct.Parity              = DDL_USART_PARITY_NONE ;
  43.     USART_InitStruct.TransferDirection   = DDL_USART_DIRECTION_TX_RX;
  44.     DDL_USART_Init(UART_INS, &USART_InitStruct);
  45.     DDL_USART_Enable(UART_INS);
  46. }

轮询方式的发送数据,基于发送为空标志位,不断的查询,发送为空即将新的数据写入串口数据寄存器即可,待全部数据都传入完成,等待发送完成标志置位,即表示所有数据发送完成。
  1. /*
  2. * @brief       发送数据
  3. *
  4. * @param       buf: 数据缓存
  5. *              buf_len: 缓存大小
  6. *
  7. * @retval      None
  8. *
  9. */
  10. void bsp_uart_send(uint8_t *buf, uint16_t buf_len)
  11. {
  12.     uint16_t i = 0;
  13.    
  14.     DDL_USART_ClearFlag_TC(UART_INS);
  15.     while (i < buf_len) {
  16.         while (DDL_USART_IsActiveFlag_TXE(UART_INS) == 0);
  17.         DDL_USART_TransmitData8(UART_INS, buf[i++]);
  18.     }
  19.     while (DDL_USART_IsActiveFlag_TC(UART_INS) == 0);
  20. }

接收数据通过检查接收非空标志判断,接收非空标志置位,则表示有新数据抵达数据寄存器,从数据寄存器取走数据后,标志位将自动清零。而判断一串完整的报文数据接收是否完整,是通过接收空闲标志确定,接收空闲标志为在一定数量的空闲位后依然无新数据到来,则置位接收空闲标志,同时也意味着连续的数据传输已经结束。空闲标志是实现不定长数据接收的关键。
  1. /*
  2. * @brief       接收数据
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void bsp_uart_recv(void)
  10. {
  11.     /* 接收非空 */
  12.     if (DDL_USART_IsActiveFlag_RXNE(UART_INS) != 0) {
  13.         if (rx_len < sizeof(rx_buf)) {
  14.             rx_buf[rx_len++] = DDL_USART_ReceiveData8(UART_INS);
  15.         }
  16.     }
  17.     /* 接收空闲则表示一帧报文结束 */
  18.     if (DDL_USART_IsActiveFlag_IDLE(UART_INS) != 0) {
  19.         rx_complete = 1;
  20.         DDL_USART_ClearFlag_IDLE(UART_INS);
  21.     }
  22. }

中断方式的串口通讯初始化,只比轮询方式多使能接收非空中断和空闲中断,同时需要使能对应的串口中断号。
注意:发送的相关中断需在要发送数据时,才能使能,否则立刻使能发送为空中断,将导致程序立刻进入中断。
  1. /*
  2. * @brief       初始化
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void bsp_uart_init(void)
  10. {
  11.         DDL_GPIO_InitTypeDef GPIO_InitStruct;
  12.     DDL_USART_InitTypeDef USART_InitStruct;
  13.    
  14.     /* 使能时钟 */
  15.     DDL_SCU_Unlock();
  16.     DDL_SCU_EnableAHBPeripheralClock(DDL_SCU_AHB_PERIPHERAL_GPIOA);
  17.     DDL_SCU_EnableAPBPeripheralClock(DDL_SCU_APB_PERIPHERAL_UART0);
  18.     DDL_SCU_Lock();
  19.    
  20.     /* 使能中断 */   
  21.     NVIC_SetPriority(UART0_IRQn, 0);
  22.     NVIC_EnableIRQ(UART0_IRQn);
  23.    
  24.     /* 配置GPIO */
  25.     /*  PA7 -> UART0_TX
  26.      *  PA8 -> UART0_RX
  27.      */
  28.     DDL_GPIO_StructInit(&GPIO_InitStruct);
  29.     GPIO_InitStruct.Pin        = DDL_GPIO_PIN_7 | DDL_GPIO_PIN_8;
  30.     GPIO_InitStruct.Mode       = DDL_GPIO_MODE_ALTERNATE;
  31.     GPIO_InitStruct.Drive      = DDL_GPIO_DRIVE_HIGH;
  32.     GPIO_InitStruct.OutputType = DDL_GPIO_OUTPUT_PUSHPULL;
  33.     GPIO_InitStruct.Pull       = DDL_GPIO_PULL_NO;
  34.     GPIO_InitStruct.Alternate  = DDL_GPIO_AF_0;
  35.     DDL_GPIO_LockKey(GPIOA, DDL_GPIO_LOCK_DISABLE);
  36.     DDL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  37.     DDL_GPIO_SetPinInputMode(GPIOA, DDL_GPIO_PIN_8, DDL_GPIO_INPUT_ENABLE);
  38.     DDL_GPIO_LockKey(GPIOA, DDL_GPIO_LOCK_ENABLE);
  39.      
  40.     /* USART */
  41.     DDL_USART_DeInit(UART_INS);
  42.     DDL_USART_StructInit(&USART_InitStruct);
  43.     USART_InitStruct.BaudRate            = 115200;
  44.     USART_InitStruct.DataWidth           = DDL_USART_DATAWIDTH_8B;
  45.     USART_InitStruct.StopBits            = DDL_USART_STOPBITS_1;
  46.     USART_InitStruct.Parity              = DDL_USART_PARITY_NONE ;
  47.     USART_InitStruct.TransferDirection   = DDL_USART_DIRECTION_TX_RX;
  48.     DDL_USART_Init(UART_INS, &USART_InitStruct);
  49.     /* 关闭发送为空中断 */
  50.     DDL_USART_DisableIT_TXE(UART_INS);
  51.     /* 使能接收非空中断 */
  52.     DDL_USART_EnableIT_RXNE(UART_INS);
  53.     /* 使能空闲中断 */
  54.     DDL_USART_EnableIT_IDLE(UART_INS);   
  55.     /* 使能串口 */
  56.     DDL_USART_Enable(UART_INS);
  57.     bsp_uart_reset_recv();
  58. }

中断方式的串口发送数据,初始化完发送的基本信息后,使能发送为空中断即可,后续工作将进入中断进行处理。
  1. /*
  2. * @brief       发送数据
  3. *
  4. * @param       buf: 数据缓存
  5. *              buf_len: 缓存大小
  6. *
  7. * @retval      None
  8. *
  9. */
  10. void bsp_uart_send(uint8_t *buf, uint16_t buf_len)
  11. {   
  12.     if ((buf != 0) && (buf_len > 0)) {
  13.         tx_buf = buf;
  14.         tx_index = 0;
  15.         tx_len = buf_len;
  16.         /* 开启发送为空中断 */
  17.         DDL_USART_EnableIT_TXE(UART_INS);
  18.     }
  19. }

串口中断处理函数内部,执行串口的发送和接收工作。发送为空中断,则将待发送数据不断传入数据寄存器,数据传输完毕后,立刻关闭发送为空中断;
串口中断接收数据与轮询方式的接收类似,只是工作改为在中断内部执行,无需主循环代码查询。
  1. /*
  2. * @brief       串口中断
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void UART0_IRQHandler(void)
  10. {
  11.     /* 发送为空 */
  12.     if (DDL_USART_IsActiveFlag_TXE(UART_INS) != 0) {
  13.         if (tx_index < tx_len) {
  14.             /* 发数据 */
  15.             DDL_USART_TransmitData8(UART_INS, tx_buf[tx_index++]);
  16.         } else {
  17.             /* 关闭发送为空中断 */
  18.             DDL_USART_DisableIT_TXE(UART_INS);
  19.         }
  20.     }
  21.     /* 接收非空 */
  22.     if (DDL_USART_IsActiveFlag_RXNE(UART_INS) != 0) {
  23.         if (rx_len < sizeof(rx_buf)) {
  24.             rx_buf[rx_len++] = DDL_USART_ReceiveData8(UART_INS);
  25.         }
  26.     }
  27.     /* 空闲 */
  28.     if (DDL_USART_IsActiveFlag_IDLE(UART_INS) != 0) {
  29.         /* 清除空闲状态 */
  30.         DDL_USART_ClearFlag_IDLE(UART_INS);
  31.         rx_complete = 1;
  32.     }
  33. }

串口的DMA通讯方式,初始化代码相对较为复杂,但此方式却是最为推荐的通讯方式。
初始化工作相对前面的两种方式,需要分别为串口的接收和发送都初始化一个DMA通道,因为串口接收的数据量没法确定,所以初始化串口接收的DMA传输数据量固定配置为接收的最大值,也因此串口不能单次接收超过此值的数据量;而串口的发送DMA配置无法确定传输的数据量和传输的存储地址,需等到发送数据时才能确认。
  1. /*
  2. * @brief       初始化
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void bsp_uart_init(void)
  10. {
  11.         DDL_GPIO_InitTypeDef GPIO_InitStruct;
  12.     DDL_USART_InitTypeDef USART_InitStruct;
  13.     DDL_DMA_InitTypeDef DMA_InitStruct;
  14.    
  15.     /* 使能时钟 */
  16.     DDL_SCU_Unlock();
  17.     DDL_SCU_EnableAHBPeripheralClock(DDL_SCU_AHB_PERIPHERAL_GPIOA);
  18.     DDL_SCU_EnableAHBPeripheralClock(DDL_SCU_AHB_PERIPHERAL_DMA1);
  19.     DDL_SCU_EnableAPBPeripheralClock(DDL_SCU_APB_PERIPHERAL_UART0);
  20.     DDL_SCU_Lock();
  21.    
  22.     /* 配置GPIO */
  23.     /*  PA7 -> UART0_TX
  24.      *  PA8 -> UART0_RX
  25.      */
  26.     DDL_GPIO_StructInit(&GPIO_InitStruct);
  27.     GPIO_InitStruct.Pin        = DDL_GPIO_PIN_7 | DDL_GPIO_PIN_8;
  28.     GPIO_InitStruct.Mode       = DDL_GPIO_MODE_ALTERNATE;
  29.     GPIO_InitStruct.Drive      = DDL_GPIO_DRIVE_HIGH;
  30.     GPIO_InitStruct.OutputType = DDL_GPIO_OUTPUT_PUSHPULL;
  31.     GPIO_InitStruct.Pull       = DDL_GPIO_PULL_NO;
  32.     GPIO_InitStruct.Alternate  = DDL_GPIO_AF_0;
  33.     DDL_GPIO_LockKey(GPIOA, DDL_GPIO_LOCK_DISABLE);
  34.     DDL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  35.     DDL_GPIO_SetPinInputMode(GPIOA, DDL_GPIO_PIN_8, DDL_GPIO_INPUT_ENABLE);
  36.     DDL_GPIO_LockKey(GPIOA, DDL_GPIO_LOCK_ENABLE);
  37.      
  38.     /* DMA */
  39.     DDL_DMA_StructInit(&DMA_InitStruct);
  40.     /* DMA -> USART_RX */
  41.     DMA_InitStruct.PeriphOrM2MSrcAddress  = (uint32_t)&UART_INS->DATA;
  42.     DMA_InitStruct.MemoryOrM2MDstAddress  = (uint32_t)rx_buf;
  43.     DMA_InitStruct.Direction              = DDL_DMA_DIRECTION_PERIPH_TO_MEMORY;
  44.     DMA_InitStruct.Mode                   = DDL_DMA_MODE_NORMAL;
  45.     DMA_InitStruct.PeriphOrM2MSrcIncMode  = DDL_DMA_PERIPH_NOINCREMENT;
  46.     DMA_InitStruct.MemoryOrM2MDstIncMode  = DDL_DMA_MEMORY_INCREMENT;
  47.     DMA_InitStruct.PeriphOrM2MSrcDataSize = DDL_DMA_PDATAALIGN_BYTE;
  48.     DMA_InitStruct.MemoryOrM2MDstDataSize = DDL_DMA_MDATAALIGN_BYTE;
  49.     DMA_InitStruct.NbData                 = sizeof(rx_buf);
  50.     DMA_InitStruct.Peripheral             = DDL_DMA_PERIPHERAL_5;
  51.     DMA_InitStruct.Priority               = DDL_DMA_PRIORITY_LOW;
  52.     DMA_InitStruct.FIFOMode               = DDL_DMA_FIFOMODE_DISABLE;
  53.     DMA_InitStruct.FIFOThreshold          = DDL_DMA_FIFOTHRESHOLD_1_4;
  54.     DMA_InitStruct.MemBurst               = DDL_DMA_MBURST_SINGLE;
  55.     DMA_InitStruct.PeriphBurst            = DDL_DMA_PBURST_SINGLE;
  56.     DDL_DMA_Init(DMA1, DDL_DMA_CHANNEL_1, &DMA_InitStruct);
  57.     /* 使能传输完成中断 */
  58.     DDL_DMA_EnableIT_TC(DMA1, DDL_DMA_CHANNEL_1);  
  59.     /* 使能中断 */   
  60.     NVIC_SetPriority(DMA_Channel1_IRQn, 0);
  61.     NVIC_EnableIRQ(DMA_Channel1_IRQn);
  62.    
  63.     /* DMA -> USART_TX */
  64.     DMA_InitStruct.PeriphOrM2MSrcAddress  = (uint32_t)&UART_INS->DATA;
  65.     DMA_InitStruct.MemoryOrM2MDstAddress  = 0;
  66.     DMA_InitStruct.Direction              = DDL_DMA_DIRECTION_MEMORY_TO_PERIPH;
  67.     DMA_InitStruct.Mode                   = DDL_DMA_MODE_NORMAL;
  68.     DMA_InitStruct.PeriphOrM2MSrcIncMode  = DDL_DMA_PERIPH_NOINCREMENT;
  69.     DMA_InitStruct.MemoryOrM2MDstIncMode  = DDL_DMA_MEMORY_INCREMENT;
  70.     DMA_InitStruct.PeriphOrM2MSrcDataSize = DDL_DMA_PDATAALIGN_BYTE;
  71.     DMA_InitStruct.MemoryOrM2MDstDataSize = DDL_DMA_MDATAALIGN_BYTE;
  72.     DMA_InitStruct.NbData                 = 0;
  73.     DMA_InitStruct.Peripheral             = DDL_DMA_PERIPHERAL_5;
  74.     DMA_InitStruct.Priority               = DDL_DMA_PRIORITY_LOW;
  75.     DMA_InitStruct.FIFOMode               = DDL_DMA_FIFOMODE_DISABLE;
  76.     DMA_InitStruct.FIFOThreshold          = DDL_DMA_FIFOTHRESHOLD_1_4;
  77.     DMA_InitStruct.MemBurst               = DDL_DMA_MBURST_SINGLE;
  78.     DMA_InitStruct.PeriphBurst            = DDL_DMA_PBURST_SINGLE;
  79.     DDL_DMA_Init(DMA1, DDL_DMA_CHANNEL_0, &DMA_InitStruct);
  80.    
  81.     /* USART */
  82.     DDL_USART_DeInit(UART_INS);
  83.     DDL_USART_StructInit(&USART_InitStruct);
  84.     USART_InitStruct.BaudRate            = 115200;
  85.     USART_InitStruct.DataWidth           = DDL_USART_DATAWIDTH_8B;
  86.     USART_InitStruct.StopBits            = DDL_USART_STOPBITS_1;
  87.     USART_InitStruct.Parity              = DDL_USART_PARITY_NONE ;
  88.     USART_InitStruct.TransferDirection   = DDL_USART_DIRECTION_TX_RX;
  89.     DDL_USART_Init(UART_INS, &USART_InitStruct);
  90.     /* 使能DMA收发 */
  91.     DDL_USART_EnableDMAReq_RX(UART_INS);
  92.     DDL_USART_EnableDMAReq_TX(UART_INS);
  93.     /* 使能空闲中断 */
  94.     DDL_USART_EnableIT_IDLE(UART_INS);
  95.     /* 使能发送完成中断 */
  96.     DDL_USART_EnableIT_TC(UART_INS);   
  97.     /* 使能中断 */  
  98.     NVIC_SetPriority(UART0_IRQn, 0);
  99.     NVIC_EnableIRQ(UART0_IRQn);
  100.     /* 使能串口 */
  101.     DDL_USART_Enable(UART_INS);
  102.     /* 接收数据 */
  103.     bsp_uart_recv();
  104. }

DMA的通讯方式,判断接收完整一串报文数据,也是通过空闲标志位确定,而确定具体已经接收了多少数据量,则是通过计算当前DMA接收通道的剩余可传输数据量来反推得出,处理完毕后,需重新开启接受的DMA通道,并配置为最大的传输数据量,以确保后续新来的数据量能完整接收。
DMA的发送数据,通过发送完成中断确定全部数据发送完毕。
  1. /*
  2. * @brief       串口中断
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void UART0_IRQHandler(void)
  10. {
  11.     uint16_t num = 0;
  12.    
  13.     /* 空闲 */
  14.     if (DDL_USART_IsActiveFlag_IDLE(UART_INS) != 0) {
  15.         /* 清除空闲状态 */
  16.         DDL_USART_ClearFlag_IDLE(UART_INS);
  17.         /* 计算已经传输的数据量 */
  18.         num = sizeof(rx_buf) - DDL_DMA_GetDataLength(DMA1, DDL_DMA_CHANNEL_1);
  19.         if (num != 0) {
  20.             rx_len = num;
  21.             rx_complete = 1;
  22.         }
  23.         bsp_uart_recv();
  24.     }
  25.     /* 发送完成中断 */
  26.     if (DDL_USART_IsActiveFlag_TC(UART_INS) != 0) {
  27.         /* 清除空闲状态 */
  28.         DDL_USART_ClearFlag_TC(UART_INS);
  29.     }
  30. }

另外,DMA接收的数据流完成中断也需要特意处理,处理方式与串口的空闲中断标志一致,即重新开启接收。
  1. /*
  2. * @brief       DMA中断
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void DMA_Channel0_IRQHandler(void)
  10. {
  11.     uint16_t num = 0;
  12.    
  13.     /* USART RX */
  14.     if (DDL_DMA_IsActiveFlag_TC1(DMA1) != 0) {
  15.         DDL_DMA_ClearFlag_TC1(DMA1);
  16.         /* 计算已经传输的数据量 */
  17.         num = sizeof(rx_buf) - DDL_DMA_GetDataLength(DMA1, DDL_DMA_CHANNEL_1);
  18.         if (num != 0) {
  19.             rx_len = num;
  20.             rx_complete = 1;
  21.         }
  22.         bsp_uart_recv();
  23.     }
  24. }

重新开启接收,需先关闭DMA的通道使能,重新配置为接收最大传输数据量后,再使能串口接收的DMA通道。
  1. /*
  2. * @brief       接收数据
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void bsp_uart_recv(void)
  10. {   
  11.     DDL_DMA_DisableChannel(DMA1, DDL_DMA_CHANNEL_1);
  12.     DDL_DMA_SetDataLength(DMA1, DDL_DMA_CHANNEL_1, sizeof(rx_buf));
  13.     DDL_DMA_EnableChannel(DMA1, DDL_DMA_CHANNEL_1);
  14. }

串口的DMA发送需每次开启发送前都配置新的数据存储地址和数据数量,因为这些信息在应用层中,不同的处理逻辑传入的值都是不同的。
  1. /*
  2. * @brief       发送数据
  3. *
  4. * @param       buf: 数据缓存
  5. *              buf_len: 缓存大小
  6. *
  7. * @retval      None
  8. *
  9. */
  10. void bsp_uart_send(uint8_t *buf, uint16_t buf_len)
  11. {   
  12.     if ((buf != 0) && (buf_len > 0)) {
  13.         DDL_DMA_DisableChannel(DMA1, DDL_DMA_CHANNEL_0);
  14.         DDL_DMA_SetDataLength(DMA1, DDL_DMA_CHANNEL_0, buf_len);
  15.         DDL_DMA_SetMemoryAddress(DMA1, DDL_DMA_CHANNEL_0, (uint32_t)buf);
  16.         DDL_USART_ClearFlag_TC(UART_INS);
  17.         DDL_DMA_EnableChannel(DMA1, DDL_DMA_CHANNEL_0);
  18.     }
  19. }

优劣势:
轮询方式:代码简单,利于理解,但需要及时查询状态,否则溢出,且发数据期间严重占用内核;
中断方式:代码简单,利于理解,收发数据期间可释放内核,但频繁中断,占用内核,且大量其他中断存在的场景可能影响数据收发的连续性;
DMA方式:收发数据不占用内核,且中断产生较少,收发数据能保证连续性,但初始化代码复杂;
从使用经验上推荐,最推荐的是DMA方式,而中断方式在大量中断的场景下要保证收发数据的绝对连续性,需要把串口的中断优先级提升到非常高的位置,至于轮询方式,只有非常简单的应用场景才能考虑使用,一般不建议使用。

4. 测试
本次测试的目的是将接收到的数据再发出来,如下代码为轮询方式,相当简洁,而中断和DMA方式,则连bsp_uart_recv()函数都不需要调用。
  1. // 外设初始化
  2. void bsp_init(void)
  3. {
  4.     bsp_uart_init();
  5. }
  1. // 应用任务
  2. void app_task(void)
  3. {
  4.     /* 接收数据 */
  5.     bsp_uart_recv();
  6.     /* 接收完成再发出来 */
  7.     if (bsp_uart_is_rx_complete() != 0) {
  8.         bsp_uart_send(bsp_uart_get_rx_buf(), bsp_uart_get_rx_len());
  9.         bsp_uart_reset_recv();
  10.     }
  11. }

如下图的测试结果:串口完整接收到数据后,只间隔不到100us就立刻发出。
77355697f394a1e047.png

5. 驱动移植说明
驱动的移植只需要修改bsp_uart_init函数即可,改为对应的IO和串口外设号,同时DMA的通道需对应手册修改。

6.详细代码
Poll.zip (2.49 MB, 下载次数: 0) Interrupt.zip (2.5 MB, 下载次数: 0)
DMA.zip (2.53 MB, 下载次数: 0)
转瞬回声 发表于 2026-2-6 19:14 | 显示全部楼层
是不是这种电机控制的项目,都要使用DMA来减少系统串口中断对电机性能的影响?
 楼主| 口天土立口 发表于 2026-2-6 19:32 | 显示全部楼层
转瞬回声 发表于 2026-2-6 19:14
是不是这种电机控制的项目,都要使用DMA来减少系统串口中断对电机性能的影响? ...

频繁的中断,在任何场景都占用内核的处理时间,根据效率需求考虑选择DMA方式
您需要登录后才可以回帖 登录 | 注册

本版积分规则

44

主题

93

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部
0