AXI UART 16550是Xilinx FPGA中提供的一个UART IP核,它允许通过AXI接口与UART设备进行通信。本文描述了如何使用Xilinx的Vivado Design Suite环境中的工具来定制和生成 UART 16550 IP核,以及如何配置和使用该IP核。
1 UART 16550 IP核的使用
以下是针对如何定制IP核的步骤的简要概述:
(1)打开或新建一个工程。
(2)如下图所示,找到UART 16550 IP核。
(3) 双击IP,或从工具栏或右键菜单中选择"Customize IP"命令,打开该IP核的配置页。
在AXI UART 16550 Vivado IDE中,以下是默认选项的描述:
AXI CLK Frequency (AXI时钟频率)
这是驱动AXI UART 16550外设的系统时钟频率(以MHz为单位)。
注意:当IP与IP Integrator一起使用时,此参数会自动更新。
UART Mode (UART模式)
选择UART的模式,具体是16550模式还是16450模式。这两种模式代表了不同的UART版本和功能集。通常,16550模式提供更多的功能和更好的性能。
Use External CLK for BAUD Rate (使用外部时钟计算波特率)
勾选此选项后,AXI UART 16550会使用外部时钟来计算波特率。此时,xin端口会被外部驱动。
不勾选此选项时,UART可能会使用内部的时钟分频器来生成波特率时钟。
Enable External Receiver CLK (启用外部接收器时钟)
勾选此选项后,rclk(接收器时钟)将由外部驱动。这通常用于同步接收操作,确保数据在正确的时钟边界上被采样。
(4)时钟
系统时钟(System Clock)
AXI UART 16550 IP核的异步微处理器接口是与UART的系统时钟输入同步的。这意味着UART的所有内部操作,如数据发送、接收、波特率生成等,都是基于这个系统时钟来进行的。系统时钟(通常标记为S_AXI_ACLK
或类似的名称)的频率是UART操作的基础。
XIN时钟(XIN Clock)
如下图所示,在选择了Use External CLK for BAUD Rate选项时,IP核会出现Xin时钟引脚。
如果xin
输入被外部驱动(即使用外部时钟源),那么这个外部时钟(XIN时钟)必须满足特定的要求。特别是,它必须小于或等于系统时钟的一半(即 xin ≤ (S_AXI_ACLK/2)
)。这个要求是AXI UART 16550 IP核正确运行的必要条件。
这个限制的原因是UART内部的一些操作(如波特率生成器)需要在一个时钟周期内完成特定的任务。如果XIN时钟太快(即超过系统时钟的一半),UART可能无法在一个时钟周期内完成这些任务,从而导致数据丢失或错误。
(5)复位
AXI UART 16550 IP核使用s_axi_aresetn信号进行复位操作,并且该信号是低电平有效的。
2 编程序列
以下是使用AXI UART 16550 IP核时配置为16550模式的一般步骤:
(1)指定异步数据通信交换的格式
例如:写入线控制寄存器来设置数据位(5、6、7或8)、打开奇偶校验并选择偶校验或奇校验、设置传输的停止位数。
通过编程线控制寄存器(Line Control Register)来设置除数锁存访问位(Divisor latch access bit)。
(2)激活中断
向中断使能寄存器(Interrupt Enable register)写入值,以允许特定的中断条件(如数据接收、发送缓冲区空等)生成中断。
(3)配置FIFO
写FIFO控制寄存器(FIFO Control register)以启用FIFOs、清除FIFOs和设置接收FIFO(RCVR FIFO)的触发级别。
(4)设置UART波特率
写除数锁存(Divisor Latch),首先写入最低有效字节,然后写入最高有效字节,以正确设置UART的波特率。
(5)处理中断
当AXI UART 16550触发中断时,您的软件必须读取相关的寄存器(如接收缓冲区寄存器),处理数据,并清除中断标志。
3.1 16550模式下的编程序列
设置目标:
波特率:56kbps
系统时钟:100Mhz
使能FIFO接收缓冲区FIFO
异步数据传输格式:8数据位(data bits)、偶校验(Even parity)和2停止位(stop bits)
设置流程
(1)写0x0000_0080到线控制寄存器
设置DLAB(Divisor Latch Access Bit)位为1,允许写入Divisor Latch的值。
//假设UART的LCR寄存器地址是0x<some_address>
uint16_t lcr_value = 0x0080; // 设置DLAB位为1
write_to_register(0x<some_address> , lcr_value); // 写入线控制寄存器
(2)写Divisor Latch以配置波特率
根据计算出的分频值设置Divisor Latch的最低有效字节(LSB)和最高有效字节(MSB)。分频器的值是通过AXI时钟频率和期望的波特率计算得出的。
divisor = (AXI CLK frequency/(16 × Baud Rate))
代码实现为:
// 假设UART的 Divisor Latch LSB和MSB的地址分别是0x<lsb_address>和0x<msb_address>
uint8_t divisor_lsb = 0x6F; // 分频器锁存器最低有效字节
uint8_t divisor_msb = 0x00; // 分频器锁存器最高有效字节(对于56 Kbps,可能不需要这个字节,取决于UART的具体实现)
write_to_register(0x<lsb_address>, divisor_lsb); // 写入分频器锁存器最低有效字节
write_to_register(0x<msb_address>, divisor_msb); // 写入分频器锁存器最高有效字节(如果需要的话)
(3)写0x0000_001F到 线控制寄存器以设置数据格式
// 重新设置线控制寄存器
uint16_t new_lcr_value = 0x001F; // 8数据位,2停止位,偶校验,DLAB=0
write_to_register(0x<some_address>, new_lcr_value); // 写入新的线控制寄存器值
(4)启用中断
// 假设中断使能寄存器的地址是0x<ier_address>
uint8_t ier_value = 0x11; // 启用THRE(Transmitter Holding Register Empty)和RDA(Receive Data Available)中断
write_to_register(0x<ier_address>, ier_value); // 写入中断使能寄存器
(5)通过中断进行数据传输和接收
-
当THRE中断触发时,意味着发送缓冲区为空,可以写入新的数据到发送保持寄存器(Transmitter Holding Register)。
-
当RDA中断触发时,意味着接收缓冲区有新的数据可读,可以从接收缓冲区寄存器(Receiver Buffer Register)中读取数据。
// 此代码表示如何处理中断
void uart_isr() {
if (is_interrupt_set(THRE_INTERRUPT)) { // 检查THRE中断是否设置
// 清除THRE中断标志(如果需要的话)
// 写入数据到Transmitter Holding Register
}if (is_interrupt_set(RDA_INTERRUPT)) { // 检查RDA中断是否设置 // 清除RDA中断标志(如果需要的话) // 从Receiver Buffer Register读取数据 } // 其他中断处理...
}
3.2 使用外部XIN时钟时16550模式下的编程序列
设置目标:
波特率:56kbps
系统时钟:100Mhz
外部xin时钟:1.8432 MHz
使能FIFO接收缓冲区FIFO
异步数据传输格式:8数据位(data bits)、偶校验(Even parity)和2停止位(stop bits)
设置流程
(1)写0x0000_0080到线控制寄存器
设置DLAB(Divisor Latch Access Bit)位为1,允许写入Divisor Latch的值。
//假设UART的LCR寄存器地址是0x<some_address>
uint16_t lcr_value = 0x0080; // 设置DLAB位为1
write_to_register(0x<some_address> , lcr_value); // 写入线控制寄存器
(2)写Divisor Latch以配置波特率
根据计算出的分频值设置Divisor Latch的最低有效字节(LSB)和最高有效字节(MSB)。分频器的值是通过AXI时钟频率和期望的波特率计算得出的。
divisor = (AXI CLK frequency/(16 × Baud Rate))
代码实现为:
// 假设UART的 Divisor Latch LSB和MSB的地址分别是0x<lsb_address>和0x<msb_address>
uint8_t divisor_lsb = 0x02; // 分频器锁存器最低有效字节
uint8_t divisor_msb = 0x00; // 分频器锁存器最高有效字节(对于56 Kbps,可能不需要这个字节,取决于UART的具体实现)
write_to_register(0x<lsb_address>, divisor_lsb); // 写入分频器锁存器最低有效字节
write_to_register(0x<msb_address>, divisor_msb); // 写入分频器锁存器最高有效字节(如果需要的话)
(3)写0x0000_001F到 线控制寄存器以设置数据格式
// 重新设置线控制寄存器
uint16_t new_lcr_value = 0x001F; // 8数据位,2停止位,偶校验,DLAB=0
write_to_register(0x<some_address>, new_lcr_value); // 写入新的线控制寄存器值
(4)启用中断
// 假设中断使能寄存器的地址是0x<ier_address>
uint8_t ier_value = 0x11; // 启用THRE(Transmitter Holding Register Empty)和RDA(Receive Data Available)中断
write_to_register(0x<ier_address>, ier_value); // 写入中断使能寄存器
(5)通过中断进行数据传输和接收
- 当THRE中断触发时,意味着发送缓冲区为空,可以写入新的数据到发送保持寄存器(Transmitter Holding Register)。
- 当RDA中断触发时,意味着接收缓冲区有新的数据可读,可以从接收缓冲区寄存器(Receiver Buffer Register)中读取数据。