【STM32】串口的阻塞、中断、DMA收发
目录
阻塞
串口阻塞发送
串口阻塞接收
中断
串口中断发送
串口中断接收
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 在中断中数据搬运,慢了就丢。