本帖最后由 FPGA课程 于 2024-9-25 17:31 编辑
软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述串口自诞生以来由于其简单可靠的传输方式,被大量使用,对于现在这个充满高速通信设备的时代来说,串口依然不过时。老早以前的台式机和笔记本可都是有串口的,虽然现在很多台式机串口没有了,笔记本也没有串口了,但是串口从来没有被丢弃,只是以另外一种形式存在,这就是USB转串口芯片。串口只需要2根线就可以实现一收一发,使用简单,可靠方便,在低速场合大量使用。比如一些工控的人机界面、我们用的一些单片机的USB下载器都是用USB转串口来实现的。 实验目的: 1:熟悉UART串口通信协议,包括数据格式、波特率、起始位、停止位、奇偶校验位等 2:熟悉ZYNQ的UART串口的功能单元以及寄存器功能 3:使用vitis-sdk实现串口的中断接收,并且环路发出 2系统框图
3UART串口通信协议介绍我们先了解串口通信协议。通常串口的一次发送或接收由四个部分组成:起始位S(“一般为逻辑‘0’)、数据位D0~D7(一般为6位~8位之间可变,数据低位在前)、校验位(奇校验、偶检验或不需要校验位)、停止位(通常为1位、1.5位、2位)。停止位必须为逻辑1。在一次串口通信过程中,数据接收与发送双方没有共享时钟,因此,双方必须协商好数据传输波特率。波特率即数据传输速率。根据双方协议好的传输速率,接收端即可对发送端的数据进行采样。常见的波特率标准为300bps,600bps,800bps,9600bps,19200bps,38400bps,115200bps等。当然更块的速度意味着对采样的要求更高,有可能误码率会逐渐提高。 通常对串口进行数据采样,采用更高频的时钟。这样做的目的是采用高频时钟来锁存低频时钟,减少数据的误码率,增加接收模块的自纠错能力。 具体的工作流程为: 发送端按照预先设定好的波特率,发送起始位(Start)+数据位(data)+奇偶校验位+结束位。其中,起始位为逻辑0,结束位为逻辑1,发送端在空闲状态为1。发送数据包格式如下图所示。
数据的奇偶校验位是可以选择的,如果不使用奇偶,那么就没有这个数据位,我们本课程中没有用到奇偶校验位。
接收端通过检测电平‘1’到‘0’的跳变来确定一个数据包的开始。确定开始位接收完成之后,依次接收数据,使用更高的采样时钟,完成数据采集。接收完数据位后,继续接收奇偶校验位和停止位。 ZYNQ的UART控制器可以完成发送数据的编码和接收数据的界面,关于更多UART的硬件知识可以阅读以下ZYNQ UART串口部分介绍。 4ZYNQ UART串口控制介绍
4.1UART控制器概述
ZYNQ的UART控制器支持全双工异步收发,支持多种可编程波特率和I/O信号格式支持自动奇偶校验和多主机检测模式。 通过配置和模式寄存器控制UART,通过读取FIFO状态寄存器、中断状态寄存器、调制解调状态寄存器、其他功能状态寄存器获取UART的工作状态。 UART的TX和RX路径是异步独立的,TX路径和RX路径各有一个64字节的FIFO。控制寄存器可以对UART中的TX和RX FIFO数据序列化和反序列化,另外还包含一个模式开关可以设置TX到RX的环路设置。 在类似调制解调器的应用中使用UART时,调制解调器控制模块会检测并生成调制解调器握手信号,并根据握手协议控制接收器和发送器路径。 4.2UART控制器特性•具有可编程波特率发生器 •具有可配置的接收和发送FIFO,具有字节,两个字节或四个字节的APB访问机制 •支持6、7或8个数据位 •支持1、1.5或2个停止位 •支持奇数,偶数,空格,标记或无奇偶校验 •支持奇偶校验,成帧和溢出错误检测 •具有断行的生成和检测 •支持自动回显,本地环回和远程环回通道模式 •支持中断产生 •具有调制解调器控制信号:CTS,RTS,DSR,DTR,RI和DCD •UART有两个时钟:高级外围总线(APB)的时钟频率高达100 MHz。 uart_ref_clock的范围是1 MHz至100 MHz。 4.3UART控制器功能描述
1:UART控制器框图
2:波特率发生器
UART的sel_clk时钟: 该时钟为UART_REF_CLKk或者UART_REF_CLK/8
波特率采样时钟使能baud_sample: baud_sample=sel_clk/CD
波特率baud_rate: baud_rate = baud_sample/ (BDIV + 1)
CD的值范围为1~65535,BDIV的值范围为4~255 以下给出了UG585中常用波特率的计算参数
3:TX发送FIFO软件需要发送的数据首先会填入到TX的FIFO。 4:TX发送串行发送模块发送模块从TxFIFO中移除数据,并将其加载到发送器移位寄存器中,以便可以对其进行串行化。发送模块将起始位,数据位,奇偶校验位和停止位移出为串行数据流。在发送波特率时钟使能(baud_tx_rate)的下降沿发送数据,最低有效位在前。 下图中的数据包含了PA奇偶校验位,很多情况下没有这位数据,那么数据就是一共需要10个发送时钟。
5:RX接收FIFO当RX串行接收模块接收到数据后,数据移入RX接收FIFO。 6:RX串行接收模块UART使用UART_REF_CLK和时钟使能(baud_sample)连续对UARTx_RxD信号进行过采样。当在中间位置连续监测到3次低电平代表收到RX的起始位。如下所示,采样速度是16倍的波特率。
当检测到有效的起始位时,接收器波特率时钟使能(baud_rx_rate)将重新同步,以便在每个位的理论中点附近对输入的UART RxD信号进行进一步采样。
7:I/O模式切换模式开关控制控制器内RxD和TxD信号的路由,共有四种工作模式如下图所示,除非只是测试UART软件代码,否则我们都是使用普通模式。
8:UART中断与状态控制器UART的中断和状态控制器在下面的寄存器介绍中介绍 9:UART调制解调器控制这部分功能我们不涉及。 XUARTPS_CR寄存器XUARTPS_CR_OFFSET (0x00U) Field Name | Bits | Type | Reset Value | Description | Reserved | 31~9bit | RW | 0x0 | 保留,读0写无效 | 暂停并发送停止位(STOPBRK) | 8 | RW | 0x0 | 1-当传输一个字节后,设置该位产生停止位,输出12bit的高电平。 0-无影响 | 暂停并发送起始位(STARTBRK) | 7
| RW | 0x0 | 1-当FIFO中已经填入需要发送的数据,并且TX发送移位寄存器已经传输完毕,启动发送起始位。必须在STOPBRK=0的情况下有效。 0-无影响 | 6bit:重启接收超时计数器(TORST) | 6 | RW | 0x0 | 1-使能接收超时计数 0-无影响 | 5bit:禁止发送(TX_DIS)
| 5 | RW | 0x0 | 1-禁止发送 0-使能 | 使能发送(TX_EN) | 4 | RW | 0x0 | 4bit: 1-使能发送,需要确保TX_DIS=0 0-禁止发送 | 禁止接收(RX_DIS)
| 3 | RW | 0x0 | 1-禁止接收,不管RXEN的值 0-使能 | 使能接收(RX_EN)
| 2 | RW | 0x0 | 使能接收,RX_DIS=0有效 0-禁止接收 | 软件复位(TXRST)
| 1 | RW | 0x0 | 1-复位发送逻辑,复位完成后自动清零 0-无影响 | 软件复位(RXRST) | 0 | RW | 0x0 | 1-复位接收逻辑,复位完成后自动清零 0-无影响 |
XUARTPS_MR寄存器XUARTPS_MR_OFFSET (0x04U) Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:14 | RO | 0x0 | 保留(只读) | 从APB配置FIFO访问的大小(WSIZE )
| 13:12 | RW | 0x0 | 00-一个或两个字节写入TX FIFO 或从 RX FIFO 读取。如果 byte_sel为高,则写入或读取一个字节,如果 byte_sel为低,则写入或读取两个字节(默认传统模式) 01-始终从FIFO写入或读取一个字节,无论 byte_sel是否处于活动状态 10-无论byte_sel是否有效,总是从FIFO写入或读取两个字节 11-无论 byte_sel为何,总是从FIFO写入或读取四个字节(假设配置的APB宽度支持四个字节) | 通道模式(CHMODE) | 9:8 | RW | 0x0 | 00-正常模式 01-echo模式 10-本地环路 11-远程环回 | 停止位(NBSTOP)
| 7:6 | RW | 0x0 | 00-1bit停止位 01-1.5bit停止位 10-2个停止位 11-保留 | 奇偶校验位(PAR) | 5:3 | RW | 0x0 | 000-偶校验 001-奇校验 010-奇偶校验强制为0 011-奇偶校验强制为1 1xx-无校验 | 字符长度(CHRL) | 2:1 | RW | 0x0 | 11-6bits 10-7bits 0x-8bits | 时钟源选择(CLKSEL) | 0 | RW | 0x0 | 0-uart_ref_clk 1-uart_ref_clk/8 |
XUARTPS_IER寄存器XUARTPS_IER_OFFSET (0x08U) 中断使能寄存器,设置1使能相关中断 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:14 | WO | 0x0 | 保留(只读) | PBRK | 13 | WO | 0x0 | 接收器中断检测中断使能(清除掩码 = 0) | TOVR | 12 | WO | 0x0 | FIFO溢出中断使能 | TNFUL | 11 | WO | 0x0 | FIFO将满中断使能 | TTRIG | 10 | WO | 0x0 | FIFO发送器FIFO出发中断使能 | DMSI | 9 | WO | 0x0 | Delta调制解调器状态指示器中断使能 | TIMEOUT | 8 | WO | 0x0 | 接收超时错误中断使能 | PARE | 7 | WO | 0x0 | 接收奇偶校验错误中断使能 | FRAME | 6 | WO | 0x0 | 接收帧错误中断使能 | ROVER | 5 | WO | 0x0 | 接收溢出中断使能 | TFUL | 4 | WO | 0x0 | 发送FIFO满中断使能 | TEMPTY | 3 | WO | 0x0 | 发送FIFO空中断使能 | RFUL | 2 | WO | 0x0 | 接收FIFO满中断使能 | REMPTY | 1 | WO | 0x0 | 接收FIFO空中断使能 | ROVR | 0 | WO | 0x0 | 接收FIFO中断触发使能 |
XUARTPS_IDR寄存器XUARTPS_IDR_OFFSET (0x0CU) 中断禁用寄存器,设置1禁用相关中断 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:14 | WO | 0x0 | 保留(只读) | PBRK | 13 | WO | 0x0 | 接收器中断检测中断禁用 | TOVR | 12 | WO | 0x0 | FIFO溢出中断禁用 | TNFUL | 11 | WO | 0x0 | FIFO将满中断禁用 | TTRIG | 10 | WO | 0x0 | FIFO发送器FIFO出发中断禁用 | DMSI | 9 | WO | 0x0 | Delta调制解调器状态指示器中断禁用 | TIMEOUT | 8 | WO | 0x0 | 接收超时错误中断禁用 | PARE | 7 | WO | 0x0 | 接收奇偶校验错误中断禁用 | FRAME | 6 | WO | 0x0 | 接收帧错误中断禁用 | ROVER | 5 | WO | 0x0 | 接收溢出中断禁用 | TFUL | 4 | WO | 0x0 | 发送FIFO满中断禁用 | TEMPTY | 3 | WO | 0x0 | 发送FIFO空中断禁用 | RFUL | 2 | WO | 0x0 | 接收FIFO满中断禁用 | REMPTY | 1 | WO | 0x0 | 接收FIFO空中断禁用 | ROVR | 0 | WO | 0x0 | 接收FIFO中断触发禁用 |
XUARTPS_IMR寄存器XUARTPS_IMR_OFFSET (0x10U) 中断掩码寄存器,是只读寄存器1代表相关中断使能,0代表相关中断禁用 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:14 | WO | 0x0 | 保留(只读) | PBRK | 13 | WO | 0x0 | 接收器中断检测中断掩码 | TOVR | 12 | RO | 0x0 | FIFO溢出中断掩码 | TNFUL | 11 | RO | 0x0 | FIFO将满中断掩码 | TTRIG | 10 | RO | 0x0 | FIFO发送器FIFO出发中断掩码 | DMSI | 9 | RO | 0x0 | Delta调制解调器状态指示器中断掩码 | TIMEOUT | 8 | RO | 0x0 | 接收超时错误中断掩码 | PARE | 7 | RO | 0x0 | 接收奇偶校验错误中断掩码 | FRAME | 6 | RO | 0x0 | 接收帧错误中断掩码 | ROVER | 5 | RO | 0x0 | 接收溢出中断掩码 | TFUL | 4 | RO | 0x0 | 发送FIFO满中断掩码 | TEMPTY | 3 | RO | 0x0 | 发送FIFO空中断掩码 | RFUL | 2 | RO | 0x0 | 接收FIFO满中断掩码 | REMPTY | 1 | RO | 0x0 | 接收FIFO空中断掩码 | ROVR | 0 | RO | 0x0 | 接收FIFO中断触发掩码 |
XUARTPS_ISR寄存器XUARTPS_ISR_OFFSET (0x14U) 中断状态寄存器,1代表相关中断产生,读该位会清除中断状态 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:14 | WTC | 0x0 | 保留(只读) | PBRK | 13 | WTC | 0x0 | 接收器中断检测中断状态 | TOVR | 12 | WTC | 0x0 | FIFO溢出中断状态 | TNFUL | 11 | WTC | 0x0 | FIFO将满中断状态 | TTRIG | 10 | WTC | 0x0 | FIFO发送器FIFO出发中断状态 | DMSI | 9 | WTC | 0x0 | Delta调制解调器状态指示器中断状态 | TIMEOUT | 8 | WTC | 0x0 | 接收超时错误中断状态 | PARE | 7 | WTC | 0x0 | 接收奇偶校验错误中断状态 | FRAME | 6 | WTC | 0x0 | 接收帧错误中断状态 | ROVER | 5 | WTC | 0x0 | 接收溢出中断状态 | TFUL | 4 | WTC | 0x0 | 发送FIFO满中断状态 | TEMPTY | 3 | WTC | 0x0 | 发送FIFO空中断状态 | RFUL | 2 | WTC | 0x0 | 接收FIFO满中断状态 | REMPTY | 1 | WTC | 0x0 | 接收FIFO空中断状态 | ROVR | 0 | WTC | 0x0 | 接收FIFO中断触发状态 |
XUARTPS_BAUDGEN寄存器XUARTPS_BAUDGEN_OFFSET (0x18U) 产生波特率分频时钟使能寄存器 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:16 | WR | 0x0 | 保留(只读) | CD | 15:0 | WR | 0x28 | 波特率时钟使能分频器 0-禁止波特率采样 1-Bypass (baud_sample = sel_clk) 2-2~65535分频 |
XUARTPS_RXTOUT寄存器XUARTPS_RXTOUT_OFFSET (0x1CU) 接收超时寄存器 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:8 | RO | 0x0 | 保留(只读) | RTO | 7:0 | WR | 0x0 | 接收超时寄存器 0-禁用接收超时 1~255个baud_sample 时钟周期作为超时检测 |
XUARTPS_RXWM寄存器XUARTPS_RXTOUT_OFFSET (0x20U) 接收FIFO中断触发值寄存器 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:6 | RO | 0x0 | 保留(只读) | RTRIG | 5:0 | WR | 0x20 | 接收FIFO触发值 0-禁用触发 1~63 FIFO当FIFO已经有了相应数据产生接收触发中断 |
XUARTPS_MODEMCR寄存器XUARTPS_MODEMCR_OFFSET (0x24U) Modem控制寄存器该寄存器本实验不涉及不介绍
XUARTPS_MODEMSR寄存器XUARTPS_MODEMSR_OFFSET (0x28U) Modem状态寄存器该寄存器本实验不涉及不介绍
XUARTPS_SR寄存器XUARTPS_SR_OFFSET (0x2CU) 只读通道状态寄存器,可以对 UART设计的原始未屏蔽状态信息的持续监控。 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:15 | RO | 0x0 | 保留(只读) | TNFUL | 14 | RO | 0x0 | 发送FIFO满连续状态(TNFUL) 1-TX FIFO未使用的空间大于1 0-TX FIFO就剩1个未使用空间 | TTRIG | 13 | RO | 0x0 | 发送FIFO触发连续状态(TTRIG) 1-TX FIFO的数据量大于或等于触发值 0-TX FIFO的数据量小于触发值 | FDELT | 12 | RO | 0x0 | 接收流延迟触发连续状态(FLOWDEL) 1-RX FIFO的数据量大于或者等于FDEL 0-RX FIFO的数据量小于FDEL | TACTIVE | 11 | RO | 0x0 | 发送状态机激活(TACTIVE) 1-激活 0-未激活 | RACTIVE | 10 | RO | 0x0 | 接收状态机激活(RACTIVE) 1-激活 0-未激活 | 保留(只读) | 9:5 | RO | 0x0 | 保留(只读) | TFUL | 4 | RO | 0x0 | 发送FIFO满连续状态(TXFULL) 1-TX FIFO满 0-TX FIFO未满 | TEMPTY | 3 | RO | 0x0 | 发送FIFO空连续状态(TXEMPTY) 1-TX FIFO空 0-TX FIFO非空 | RFUL | 2 | RO | 0x0 | 接收FIFO满连续状态(RXFULL) 1-RX FIFO满 0-RX FIFO未满 | REMPTY | 1 | RO | 0x0 | 接收FIFO空连续状态(RXEMPTY) 1-RX FIFO空 0-RX FIFO非空 | RTRIG | 0 | RO | 0x0 | 接收FIFO触发连续状态(RXOVR) 1-RX FIFO的数据量大于或等于触发值 0-RX FIFO的数据量小于触发值 |
XUARTPS_FIFO寄存器XUARTPS_FIFO_OFFSET (0x30U) 接收FIFO寄存器 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:8 | RO | 0x0 | 保留(只读) | FIFO | 7:0 | WR | 0x20 | 7~0bit:发送或者接收FIFO |
Baud_rate_divider_reg0寄存器Baud_rate_divider_reg0 (0x34U) baud_sample被分频成多少产生波特率 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:8 | WR | 0x0 | 保留(只读) | BDIV | 7:0 | WR | 0x20 | 0~3:保留 4~255:用于波特率分频 |
Flow_delay_reg0寄存器Flow_delay_reg0 (0x38U)本实验用不到该寄存器 只有在调制解调器控制寄存器的 FCM 字段中启用了自动流控制模式时,才使用流控制延迟寄存器。 当使能自动流控制模式时,该寄存器指定EMIOUARTxRTSN输出被取消置位的接收器FIFO级别。 EMIOUARTxRTSN 输出仅在填充水平降至低于 FDEL 的 4 倍以下时才会再次置位。
Tx_FIFO_trigger_level0寄存器Tx_FIFO_trigger_level0 (0x3CU) 发送FIFO数据量触发中断寄存器 Field Name | Bits | Type | Reset Value | Description | 保留(只读) | 31:6 | WR | 0x20 | 保留(只读) | TTRIG | 5:0 |
|
| 0:禁用FIFO触发功能 1~63:设置FIFO触发的数据量 | 5搭建SOC系统工程
详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。 5.1SOC系统工程直接使用已经搭建好的“01Vitis Soc开发入门”这篇文章中的工程。
5.2编译并导出平台文件以下步骤简写,有不清楚的看第一篇文章。 1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。 2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。 3:添加配套工程路径下uisrc/04_pin/fpga_pin.xdc约束文件 4:生成Bit文件。 5:导出到硬件: FileàExport HardwareàInclude bitstream 6:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
6 搭建Vitis-sdk工程创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。 6.1创建SDK Platform工程
右击soc_base编译,编译的时间可能会有点长 6.2创建uart_intr APP工程
7程序分析7.1init_intr_sys函数- void init_intr_sys(void)
- {
- Init_Intr_System(&Intc);
-
- Init_UartPsIntr(&UartPs,UART_DEVICE_ID);
-
- UartPs_Setup_IntrSystem(&Intc, &UartPs, UART_INT_IRQ_ID);
-
- Setup_Intr_Exception(&Intc);
- }
复制代码
此函数在程序文件uart_intr_test.c中通过调用相关中断函数实现中断功能。为了实现多类型中断,用户必须根据以下方式设置中断。 7.2Init_Intr_System(&Intc)函数- int Init_Intr_System(XScuGic * IntcInstancePtr)
- {
- int Status;
- XScuGic_Config *IntcConfig;
- /*
- * Initialize the interrupt controller driver so that it is ready to
- * use.
- */
- IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
- if (NULL == IntcConfig) {
- return XST_FAILURE;
- }
- Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
- IntcConfig->CpuBaseAddress);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- return XST_SUCCESS;
- }
复制代码
1:XScuGic_LookupConfig(INTC_DEVICE_ID) 函数
右击XScuGic_ConfigTable查看参数定义:
可以继续右击以下参数查看其定义
可以继续右击以下参数查看其定义
ZYNQ中定义
MPSOC中定义
2: XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress)
中断部分主要内容是回调函数的设置,以下回调函数相关参数定义如下:
以下代码对所有为定义的全局中断进行定义回调函数和回调参数。
StubHandler函数定义如下:
7.3Setup_Intr_Exception函数
- void Setup_Intr_Exception(XScuGic * IntcInstancePtr)
- {
- /* Enable interrupts from the hardware */
- Xil_ExceptionInit();
- Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
- (Xil_ExceptionHandler)XScuGic_InterruptHandler,
- (void *)IntcInstancePtr);
- Xil_ExceptionEnable();
- }
复制代码
这个函数中对设置全局中断回调函数,以及使能全局中断 1:Xil_ExceptionInit()函数这个函数目前说明都没做,只是未来保留兼容性,预留在这里。
2:Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr)函数
2.1XIL_EXCEPTION_ID_INT定义
2.2: XExc_VectorTableEntry定义 任何的中断都可以理解位异常处理,这里定义了MPSOC或者ZYNQ支持的异常处理类型。
2.3XScuGic_InterruptHandler函数 可以看到这个函数会根据读取到的中断号调用相应的回调函数,这个回调函数会在具体的外设中断初始化中设置。
2.3中断异常回调函数
由于XIL_EXCEPTION_ID_INT=2所以Xil_ExceptionNullHandler在中断产生的时候会被调用。
3:Xil_ExceptionEnable()函数
这条函数最终指向了汇编指令:
7.4Init_UartPsIntr函数- int Init_UartPsIntr(XUartPs *UartInstPtr,u16 DeviceId )
- {
- int Status;
- XUartPs_Config *Config;
- Config = XUartPs_LookupConfig(DeviceId);
- if (NULL == Config) {
- return XST_FAILURE;
- }
- Status = XUartPs_CfgInitialize(UartInstPtr, Config, Config->BaseAddress);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- return XST_SUCCESS;
- }
复制代码
1:XUartPs_LookupConfig(DeviceId)函数首先依然是通过查找配置程序来获取串口的硬件配置。我们跟踪这个程序,看看他获取的配置是什么。
右击参数打开参数定义
可以看到,这个数组里存放的是UART的设备ID、UART的基地址、时钟频率、MODEM功能 (没用到)。
可以继续右击这些参数定位到这些参数的定义
在xparameters.h中找到如下定义,可以看到UART0的基地址为0xFF000000,UART的时钟为100M
2:XUartPs_CfgInitialize(UartInstPtr, Config, Config->BaseAddress)函数这个函数对UART部分寄存器进行了初始化。可以看到这个函数的第一个参数指向了定义的UART指针,我们就跟踪一下这个指针。 2.1:XuartPs结构体 定义了包括UART的时钟、波特率、收发缓存、回调函数等一共10项参数: - /**
- * The XUartPs driver instance data structure. A pointer to an instance data
- * structure is passed around by functions to refer to a specific driver
- * instance.
- */
- typedef struct {
- XUartPs_Config Config; /* Configuration data structure */
- u32 InputClockHz; /* Input clock frequency */
- u32 IsReady; /* Device is initialized and ready */
- u32 BaudRate; /* Current baud rate */
- XUartPsBuffer SendBuffer;
- XUartPsBuffer ReceiveBuffer;
- XUartPs_Handler Handler;
- void *CallBackRef; /* Callback reference for event handler */
- u32 Platform;
- u8 is_rxbs_error;
- } XUartPs;
复制代码
参数1:XUartPs_Config Config;保存XUartPs_LookupConfig(DeviceId)函数获取的参数值,包括UART的地址、时钟频率等:
参数2:u32 InputClockHz 保存UART时钟频率 参数3:u32 IsReady 指示UART是否已经初始化完成 参数4:u32 BaudRate 指示UART的波特率 参数5:XUartPsBuffer SendBuffer 定义并指向UART的发送缓存结构体 参数6:XUartPsBuffer ReceiveBuffer定义并指向UART的接收缓存结构体
参数7:XUartPs_Handler Handler 定义回调函数
参数8:void *CallBackRef 回调函数的参数,代表了UART中断事件的类型 参数9:Platform 保存平台信息,目前对于XILINX的硬件来说支持ZYNQ、MPSOC、Microblaze,可以通过XGetPlatform_Info()获取 参数10:is_rxbs_error 接收的error参数,目前不清楚具体用在什么地方 2.2:参数初始化 之后需要对参数做一些初始化
2.3:XUartPs_SetBaudRate函数 计算波特率,这个函数可以自动计算波特率,并且设置相关的寄存器,具体的可以右击进去查看下。 2.3.1:波特率计算: 计算所有的波特率分频情况,找出最佳值,这个计算方法大家可以记住下,说不定其他算法领域也会用到。
2.3.2:设置波特率时钟使能: XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_BAUDGEN_OFFSET, Best_BRGR);
波特率发生器寄存器:XUARTPS_BAUDGEN_OFFSET=0x0018
2.3.3:设置波特率分频系数: XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_BAUDDIV_OFFSET, Best_BAUDDIV);
波特率分频寄存器:XUARTPS_BAUDDIV_OFFSET=0x0034
2.3.4软件复位RX/TX模块 XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_CR_OFFSET,XUARTPS_CR_TXRST | XUARTPS_CR_RXRST);
控制寄存器:XUARTPS_CR_OFFSET=0x0000
TX复位:XUARTPS_CR_TXRST=0x00000002
RX复位:XUARTPS_CR_RXRST=0x00000001
2.3.5:使能UART XUartPs_EnableUart(InstancePtr) \
Xil_Out32(((InstancePtr)->Config.BaseAddress + (u32)XUARTPS_CR_OFFSET), \
((Xil_In32((InstancePtr)->Config.BaseAddress + (u32)XUARTPS_CR_OFFSET) & \
(u32)(~XUARTPS_CR_EN_DIS_MASK)) | ((u32)XUARTPS_CR_RX_EN | (u32)XUARTPS_CR_TX_EN))) 使能或禁用掩码:XUARTPS_CR_EN_DIS_MASK=0x0000003C RX使能:XUARTPS_CR_RX_EN=0x00000004 TX使能:XUARTPS_CR_TX_EN=0x00000010
2.4:UART数据模式设置 再次回到XUartPs_CfgInitialize函数查看UART模式寄存器设置:数据8bit、无奇偶校验、1个停止位。 寄存器偏移地址:XUARTPS_MR_OFFSET=0x0004
2.5:RxFIFO触发长度寄存器设置 XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_RXWM_OFFSET, 0x08U);
寄存器偏移地址:XUARTPS_RXWM_OFFSET= 0x00000020
2.6:RxFIFO超时寄存器设置 XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_RXTOUT_OFFSET, 0x01U); 寄存器偏移地址:XUARTPS_RXTOUT_OFFSET = 0x001C
2.7:中断禁用寄存器设置 XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_IDR_OFFSET,XUARTPS_IXR_MASK); 中断禁用寄存器地址偏移:XUARTPS_IDR_OFFSET=0x000C XUARTPS_IXR_MASK = 0x00003FFF 禁止所有中断
7.5UartPs_Setup_IntrSystem(&Intc, &UartPs, UART_INT_IRQ_ID)函数- int UartPs_Setup_IntrSystem(XScuGic *IntcInstancePtr,XUartPs *UartInstancePtr,u16 UartIntrId)
- {
- int Status;
- u32 IntrMask;
- /*
- * interrupt for the device occurs, the device driver handler
- * performs the specific interrupt processing for the device
- */
- Status = XScuGic_Connect(IntcInstancePtr, UartIntrId,
- (Xil_ExceptionHandler) XUartPs_InterruptHandler,
- (void *) UartInstancePtr);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- /*
- * Setup the handlers for the UART that will be called from the
- * interrupt context when data has been sent and received, specify
- * a pointer to the UART driver instance as the callback reference
- * so the handlers are able to access the instance data
- */
- XUartPs_SetHandler(UartInstancePtr, (XUartPs_Handler)UartPs_Intr_Handler, UartInstancePtr);
- /*
- * Enable the interrupt of the UART so interrupts will occur, setup
- * a local loopback so data that is sent will be received.
- */
- IntrMask =
- XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING |
- XUARTPS_IXR_OVER | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXFULL |
- XUARTPS_IXR_RXOVR;
- if (UartInstancePtr->Platform == XPLAT_ZYNQ_ULTRA_MP) {
- IntrMask |= XUARTPS_IXR_RBRK;
- }
- XUartPs_SetInterruptMask(UartInstancePtr, IntrMask);
- XUartPs_SetRecvTimeout(UartInstancePtr, 32);
- /* Enable the interrupt for the device */
- XScuGic_Enable(IntcInstancePtr, UartIntrId);
- return XST_SUCCESS;
- }
复制代码
1:XScuGic_Connect(IntcInstancePtr, UartIntrId, (Xil_ExceptionHandler) XUartPs_InterruptHandler, (void *) UartInstancePtr)函数
这里把GIC的53号中断回调函数XUartPs_InterruptHandler和回调参数UartInstancePtr,这样当XUartPs_InterruptHandler函数回调的时候,可以通过参数UartInstancePtr继续回调。
读者可以看前面Setup_Intr_Exception函数中关于Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr)函数的说明再看这里就能明白了。 1.1:XUartPs_InterruptHandler 此回调函数中分别根据UART的中断寄存器和中断状态寄存器判断有效的中断包括:接收中断、发送中断、错误中断、超时中断、调制解调中断
1.2 ReceiveDataHandler(InstancePtr)函数 我们再展开比如ReceiveDataHandler(InstancePtr)函数,这个函数中会接收FIFO中所有数据,并且最后调用以下函数,进一步回调并且传递相关参数。
以上代码中InstancePtr->CallBackRef会调用UartPs_Intr_Handler。那么这里读者应该记得在Init_UartPsIntr函数中,也对UART的回调函数进行了定义代码如下:XUartPs_SetHandler(UartInstPtr, (XUartPs_Handler)UartPs_Intr_Handler, UartInstPtr);这样最终就能回调我们的函数:void UartPs_Intr_Handler(void *CallBackRef, u32 Event, unsigned int EventData) 2:XUartPs_SetHandler(UartInstPtr, (XUartPs_Handler)UartPs_Intr_Handler, UartInstPtr)函数此函数用于设置UART的中断回调函数,通过以下定义可以确定回调函数是通过无符号的指针函数定义。
3:XUartPs_SetInterruptMask(UartInstPtr, IntrMask)设置UART中断使能
可以看到以上需要设置IER 中断使能和IDR中断禁用寄存器 中断使能寄存器地址偏移:XUARTPS_IER_OFFSET=0x0008
中断禁用寄存器地址偏移:XUARTPS_IER_OFFSET=0x000C,这个寄存器在波特率设置函数中也有介绍。
4:XUartPs_SetRecvTimeout(UartInstPtr, 8)函数该函数设置了空闲超时等待的时间为8x4 = 32为32个波特率采样时钟,意思就是当串口上超过32个波特率采样时钟没有数据的时候,就会触发一次超时中断,这样可以通知用户程序去读取已经接收到的数据。
接收超时寄存器偏移地址:XUARTPS_RXTOUT_OFFSET=0x001C
这个函数的最后是再次启动超时计数器:
5:XScuGic_Enable(IntcInstancePtr, UartIntrId)函数在以下代码中,首先绑定中断号,CPU号,我们也可以进一步分中断
2.1XScuGic_InterruptMaptoCpu(InstancePtr, Cpu_Id, Int_Id)函数 在这个函数中具体中断和CPU如何关联,如何设置SPI(共享外设中断)中断寄存器的相关位我们无法进一步分分析,到此为止。
6:UartPs_Intr_Handler函数在这个函数中,我们把中断接收到的数据发送出去。
8方案演示
8.1硬件准备
实验需要用到JTAG下载器、USB转串口外设,另外需要把核心板上的2P模式开关设置到JTAG模式,即ON ON
8.2实验结果通过串口输入控制台输入数据到zynq,zynq产生接收中断,并且再通过串口输出接收到的数据。
|