【正点原子FPGA连载】第十六章DP彩条显示实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html
第十六章DP彩条显示实验
DP接口即DisplayPort接口,DisplayPort是由视频电子标准协会(VESA)发布的显示接口。作为DVI的继任者,DisplayPort将在传输视频信号的同时加入对高清音频信号传输的支持,同时支持更高的分辨率和刷新率。它能够支持单通道、单向、四线路连接,数据传输率10.8Gbps,足以传送未经压缩的视频和相关音频,同时还支持1Mbps的双向辅助通道,供设备控制之用,此外还支持8位和10位颜色。在数据传输上,DisplayPort使用了“micro-packetised”格式。VESA还表示,DisplayPort具备高度的可扩展性,可以在今后不断加入更多新内容。本节实验将带领大家一起去学习使用DP接口实现彩条显示的实验。
本章包括以下几个部分:
1616.1简介
16.2实验任务
16.3硬件设计
16.4软件设计
16.5下载验证
16.1简介
DP接口即DisplayPort接口,DisplayPort是由视频电子标准协会(VESA)发布的显示接口。它不仅可以支持全高清显示分辨率(1920×1080),还能支持4k分辨率(3840×2160),以及最新的8k分辨率(7680×4320)。DP接口不仅传输率高,而且可靠稳定,其接口传输的信号由传输图像的数据通道信号以及传输图像相关的状态、控制信息的辅助通道信号组成,具体包含DisplayPort数据传输主要通道(Main Link)、辅助通道(AUX Channel)与连接(Link Training)。
DP接口示意图如下所示:
图 16.1.1 DP接口示意图
从上图中可以看出DP接口由主链路(Main link)、辅助通道(AUC_CH)以及热插拔检测(HPD)信号线组成。其中主链路是单向的、高带宽、低延迟的信道,用于传输同步数据流,如未压缩的视频和音频;辅助通道是半双工,双向通道,用于链路管理和设备控制;热插拔检测(HPD)信号线可以作热插拔检测信号也可以作为接收设备的中断请求。
主链路由四对交流耦合差分信号通道组成(支持只是用一对通信、2对通信或者4对通信三种模式),单个通道的传输速率有1.62Gbps、2.7Gbps、5.4Gbps以及8.1Gbps四种,多个通道同时工作的情况下,所有通道必须使用同一种传输速率。信道的传输速率与像素传输速率的解耦有关,信道的数量与像素位深度(每像素位或bpp)和组件位深度(每组件位或bpc)的解耦有关。在DisplayPort中,无论主链路的个数如何,对于像素编码格式为RGB、YCbCr 4:4:4 / 4:2:2 / 4:2:0 Y-only的像素数据,支持6,8,10,12,16的位深度;而对于RAW像素编码格式得数据,支持像素6、7、8、10、12、14和16位的位深度。
这里还要提醒大家一点DP接口协议中是没有单独得像素时钟通道的,时钟是从ANSI 8b/10b编码规则编码的数据流中提取的。
在进行DP协议数据传输时可以选择不同的通道数量(1、2或4),并且单个通道的速率也是可选的,下面给大家列出一张表展示不同通道速率不同通道模式下DP传输的带宽,如下表所示:
图 16.1.2 DP Main-Link Application Bandwidth
看完了DP的主链路,我们再来看辅助通道(AUC_CH),AUX_CH采用交流耦合差分传输方式,是一条双向半双工传输通道,单一方向速率仅1Mbit/s左右,用来传输设定与控制指令。在AUX CH上使用Manchester-II编码作为AUX交易的信道编码,与Main-Link的情况一样,从数据流中提取时钟。AUX CH有一个半双工,双向PHY层。Source设备是主设备,Sink设备是从设备。Sink设备可以通过切换HPD信号来提示Source设备发起一个AUX请求事务来读取DPCD Link/Sink状态寄存器位,包括IRQ_HPD向量寄存器位。AUX(Auxiliary)的用途包括读取扩展显示识别数据(EDID),以确保DP信号的正确传输;读取显示器所支持的DP接口的信息(如主要通道的数量和DP信号的传输速率;进行各种显示组态暂存器的设定);读取显示器状态暂存器等等。因此,只有先保证AUX的信号正确才能使DP接口的主链路信号正确传输。
而HPD通道就比较简单了,这个通道是由接收端发出的单向中断信号。当接收端设备拉低HPD信号,信号脉冲宽度在0.25ms~2ms之间,此时发送端会在HPD信号上升沿后的100ms以内重新读取接收端设备信息。如果HPD信号拉低时间超过2ms(例如DP数据线被拔掉了,断开连接),发送设备就会停止发送数据,等待接收设备重新连接上。
下面我们来具体了解一下DP接口每个引脚的定义,DP接口图如下所示:
图 16.1.3 DP接口
上图所示的左边大的是正常的DisplayPort接口,而右边小的是Mini DisplayPort接口。Mini DisplayPort是一个微型版本的DisplayPort,由苹果公司于2008年10月14日发表。DP接头的尺寸约为16x4.8毫米,mini DP接头尺寸约为其一半。DP2.0最高支持8K 60Hz,mini DP最高仅支持4K 60Hz。DP版本仍在更新,mini DP一直停留在1.2版本,并不再更新。我们MPSOC开发板板载的就是Mini DisplayPort接口。在引脚上无论Mini DP接口还是普通DP接口都采用了20针引脚设计,如下图所示:
图 16.1.4 DP接口引脚图
每个引脚的功能如下表所示:
到这里关于DP接口就给大家介绍完了,关于更加详细的DP标准介绍大家可以去查看官方的DP协议标准文档。
16.2实验任务
本节实验将使用MPSOC开发板实现DP接口彩条显示实验,如果大家没有DP显示器也不要紧,可以使用Mini DP转HDMI线实现DP接口数据在HDMI显示屏上显示。Mini DP转HDMI线可以在正点原子店铺购买,大家切记不同厂家的转接线存在一定差异,其他渠道的转接线可能无法正常显示。
16.3硬件设计
MPSOC开发板板载一个Mini DP接口,其原理图如下所示:
图 16.3.1 Mini DP接口原理图
从上图中可以看到MPSOC开发板板载的Mini DP接口采用的是2通道模式,且差分引脚连接到了BANK505;辅助通道和热插拔引脚连接到BANK501的MIO27~MIO30,如下图所示:
图 16.3.2 MIO分配
知道了DP接口的实际物理硬件连接后我们就可以到Vivado中去搭建嵌入式工程了,本节实验我们将调用MPSOC内部的DP硬核资源实现DP彩条显示实验。
第一步我们要创建一个Vivado工程,命名为dp_colorbar,具体的创建步骤在前面的“hello world”实验中有详细的讲解,这里不再重复赘述。
第二步在创建好的工程里搭建嵌入式硬件内核,点击Flow Navigator栏的Create Block Design选项,然后添加ZYNQ UltraScale内核(具体步骤参考“hello world”实验)。添加完内核接下来我们来配置内核,因为本节实验需要调用DP硬核资源,所以第一步我们配置DP接口,如下图所示:
图 16.3.3 配置DP接口
如上图所示我们先找到I/O Configuration配置选项,然后修改MIO的电压(通过查看原理图),其中MIO0MIO25使用的电压是1.8V,MIO26MIO51使用的电压是3.3V,MIO52~MIO77使用的电压还是1.8V。修改完MIO电压后找到High Speed选项将Display Port选项勾上,并且配置好DPAUX接口对应的MIO是27到30,通道选项选择Dual Higher(双通道)。到这里DP接口配置就完成了,接下来开始对时钟进行配置,如下图所示:
图 16.3.4 配置时钟
如上图所示,输入时钟保持默认就好,不用修改,我们需要修改的是输出时钟,如下图所示:
图 16.3.5 时钟配置
对于输出时钟的配置这里给大家解释一下,首先看上图中标序号4(Range)的那一列,这一列代表所配器件支持的时钟范围,只要在这个范围内都是有效的。而标序号2的这一列代表我们想给这个器件配置什么频率的时钟,序号3代表的是实际产生的时钟频率。因此我们只需要遵行一个原则只要时钟范围在Range内,尽可能的通过改变Source选项使得Requested Freq与Actual Frequency的值相等或者非常接近就可以了。序号5代表我们配置DP相关的时钟。
配置完输出时钟最后我们再来配置DDR(这里注意DDR可以不用配置,因为本节实验不需要用到它,之所以在这里配置是方便之后的工程去使用这个硬件平台),如下图所示:
图 16.3.6 DDR配置
从上图可以看到对于DDR的配置我们只需要关注四个选项即图中标号为2、3、4、5的四个选项即可。其中Load DDR Presets选项是用来选择DDR的型号,MPSOC开发板板载的DDR4型号为MT40A256M16GE,所以这里选择MT40A256M16GE;然后Effective DRAM Bus Width选择有效数据位宽,因为我们是4片DDR4连用(单片数据位宽16bit),因此数据位宽是64bit;再然后DRAM Device Capacity选择每片DDR的容量,根据数据手册可知我们板载的DDR4单片容量为4Gb,所以这里选择4096MBits;最后一个要配置的就是行地址位宽,这里同样根据数据手册知道我们板载的DDR4行地址数据位宽是15位的位宽。
现在我们需要调用的资源就已经配置完了,但是在搭建平台的时候有一些资源是默认选中的如用于PS-PL连接的HP接口以及复位信号,如下图所示:
图 16.3.7 PS-PL互联接口
如上图所示我们把Fabric Reset Enable(PS端产生的复位信号给PL端使用)和AXI HPM0 LPD选项给去掉,因为本节实验只用到了PS部分,没用到PL部分。
同理我们也可以把PL端的时钟信号也去掉,如下图所示:
图 16.3.8 PL端时钟信号
将PL端的时钟信号也去掉后我们的硬件平台就搭建好了,就是一个光秃秃的ZYNQ硬核,(注意,本次配置没有添加串口控制器UART0,需要的话,读者也可以添加)如下图所示:
图 16.3.9 DP显示硬件平台
到这里整个硬件平台就设置完了,接下来我们就可以进入软件代码的编写了。
16.4软件设计
在进入软件平台编写前需要先将之前搭建好的硬件平台导出,导出硬件后我们打开Vitis开始创建软件工程,具体步骤大家可以参考前面的“hello world”实验,软件工程创建完成后如下图所示:
图 16.4.1 DP显示软件工程
在Explorer窗口可以看到我们创建好的工程,分为两部分,上半部分(design_1_wrapper_1)是板级支持包,下半部分(design_1_wrapper_system)是系统工程用来编写代码的。接下来我们先来设置板级支持包,如下图所示:
图 16.4.2 设置板级支持包
按照上图所标的序号步骤,打开板级支持包的设置,如下图所示:
图 16.4.3 修改板级支持包
这里只需要将psu_dp选项的驱动改成dppsu就可以了。关于psu_dp驱动的选择Xilinx官方给出了如下的解释:
图 16.4.4 驱动选择介绍
上图中的意思就是当我们要使用这个驱动的示例程序(本节实验就是在官方的示例程序中修改过来的)时我们要选择驱动,总共有4种用法。第一种是数据来源基于内存的模式,继续往后看大家可以知道本节实验的彩条数据是直接在软件C代码中产生的,也就是说数据来源基于Memory。这种模式下psu_dp驱动选择dppsu,psu_dpdma驱动选择dpdma,这也是本节实验所选的模式。第二种模式是数据从PL端过来到DP控制器,在这种模式下psu_dp驱动也是选择dppsu。第三种是数据从PL端到PL端,这种模式下psu_dp驱动选择avbuf。第四种是数据从Memory到PL端,这种模式下psu_dp驱动也是选择avbuf。
当我们设置好板级支持包后就可以打开示例工程了,如下图所示:
图 16.4.5 打开示例工程
如上图所示先回到板级支持包设置界面,找到Peripheral Drivers选项,然后往下浏览找到psu_dpdma选项,选择导入示例工程(Import Examples),如下图所示:
图 16.4.6 导入示例工程
导入过后我们在左边Explorer栏就可以看到如下示例工程:
图 16.4.7 示例工程
在示例工程中xdpdma_video_example.c是主函数文件,通过它来调用其他的库函数来完成对DP控制器的初始化和系统配置,并产生彩条数据,最终实现DP彩条显示的功能。下面我们就一起来分析一下这个主函数文件。其代码如下所示:
1 #include "xil_exception.h"
2 #include "xil_printf.h"
3 #include "xil_cache.h"
4 #include "xdpdma_video_example.h"
5
6 /************************** Constant Definitions *****************************/
7 #define DPPSU_DEVICE_ID XPAR_PSU_DP_DEVICE_ID
8 #define AVBUF_DEVICE_ID XPAR_PSU_DP_DEVICE_ID
9 #define DPDMA_DEVICE_ID XPAR_XDPDMA_0_DEVICE_ID
10 #define DPPSU_INTR_ID 151
11 #define DPDMA_INTR_ID 154
12 #define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
13
14 #define DPPSU_BASEADDR XPAR_PSU_DP_BASEADDR
15 #define AVBUF_BASEADDR XPAR_PSU_DP_BASEADDR
16 #define DPDMA_BASEADDR XPAR_PSU_DPDMA_BASEADDR
17
18 #define BUFFERSIZE 1920 * 1080 * 4 /* HTotal * VTotal * BPP */
19 #define LINESIZE 1920 * 4 /* HTotal * BPP */
20 #define STRIDE LINESIZE /* The stride value should
21 be aligned to 256*/
22
23 /************************** Variable Declarations ***************************/
24 u8 Frame[BUFFERSIZE] __attribute__ ((__aligned__(256)));
25 XDpDma_FrameBuffer FrameBuffer;
26
27 int main()
28 {
29 int Status;
30
31 Xil_DCacheDisable();
32 Xil_ICacheDisable();
33
34 xil_printf("DPDMA Generic Video Example Test \r\n");
35 Status = DpdmaVideoExample(&RunCfg);
36 if (Status != XST_SUCCESS) {
37 xil_printf("DPDMA Video Example Test Failed\r\n");
38 return XST_FAILURE;
39 }
40
41 xil_printf("Successfully ran DPDMA Video Example Test\r\n");
42
43 return XST_SUCCESS;
44 }
45
46 int DpdmaVideoExample(Run_Config *RunCfgPtr)
47
48 {
49 u32 Status;
50 /* Initialize the application configuration */
51 InitRunConfig(RunCfgPtr);
52 Status = InitDpDmaSubsystem(RunCfgPtr);
53 if (Status != XST_SUCCESS) {
54 return XST_FAILURE;
55 }
56
57 xil_printf("Generating Overlay.....\n\r");
58 GraphicsOverlay(Frame, RunCfgPtr);
59
60 /* Populate the FrameBuffer structure with the frame attributes */
61 FrameBuffer.Address = (INTPTR)Frame;
62 FrameBuffer.Stride = STRIDE;
63 FrameBuffer.LineSize = LINESIZE;
64 FrameBuffer.Size = BUFFERSIZE;
65
66 SetupInterrupts(RunCfgPtr);
67
68 return XST_SUCCESS;
69 }
70
71 void InitRunConfig(Run_Config *RunCfgPtr)
72 {
73 /* Initial configuration parameters. */
74 RunCfgPtr->DpPsuPtr = &DpPsu;
75 RunCfgPtr->IntrPtr = &Intr;
76 RunCfgPtr->AVBufPtr = &AVBuf;
77 RunCfgPtr->DpDmaPtr = &DpDma;
78 RunCfgPtr->VideoMode = XVIDC_VM_1920x1080_60_P;
79 RunCfgPtr->Bpc = XVIDC_BPC_8;
80 RunCfgPtr->ColorEncode = XDPPSU_CENC_RGB;
81 RunCfgPtr->UseMaxCfgCaps = 1;
82 RunCfgPtr->LaneCount = LANE_COUNT_2;
83 RunCfgPtr->LinkRate = LINK_RATE_540GBPS;
84 RunCfgPtr->EnSynchClkMode = 0;
85 RunCfgPtr->UseMaxLaneCount = 1;
86 RunCfgPtr->UseMaxLinkRate = 1;
87 }
88
89 int InitDpDmaSubsystem(Run_Config *RunCfgPtr)
90 {
91 u32 Status;
92 XDpPsu *DpPsuPtr = RunCfgPtr->DpPsuPtr;
93 XDpPsu_Config *DpPsuCfgPtr;
94 XAVBuf *AVBufPtr = RunCfgPtr->AVBufPtr;
95 XDpDma_Config *DpDmaCfgPtr;
96 XDpDma *DpDmaPtr = RunCfgPtr->DpDmaPtr;
97
98
99 /* Initialize DisplayPort driver. */
100 DpPsuCfgPtr = XDpPsu_LookupConfig(DPPSU_DEVICE_ID);
101 XDpPsu_CfgInitialize(DpPsuPtr, DpPsuCfgPtr, DpPsuCfgPtr->BaseAddr);
102 /* Initialize Video Pipeline driver */
103 XAVBuf_CfgInitialize(AVBufPtr, DpPsuPtr->Config.BaseAddr, AVBUF_DEVICE_ID);
104
105 /* Initialize the DPDMA driver */
106 DpDmaCfgPtr = XDpDma_LookupConfig(DPDMA_DEVICE_ID);
107 XDpDma_CfgInitialize(DpDmaPtr,DpDmaCfgPtr);
108
109 /* Initialize the DisplayPort TX core. */
110 Status = XDpPsu_InitializeTx(DpPsuPtr);
111 if (Status != XST_SUCCESS) {
112 return XST_FAILURE;
113 }
114 /* Set the format graphics frame for DPDMA*/
115 Status = XDpDma_SetGraphicsFormat(DpDmaPtr, RGBA8888);
116 if (Status != XST_SUCCESS) {
117 return XST_FAILURE;
118 }
119 /* Set the format graphics frame for Video Pipeline*/
120 Status = XAVBuf_SetInputNonLiveGraphicsFormat(AVBufPtr, RGBA8888);
121 if (Status != XST_SUCCESS) {
122 return XST_FAILURE;
123 }
124 /* Set the QOS for Video */
125 XDpDma_SetQOS(RunCfgPtr->DpDmaPtr, 11);
126 /* Enable the Buffers required by Graphics Channel */
127 XAVBuf_EnableGraphicsBuffers(RunCfgPtr->AVBufPtr, 1);
128 /* Set the output Video Format */
129 XAVBuf_SetOutputVideoFormat(AVBufPtr, RGB_8BPC);
130
131 /* Select the Input Video Sources.
132 * Here in this example we are going to demonstrate
133 * graphics overlay over the TPG video.
134 */
135 XAVBuf_InputVideoSelect(AVBufPtr, XAVBUF_VIDSTREAM1_NONE,
136 XAVBUF_VIDSTREAM2_NONLIVE_GFX);
137 /* Configure Video pipeline for graphics channel */
138 XAVBuf_ConfigureGraphicsPipeline(AVBufPtr);
139 /* Configure the output video pipeline */
140 XAVBuf_ConfigureOutputVideo(AVBufPtr);
141 /* Disable the global alpha, since we are using the pixel based alpha */
142 XAVBuf_SetBlenderAlpha(AVBufPtr, 0, 0);
143 /* Set the clock mode */
144 XDpPsu_CfgMsaEnSynchClkMode(DpPsuPtr, RunCfgPtr->EnSynchClkMode);
145 /* Set the clock source depending on the use case.
146 * Here for simplicity we are using PS clock as the source*/
147 XAVBuf_SetAudioVideoClkSrc(AVBufPtr, XAVBUF_PS_CLK, XAVBUF_PS_CLK);
148 /* Issue a soft reset after selecting the input clock sources */
149 XAVBuf_SoftReset(AVBufPtr);
150
151 return XST_SUCCESS;
152 }
153
154 void SetupInterrupts(Run_Config *RunCfgPtr)
155 {
156 XDpPsu *DpPsuPtr = RunCfgPtr->DpPsuPtr;
157 XScuGic *IntrPtr = RunCfgPtr->IntrPtr;
158 XScuGic_Config *IntrCfgPtr;
159 u32 IntrMask = XDPPSU_INTR_HPD_IRQ_MASK | XDPPSU_INTR_HPD_EVENT_MASK;
160
161 XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_INTR_DIS, 0xFFFFFFFF);
162 XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_INTR_MASK, 0xFFFFFFFF);
163
164 XDpPsu_SetHpdEventHandler(DpPsuPtr, DpPsu_IsrHpdEvent, RunCfgPtr);
165 XDpPsu_SetHpdPulseHandler(DpPsuPtr, DpPsu_IsrHpdPulse, RunCfgPtr);
166
167 /* Initialize interrupt controller driver. */
168 IntrCfgPtr = XScuGic_LookupConfig(INTC_DEVICE_ID);
169 XScuGic_CfgInitialize(IntrPtr, IntrCfgPtr, IntrCfgPtr->CpuBaseAddress);
170
171 /* Register ISRs. */
172 XScuGic_Connect(IntrPtr, DPPSU_INTR_ID,
173 (Xil_InterruptHandler)XDpPsu_HpdInterruptHandler, RunCfgPtr->DpPsuPtr);
174
175 /* Trigger DP interrupts on rising edge. */
176 XScuGic_SetPriorityTriggerType(IntrPtr, DPPSU_INTR_ID, 0x0, 0x03);
177
178
179 /* Connect DPDMA Interrupt */
180 XScuGic_Connect(IntrPtr, DPDMA_INTR_ID,
181 (Xil_ExceptionHandler)XDpDma_InterruptHandler, RunCfgPtr->DpDmaPtr);
182
183 /* Initialize exceptions. */
184 Xil_ExceptionInit();
185 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
186 (Xil_ExceptionHandler)XScuGic_DeviceInterruptHandler,
187 INTC_DEVICE_ID);
188
189 /* Enable exceptions for interrupts. */
190 Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
191 Xil_ExceptionEnable();
192
193 /* Enable DP interrupts. */
194 XScuGic_Enable(IntrPtr, DPPSU_INTR_ID);
195 XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_INTR_EN, IntrMask);
196
197 /* Enable DPDMA Interrupts */
198 XScuGic_Enable(IntrPtr, DPDMA_INTR_ID);
199 XDpDma_InterruptEnable(RunCfgPtr->DpDmaPtr, XDPDMA_IEN_VSYNC_INT_MASK);
200
201 }
202
203 u8 *GraphicsOverlay(u8* Frame, Run_Config *RunCfgPtr)
204 {
205 u64 Index;
206 u32 *RGBA;
207 RGBA = (u32 *) Frame;
208 for(Index = 0; Index < BUFFERSIZE/4; Index ++) {
209 if(Index%1920<LINESIZE/4/5){
210 RGBA[Index] = 0xFFFFFFFF;
211 }
212 else if(Index%1920<LINESIZE/4/5*2){
213 RGBA[Index] = 0x000000FF;
214 }
215 else if(Index%1920<LINESIZE/4/5*3){
216 RGBA[Index] = 0xFF0000FF;
217 }
218 else if(Index%1920<LINESIZE/4/5*4){
219 RGBA[Index] = 0xFF00FF00;
220 }
221 else if(Index%1920<LINESIZE/4/5*5){
222 RGBA[Index] = 0xFFFF0000;
223 }
224 }
225 return Frame;
226 }
代码1~20行主要是包含头文件和宏定义,我们需要关注的是第18、19行,这两行定义的是图像的分辨率,我们本节实验使用的是1080P分辨率。代码第24行定义了一个数组Frame,这个数组的类型是u8,数组长度是BUFFERSIZE。attribute ((aligned(256)))是让结构体按照256字节去对齐,主要是方便分配统一地址长度,不了解的同学可以去学习一下C语言的使用。代码第25行是结构体变量定义,这个结构体定义了显示图像的信息包括地址、尺寸、步进以及行尺寸。
代码第27~44行是主函数,这个主函数的作用就是调用子函数来具体实现DP彩条显示的功能,首先看代码第31、32行是关闭数据缓存和指令缓存。这里给大家解释一下什么叫缓存,缓存(cache)是介于CPU和直接存储(DDR4)之间的临时缓存,它用来存储CPU之前使用过的数据或者循环调用的数据(这里通常缓存一行),它的速度接近于CPU的速度。因此当CPU需要调用数据的时候可以不直接对DDR进行操作,而是调用cache里的数据(cache已经提前缓存了一行数据),这样CPU调用起来会非常的快(如果直接对DDR进行数据读写是有一定延时的,因为DDR的速度跟不上CPU的反应速度),提高了CPU的效率。但是这种操作模式也有一个弊端,就是当DDR的数据发生改变时cache里的数据不能及时更新,会导致CPU拿到错误数据,反之CPU改变了cache里的数据但是它没有及时映射到DDR中去,也会导致DDR的数据有问题。为了解决这个弊端有两种方案,第一种简单粗暴直接关闭cache,让CPU直接对DDR进行操作,这样会在一定程度上降低CPU的效率;另一种方案就是通过调用清空cache函数(CacheFlush)和无效函数(CacheInvalidateRange)来刷新cache的值。其中CacheFlush函数是将cache中的值清空,注意这里的清空不是指把数据删除,而是把数据推送到DDR中去;而CacheInvalidateRange函数则是表明当前cache中的值无效,需要重新从DDR中读取数据。这样一来就可以实时更新cache中的数据了,关于这两个函数的用法和介绍在代码第三行包含的xil_types.h头文件中有详细介绍(包含指令缓存和数据缓存),大家可以打开这个头文件去学习。
本节实验其实没有用到DDR因为我们的数据是直接在GraphicsOverlay函数中生成的,因此根本没必要使用cache,所以干脆就将cache给关掉。
代码第34行没什么好讲的就是串口打印信息,这里打印的内容大家也可以自行修改。
代码第35~39行是调用DpdmaVideoExample函数,并判断DpdmaVideoExample函数返回的值是XST_SUCCESS还是XST_FAILURE,如果是XST_SUCCESS表明这个例程实验成功,反之则失败。
接下来我们重点来看DpdmaVideoExample函数。这个函数的作用是给大家演示如何在overlay模式下使用XDpDma驱动程序(DP控制器的驱动程序)。在讲解代码之前先给大家解释一下什么是overlay模式,overlay是一种数字视频的显示技术,允许视频信号不经过显卡GPU的处理,仅通过显存直接显示在屏幕上,简单来说就是我们显示一幅图像,它会直接覆盖到原来的图像上,他后面的图像是不变的,只是被挡住看不见了。因此当我们显示动态视频时就可以通过当前图像覆盖上一帧图像的方式来实现视频显示了。然后我们来分析DpdmaVideoExample函数内部的代码。
代码第4955行是初始化DP控制器的配置,并返回配置结果,如果配置失败则返回XST_FAILURE,配置成功则返回XST_SUCCESS。那么具体配置了哪些内容呢?我们可以看看形参RunCfgPtr都包含了哪些参数,看代码7187行,InitRunConfig函数就是给RunCfgPtr这个结构体具体配置实际参数。前四个参量是DP控制器的系统函数(API),包含了整个DP的通信协议的配置,例如DP的主链路配置,辅助通道配置等等,大家可以按住Ctrl键打开这四个参量去具体看一下(看这些配置之前一定要先了解DP接口协议,否则你看不懂它配置的是啥)。代码第78~86行就是具体的用户配置了,例如第78行配置图像的分辨率是1080P帧率是60Hz、第79行配置颜色深度、第80行颜色编码模式、第82行设置DP通道数量以及83行设置通道速度等等,其他设置我们保持默认即可。了解完RunCfgPtr之后再回头看DpdmaVideoExample函数就很容易理解了,代码第52行执行的就是初始化函数InitDpDmaSubsystem。
代码第89152行就是InitDpDmaSubsystem初始化函数的内容了,这个函数里面调用了多个初始化子函数,例如代码第100101行就是先根据设备ID去查找配置(XDpPsu_LookupConfig),找到配置信息后再去对DP主设备(数据发送端)进行配置(XDpPsu_CfgInitialize)。针对于InitDpDmaSubsystem初始化函数它调用的每一个子函数在代码上方都有英文注释它的作用,并且打开这个子函数还有更详细的注释内容,所以在这里就不带大家每一行都去分析了。
初始化成功后接下来就需要显示图片了,我们再次回到DpdmaVideoExample函数,看代码第58行GraphicsOverlay函数,这个函数里面就包含了彩条显示的信息。GraphicsOverlay函数的具体内容在代码的第203~226行,这是一个指针函数作为参量来被使用,GraphicsOverlay函数返回的值是Frame,这个值里面包含了一帧图像的像素显示信息,最终会传递到FrameBuffer中去,FrameBuffer我们等下再来讲解。我们先来详细讲解一下GraphicsOverlay函数。
在代码的第205行定义了一个64位的变量Index,代码第206行定义了一个32位的指针变量RGBA。代码第207行是先将8位一维数组Frame强制转换成32位指针数组(在代码第24行定义了这个数组Frame,这个数组的类型是u8,数组长度是BUFFERSIZE)。转换完成之后将数组的首地址传给指针变量RGBA,此时指针RGBA直接指向数组Frame里面的元素,那么接下来对RGBA进行操作就可以直接映射到Frame里面的元素(关于指针数组的访问大家有不理解的可以去查找相关C语言的语法书),因此代码第208~224行就执行了一个for循环语句变量Index从0累加到BUFFERSIZE/4(代码第18、19行定义了BUFFERSIZE和LINESIZE的大小),在这个范围内通过if语句去判断RGBA[Index]处于Frame的哪个区域,我们将一帧图像的数据(在Frame中开辟1920*1080大小的区域存储一帧图像数据)分成五个竖直条状,每个条状赋予不同颜色的数值,最终达到彩条显示的效果。最后GraphicsOverlay函数返回Frame,这个Frame在哪里被使用的呢,我们再次返回DpdmaVideoExample函数,在代码第61行就使用了Frame(这里再次进行类型转换,INTPTR类型主要是让Frame在跨平台使用时适应各个平台的长度,不清楚的可以去查询C99标准中关于intptr_t的定义),这里是将Frame包括STRIDE、LINESIZE、BUFFERSIZE都赋值给了结构体FrameBuffer。那么FrameBuffer这个结构体又是干嘛的呢?我们找到这个结构体的用处。
在xdpdma.h中可以找到如下代码:
void XDpDma_InitVideoDescriptor(XDpDma_Descriptor *CurrDesc,XDpDma_FrameBuffer *FrameBuffer);
void XDpDma_DisplayVideoFrameBuffer(XDpDma *InstancePtr,XDpDma_FrameBuffer *Plane0,XDpDma_FrameBuffer *Plane1,XDpDma_FrameBuffer *Plane2);
void XDpDma_DisplayGfxFrameBuffer(XDpDma *InstancePtr,XDpDma_FrameBuffer *Plane);
大家可以看到有三个函数都调用了XDpDma_FrameBuffer(FrameBuffer就是XDpDma_FrameBuffer类型,结构体语法大家自行了解),那么这三个函数都有什么作用呢?其中XDpDma_InitVideoDescriptor函数的作用是初始化视频和图像的通道信息;XDpDma_DisplayVideoFrameBuffer函数是将下一帧要显示的视频进行通道缓存;XDpDma_DisplayGfxFrameBuffer函数是将下一帧要显示的图形进行通道缓存;看到这里大家就应该明白了其实FrameBuffer就是包含了一帧图片的像素内容,大小尺寸,将这些信息传递给DP驱动程序,最后将显示内容给显示出来。那么怎么将我们想要显示的图片放到FrameBuffer中去呢?为了方便大家理解我给大家画了一个示意图,如下图所示:
图 16.4.8 DP显示示意图
上图中左边是需要显示的图像,其中每个像素都是由三个字节的RGB颜色数据组成,一幅图像有着自己的分辨率即行有多少个像素,列有多少个像素。而每个像素占三个字节的存储空间,那么整幅图像在DDR中就占用了行分辨率(一行有多少个像素)场分辨率(一列有多少个像素)3个字节的空间。上图右边就是DP显示的帧缓存空间,这一帧的空间也是由一个个单独像素空间组成的,单独像素空间是由4个字节的RGBA颜色数据组成(这里默认是RGBA模式,也可以配置成其他模式,例如RGB模式等),其中RGBA的A指的是透明度。因此我们想要显示一幅完整的图片只需要将上图右边的帧缓存空间配置成和我们左边图像一样大小,并且将左边的像素数据转移到右边的缓存空间中就可以显示图像了。
我们以本节彩条实验为例,在代码中GraphicsOverlay函数产生了一幅19201080大小的彩条图像,这个图像的数据格式就是RGBA,因此它占用的总空间就是19201080*4个字节的空间,我们把这个彩条的所有像素数据装在Frame这个数组当中。要想把这幅彩条图案显示出来就需要把Frame数组当中的所有颜色数据搬运到DP显示帧缓存区域即FrameBuffer当中的Address存储区。那么怎么搬运呢?其实很简单,我们只需要将Frame数组的首地址配置给Address,并且告速FrameBuffer我给你的图像有多大即可,如代码第61~64行所示,这样系统提供的API函数就会自动把这幅彩条图案搬运到DP显示帧缓存区域并且最终显示出来。
最后我们再次回到DpdmaVideoExample函数,这个函数里还调用了一个中断函数SetupInterrupts,这个函数就是DP控制器的一个中断回调函数如代码第154行201行。这个中断函数的主要作用是用来检测热插拔信号,有两种检测模式,一种是状态检测,一种是脉冲检测,分别对应代码第164、165行。这也跟前文讲述热插拔信号的作用相吻合(当接收端设备拉低HPD信号,信号脉冲宽度在0.25ms2ms之间,此时发送端会在HPD信号上升沿后的100ms以内重新读取接收端设备信息。如果HPD信号拉低时间超过2ms(例如DP数据线被拔掉了,断开连接),发送设备就会停止发送数据,等待接收设备重新连接上)。大家可以自己试试DEBUG代码,不连接DP线,空载状态下DEBUG代码,会发现代码运行到热插拔中断函数时就会停下来,等待热插拔信号,此时插上DP线程序就可以继续运行下去了,并点亮彩条。
关于中断的详细讲解大家可以去参考前面中断实验的例程,关于DP彩条显示实验的代码到此就讲解完了。这里需要提醒大家注意一点,上文讲解的时候是用的官方示例去讲解的,大家拿到手的例程是我们自己创建的空白工程,然后在空白工程里调用DP的相关控制函数。当然调用的函数和示例模板是一摸一样的,给大家提供的例程除了可以显示竖状彩条外还可以显示横向彩条(横向彩条被注释了,大家需要使用可以解除注释)。
16.5下载验证
首先我们将下载器与MPSOC开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将开发板USB_UART (PS_PORT)接口与电脑连接,用于串口通信(注意创建工程的时候我并没有勾选UART如果大家需要使用UART请自行勾选,方法参考前面“Hello World实验”),然后将DP转接线连接到HDMI线,再连接至HDMI显示屏(这里需要使用主动式转换器,并不是所有的转接线都通用,请大家前往正点原子官方店铺购买),最后将开发板上四个启动模式开关均置为ON,即设置为JTAG模式。最后连接开发板电源给开发板上电。如下图所示:
图 16.5.1 硬件连接图
然后下载代码,稍等一会,显示屏上即可显示彩条图案,如下图所示,说明实验成功。
图 16.5.2显示彩条
相关文章:

