Universal Asynchronous Receiver Transmitter (UART) 通用异步接收发射机
逻辑部分
特征
- AXI4-Lite 接口,用于寄存器访问和数据传输
- 全双工
- 16个字符传输和接收 FIFO
- 可配置的数据比特数(5-8)每个字符
- 可配置的奇偶比特(奇数或偶数或无)
- 可配置的波特率
方框图
- AXI Interface: 该模块实现了用于寄存器访问和数据传输的 AXI4-Lite 从接口。
- UART Lite Registers: 这个块包括内存映射寄存器(如图1-1所示)。它由一个控制寄存器、一个状态寄存器和一对发送/接收 FIFO 组成,它们都有16个字符的深度。
- Rx Control - 这个块采样接收有关生成波特率的数据,并将其写入接收数据 FIFO。°
- Tx Control - 该块读取来自传输数据 FIFO 的数据,并在 UART Tx 接口上发送。
- Interrupt Control - AXI UART Lite 核心提供中断启用/禁用控制。如果启用了中断,则当RX_FIFO 变为非空或当TX_FIFO 变为空时,将产生上升沿敏感中断。
如何区别这两种中断?(通过查看状态寄存器查看,但是需要看看官方例程怎么处理的)
寄存器映射
- RX FIFO是一个只读寄存器,若设置数据位宽为8时,则其高24位保留,低8位为有效数据位。
- TX FIFO是一个只写寄存器,若设置数据位宽为8时,则其高24位保留,低8位为有效数据位。
- CTRL_REG包含用于接收和传输数据 FIFO 的启用中断位和复位引脚。这是一个只写寄存器。其各个bit定义如表2-7所示。
- STAT_REG包含在启用中断和出现错误时接收和传输数据 FIFO 的状态。这是一个只读寄存器。
若是在程序开始就初始化了串口,但是并没有读取串口数据,而在接收了几个数据之后再读取,那读到的是之前接收的数据,还是最新接收到数据?
// 设想自己实现UART的运行
1.CTRL_REG开启接收和传输FIFO的启用,以及中断开启。地址:(baseaddr+0x0C),命令:0x0000_0013
这里可以查看STAT_REG来判断命令是否设置成功了。地址:(baseaddr+0x08),数据:0x0000_0014
2.TX_FIFO写入数据,进行传输。同时通过STAT_REG判断发送FIFO是否已经满了,若是满了就暂停发送,一直等到其为空。
if(read(baseaddr+0x08) & 0xc == 0x8){}
else {write((baseaddr+0x04), data);} // 此处如何判断数据写入FIFO?
3.RX_FIFO可以进行接收数据,进行读取。首先需要判断TX_FIFO是否有数,若是没有数就进行读取,那么读到的数是随机的。
if(read(baseaddr+0x08) & 0x3 == 0x0){}
else {data <= read(baseaddr+0x00);} // 此处如何判断数据读出FIFO?
4.中断的读写控制,当RX_FIFO 变为非空或当TX_FIFO 变为空时,将产生上升沿敏感中断。 // 实际情况如何?
SDK-自己编写部分
上图为先TX_FIFO中写入数据,由于发送的FIFO深度有限为16,而且uart发送速率很忙,而我们写入的数据量很大(1000)。故而,其很快就被写满,等待其发送完再继续写入FIFO。
其次是中断的尝试,在TX_FIFO中数据全部被发送,TX_FIFO变为空之后,就会触发中断信号。
对于RX_FIFO的接收,为了防止RX_FIFO满了,就每接收一个字节就进行读取一次。可见每次接收到RX_FIFO的数据是递增的,且每次都可以成功触发中断信号。
对于uart的接收中断而言,在TX_FIFO接收到第一个数据,从空变为非空时,触发中断信号。
此处为RX_FIFO每次接收两个字节数据,而读取RX_FIFO一直读到其为空,故而中断触发两次,一次为RX_FIFO非空?还是写入两次就触发两次中断?
此处为RX_FIFO每次接收三个字节数据,而读取RX_FIFO一直读到其为空,触发中断两次,是因为一次为RX_FIFO非空,一次为读取读到RX_FIFO内部数据为1?
此图和上图是一个条件下的放大图,可见在读取到RX_FIFO为1时并继续读取时,会触发中断。并且可见,此时还会触发两次中断。(所以,中断并不是判断RX_FIFO非空的充要条件,还是需要读取STAT_REG寄存器进行判断)
SDK-官方例程
polled例程
初始化
// 此处为初始化,把设备ID、基地址、波特率、奇偶校验、数据位宽信息赋值给对应设备结构体
Status = XUartLite_Initialize(&UartLite, DeviceId);
自我检测
// 此处为uart串口自我测试,其中先是对TX_FIFO进行复位,再读取状态寄存器,判断TX_FIFO是否为空。
// 若是为空,则自我测试成功;若是不为空,则自我测试失败。
Status = XUartLite_SelfTest(&UartLite);
发送数据
// 此处为uart串口发送数据,其中先是对控制寄存器中禁止中断,以此停止可能因为调用中断函数而驱动的操作。
// 接着将需要发送数据的字节数,剩余字节数和发送数据的地址给串口结构体
// 接下来回复中断使能信号。这样是为了尽量减少中断被禁用的时间,因为中断被禁止后,接收可能会被填满。
// 接下来便是接收数据的操作,在下图中进行介绍。
SentCount = XUartLite_Send(&UartLite, SendBuffer, TEST_BUFFER_SIZE);
// 同样先读取状态寄存器,判断TX_FIFO是否为满,并且禁止中断
// 并且若是TX_FIFO并为满,且目前发送的字节数小于串口结构体中需要发送的字节数,则向TX_FIFO寄存器中写入一个字节数据。并对已发送计数加一。
// 重复上述操作,直至TX_FIFO为满或者数据发送完毕(字节数小于16)
// 更新串口结构体中的数据地址和待发送长度
// 重新使能中断部分
BytesSent = XUartLite_SendBuffer(InstancePtr);
因为TX_FIFO深度为16,所以一次最多写入16个字节的数据。
- 若是想要不断发送数据,就需要采用中断,即TX_FIFO为空再进行发送
- 采用不断询问的方式,用while循环,每次进行发送,直到发送完成
- 使用函数中有静态计数变量,计数每次写入TX_FIFO字节数,下次发送从本次后面继续发送
- 使用xil_printf函数
接收数据
// 禁止中断
// 将要接收的字节长度和地址保存入串口寄存器
// 打开中断
// 进行数据接收部分
// 首先是取消中断
// 若是当前已接收数据字节数小于需要此次一共所需接收字节数,进入while循环。
// 获取状态寄存器,查看RX_FIFO是否有数,若是有数则进行读取。一直等到RX_FIFO为空则退出while循环
// 更新目前已接收数据数量和未接收数据数量
// 重新打开中断
ReceivedCount = XUartLite_ReceiveBuffer(InstancePtr);
因为接收字节,RX_FIFO可能会有一些延迟,所以添加while循环。每次都只是读完RX_FIFO中的已有数据
中断例程
待续