FPGA MPSOC-5EV高速摄像机+专业显示器功能之 硬件开发,我是用5EV,开发一个带HDMIL2.0输入输出显示功能,有6路12G-SDI输入输出显示一个专业显示器。主要跟大家一起分享,这个开发的过程,有很多不足,希望大家指整,欢迎各位老铁发邮件给我(1041503164@qq.com)指证。首先,把我们要需求理清楚,
我会在重点前面两块---硬件与软件记录分享
我们目标产品是做一个高速摄像机,的硬件需求是有1-12GSDI输入,同时环出;2-HDMI输入输出,要支持4K@60;3-千兆网口;4-RS422接口出与入;5-EDP输出到屏;7-支持硬件存取;8-音频输入输出;9-按键有的快捷键;10-电池供电;11-TAILL灯指示;
FPGA图形化软件IP设计:
对应上面的接进行输入输出操作;1-12GSDI输入,我用的是SMPTE UHD-SDI TXSUB SYSTEM,赛林思自带IP核。这个还没有做,做好给大家分享。
根据上面的需求,我做成硬件框图:
我们调试口,高速网口是,UART,JTAG调试FLAHS,及EMMC都是在PS端,12G-SDI IN 和OUT,HDMI IN OUT,都是在PL端。接下来根每一个模块把原理图做上去,因为原理图比较大只贴几张图,12G-SDI图输出图
隔了很久没有更新了,把最近工作做一次总结,这部分与FPGA的高速走线进行连接,再有IBERT 进行硬件检测,得出硬件基本性能,用单例程对12G-SDI进行运行,情况:对12G-SDI,在没有打通v_smpte_uhdsdi_tx_ss的IP时可以用IBERT来测试这个接口的硬件是否可行的,我的测试如下:
在ERAR没有出现错误,STAT没有红就说明,硬件这块基本是通的,可以通过网眼图的大小看电路板相关指标。
接下来是对PS端的UART1,UART2,QSPI,要对各个模块的FPGA进行软件调试,首先要在VIVADO里把UART1,UART2勾选上。
导出FPGA的XSA文件
单程序UART调试成功:
VITIS代码,是自发,自收。从PC机通过串口发通过FPGA转发出来。具体效果如上图
****************************************************/
#include "sys_intr.h"
#include "uartps_intr.h"
/**********************************cxc***********************/
#include "xparameters.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "xplatform_info.h"
#include <xil_printf.h>
#ifndef GPIO_DEVICE_ID
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#endif
#define printf xil_printf /* Smalller foot-print printf */
XGpioPs Gpio; /* The driver instance for GPIO Device. */
#define LED0 (40)//MIO
#define LED1 (42)//MIO
#define LED2 (78)//EMIO
#define LED3 (79)//EMIO
#define BTN0 (43)//MIO
#define BTN1 (80)//EMIO
int uart_read_data ; /* Buffer for uart_read_data Data */
/*************************cxc******************************/
extern XScuGic Intc; //GIC
extern XUartPs UartPs;//uart
extern u8 *SendBuffer; /* Buffer for Transmitting Data */
extern u8 *RecvBuffer ; /* Buffer for Receiving Data */
void init_intr_sys(void)
{
Init_Intr_System(&Intc);
Init_UartPsIntr(&UartPs,UART_DEVICE_ID);
UartPs_Setup_IntrSystem(&Intc, &UartPs, UART_INT_IRQ_ID);
Setup_Intr_Exception(&Intc);
}
int GpioPolledInit(u16 DeviceId);
int main(void)
{
//xil_printf(“Hello World”);
init_intr_sys();
XUartPs_Recv(&UartPs, RecvBuffer, TEST_BUFFER_SIZE);
//uart_read_data=XUartPs_ReceiveBuffer();
if (uart_read_data==2)
{
XGpioPs_WritePin(&Gpio, LED1, 1);
}
else
{
XGpioPs_WritePin(&Gpio, LED1, 0);
}
// while(1);
return 0;
}
/*******cxc*********************************/
int GpioPolledInit(u16 DeviceId)
{
int Status;
XGpioPs_Config *ConfigPtr;
/* Initialize the GPIO driver. */
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
//set LED0~LED5 output
XGpioPs_SetDirectionPin(&Gpio, LED0, 0x1);
XGpioPs_SetDirectionPin(&Gpio, LED1, 0x1);
XGpioPs_SetDirectionPin(&Gpio, LED2, 0x1);
XGpioPs_SetDirectionPin(&Gpio, LED3, 0x1);
//set BTN0~BTN2 input
XGpioPs_SetDirectionPin(&Gpio,BTN0, 0x0);
XGpioPs_SetDirectionPin(&Gpio,BTN1, 0x0);
XGpioPs_SetOutputEnablePin(&Gpio, LED0, 1);
XGpioPs_SetOutputEnablePin(&Gpio, LED1, 1);
XGpioPs_SetOutputEnablePin(&Gpio, LED2, 1);
XGpioPs_SetOutputEnablePin(&Gpio, LED3, 1);
XGpioPs_WritePin(&Gpio, LED0, 1);
XGpioPs_WritePin(&Gpio, LED1, 1);
XGpioPs_WritePin(&Gpio, LED2, 1);
XGpioPs_WritePin(&Gpio, LED3, 1);
return Status;
}
/**************cxc*************************/
通串口输入1关闭GPIO,为后面的各个子模块合并做基础,这里有几个知识点,1-GOPI操作只用CXC围起来的哪些代码进行修改就是可以操作任何一个GPIO口了。
操作效果如下:在串口输1,两个灭了
近期把一个7010的PL端一个串口程序移到4EV上,自发自收,波特率不对,这块一直没有解决先做一个记号,继续板砖。移植这个程有几点是原来没有的第一个是把差分时钟转成单端时钟,还有就是原来是50M现在改成200M这也可能是波特率不对原因----------备注:此程序可以UART进行自发自收,能控制GPIO口,但还有一个问题收发波特率不对。
把代码发出来,TOP的代码:
//****************************************Copyright (c)***********************************//
//----------------------------------------------------------------------------------------
// File name: uart_loop
// Last modified Date: 2019/10/8 17:25:36
// Last Version: V1.1
// Descriptions: 串口数据环回模块
//----------------------------------------------------------------------------------------
// Created by:
// Created date: 2019/10/8 17:25:36
// Version: V1.0
// Descriptions: The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module uart_loop(
input sys_clk, //系统时钟
input sys_rst_n, //系统复位,低电平有效
input recv_done, //接收一帧数据完成标志
input [7:0] recv_data, //接收的数据
input tx_busy, //发送忙状态标志
output reg send_en, //发送使能信号
output reg [7:0] send_data //待发送数据
);
//reg define
reg recv_done_d0;
reg recv_done_d1;
reg tx_ready;
//wire define
wire recv_done_flag;
//*****************************************************
//** main code
//*****************************************************
//捕获recv_done上升沿,得到一个时钟周期的脉冲信号
assign recv_done_flag = (~recv_done_d1) & recv_done_d0;
//对发送使能信号recv_done延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
recv_done_d0 <= 1'b0;
recv_done_d1 <= 1'b0;
end
else begin
recv_done_d0 <= recv_done;
recv_done_d1 <= recv_done_d0;
end
end
//判断接收完成信号,并在串口发送模块空闲时给出发送使能信号
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
tx_ready <= 1'b0;
send_en <= 1'b0;
send_data <= 8'd0;
end
else begin
if(recv_done_flag)begin //检测串口接收到数据
tx_ready <= 1'b1; //准备启动发送过程
send_en <= 1'b0;
send_data <= recv_data; //寄存串口接收的数据
end
else if(tx_ready && (~tx_busy)) begin //检测串口发送模块空闲
tx_ready <= 1'b0; //准备过程结束
send_en <= 1'b1; //拉高发送使能信号
end
end
end
/*
ila_0 u_ila_0 (
.clk(sys_clk), // input wire clk
.probe0(send_data), // input wire [0:0] probe0
.probe1(send_en),
.probe2(tx_ready)
//.probe1(led_o) // input wire [0:0] probe0
);
*/
endmodule
UART接收模块的代码:
//`timescale 1ns / 1ps
//----------------------------------------------------------------------------------------
// File name: uart_recv
// Last modified Date: 2019/10/9 9:56:36
// Last Version: V1.1
// Descriptions: UART串口接收模块
//----------------------------------------------------------------------------------------
// Created by:
// Created date: 2019/10/9 9:56:36
// Version: V1.0
// Descriptions: The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module uart_recv(
input sys_clk, //系统时钟
input sys_rst_n, //系统复位,低电平有效
input uart_rxd, //UART接收端口
output reg uart_done, //接收一帧数据完成标志
output reg [7:0] uart_data //接收的数据
);
//parameter define
parameter CLK_FREQ = 200000000; //系统时钟频率
parameter UART_BPS = 9600; //串口波特率
localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,
//需要对系统时钟计数BPS_CNT次
//reg define
reg uart_rxd_d0;
reg uart_rxd_d1;
reg [15:0] clk_cnt; //系统时钟计数器
reg [ 3:0] rx_cnt; //接收数据计数器
reg rx_flag; //接收过程标志信号
reg [ 7:0] rxdata; //接收数据寄存器
//wire define
wire start_flag;
//*****************************************************
//** main code
//*****************************************************
//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);
//对UART接收端口的数据延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
uart_rxd_d0 <= 1'b0;
uart_rxd_d1 <= 1'b0;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
end
end
//当脉冲信号start_flag到达时,进入接收过程
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
rx_flag <= 1'b0;
else begin
if(start_flag) //检测到起始位
rx_flag <= 1'b1; //进入接收过程,标志位rx_flag拉高
//计数到停止位中间时,停止接收过程
else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2))
rx_flag <= 1'b0; //接收过程结束,标志位rx_flag拉低
else
rx_flag <= rx_flag;
end
end
//进入接收过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
clk_cnt <= 16'd0;
else if ( rx_flag ) begin //处于接收过程
if (clk_cnt < BPS_CNT - 1)
clk_cnt <= clk_cnt + 1'b1;
else
clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零
end
else
clk_cnt <= 16'd0; //接收过程结束,计数器清零
end
//进入接收过程后,启动接收数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
rx_cnt <= 4'd0;
else if ( rx_flag ) begin //处于接收过程
if (clk_cnt == BPS_CNT - 1) //对系统时钟计数达一个波特率周期
rx_cnt <= rx_cnt + 1'b1; //此时接收数据计数器加1
else
rx_cnt <= rx_cnt;
end
else
rx_cnt <= 4'd0; //接收过程结束,计数器清零
end
//根据接收数据计数器来寄存uart接收端口数据
always @(posedge sys_clk or negedge sys_rst_n) begin
if ( !sys_rst_n)
rxdata <= 8'd0;
else if(rx_flag) //系统处于接收过程
if (clk_cnt == BPS_CNT/2) begin //判断系统时钟计数器计数到数据位中间
case ( rx_cnt )
4'd1 : rxdata[0] <= uart_rxd_d1; //寄存数据位最低位
4'd2 : rxdata[1] <= uart_rxd_d1;
4'd3 : rxdata[2] <= uart_rxd_d1;
4'd4 : rxdata[3] <= uart_rxd_d1;
4'd5 : rxdata[4] <= uart_rxd_d1;
4'd6 : rxdata[5] <= uart_rxd_d1;
4'd7 : rxdata[6] <= uart_rxd_d1;
4'd8 : rxdata[7] <= uart_rxd_d1; //寄存数据位最高位
default:;
endcase
end
else
rxdata <= rxdata;
else
rxdata <= 8'd0;
end
//数据接收完毕后给出标志信号并寄存输出接收到的数据
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
uart_data <= 8'd0;
uart_done <= 1'b0;
end
else if(rx_cnt == 4'd9) begin //接收数据计数器计数到停止位时
uart_data <= rxdata; //寄存输出接收到的数据
uart_done <= 1'b1; //并将接收完成标志位拉高
end
else begin
uart_data <= 8'd0;
uart_done <= 1'b0;
end
end
endmodule
中转模块代码:
//****************************************Copyright (c)***********************************//
//----------------------------------------------------------------------------------------
// File name: uart_loop
// Last modified Date: 2019/10/8 17:25:36
// Last Version: V1.1
// Descriptions: 串口数据环回模块
//----------------------------------------------------------------------------------------
// Created by:
// Created date: 2019/10/8 17:25:36
// Version: V1.0
// Descriptions: The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module uart_loop(
input sys_clk, //系统时钟
input sys_rst_n, //系统复位,低电平有效
input recv_done, //接收一帧数据完成标志
input [7:0] recv_data, //接收的数据
input tx_busy, //发送忙状态标志
output reg send_en, //发送使能信号
output reg [7:0] send_data //待发送数据
);
//reg define
reg recv_done_d0;
reg recv_done_d1;
reg tx_ready;
//wire define
wire recv_done_flag;
//*****************************************************
//** main code
//*****************************************************
//捕获recv_done上升沿,得到一个时钟周期的脉冲信号
assign recv_done_flag = (~recv_done_d1) & recv_done_d0;
//对发送使能信号recv_done延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
recv_done_d0 <= 1'b0;
recv_done_d1 <= 1'b0;
end
else begin
recv_done_d0 <= recv_done;
recv_done_d1 <= recv_done_d0;
end
end
//判断接收完成信号,并在串口发送模块空闲时给出发送使能信号
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
tx_ready <= 1'b0;
send_en <= 1'b0;
send_data <= 8'd0;
end
else begin
if(recv_done_flag)begin //检测串口接收到数据
tx_ready <= 1'b1; //准备启动发送过程
send_en <= 1'b0;
send_data <= recv_data; //寄存串口接收的数据
end
else if(tx_ready && (~tx_busy)) begin //检测串口发送模块空闲
tx_ready <= 1'b0; //准备过程结束
send_en <= 1'b1; //拉高发送使能信号
end
end
end
/*
ila_0 u_ila_0 (
.clk(sys_clk), // input wire clk
.probe0(send_data), // input wire [0:0] probe0
.probe1(send_en),
.probe2(tx_ready)
//.probe1(led_o) // input wire [0:0] probe0
);
*/
endmodule
发送模块代码:
//`timescale 1ns / 1ps
//----------------------------------------------------------------------------------------
// File name: uart_send
// Last modified Date: 2019/10/9 10:07:36
// Last Version: V1.1
// Descriptions: UART串口发送模块
//----------------------------------------------------------------------------------------
// Created by:
// Created date: 2019/10/9 10:07:36
// Version: V1.0
// Descriptions: The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module uart_send(
input sys_clk, //系统时钟
input sys_rst_n, //系统复位,低电平有效
input uart_en, //发送使能信号
input [7:0] uart_din, //待发送数据
output uart_tx_busy, //发送忙状态标志
output reg uart_txd //UART发送端口
);
//parameter define
parameter CLK_FREQ = 200000000; //系统时钟频率
parameter UART_BPS = 9600; //串口波特率
localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次
//reg define
reg uart_en_d0;
reg uart_en_d1;
reg [15:0] clk_cnt; //系统时钟计数器
reg [ 3:0] tx_cnt; //发送数据计数器
reg tx_flag; //发送过程标志信号
reg [ 7:0] tx_data; //寄存发送数据
//wire define
wire en_flag;
//*****************************************************
//** main code
//*****************************************************
//在串口发送过程中给出忙状态标志
assign uart_tx_busy = tx_flag;
//捕获uart_en上升沿,得到一个时钟周期的脉冲信号
assign en_flag = (~uart_en_d1) & uart_en_d0;
//对发送使能信号uart_en延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
uart_en_d0 <= 1'b0;
uart_en_d1 <= 1'b0;
end
else begin
uart_en_d0 <= uart_en;
uart_en_d1 <= uart_en_d0;
end
end
//当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
tx_flag <= 1'b0;
tx_data <= 8'd0;
end
else if (en_flag) begin //检测到发送使能上升沿
tx_flag <= 1'b1; //进入发送过程,标志位tx_flag拉高
tx_data <= uart_din; //寄存待发送的数据
end
//计数到停止位结束时,停止发送过程
else if ((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT -(BPS_CNT/16))) begin
tx_flag <= 1'b0; //发送过程结束,标志位tx_flag拉低
tx_data <= 8'd0;
end
else begin
tx_flag <= tx_flag;
tx_data <= tx_data;
end
end
//进入发送过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
clk_cnt <= 16'd0;
else if (tx_flag) begin //处于发送过程
if (clk_cnt < BPS_CNT - 1)
clk_cnt <= clk_cnt + 1'b1;
else
clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零
end
else
clk_cnt <= 16'd0; //发送过程结束
end
//进入发送过程后,启动发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
tx_cnt <= 4'd0;
else if (tx_flag) begin //处于发送过程
if (clk_cnt == BPS_CNT - 1) //对系统时钟计数达一个波特率周期
tx_cnt <= tx_cnt + 1'b1; //此时发送数据计数器加1
else
tx_cnt <= tx_cnt;
end
else
tx_cnt <= 4'd0; //发送过程结束
end
//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
uart_txd <= 1'b1;
else if (tx_flag)
case(tx_cnt)
4'd0: uart_txd <= 1'b0; //起始位
4'd1: uart_txd <= tx_data[0]; //数据位最低位
4'd2: uart_txd <= tx_data[1];
4'd3: uart_txd <= tx_data[2];
4'd4: uart_txd <= tx_data[3];
4'd5: uart_txd <= tx_data[4];
4'd6: uart_txd <= tx_data[5];
4'd7: uart_txd <= tx_data[6];
4'd8: uart_txd <= tx_data[7]; //数据位最高位
4'd9: uart_txd <= 1'b1; //停止位
default: ;
endcase
else
uart_txd <= 1'b1; //空闲时发送端口为高电平
end
endmodule