【正点原子FPGA连载】第十六章DP彩条显示实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第十六章DP彩条显…...

数据结构与算法—链表list
目录 链表 链表类型 链表插入 链表删除 写程序注意点 与数组区别 链表应用 LRU 实现思想 链表 链表,一种提高数据读取性能的技术,在硬件设计、软件开发中有广泛应用。常见CPU缓存,数据库缓存,浏览器缓存等。缓存满时&#…...

自定义View练习题目整理
一、动态音频播放柱形图 1、效果图: 2、步骤 (1)、新建自定义View类,继承View (2)、重写onDraw()方法,使用画笔和画布循环画一定数量的柱形 Overrideprotected void onDraw(Canvas canvas) {s…...

LAMP平台部署及应用
LAMP平台部署及应用 📒博客主页: 微笑的段嘉许博客主页 💻微信公众号:微笑的段嘉许 🎉欢迎关注🔎点赞👍收藏⭐留言📝 📌本文由微笑的段嘉许原创! Ὄ…...

ubuntu20.04安装python3虚拟环境
1.安装pip3 sudo apt install python3-pip2.安装虚拟环境 sudo apt install virtualenv sudo apt install virtualenvwrapper3.修改配置文件设置环境变量 打开.bashrc并编辑 gedit ~/.bashrc在.bashrc文件后面加入下面两行 export WORKON_HOME$HOME/.virtualenvs source …...

VUE3源码分析————rollup打包
文章目录什么是rolluprollup打包和webpack打包的区别rollup打包准备一、安装yarn开始rollup打包一、初始化二、package.json文件配置三、新建并配置打包文件夹四、下载rollup及打包执行文件五、文件大致分布
【JavaScript】前端实现电子签名:
文章目录一、效果:二、实现:三、扩展一、效果: 二、实现: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"vie…...

Windows 11 22H2 中文版、英文版 (x64、ARM64) 下载 (updated Feb 2023)
Windows 11, version 22H2,2023 年 2 月 更新 请访问原文链接:https://sysin.org/blog/windows-11/,查看最新版。原创作品,转载请保留出处。 作者主页:www.sysin.org 全新推出 Windows 11 全新 Windows 体验&#x…...

【java】Spring Cloud --Spring Cloud Alibaba 教程
文章目录Spring Cloud Alibaba是什么Spring Cloud AlibabaSpring Cloud Alibaba 组件Spring Cloud Alibaba 的应用场景Spring Cloud 两代实现组件对比Spring Cloud Alibaba 版本依赖Spring Cloud Alibaba 组件版本关系Spring Cloud Alibaba NacosNacos 的特性服务发现服务健康监…...

通过操作Cortex-A7核,串口输入相应的命令,控制LED灯进行工作增加编程要求
2.编程要求: 1)结构体封装 typedef struct{ char* cmd_arr; //命令行字符串 gpio_t* gpiox;//GPIO组号 unsigned int pin; //引脚编号 status_t status; //LED灯状态 void(*gpio_write_pin)(gpio_t* gpiox,unsigned int pin,status_t status); }cmd_t; 2…...

银行家算法
银行家算法 银行家算法是一种用来避免操作系统死锁出现的有效算法,所以在引入银行家算法的解释之前,有必要简单介绍一下死锁的概念。 一、死锁 死锁:是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成…...

181、【动态规划】leetcode ——72. 编辑距离(C++版本)
题目描述 原题链接:72. 编辑距离 解题思路 动态规划五步曲: (1)dp[i][j]含义: 以word1[i - 1]和word2[j - 1]结尾子串,经过最少次增删改后,可让word1变为word2的步数。dp中的i对应word1中的i…...

mysql 中关于慢查询日志
慢查询日志 慢查询日志主要用来记录执行时间超过设置的某个时长的SQL语句,能够帮助数据库维护人员找出执行时间比较长、执行效率比较低的SQL语句,并对这些SQL语句进行针对性优化。 开启慢查询 可以在 my.cnf 文件或者 my.ini 文件中配置开启慢查询日志…...

程序员必备的软技能-金字塔原理拆解(上)
原书 290千字,本文预计 14千字,拆解比 20:1,预计阅读时长 15分钟序言日常工作中,常常因为思维、表达方式不对产生不想要的结果:写了一个小时的周报,领导却不满意?跟团队讲了半天自己…...

关于我利用python开发的PC端标注软件及目标检测软件
如何利用python快速开发PC端目标检测及数据标注软件概述开发软件背景开发第一步:功能需求分析开发第二步: 前端分区设计开发第三步:功能开发开发第四步:程序功能的打包与检查开发第五步:程序的反馈与改善一个例子的展示…...

Git导出增量包的操作步骤
前言在项目开发部署中,通常是将一个Git项目全量打包发布,但有的场景只需要导出有变更的那部分文件,增量发布,此时就需要使用Git导出增量包了。一、查看提交记录拿到提交ID码①例如使用的gitlab使用方法参考下图(一目了然) 【推荐】…...

JavaWeb--JavaScript
JavaScript1 JavaScript简介2 JavaScript引入方式2.1 内部脚本2.2 外部脚本3 JavaScript基础语法3.1 书写语法3.2 输出语句3.3 变量3.4 数据类型3.5 运算符3.5.1 \\ 和 区别3.5.2 类型转换3.6 流程控制语句3.6.1 if 语句3.6.2 switch 语句3.6.3 for 循环语句3.6.4 while 循环语…...

mars3d加载建筑物白膜及简单建筑物样式
首先需要拥有shp格式的数据。可以通过水经微图下载,注意此软件是付费的将shp格式的数据处理为切片数据,可以使用cesiumlab处理完成得到json数据就可以在mars3d中加载了 function init() { // 判断webgl支持 if (!mars3d.Util.webglreport()) { …...

数据结构之顺序表
本章重点: 1.线性表 2.顺序表 3.链表 4.顺序表和链表的区别和联系 目录 1.线性表 2.顺序表 2.1概念及结构 2.2接口实现 2.2.1 SeqList.h 2.2.2 SeqList.c 2.3数组相关面试题 2.3.1移除元素 2.3.2删除有序数组中的重复项 编辑 2.3.3合并两个有序数组…...

【数据挖掘实战】——家用电器用户行为分析及事件识别
项目地址:Datamining_project: 数据挖掘实战项目代码 目录 一、背景和挖掘目标 1、问题背景 2、原始数据 3、挖掘目标 二、分析方法与过程 1、初步分析 2、总体流程 第一步:数据抽取 第二步:探索分析 第三步:数据的预处…...

肠道核心菌属——双歧杆菌属,了解并拥有它
双歧杆菌 双歧杆菌属(Bifidobacterium)是放线菌门严格厌氧的革兰氏阳性多形性杆状细菌。末端常常分叉,故名双歧杆菌。是人和动物肠道的重要核心菌群和有益生理菌群,也是母乳喂养婴儿中发现的第二大菌。 肥胖、糖尿病和过敏等各种疾…...

Python 之 Pandas 生成时间戳范围、Pandas 的时期函数 Period() 和时间序列 - 重采样 resample
文章目录一、生成时间戳范围1. 指定值2. 指定开始日期并设置期间数3. 频率 freq4. closed二、Pandas 的时期函数 Period()三、时间序列 - 重采样 resample在开始之前,我们先导入 numpy 和 pandas 库,同时导入 python 内置的模块。 import pandas as pd…...

利用Python和Sprak求曲线与X轴上方的面积
有n组标本(1, 2, 3, 4), 每组由m个( , , ...)元素( , )组成(m值不定), . 各组样本的分布 曲线如下图所示. 通过程序近似实现各曲线与oc, cd直线围成的⾯积. 思路 可以将图像分成若干个梯形,每个梯形的底边长为(Xn1 - Xn-1),面积为矩形的一半,…...

利用机器学习(mediapipe),进行人手的21个3D手关节坐标检测
感知手的形状和动作的能力可能是在各种技术领域和平台上改善用户体验的重要组成部分。例如,它可以构成手语理解和手势控制的基础,并且还可以在增强现实中将数字内容和信息覆盖在物理世界之上。虽然自然而然地出现在人们手中,但是强大的实时手感知力无疑是一项具有挑战性的计…...

【添砖java】谁说编程第一步是hello world
编程第一步明明是下载编译器和配置环境(小声逼逼)。 Windows下的java环境安装: java的安装包分为两类,一类是JRE(Java Runtime Environmental),是一个独立的java运行环境;一类是JDK…...

el-table大数据量渲染卡顿问题
1、场景描述 在项目开发中,遇到在表格中一次性加载完的需求,且加载数量不少,有几百几千条,并且每条都可能有自己的下拉框,输入框来做编辑功能,此时普通的el-table肯定会导致浏览器卡死,那么怎么…...

MyBatis-Plus 实现分页的几种写法
简介MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。快速开始添加依赖全新的 MyBatis-Plus 3.0 版本基于 JDK8ÿ…...

记一次Binder内存不足导致的应用被杀
每个进程的可用Binder内存大小是 1M-8KB 也就是900多KB 事情的起因的QA压测过程发生进程号变更,怀疑APP被杀掉过,于是开始看日志(实际后来模拟的时候可以发现app确实被杀掉了) APP的压测平台会上报进程号变更时间点,发…...

Zabbix4.0架构理解-zabbix的工作方式
目录 1.1、zabbix4.0架构图 1.2、zabbix的进程 1、 zabbix server 2、zabbix agent 3、 zabbix proxy 4、 java gateway 5、zabbix get 1.3、zabbix的几种工作方式 1、通过zabbix agent 2、通过zabbix proxy 3、通过 zabbix java gateway 4、其他 1.3、zabbix 数据走…...

MySQL中的一些非常实用的函数、语法
前言我最近几年用MYSQL数据库挺多的,发现了一些非常有用的小玩意,今天拿出来分享到大家,希望对你会有所帮助。1.group_concat在我们平常的工作中,使用group by进行分组的场景,是非常多的。比如想统计出用户表中&#x…...