这一部分分为两个部分:
该部分最后会设计一个自动收发装置,即当上位机通过串口向开发板发送数据后,开发板会发回所有的数据。假定上位机发送的数据小于256位,这样可以用一个u8型即可以完成索引。
该部分主要是使用串口收发数据,使用的资源是PS中MIO12、13两个IO口确定的UART1通道。然后其余无需任何其他资源。故而可以将AXI总线关闭。
主要修改一下几个部分:
导出硬件配置时由于没有AXI总线参与,就不需要bitstream了,然后开始编写PS程序。
通过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 |
具体每种模式是怎么工作的如下图所示。
具体来说:
Normal Mode是最标准的Uart模式,即收发数据均通过控制器
Automatic Echo 即接收的数据直接发送出去,并接收回控制器。
Local Loopback 即控制器控制发送的数据直接接收回控制器。
Remote Loopback 即接收来的数据直接发送出去,数据不经过控制器。
[参考自ZYNQ7000 TRM (Page.598)]
这里发生了很多很诡异的事情,总结下来就是:实际上我使用的串口调试助手的运行机制比我想象中的要复杂得多。针对串口这一极为简单的通讯方式,大多数都给出了很多优化的方式。如果不考虑种种优化的具体机制,而按照最直观的方式去考虑串口的运行原理,其容易发生各种问题。
具体需要使用到的函数有:
u32 XUartPs_Recv(
XUartPs *InstancePtr,
u8 *BufferPtr,
u32 NumBytes
); // 串口接收
u32 XUartPs_Send(
XUartPs *InstancePtr,
u8 *BufferPtr,
u32 NumBytes
); // 串口发送
void xil_printf(
const char8 *ctrl1,
...
); // 串口发送(发送字符串)
这里收发实验实际上有一些小问题,该部分仅仅用于较为简单的测试,比较容易出现各种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的话,就比较糟糕了,这里就不多做纠结了。
以上,串口发送部分完成。