目录
硬件环境:Xilinx ZynqMP XCZU15eg-ffvb1156-2-i
五:导出硬件平台信息,然后在SDK里新建一个helloworld工程;
六:将板子的A1和B1 以及A2,B2分别与外部串口通信工具连接,也即用485转USB连接电脑。
硬件环境:Xilinx ZynqMP XCZU15eg-ffvb1156-2-i
软件环境:Vivado2018.3
一:新建工程
打开Vivado,建立一个工程,选择芯片xczu15eg-ffvb-2-i,建立完毕后建立blockDesign,添加芯片进去。
二:芯片配置
- 首先配置bank电压,分别为LVCMOS18,LVCMOS18,LVCMOS18,LVCMOS33
- 配置低速IO,包括QSPI,SD,UART等
- 配置DDR
- 打开PLtoPS的中断;具体配置如下图所示:
三:添加IP核
先来一张总的设计图:
- 添加 AXI GPIO模块,并配置为输出,位宽为1,用于第一路 RS485的 DE 控制
- 添加UART16550模块,用于第一路RS485的数据接口
- 自动连接
- 删除UART引脚,并展开UART接口
- 添加constant模块,并设置位宽为1,值为1;
- 连接ctsn,rin信号
- 再添加一个constant模块,值设置为0;
- 连接freeze,dcdn,dsrn信号
- 将sin和sout引脚导出,修改引脚名字为RS485_0_RXD和RS485_0_TXD;
- 并将GPIO的引脚名字修改为RS485_0_DE;至此第一路配置结束。
- 同样的方法开始配置第二路RS485接口;
- 添加concat模块,连接两路中断到pl_ps_irq0;
- 自动连接并修改第二路的引脚名称;
四:引脚绑定,并生成bitstream
引脚绑定主要为两路RS485的DE,以及TXD和RXD,根据硬件原理图绑定相应引脚,也即添加.xdc文件,然后create HDL Wrapper以及Generate Output Products,最后编译并生成bitstream
五:导出硬件平台信息,然后在SDK里新建一个helloworld工程;
六:将板子的A1和B1 以及A2,B2分别与外部串口通信工具连接,也即用485转USB连接电脑。
七:PS端代码:废话少说直接上代码:
//主程序
#include "xil_exception.h"
#include "xil_printf.h"
#include"sleep.h"
#include <string.h>
#include "RS485_Intr_Recv.h"
/************************** Constant Definitions ******************************/
/*
* The following constants map to the XPAR parameters created in the
* xparameters.h file. They are defined here such that a user can easily
* change all the needed parameters in one place.
*/
#define UART_DEVICE_ID XPAR_UARTNS550_0_DEVICE_ID
#define UART_IRPT_INTR XPAR_FABRIC_AXI_UART16550_0_IP2INTC_IRPT_INTR
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TEST_BUFFER_SIZE 200
/************************** Variable Definitions ******************************/
XUartNs550 UartNs550Instance; /* Instance of the UART Device */
INTC IntcInstance; /* Instance of the Interrupt Controller */
u8 SendBuffer[TEST_BUFFER_SIZE]; /* Buffer for Transmitting Data */
u8 RecvBuffer[TEST_BUFFER_SIZE]; /* Buffer for Receiving Data */
u8 *RecvBufferPtr;
volatile u32 TotalRecvCnt;
static volatile int TotalSentCount;
static volatile int TotalErrorCount;
unsigned int flag = 0;
int main(void)
{
RecvBufferPtr = RecvBuffer;
UartNs550Init(UART_DEVICE_ID, &UartNs550Instance);
XUartNs550_Recv(&UartNs550Instance, RecvBuffer, 108);
while(1)
{
usleep(1000000);
if(TotalRecvCnt > 0)
{
XUartNs550_Recv(&UartNs550Instance, RecvBuffer, 200);
TotalRecvCnt = 0;
flag = 1;
}
if(flag == 1)
{
for(int i =0;i<108;i++)
{
xil_printf("%c",RecvBuffer[i]);
}
flag=0;
memset(RecvBuffer,0,200);
}
}
}
int UartNs550Init(u16 DeviceId, XUartNs550 *UartNs550)
{
u16 Options;
/*Initialize the UART driver so that it's ready to use.*/
XUartNs550_Initialize(UartNs550, DeviceId);
/*Perform a self-test to ensure that the hardware was built correctly.*/
XUartNs550_SelfTest(UartNs550);
/** Connect the UART to the interrupt subsystem such that interrupts can
* occur. This function is application specific.
*/
UartNs550SetupIntrSystem(&IntcInstance, UartNs550, UART_IRPT_INTR);
/** 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.
*/
XUartNs550_SetHandler(UartNs550, UartNs550IntrHandler, UartNs550);
// XUartNs550_SetRecvTimeout(UartNs550, 8);
XUartNs550_SetFifoThreshold(UartNs550, XUN_FIFO_TRIGGER_08);
Options = XUN_OPTION_DATA_INTR | XUN_OPTION_FIFOS_ENABLE |XUN_OPTION_RESET_TX_FIFO;
XUartNs550_SetOptions(UartNs550, Options);
/* Set uart mode Baud Rate 115200, 8bits, no parity, 1 stop bit */
XUartNs550_SetDataFormat(UartNs550, &UartNs550Format);
}
/*****************************************************************************/
/**
*
* This function is the handler which performs processing to handle data events
* from the UartNs550. It is called from an interrupt context such that the
* amount of processing performed should be minimized.
*
* This handler provides an example of how to handle data for the UART and
* is application specific.
*
* @param CallBackRef contains a callback reference from the driver,
* in thiscase it is the instance pointer for the UART driver.
* @param Event contains the specific kind of event that has occurred.
* @param EventData contains the number of bytes sent or received for sent
* and receive events.
*
* @return None.
*
* @note None.
*
*******************************************************************************/
void UartNs550IntrHandler(void *CallBackRef, u32 Event, unsigned int EventData)
{
u8 Errors;
XUartNs550 *UartNs550Ptr = (XUartNs550 *)CallBackRef;
static unsigned int ReceivedCount = 0;
/*
* All of the data has been sent.
*/
if (Event == XUN_EVENT_SENT_DATA) {
TotalSentCount = EventData;
}
if (Event == XUN_EVENT_RECV_DATA)
{
TotalRecvCnt = EventData;
}
/*Data was received, but not the expected number of bytes, a
* timeout just indicates the data stopped for 4 character times.
*/
if (Event == XUN_EVENT_RECV_TIMEOUT)
{
TotalRecvCnt = EventData;
}
if (Event == XUN_EVENT_RECV_ERROR) {
TotalRecvCnt = EventData;
TotalErrorCount++;
Errors = XUartNs550_GetLastErrors(UartNs550Ptr);
}
}
/******************************************************************************/
/**
*
* This function setups the interrupt system such that interrupts can occur
* for the UART. This function is application specific since the actual
* system may or may not have an interrupt controller. The UART could be
* directly connected to a processor without an interrupt controller. The
* user should modify this function to fit the application.
*
* @param IntcInstancePtr is a pointer to the instance of the Interrupt
* Controller.
* @param UartInstancePtr is a pointer to the instance of the UART.
* @param UartIntrId is the interrupt Id and is typically
* XPAR_<INTC_instance>_<UARTNS550_instance>_VEC_ID value from
* xparameters.h.
*
* @return XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note None.
*
*******************************************************************************/
static int UartNs550SetupIntrSystem(INTC *IntcInstancePtr,
XUartNs550 *UartInstancePtr,
u16 UartIntrId)
{
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;
}
XScuGic_SetPriorityTriggerType(IntcInstancePtr, UartIntrId,
0xA0, 0x3);
/*
* Connect the interrupt handler that will be called when an
* interrupt occurs for the device.
*/
Status = XScuGic_Connect(IntcInstancePtr, UartIntrId,
(Xil_ExceptionHandler)XUartNs550_InterruptHandler,
UartInstancePtr);
if (Status != XST_SUCCESS) {
return Status;
}
/*Enable the interrupt for the Timer device.*/
XScuGic_Enable(IntcInstancePtr, UartIntrId);
/*Initialize the exception table. */
Xil_ExceptionInit();
/*Register the interrupt controller handler with the exception table.*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)INTC_HANDLER,
IntcInstancePtr);
/* Enable exceptions.*/
Xil_ExceptionEnable();
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function disables the interrupts that occur for the UartNs550 device.
*
* @param IntcInstancePtr is the pointer to the instance of the Interrupt
* Controller.
* @param UartIntrId is the interrupt Id and is typically
* XPAR_<INTC_instance>_<UARTNS550_instance>_VEC_ID
* value from xparameters.h.
*
* @return None.
*
* @note None.
*
******************************************************************************/
static void UartNs550DisableIntrSystem(INTC *IntcInstancePtr, u16 UartIntrId)
{
/*Disconnect and disable the interrupt for the UartNs550 device.*/
XScuGic_Disable(IntcInstancePtr, UartIntrId);
XScuGic_Disconnect(IntcInstancePtr, UartIntrId);
}
//头文件如下
* RS485_Intr_Recv.h
*
* Created on: 2022年6月23日
* Author: tan804
*/
#ifndef SRC_RS485_INTR_RECV_H_
#define SRC_RS485_INTR_RECV_H_
#include "xscugic.h"
#include "xparameters.h"
#include "xuartns550.h"
#define INTC XScuGic
#define INTC_HANDLER XScuGic_InterruptHandler
XUartNs550Format UartNs550Format =
{
115200,
XUN_FORMAT_8_BITS,
XUN_FORMAT_NO_PARITY,
XUN_FORMAT_1_STOP_BIT
};
/************************** Function Prototypes *******************************/
int UartNs550IntrExample(INTC *IntcInstancePtr,
XUartNs550 *UartInstancePtr,
u16 UartDeviceId,
u16 UartIntrId);
void UartNs550IntrHandler(void *CallBackRef, u32 Event, unsigned int EventData);
static int UartNs550SetupIntrSystem(INTC *IntcInstancePtr,
XUartNs550 *UartInstancePtr,
u16 UartIntrId);
static void UartNs550DisableIntrSystem(INTC *IntcInstancePtr, u16 UartIntrId);
int UartNs550Init(u16 DeviceId, XUartNs550 *UartNs550);
#endif /* SRC_RS485_INTR_RECV_H_ */
八:结果总结
在串口助手中利用一个串口485发送数据,可以看到另一个串口PS端UART会打印出数据。
在本次实验中利用开发板PL端的1个RS485接口实现了PS端的中断控制通信,在代码设置一个RS485口为数据输入。
此外:RS485通信等价于RS422通信的单路,也即RS485设置为输出时,其引脚A,B等同于RS422的T+和T-。设置为输入是,其引脚A,B等同于RS422的R+和R-。因此根据工程需要可以和RS422引脚直用杜邦线连接通信,两路RS485等于一路RS422。这样就不用把RS485频繁切换输入输出方向了。