STM32的串口使用方式很多,其中IDLE+DMA方式可能是最受欢迎的一种。但是官方对这个组合的例程不太详细,网友分享的又多种多样,直接COPY后常常出现各种疑难杂症,故在此记录几个重点。
环境:
STM32CubeMX 6.1.2
1.8.4 HAL_Lib
STM32F103C8T6
KEIL 5.34
- 接收数据是单次的,就是说每次收完数据都会关掉DMA,需要手动再次开启,即执行一次HAL_UARTEx_ReceiveToIdle_DMA函数才能继续接收下一帧数据
- 发数据前需要先判断上一次发送是否完成,否则会丢失数据,可在中断里自定义标志(官方例程做法),也可直接判断huartx.gState是否等于HAL_UART_STATE_READY
- 收到数据后依次进入的函数:USARTx_IRQHandler->HAL_UARTEx_RxEventCallback
- 发送数据后依次进入的函数:DMA1_Channelx_IRQHandler->HAL_UART_TxHalfCpltCallback->DMA1_Channelx_IRQHandler->HAL_UART_TxCpltCallback
- 收数据产生错误需要在HAL_UART_ErrorCallback里有处理代码,而且一旦出错将不会再进入HAL_UARTEx_RxEventCallback,很多人把HAL_UARTEx_ReceiveToIdle_DMA放在HAL_UARTEx_RxEventCallback中,此时就不能再收到数据(有同学说那我把错误中断EIE关掉不就行了?理论上可行,但是HAL_UARTEx_ReceiveToIdle_DMA中会打开错误中断EIE,除非你把这里的也关了,一般不建议修改库代码)
以上要点中的一些疑问:
1.明明用了DMA,为何接收过程没有进入DMA中断?
答:DMA中断发生前先发生了串口中断,而串口中断程序USARTx_IRQHandler中调用了HAL_DMA_Abort,所以DMA1_Channelx_IRQHandler不再进入。串口中断就是比DMA中断快,找不到相关说明,但是实测就是这样,与中断优先级无关。
2.为什么发送过程没有进入USARTx_IRQHandler?
答:因为我关了串口发送中断。如果打开发送中断,那么发送后的执行流程是这样的USARTx_IRQHandler->DMA1_Channelx_IRQHandler->HAL_UART_TxHalfCpltCallback->DMA1_Channelx_IRQHandler->HAL_UART_TxCpltCallback
3.HAL_UART_TxHalfCpltCallba是什么鬼?
答:DMA发送完成一半的中断,我目前还没遇到需要用到这个函数的场景。可以手动关掉它,关掉后的发送流程即变为:
DMA1_Channelx_IRQHandler->HAL_UART_TxCpltCallback
一般把发送完成的处理代码放在HAL_UART_TxCpltCallback就行
4.使用这套方案需要开哪些中断?
答:DMA发送中断,串口IDLE中断,总中断。
DMA发送中断需要手动开,也可以MX里配置
串口IDLE中断在调用HAL_UARTEx_ReceiveToIdle_DMA时自动打开
总中断需要手动开
5.为什么DMA接收中断和UART发送中断不用开?
答:用不到