ZYNQ PS使用axi uartlite进行串口收发

前言

由于使用的ZYNQ PS部分只有两个串口,其中一个还当成了控制台用,串口不够用,于是使用PL的逻辑部分并利用IP核:AXI UARTLITE 为PS增加串口数量,并添加了AXI TIMER。
Vivado和Vitis为2020,PS为裸机使用。
包含以下内容:
1、Vivado的配置
2、axi uartlite代码
3、axi timer代码
4、利用IP核:axi timer,实现类似串口空闲中断的功能,这种思路我在FPGA、单片机和一些软件开发时经常使用,比较方便。
5、使用效果

一、Vivado配置

1.1. axi uartlite添加

我这里使用的波特率是460800,PL的时钟频率为120M,在配置的时候发现PL频率需要大于100M才能使用460800的波特率,比较费解。

1.2. axi timer添加


使能了两个通道

1.3. PL时钟修改

1.4. ZYNQ系统


最终的ZYNQ系统如图,注意axi uartlite 和axi timer都使能了中断。

二、axi uartlite代码

2.1. 头文件包含:

#include "xuartlite.h"
#include "xuartlite_l.h"
#include "stdio.h"
#include "xparameters.h"
#include "xil_printf.h"
#include "xscugic.h"

#define AXI_UARTLITE_DEVICE_ID		XPAR_AXI_UARTLITE_0_DEVICE_ID				//AXI_Timer设备ID
#define AXI_UARTLITE_IRPT_INTR		XPAR_FABRIC_AXI_UARTLITE_0_INTERRUPT_INTR	//AXI TIMER 中断号

XUartLite xUartLite1;

uint16_t xUartLite1_RecvCount=0;
uint8_t xUartLite1_RecvBuffer[1024];

2.2. 串口接收

初始化代码,注意串口中断回调是xUartLite1_Int_Handler函数,Sys_ScuGic顾名思义 是系统中断控制器的实例:

int xUartLite1_Init(XUartLite *xUartLite_Ptr)
{
	int Status;

	//初始化AXI UART
	Status = XUartLite_Initialize(xUartLite_Ptr,AXI_UARTLITE_DEVICE_ID);

	//自检
	Status = XUartLite_SelfTest(xUartLite_Ptr);
	if (Status != XST_SUCCESS)\
	{
		return XST_FAILURE;
	}

	//中断设置
	XScuGic_SetPriorityTriggerType(&Sys_ScuGic, AXI_UARTLITE_IRPT_INTR,0xA0, 0x3);

	//中断设置
	XScuGic_Connect(&Sys_ScuGic, AXI_UARTLITE_IRPT_INTR,(Xil_ExceptionHandler)xUartLite1_Int_Handler, (void *)xUartLite_Ptr);

	//使能 GIC中的中断
	XScuGic_Enable(&Sys_ScuGic, AXI_UARTLITE_IRPT_INTR);

	//中断使能
	XUartLite_EnableInterrupt(xUartLite_Ptr);

	return XST_SUCCESS;
}

中断回调函数:
串口相关的中断都会进入此函数,例如发送完成、接收到数据等,在此函数中,仅判断是否接收到数据,然后进入xUartLite1_RecvInt_Handler函数进行数据读取。

void xUartLite1_Int_Handler()
{
	int IsrStatus;

	IsrStatus = XUartLite_ReadReg(xUartLite1.RegBaseAddress,XUL_STATUS_REG_OFFSET);

	if((IsrStatus & (XUL_SR_RX_FIFO_FULL | XUL_SR_RX_FIFO_VALID_DATA)) != 0)
	{
		xUartLite1_RecvInt_Handler();
	}
}

//串口接收回调
void xUartLite1_RecvInt_Handler()
{
	//获取接收字节
	xUartLite1_RecvBuffer[xUartLite1_RecvCount++] = XUartLite_RecvByte(xUartLite1.RegBaseAddress);

	xTmrCh2_Count = 0;
}

2.3. 串口发送

直接调用库函数:

void xUartLite1_Send(uint8_t *Data,uint16_t Lenth)
{
	XUartLite_Send(&xUartLite1,Data,Lenth);
}

三、axi timer代码

比较简单,不做解释了,就这些代码,注意timer的ch1定时周期是1秒,ch2是1us,ch2是供串口使用的

#include "timer.h"

#define AXI_TIMER_DEVICE_ID		XPAR_AXI_TIMER_0_DEVICE_ID				//AXI_Timer设备ID
#define AXI_TIMER_IRPT_INTR		XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR	//AXI TIMER 中断号

uint8_t xTmr1Ch1_IntFlg=0;
uint32_t xTmrCh2_Count=0;

extern uint16_t xUartLite1_RecvCount;
extern uint8_t xUartLite1_RecvBuffer[1024];
extern void xUartLit1_ReceiveData_Process(uint8_t *Data,uint16_t Lenth);

XTmrCtr		xTmr1_Inst;	//AXI Timer

#define AXI_TIMER_CH1_PERIOD_US		1000000	//AXI TIMER CH1 的定时器周期为1S
#define AXI_TIMER_CH2_PERIOD_US		1		//AXI TIMER CH2 的定时器周期为1US

