UART

外设特性

SPV1x有2个UART模块,每个模块均支持以下功能特性。

  1. 支持5~8bit数据位宽

  2. 支持奇偶校验和0/1校验

  3. 支持1bit/2bit停止位

  4. 支持自动波特率检测

  5. 支持Timeout功能

  6. 支持Loopback功能

  7. 支持帧错误检测

  8. 接收和发送以不同的波特率运行

  9. 接收和发送各自有8级深度的FIFO

外设使用

1. UART时钟和复位配置

  1. 配置CMU_UARTnCLK,选择UART时钟源。

  2. 配置CMU_CLKEN0.UARTn,开启UART时钟。

  3. 配置RMU_RSTEN0.UARTn,释放UART模块。

2. 数据位宽选择

配置UART_CFG.DATA_WIDTH,选择数据位宽。

3. 校验功能配置

  1. 配置UART_CFG.PARITY_EN,开启或关闭校验功能。

  2. 如果开启了校验功能,配置UART_CFG.PARITY_SEL选择校验方式。用户可以置位UART_IE.PARITY_ERR,使能校验错误中断功能。

  3. 如果关闭了校验功能,校验方式的选择可以忽略。

4. 停止位选择

配置UART_CFG.STOP_BIT选择1bit/2bit停止位。

5. 波特率配置

UART支持收发以不同的波特率进行。UART_DIV分为高16bit(RX_DIV)和低16bit(TX_DIV),RX_DIV用于控制接收的波特率,TX_DIV用于控制发送的波特率。 UART的波特率计算公式为:DIV = CLOCK/BAUD - 1。 DIV为波特率分频值(RX_DIV或TX_DIV),CLOCK为UART时钟源的频率,BAUD为目标波特率。

6. Timeout功能配置

Timeout功能用于需要进行数据流断帧的场景。在数据接收过程中,上一字节接收完成后,下一字节的起始位未在指定的时间内开始传输,就会触发Timeout。Timeout由UART_CFG.TO_DIV进行配置,其单位为接收波特率的比特宽度,可配置的范围为1~64bit。 Timeout功能总是开启,如果不需要使用Timeout功能,软件只要关闭Timeout中断(UART_IE.TIME_OUT)并忽略其Pending标志(UART_PD.TIME_OUT)即可。

7. Loopback功能配置

配置UART_CFG.LOOPBACK_EN可开启和关闭Loopback功能。Loopback功能开启后,UART的RX会在内部接到TX。 Loopback功能用于快速验证UART的模块的收发配置是否正常。

8. 帧错误检测功能

在数据接收过程中,如果在停止位阶段采集到低电平,就会触发帧错误。 帧错误检测功能总是开启,如果不需要使用帧错误检测功能,软件只要关闭帧错误检测中断(UART_IE.FRAME_ERR)并忽略其Pending标志(UART_PD. FRAME_ERR)即可。

9. FIFO相关配置

UART的发送和接收都有8级FIFO。合理的使用FIFO资源,可以降低软件的复杂度,提升软件的性能。FIFO主要用到的功能有2点:

  1. FIFO的阈值功能:UART_CFG.RX_FIFO_THR/UART_CFG.TX_FIFO_THR用于配置FIFO的阈值。在UART的中断收发场景中,如果每个字节的收发都进一次中断,会导致中断开销过大,通过使用阈值中断,可以在FIFO中有足够数据的时候,才进中断处理,并且能在一次中断中读取多个接收的数据或写入多个要发送的数据,以减少进出中断时的上下文切换开销。

  2. FIFO复位功能:UART_CTL.RX_FIFO_RST/UART_CTL.TX_FIFO_RST用于控制FIFO的复位,使用复位功能,可快速清除FIFO中的数据。

10. UART使能

UART的接收和发送功能可以独立工作,因此接收和发送有单独的使能控制位。 UART_CTL.RX_EN控制接收功能的使能,UART_CTL.TX_EN控制发送功能的使能。

11. 中断功能配置

如果要使用UART的中断功能,需要在UART_IE中配置对应的中断使能。进入中断后,通过判断UART_PD中的Pending位,来确认中断的原因。 为了让CPU接收UART的中断请求,还需要配置CLIC和全局中断使能。

12. 自动波特率检测功能配置

如果使用自动波特率检测功能,则按以下步骤操作:

  1. 配置UART_CTL.ABD,开启自动波特率检测。

  2. 如果不使用中断,则等待UART_CTL.ABD变回0。UART完成自动波特率检测后,硬件会将UART_CTL.ABD清零。

  3. 如果使用中断,则开启UART_IE.ABD,并配置CLIC和全局中断使能。自动波特率检测完成后,将触发UART中断。

  4. 自动波特率检测完成后,读取UART_RX_CNT中的测量值。如果测量的是单比特的位宽,则UART_RX_CNT的值可以作为RX_DIV或TX_DIV直接写入UART_DIV寄存器;如果测量的是多比特位宽(比如N比特),则RX_DIV或TX_DIV=(UART_RX_CNT+1)/N-1。

