【STM32】串口的阻塞、中断、DMA收发

发布于:2025-09-15 ⋅ 阅读:(15) ⋅ 点赞:(0)

目录

阻塞

串口阻塞发送

串口阻塞接收

中断

串口中断发送

串口中断接收

DMA

串口DMA发送

串口DMA接收

本文基于STM32F103CBT6单片机,分别使用串口的阻塞收发函数、中断收发函数、DMA收发函数进行测试

阻塞

串口阻塞发送

定义字符数组p

char p[10] = "hello\r\n";

调试观察其地址为0x20000004

在单片机内存中如下图

主循环中进行发送

  while (1)

  {

   HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);

   HAL_UART_Transmit(&huart1, p, 15, 100);

   HAL_Delay(500);

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }

发15个字节,起始地址是字符数组第1个元素的地址,亦即0x20000004

打印出来的内容就是0x2000004++15这个范围的内容

当发送量巨大而超时时间又比较短时,就会超时

这种阻塞式发送,MCU会一直处理串口的发送,后续的代码只能等待发送完毕才能执行

串口的波特率是115200,5ms大概发送五十多个字节

当发送超时后,将停止串口发送,并直接执行下一条语句

串口阻塞接收

定义一个存放接收内容的数组

char rx[10];

主循环内使用接收函数,超时时间为500ms

  while (1)

  {

   HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);

   HAL_UART_Receive(&huart1, rx, 10, 500);

   HAL_Delay(500);

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }

观察发现LED灯的闪烁频率由1hz变成0.5hz了,这是因为现在没有给串口发送任何东西,以至于串口一直超时,直到超时时间到达设定的500ms才会继续往下运行。

如果超时时间设置特别小,而需要接收的数据量又比较大的话,就永远也不会接收满了,就一直接收超时,跳转下一条语句

上位机不停地发送数据,但接收函数的超时时间仅设置1ms,就不能接收满预设的100字节,会一直超时,最多大概收到23个字节

如果超时时间较长,只要串口没收满设定的10字节数组,就会一直等待,后续代码无法执行

如果在等待期间有接收满10字节,就会立刻运行下面的代码,并将收到的内容存放至rx数组

中断

串口中断发送

勾选串口全局中断,生成代码

在主循环前面发送1024个字节的数据

uartStatus = HAL_UART_Transmit_IT(&huart1, p, 1024);

  /* USER CODE END 2 */

  /* Infinite loop */

  /* USER CODE BEGIN WHILE */

  while (1)

  {

   

   HAL_Delay(500);

   HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }

并在翻转电平和中断函数内打断点,程序会先进入中断服务函数,因为发送1024字节的时间小于500ms

如果把串口发送放入主循环,且delay时间设置50ms会造成串口busy

主循环每执行一次串口发送,except++

每成功发送完毕一次,sendok++

调试发现基本有一半的时间里是处于串口发送繁忙状态而没有发出

因为发送1024字节大概需要100ms,差不多两次循环才能让串口处于发送空闲状态

中断的好处是发送过程中MCU不会一直等待直到发送完毕,可以继续执行下面的语句

只要注意不要在还没有发送完毕就继续开始新的发送

串口中断接收

在主循环前调用串口中断接收

当串口助手发送超过10字节的数据时,进入串口接收中断,切换LED亮灭,并将设定的10字节内容存放至rx数组

继续发送,将不再进入接收中断

在主循环内反复调用串口中断接收函数

串口助手不发送任何内容,串口将处于接收繁忙状态

串口助手每50ms发送一次超过10字节的数据,串口接收状态将一直处于OK

由于调用接收中断和上位机下发内容的周期都是接近50ms,那么在调用语句后不超过50ms的时间内(下一次调用语句前),上位机就能及时地发送超过10字节的内容,触发接收中断

如果串口助手发送间隔小于50ms,会触发overrun错误

串口框架图表明引脚检测到电平信号后进行转换,给移位寄存器,再给RDR寄存器

结合overrun故障的描述就是当RDR寄存器不是空的时候,移位寄存器试图给RDR寄存器transfer数据——还没来得及拿走旧的数据,新的数据又来了

当出现这个故障时,RDR寄存器的值不会丢失,只更新移位寄存器的内容

在调用接收中断函数前将overrun标志位清除,这样即便串口助手发送频率高于接收函数调用频率,也可以接收到新发的数据

DMA

串口DMA发送

增加发送DMA

在主循环内每隔50ms进行一次DMA发送

第一次发送成功,串口助手显示接收到1024字节

由于发送1024字节所需时间大于50ms,在第二次进入循环时就会发生busy

预期发送次数大约是实际发送完毕次数的一半,因为发送1024字节的数据,大概要100ms,每2次才能成功发送一次

DMA的好处是发送过程中MCU不会一直等待直到发送完毕,可以继续执行下面的语句

只要注意不要在还没有发送完毕就继续开始新的发送

串口DMA接收

增加接收DMA

串口助手每10ms发送一次数据,偶尔会有busy出现,但不会像中断接收那样overrun

参考DMA的接收机制,当出现RXNE后,数据就被搬运走了,这个过程很快

串口的DMA和串口中断有点相似,但DMA更适合处理高速,大量的数据,DMA 硬件会在同一个总线时钟周期自动把 RDR 里的数据搬到内存缓冲区。读走后,RDR 立即空闲,RXNE 被自动清零。只要 DMA 通道使能且缓冲区未满,RDR 几乎“瞬时”被取走。过程无需 CPU 参与,搬运速度远快于中断响应。

中断则靠 CPU 在中断中数据搬运,慢了就丢。