基于ZYNQ-7000开发板的调试系列(3)

3. 基于PS的串口通讯

这一部分分为两个部分:

  1. 串口发送数据
  2. 串口接收数据

该部分最后会设计一个自动收发装置,即当上位机通过串口向开发板发送数据后,开发板会发回所有的数据。假定上位机发送的数据小于256位,这样可以用一个u8型即可以完成索引。

基于PS的串口通讯

该部分主要是使用串口收发数据,使用的资源是PS中MIO12、13两个IO口确定的UART1通道。然后其余无需任何其他资源。故而可以将AXI总线关闭。

1. 创建Block

1. 创建一个ZYNQ7 Processing System

主要修改一下几个部分:

  1. PS-PL Configuration 关闭AXI总线。
    基于ZYNQ-7000开发板的调试系列(3)_第1张图片
  2. Peripheral I/O Pins 开启UART1。
    基于ZYNQ-7000开发板的调试系列(3)_第2张图片3. DDR Configuration 修改DDR配置。
    基于ZYNQ-7000开发板的调试系列(3)_第3张图片
    自动连接后创建HDL Wrapper后即可生成Bitstream文件,具体完成就是如下的样子。
    基于ZYNQ-7000开发板的调试系列(3)_第4张图片
    [参考自CREATE HDL WRAPPER的问题]
    主要是这段话:
    The Create HDL Wrapper dialog box lets you define a new HDL wrapper for the current block design. The wrapper defines the block design as the top-level design, so you can synthesize, implement, and generate a bitstream for the block design.

导出硬件配置时由于没有AXI总线参与,就不需要bitstream了,然后开始编写PS程序。
基于ZYNQ-7000开发板的调试系列(3)_第5张图片

2. PS程序编写

1. 初始化

通过Launch SDK打开Xilinx SDK。创建一个空的应用项目。这里使用串口,实际上这里直接可以使用HelloWorld模板。但是为了学习需要,这一部分还是先建立空白应用项目。
先引入一部分必须引入的模块:

#include "xparameters.h"
#include "xuartps.h"
#include "xil_printf.h"

然后是初始化和自检测环节:
用到的函数有

XUartPs_Config *XUartPs_LookupConfig(
	u16 DeviceId
); // 根据设备ID查看配置文件
s32 XUartPs_CfgInitialize(
	XUartPs *InstancePtr,
	XUartPs_Config * Config, 
	u32 EffectiveAddr
); // 初始化
s32 XUartPs_SelfTest(
	XUartPs *InstancePtr
); // 自测
void XUartPs_SetOperMode(
	XUartPs *InstancePtr, 
	u8 OperationMode
); // 设置工作模式
s32 XUartPs_SetBaudRate(
	XUartPs *InstancePtr, 
	u32 BaudRate
); // 设置波特率

这里最后最后这里需要稍微注意一下:

XUartPs_SetOperMode(&psUart, XUARTPS_OPER_MODE_NORMAL);

这里有四种模式:

No. Mode keyword
1 Normal XUARTPS_OPER_MODE_NORMAL
2 Automatic Echo XUARTPS_OPER_MODE_AUTO_ECHO
3 Local Loopback XUARTPS_OPER_MODE_LOCAL_LOOP
4 Remote Loopback XUARTPS_OPER_MODE_REMOTE_LOOP

具体每种模式是怎么工作的如下图所示。
基于ZYNQ-7000开发板的调试系列(3)_第6张图片
具体来说:
Normal Mode是最标准的Uart模式,即收发数据均通过控制器
Automatic Echo 即接收的数据直接发送出去,并接收回控制器。
Local Loopback 即控制器控制发送的数据直接接收回控制器。
Remote Loopback 即接收来的数据直接发送出去,数据不经过控制器。
[参考自ZYNQ7000 TRM (Page.598)]

2. 通讯

这里发生了很多很诡异的事情,总结下来就是:实际上我使用的串口调试助手的运行机制比我想象中的要复杂得多。针对串口这一极为简单的通讯方式,大多数都给出了很多优化的方式。如果不考虑种种优化的具体机制,而按照最直观的方式去考虑串口的运行原理,其容易发生各种问题。

