UART
硬件控制(RTS/CTS)
RTS (Require ToSend,发送请求)为输出信号,用于指示本设备准备好可接收数据,低电平有效,低电平说明本设备可以接收数据。
CTS (Clear ToSend,发送允许)为输入信号,用于判断是否可以向对方发送数据,低电平有效,低电平说明本设备可以向对方发送数据。
两者可在 HAL 中选择是否启用。(由硬件接口判断,并且注意在串口监视软件中设置相应的 Hardware Control
)由于是硬件接口,无需修改软件层面。
中断控制
涉及 USART 的中断事件表
Interrupt Event | Event Flag | Enable Control Bit |
---|---|---|
TransmitData Register Empty | TXE | TXEIE |
Clear To Send (CTS) flag | CTS | CTSIE |
Transmission Complete | TC | TCIE |
Received Data Ready to be Read | RXNE | RXNEIE |
Overrun Error Detected | ORE | RXNEIE |
Idle LineDetected | IDLE | IDLEIE |
Parity Error | PE | PEIE |
BreakFlag | LBD | LBDIE |
Noise Flag, Overrun error and Framing Error in multi buffer communication | NF or ORE or FE | EIE |
以上任意一个中断事件发生,都会进入 USARTX_IRQHandler
函数。这里需要用户自己判断标志位(具体可以参考 HAL 库做法)并处理相应中断,然后清除标志位
HAL
HAL 库实现 UART_HandleTypeDef
作为一个 UART
实例。
总的传诵方式分为三种:DMA、IT、阻塞传输。
阻塞传输:
HAL_UART_Transmit
与 HAL_UART_Receive
是阻塞传输。两个函数都会传入一个Size
,为接受/发送数据的长度。待传送字符串传递完后执行下一行代码。
阻塞传输注意超时设置,这个会由于硬件关系而有不同,数量级在千级别。
中断传输:
HAL_UART_Transmit_IT
与 HAL_UART_Receive_IT
是非阻塞传输。 HAL 开启中断传送,并将 UART
设置位忙碌状态(供其余函数查询是否传输完毕)。
中断传输会调用回调函数。HAL_UART_TxCpltCallback
、HAL_UART_TxHalfCpltCallback
、 HAL_UART_RxCpltCallback
、 HAL_UART_RxHalfCpltCallback
会在相应的中断处理函数执行到一定状态后调用。可以重新定义这些函数实现不同的功能。HAL_UART_IRQHandler
是 HAL 中断处理函数。在相应的中断处理函数中调用。修改一般在回调函数中进行。一般通过这个函数了解 UART
执行过程。
中断传输问题在于一定要收满字长才会停止,否则会一直等待。
DMA 传输 (见下一节)
总结:UART 一般通过这些函数调用:HAL_UART_Transmit
、 HAL_UART_Receive
、HAL_UART_Transmit_IT
、 HAL_UART_Receive_IT
。如果需要添加特定功能,则在回调函数中修改。必要时才修改中断处理函数。
DMA
The DMA Controller
DMA 单元有如下接口:
- 两个主端口(peripheral & memory port)连接到 AHB 总线上。一个和外设(slave peripheral)交互,另一个和内存控制器交互。
- 一个从端口(slave port),实现 CPU 对 DMA 单元控制。
- 拥有一系列独立和可编程通道(request sources),每个与一个外设的请求线连接。
- 每一个通道拥有优先级供调度使用。
- 数据可以双向流动。(
memory-to-peripheral
&peripheral-to-memory
)。
- STM32F1
从上图可以看出:
- DAM 中的一条 REQ LINE (Channel) 可以与多个外设相连。DMA 通道与芯片的外设在硬件上相连,通过手册确定需要的通道。同一时间内,同一通道上只有一个设备活动。
- 每个通道拥有优先级,决定对总线的使用权。
- STM32F4
每个 DMA 拥有8个独立的流(Stream);每个流拥有至多8个通道(Channel)。同一时间内,流中最多有一个通道活动。
这一系列 DMA 中,只有 DMA2 可以配置为内存之间传输。
DMA 工作阶段
- sample and arbitration phase.
- address computation phase.
- bus access phase.(More than one clock cycle, but there is a maximum duraion)
- final acknowledgement phase.
Handle Type
typedef struct {
DMA_Channel_TypeDef *Instance;/* Register base address*/
DMA_InitTypeDef Init;/* DMA communication parameters */
HAL_LockTypeDef Lock;/* DMA locking object*/
__IO HAL_DMA_StateTypeDef State;/* DMA transfer state*/
void* Parent;/* Parent object state*/
void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);
void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);
void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);
__IO uint32_tErrorCode;/* DMA Error code*/
} DMA_HandleTypeDef;
instance
:与 DMA 通道相关。Init
: DMA 配置参数设置。Parent
: 指向外设的Handler
。XferCpltCallback
、XferHalfCpltCallback
、XferErrorCallback
: DMA 回调函数。在中断处理函数中调用。
使用
- Polling 方式:
HAL_DMA_Start
开始 DMA 传输;HAL_DMA_PollForTransfer
查询 DMA 传输状态,传输完成(或者出错后)返回。 - 中断方式:
HAL_DMA_Start_IT
开始中断。HAL_DMA_RegisterCallback
设置回调函数。在回调函数中改变传输完成的标志位,供主函数判断是否完成。
注意在
NIVC
中配置生成 HAL 中断处理函数。
下面以 UART
开启方式说明:
HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t *)tmp, Size);
// 省略
/* Enable the DMA transfer for the receiver request by setting the DMAR bit
in the UART CR3 register */
SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);
先是用 HAL_DMA_Start_IT
设置 DMA
传输,包括双方的地址,传输数据的长度。然后设置 UART
相应位,开启 DMA
中断请求。每当数据接收完成后,就会发出一次 DMA
请求,传送一次数据。直到输送完指定长度为止。这样在每次传输时,都不用经过 CPU,减轻了 CPU 负担。
内存到内存的传输不需要设置请求,开始后直接传送。
CubeMX 配置
图中,Priority
表示该请求的优先级。下面 Increment Address
表示每次传送是否自增地址。(对于串口,其数据寄存器固定,无需自增;而内存需要自增地址,传送下一个数据。)Data Width
每次传送的数据长度。(串口是一个字节的数据寄存器 TDR
。)
F4 中内存传输函数
HAL_DMAEx_ChangeMemory
, 可以独立于 CPU 传输数据。
DMA 传输数据时,数据应该声明为全局变量或者
static
变量。对于函数中的局部变量,在函数返回时会释放掉,之后用作另外数据的存储地址。此时再用 DMA 传输反而会污染数据。
Data Width
根据具体的应用场景而定,会对传输速度有较大影响。例如在内存中传输整数数组时,采用字(Word)比字节(Byte) 快。
实验记录
- minicom
minicom 是在端口上敲入字符就传送,而不是敲下回车才一起传送。所以用 HAL_UART_Transmit
只会收到第一个字符。
修改方法
- 修改阻塞传输只需要修改对应函数即可。
HAL_UART_Transmit
与HAL_UART_Receive
。 - 修改中断传输需要将修改
HAL_UART_IRQHandler
、UART_Receive_IT
、UART_Transmit_IT
、UART_EndTransmit_IT
。(最终依据要求确定需要修改的函数。)参考这些函数中读取数据和使能/失能中断标志位的方法。HAL_UART_IRQHandler
如下所示:
中断标志位判断,再依据此调用相应的处理函数
UART_Receive_IT
、UART_Transmit_IT
、UART_EndTransmit_IT
。