注意事项

  1. 当使用自动波特率检测功能时,测量单比特的位宽往往导致计算出来的波特率误差较大,在高波特率下,误差尤为明显。建议通过测量多个比特的位宽,以减少计算出来的波特率的误差。此外,在自动波特率检测完成之前,先不要使能UART的 接收功能,避免收到无用的数据。

  2. 向TX_FIFO写入数据时,需要确认TX_FIFO的剩余空间,避免写入过量的数据,导致TX_FIFO上溢。同理,从RX_FIFO读取数据时,需要确认RX_FIFO中有效数据的数量,避免过量读取数据,导致RX_FIFO下溢。

  3. 如果UART在使用过程中,会动态开启和关闭UART内部中断,则在UART中断函数中,需要同时判断UART_IEUART_PD,确认中断使能且相应Pending bit置位,再去执行对应的处理逻辑。处理完对应的中断后,软件需要显式的对UART_PD中对应的Pending bit写1,以清除其Pending状态。

  4. 写入TX_FIFO中的数据需要一定的时间才能从TX引脚发送完成(通过CPU写入和DMA写入都如此)。当UART_STA.TX_BUSY为0,且UART_STA.TX_FIFO_EMPTY为1时,就可以确定写入的数据已全部传输完成。

  5. 如果要配合DMA进行UART数据收发,配置UART_CTL.RX_DMA_EN和UART_CTL.TX_DMA_EN开启接收和发送的DMA请求。此外,还需要将UART_CFG.RX_FIFO_THR设为1,使得UART每收到一个字节数据,都发起一次DMA请求,将数据读到指定的内存空间。


API说明

UART API提供基础的UART初始化和数据收发功能,便于快速上手UART的使用。

void uart_set_tx_pin(gpio_pin_t gpio_pin, uint32_t mfp)

设置UART的发送引脚。

参数
  • gpio_pin – GPIO端口号,gpio_pin_t中的枚举值。

  • mfp – 引脚的MFP值。

返回

void uart_set_rx_pin(gpio_pin_t gpio_pin, uint32_t mfp)

设置UART的接收引脚。

参数
  • gpio_pin – GPIO端口号,gpio_pin_t中的枚举值。

  • mfp – 引脚的MFP值。

返回

void uart_init(uint32_t chx, uint32_t baud, void (*recv_cb)(uint8_t))

UART初始化。

参数
  • chx – UART通道,0~1。

  • baud – UART波特率。

  • recv_cb – 串口接收回调函数。

返回

void uart_deinit(uint32_t chx)

UART去初始化。

参数
  • chx – UART通道,0~1。

返回

void uart_write(uint32_t chx, uint8_t *buf, uint32_t len)

UART写(发送)数据。

参数
  • chx – UART通道,0~1。

  • buf – 指向待写入的数据

  • len – 待写入数据的长度

返回

uint32_t uart_read(uint32_t chx, uint8_t *buf, uint32_t len, uint32_t timeout)

UART读(接收)数据。

参数
  • chx – UART通道,0~1。

  • buf – 指向容纳待读取数据的缓冲区

  • len – 待读取数据的长度

  • timeout – 读取超时时间,单位us

返回

实际读取到数据量

返回类型

uint32_t

API使用示例

  1. “board.h” 中设置 __USE_UART0_PIN_CFG__USE_UART1_PIN_CFG 为1,表示需要启用UART0或UART1引脚配置。

#define       __USE_UART1_PIN_CFG                     (1)
  1. “board.h” 中设置并定义对应UART需要用到的引脚以及引脚对应的MFP值。

#define       UART1_TX_PIN            (GPIO_Pin_08)
#define       UART1_TX_MFP            (3)
#define       UART1_RX_PIN            (GPIO_Pin_09)
#define       UART1_RX_MFP            (3)

备注

  • 如果 __USE_UART0_PIN_CFG__USE_UART1_PIN_CFG 为0,则需要用户手动配置UART引脚。

  1. 调用 uart_init() 初始化对应的UART模块。

该函数会初始化UART用到的引脚(如果 __USE_UART0_PIN_CFG__USE_UART1_PIN_CFG 为1),设置OSC_DEV时钟作为UART的时钟。如果 recv_cb 参数为NULL,那么UART接收通过 uart_read() 函数完成,否则,UART接收到数据后,将调用 recv_cb 函数指针,并传入接收到的数据。

uart_init(1, 115200, uart1_cb);
  1. 调用 uart_write() 发送数据

uart_write() 阻塞式发送数据,直到所有数据从引脚上发送完成。在低波特率下, uart_write() 的执行时间会较长。

uart_write(1, "hello", 5);
  1. 调用 uart_read() 接收数据,或者在 recv_cb 中处理接收的数据。

uart_read() 会阻塞式接收数据,直到指定长度的数据接收完成或者超时。

recv_cb 会在UART接收中断被调用,当接收连续数据时, recv_cb 会被频繁调用。

备注

  • 由于 recv_cb 是在中断函数中被调用的,因此 recv_cb 所指向的函数应力求简短。

  1. 如果不需要再使用UART,调用 uart_deinit() 去初始化。

去初始化会关闭UART的时钟,并让模块处于复位状态。相应的引脚复用也会被清除(如果 __USE_UART0_PIN_CFG__USE_UART1_PIN_CFG 为1)。


寄存器定义

../../_images/kiwi-reg-uart-cfg-1.png ../../_images/kiwi-reg-uart-cfg-2.png
../../_images/kiwi-reg-uart-div.png
../../_images/kiwi-reg-uart-ctl.png
../../_images/kiwi-reg-uart-ie.png
../../_images/kiwi-reg-uart-pd.png
../../_images/kiwi-reg-uart-sta.png
../../_images/kiwi-reg-uart-rx-cnt.png
../../_images/kiwi-reg-uart-tx-dat.png
../../_images/kiwi-reg-uart-rx-dat.png