具体需要使用到的函数有:

u32 XUartPs_Recv(
	XUartPs *InstancePtr,
	u8 *BufferPtr, 
	u32 NumBytes
); // 串口接收
u32 XUartPs_Send(
	XUartPs *InstancePtr, 
	u8 *BufferPtr,
	u32 NumBytes
); // 串口发送
void xil_printf(
	const char8 *ctrl1, 
	...
); // 串口发送(发送字符串)

3. 具体收发实验

这里收发实验实际上有一些小问题,该部分仅仅用于较为简单的测试,比较容易出现各种bug。
在此基础上,需要规定数据具有头部,头部由3个字节组成:0x55、0xAA、数据长度。之后可以接收数据长度的数据串。
具体的代码如下:

#include "xparameters.h"
#include "xuartps.h"
#include "xil_printf.h"

#define UART_DEVICE_ID 	XPAR_XUARTPS_0_DEVICE_ID
#define BUF_SIZE		255
#define BAUDRATE		115200
#define HEAD			0x03

int main();
int Initial();

XUartPs psUart;
static u8 sendBuf[BUF_SIZE];
static u8 recvBuf[BUF_SIZE];

int main(){
	u8 sendCount;
	u8 recvCount;
	u8 recvLeft;
	u8 LoopCount;
	u8 tmp;
	Initial();
	while(1){
		for(tmp=0; tmp<BUF_SIZE; tmp++){
			recvBuf[tmp] = 0x00;
			sendBuf[tmp] = 0x00;
		}
		tmp = 0x00;
		recvLeft = 0x00;

		while(tmp < HEAD){
			tmp += XUartPs_Recv(&psUart, &recvBuf[tmp], (HEAD-tmp));

		}
		if(recvBuf[0] == 0x55 && recvBuf[1] == 0xAA){
			recvCount = recvBuf[2];
			for(tmp=0xFF; tmp>2; tmp--){
				if(recvBuf[tmp] != 0x00){
					recvLeft = tmp;
					break;
				}
			}
			if(recvLeft != 0){
				for(tmp=0x02; tmp<recvLeft; tmp++){
					recvBuf[tmp-2] = recvBuf[tmp];
				}
			}
		}
		else continue;
		tmp = recvLeft;
		while(tmp < recvCount){
			tmp += XUartPs_Recv(&psUart, &recvBuf[tmp], (recvCount-tmp));
		}
		sendCount = recvCount;
		for(int i=0; i<sendCount; i++){
			sendBuf[i] = recvBuf[i];
		}
		XUartPs_Send(&psUart, &sendBuf[0], sendCount);
		while (XUartPs_IsSending(&psUart)) {
			LoopCount++;
		}
		tmp += XUartPs_Recv(&psUart, &recvBuf[tmp], 0xFF);// To avoid residual data sent by user
	}
	return 0;
}


int Initial(){
	int status;
	XUartPs_Config *config;


	config = XUartPs_LookupConfig(UART_DEVICE_ID);
	if(config == NULL){
		return XST_FAILURE;
	}
	status = XUartPs_CfgInitialize(&psUart, config, config->BaseAddress);
	if(status != XST_SUCCESS){
		return XST_FAILURE;
	}
	status = XUartPs_SelfTest(&psUart);
	if (status != XST_SUCCESS){
		return XST_FAILURE;
	}

	XUartPs_SetOperMode(&psUart, XUARTPS_OPER_MODE_NORMAL);
	XUartPs_SetBaudRate(&psUart, BAUDRATE);
	return XST_SUCCESS;
}

这个程序必须具有一个头部否则无法知道具体要接收多少位,而且,由于串口调试助手的优化,数据很有可能不是同一时间一起发送的,导致仅仅接收一次很有可能出现丢包的现象。
当然也通过在发送数据末尾加flag指示数据发送结束,但是如果碰到数据正好是flag的话,就比较糟糕了,这里就不多做纠结了。
以上,串口发送部分完成。

你可能感兴趣的:(FPGA)