//定时器中断回调
void xTmrCtr_Int_Handler(void *CallBackRef, u8 TmrCtrNumber)
{
	//AXI TIMER 的定时器1
	if(TmrCtrNumber == AXI_TIMER_CHANNEL_1)
	{
		xTmr1Ch1_IntFlg = 1;
	}

	//AXI TIMER 的定时器2
	if(TmrCtrNumber == AXI_TIMER_CHANNEL_2)
	{
		xTmrCh2_Count++;

		if(xUartLite1_RecvCount != 0 && xTmrCh2_Count > 100)
		{
			xUartLit1_ReceiveData_Process(xUartLite1_RecvBuffer,xUartLite1_RecvCount);

			//重新开始接收
			xUartLite1_RecvCount = 0;
		}
	}
}

//定时的微秒数转寄存器值
//AXI TIMER是倒计时,如果想定时1MS,那么寄存器中要写入:最大值-1MS
//AXI TIMER的时钟为120M,1US为120个Tick
u32 xTmr_US_To_RegValue(u32 US)
{
	u32 Value;

	Value = 120*US;

	return 0xFFFFFFFF - Value;
}

//定时的微秒数转纳秒
u32 xTmr_US_To_NS(u32 US)
{
	return US*1000;
}

//AXITIMER初始化
int xTmrCtr_Init(XTmrCtr *xTmrCtr_Ptr)
{
	int Status;

	//初始化AXI_TIMER 0
	Status = XTmrCtr_Initialize(xTmrCtr_Ptr, AXI_TIMER_DEVICE_ID);
	if(Status != XST_SUCCESS)
	{
		return XST_FAILURE;
	}

	//自检:两个定时器
	Status = XTmrCtr_SelfTest(xTmrCtr_Ptr, 2-1);
	if(Status != XST_SUCCESS)
	{
		return XST_FAILURE;
	}

	//设置中断回调函数
	XTmrCtr_SetHandler(xTmrCtr_Ptr,xTmrCtr_Int_Handler,xTmrCtr_Ptr);

	//定时器1中断计数
	XTmrCtr_SetResetValue(xTmrCtr_Ptr,AXI_TIMER_CHANNEL_1,xTmr_US_To_RegValue(AXI_TIMER_CH1_PERIOD_US));
	//定时器2中断计数
	XTmrCtr_SetResetValue(xTmrCtr_Ptr,AXI_TIMER_CHANNEL_2,xTmr_US_To_RegValue(AXI_TIMER_CH2_PERIOD_US));

	//定时器中断打开
	XTmrCtr_SetOptions(xTmrCtr_Ptr, AXI_TIMER_CHANNEL_1, XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION);
	XTmrCtr_SetOptions(xTmrCtr_Ptr, AXI_TIMER_CHANNEL_2, XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION);

	//中断设置
	XScuGic_Connect(&Sys_ScuGic, AXI_TIMER_IRPT_INTR,(Xil_ExceptionHandler)XTmrCtr_InterruptHandler, (void *)xTmrCtr_Ptr);

	//使能 GIC中的定时器中断
	XScuGic_Enable(&Sys_ScuGic, AXI_TIMER_IRPT_INTR);

	return XST_SUCCESS;
}

void xTmr_Enable(XTmrCtr *xTmrCtr_Ptr,uint8_t Channel)
{
	XTmrCtr_Start(xTmrCtr_Ptr,Channel);
}

void xTmr_Disable(XTmrCtr *xTmrCtr_Ptr,uint8_t Channel)
{
	XTmrCtr_Stop(xTmrCtr_Ptr,Channel);
}

四、利用定时器实现串口空闲中断

串口波特率是460800,一个字节大约耗时21us,每当收到串口数据时,定时器清0,在定时器1us的回调中,检测到串口接收长度不为0,且定时器时间>100us,认为一帧数据接收完成,100us的时间可以调整。

//串口接收
void xUartLite1_RecvInt_Handler()
{
	//获取接收字节
	xUartLite1_RecvBuffer[xUartLite1_RecvCount++] = XUartLite_RecvByte(xUartLite1.RegBaseAddress);

	xTmrCh2_Count = 0;
}

//定时器判断串口接收
//定时器中断回调
void xTmrCtr_Int_Handler(void *CallBackRef, u8 TmrCtrNumber)
{
	//AXI TIMER 的定时器1
	if(TmrCtrNumber == AXI_TIMER_CHANNEL_1)
	{
		xTmr1Ch1_IntFlg = 1;
	}

	//AXI TIMER 的定时器2
	if(TmrCtrNumber == AXI_TIMER_CHANNEL_2)
	{
		xTmrCh2_Count++;

		//认为一帧数据接收完成,可以处理
		if(xUartLite1_RecvCount != 0 && xTmrCh2_Count > 100)
		{
			xUartLit1_ReceiveData_Process(xUartLite1_RecvBuffer,xUartLite1_RecvCount);
			//重新开始接收
			xUartLite1_RecvCount = 0;
		}
	}
}

五、使用效果

每次收到一帧数据后,打印长度、第一个数据和最后一个数据

void xUartLit1_ReceiveData_Process(uint8_t *Data,uint16_t Lenth)
{
	xil_printf("AXI UartLite Receive Process,Lenth: %d,Data:%02X,%02X\n",Lenth,Data[0],Data[Lenth-1]);
}


完整工程下载连接,包括vivado和vitis:
https://download.csdn.net/download/qq_36365231/88677067

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值