本帖最后由 FPGA课程 于 2024-9-27 16:24 编辑
软件版本: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概述工业场合RS232/RS422/RS485这种低速接口往往大量实用,但是对于ARM一般只能支持2个PS串口,如果需要更多的串口扩展起来就比较困难了。但是对于SOC来说,通过AXI-UART就能非常方便扩展串口。理论上,只要FPGA的资源够用,可以扩展任意路串口。 本文不再对UART串口通信协议做介绍,如果有不清楚的读者,可以阅读前面的实验“PS UART串口通信”。 本文实验目的: 1:通过阅读pg142-axi-uartlite.pdf熟悉AXI-UartLite控制器的硬件资源(配套工程的soc_prj/06_doc路径)
2:通过VIVADO搭建axi-uartlite的SOC工程
3:使用VITIS-SDK编写axi-uartlite测试程序,实现类似PS-UART中断接收的环路测试程序
2系统框图
3AXI-UartLite IP概述
3.1特性-AXI-Lite接口接到ZYNQ IP -全双工模式 -16个字符的发送和接收fifo -可配置的数据位宽5~8bits -可配置奇偶校验或无校验 -波特率可配置 3.2寄存器概述
1:RX FIFO寄存器(offset=0h)
2:TX FIFO寄存器(offset=04h)
3:CTRL_REG控制寄存器(offset=0CH)
4:STAT_REG控制寄存器(offset=08H)
4硬件电路分析开发板上有2个串口一个接入到PS的ARM;另外一个接入到PL的FPGA,本实验使用接入到ARM的串口 配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。
5搭建SOC系统工程详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。 5.1SOC系统工程
1:中断设置
2:设置GP Master接口
3:设置复位输出
4:设置PL时钟
5:UART-Lite IP
5.2设置AXI外设地址分配只要添加的AXI总线外设都要正确分配地址,这一步不能遗漏。
5.3编译并导出平台文件以下步骤简写,有不清楚的看第一篇文章。 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创建APP工程
7程序分析
7.1uartlite_pl_intr.c主程序- #include "sys_intr.h"
- #include "uartlite_pl_intr.h"
- void init_intr_sys(void)
- {
- Init_Intr_System(&Intc);//初始化全局中断
- UartLite_Init(&UartLiteInst,UARTLITE_DEVICE_ID); //初始化串口
- UartLite_setup_IntrSystem(&Intc, &UartLiteInst, UARTLITE_IRPT_INTR); //初始化串口中断
- Setup_Intr_Exception(&Intc);
- }
- int main(void)
- {
- init_intr_sys();
- XUartLite_Recv(&UartLiteInst, RecvBuffer, UART_BUFFER_SIZE); //首先启动一次接收
- while(1);
- return 0;
- }
复制代码
主程序中,首先对中断系统以及PL的UART中断进行初始化,然后启动一次100个字节的接收。这主要是为了启动传输,如果单次传输的数据可能大于100可以把buf设置大一些。当FIFO有数据就会产生中断。 7.2uartlite_pl_intr.c程序- #include "uartlite_pl_intr.h"
- u8 *SendBuffer = (void*)0x08000000;
- u8 *RecvBuffer = (void*)0x08100000;
- volatile int TotalSentCount;
- volatile int TotalRecvCount;
- static void UartLiteSendHandler(void *CallBackRef, unsigned int EventData);
- static void UartLiteRecvHandler(void *CallBackRef, unsigned int EventData);
- static void UartLiteSendHandler(void *CallBackRef, unsigned int EventData)
- {
- //TotalSentCount = EventData;
- }
- //串口接收中断回调函数
- static void UartLiteRecvHandler(void *CallBackRef, unsigned int EventData)
- {
- int i;
- XUartLite * Uartpl = (XUartLite *) CallBackRef;
- TotalRecvCount = EventData;
- for(i=0;i < TotalRecvCount;i++)//把接收缓存中数据保存到发送缓存
- SendBuffer[i] = RecvBuffer[i];
- XUartLite_Send(Uartpl, SendBuffer, TotalRecvCount); //启动发送
- XUartLite_Recv(Uartpl, RecvBuffer, UART_BUFFER_SIZE); //再次启动接收
- }
- void UartLiteDisableIntrSystem(XScuGic *IntcInstancePtr,u16 UartLiteIntrId)
- {
- XScuGic_Disable(IntcInstancePtr, UartLiteIntrId);
- XScuGic_Disconnect(IntcInstancePtr, UartLiteIntrId);
- }
- //串口中断设置函数
- int UartLite_setup_IntrSystem(XScuGic * IntcInstancePtr , XUartLite *UartLiteInstPtr ,u16 UartLiteIntrId)
- {
- int Status;
- XScuGic_SetPriorityTriggerType(IntcInstancePtr, UartLiteIntrId,0xA0, 0x3); //设置串口PL中断
- //设置串口中断回调函数,会在XUartLite_InterruptHandler函数中继续产生回调,读者可以去看下
- Status = XScuGic_Connect(IntcInstancePtr, UartLiteIntrId,(Xil_ExceptionHandler)XUartLite_InterruptHandler,UartLiteInstPtr);
- if (Status != XST_SUCCESS) return Status;
- XUartLite_EnableInterrupt(UartLiteInstPtr); //使能串口中断
- XScuGic_Enable(IntcInstancePtr, UartLiteIntrId); //使能全局中断
- return XST_SUCCESS;
- }
- int UartLite_Init(XUartLite *UartLiteInstPtr,u16 UartLiteDeviceId)
- {
- int Status;
- Status = XUartLite_Initialize(UartLiteInstPtr, UartLiteDeviceId);
- if (Status != XST_SUCCESS) return XST_FAILURE;
- //设置串口的发送中断回调函数
- XUartLite_SetSendHandler(UartLiteInstPtr, UartLiteSendHandler,UartLiteInstPtr);
- //设置串口的接收中断回调函数
- XUartLite_SetRecvHandler(UartLiteInstPtr, UartLiteRecvHandler,UartLiteInstPtr);
- return XST_SUCCESS;
- }
复制代码
1:XScuGic_SetPriorityTriggerType(XScuGicPtr, UartLiteIntrId,0xA0, 0x3)函数这个函数用于设置PL中断的触发方式, 参数UartLiteIntrId =61代表了PL的第一个中断。 参数0xA0代表了中断的优先级,这个值越小代表拥有越高的优先级 参数0x3代表输入的PL信号上升沿触发中断 读者如果感兴趣可以继续追踪一下这个函数,看下如何设置中断寄存器的配置。 2:UartLiteRecvHandler(void *CallBackRef, unsigned int EventData)函数当接收到数据后,把数据从接收buf复制到发送buf然后通过调用XUartLite_Send(Uartpl, SendBuffer, TotalRecvCount)函数把数据输送出去。这样我们就能验证发送给UART的数据和我们在串口控制台接收的数据是否一致。 3:XUartLite_Recv(Uartpl, RecvBuffer, UART_BUFFER_SIZE)函数
这个函数主要调用了XUartLite_ReceiveBuffer(InstancePtr)函数用于接收数据,这个函数中有如下代码,只要ReceivedCount < InstancePtr->ReceiveBuffer.RemainingBytes,并且判断到RX_FIFO中有数据,就会持续接收。
4:XUartLite_Send(Uartpl, SendBuffer, TotalRecvCount)函数
继续调用XUartLite_SendBuffer(InstancePtr)完成数据的发送, XUartLite_SendBuffer(InstancePtr)中有这么一段程序。这个程序中把buf中的数据写入到FIFO中。
5:XUartLite_InterruptHandler(XUartLite *InstancePtr)
当接收FIFO变为非空或当发送FIFO变为空时产生中断。这里主要时接收FIFO有数据后产生中断。还记得我们前面设置接收长度为100吗?当第一次中断发生后,只要FIFO数据有效,就读继续FIFO,之后回调接收函数。 6:修改ReceiveDataHandler(XUartLite *InstancePtr)库函数为了提高传输效率以及实现不定长度数据的接收,我们需要对XILINX 官方SDK的驱动做简单改进,把库函数uartlite_intr.c复制出来,放到我们代码路径下,这样整个程序会被有效于库函数调用。
8方案演示
8.1硬件准备
本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON(注意新版本的 MLK_H3_CZ08-7100-MZ7100FC),支持 JTAG 模式,对于老版本的核心板,JTAG 调试的时候 一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
8.2实验结果
|