文章目录
- 1 实验任务
- 2 系统框图
- 3 硬件设计
- 4 软件设计
1 实验任务
本实验任务是PS端写彩条数据至DDR3内存中,然后通过PL端的VDMA IP核将彩条数据通过HDMI接口输出显示。
2 系统框图
本实验是用HDMI接口固定输出1080P的彩条图,所以:
- rgb2lcd模块实际是rgb2dvi模块
- AXI GPIO不存在,因为不需要读取LCD屏幕的ID
- 动态时钟配置改为PLL,输出固定频率,与AXI-Interconnect不连
- VTC输出固定时序,与AXI-Interconnect不连
3 硬件设计
注意事项:
- VTC的clken引脚
- 该引脚不连接,VTC也能正常工作
- pg016中描述该引脚是"Video Core active-High Clock Enable",即高有效
- 在Block Design中添加VTC后,clken引脚前边有个小圆圈,且双击该引脚,Polarity参数显示为ACTIVE_LOW,即低有效
- 将该引脚接常量1,视频输出正常
- 将该引脚接常量0,视频无法输出,VTC未工作
- 结论:以文档为准,clken高电平有效
- rgb2dvi模块
- 使用正点原子的rgb2dvi模块,数据的3个字节是G在中间,R和B在两头
- 使用Digilent的rgb2dvi模块,数据的3个字节是B在中间,R和G在两头(调试时一脸懵逼)
4 软件设计
注意事项:
- PS往DDR3写入数据后,要使用Xil_DCacheFlushRange刷新;
- run_triple_frame_buffer函数并非只能用于三帧缓存的情况,实际1-32帧缓存均可使用该函数(取名triple可能和VDMA模式使用三帧缓存有关);ReadSetup函数和WriteSetup函数会根据VDMA配置时选择的Frame Buffers数量设置相应数量的帧缓冲区起始地址;本实验选择Frame Buffers数量=1,PS端只往DDR3中写入一帧彩条图;
- XAxiVdma_DmaStop函数往VDMACR寄存器的bit0写0(Run / Stop controls the running and stopping of the VDMA channel. ),当前VDMA操作完成后停止(0 = Stop – VDMA stops when current (if any) VDMA operations are complete)
/***************************** Include Files *********************************/
#include "stdio.h"
#include "xparameters.h"
#include "xstatus.h"
#include "xaxivdma.h"
#include "vdma_api.h"
#include "xil_cache.h"
#include "xuartps.h"
#include "sleep.h"
/************************** Constant Definitions *****************************/
#define VDMA_DEVICE_ID XPAR_AXIVDMA_0_DEVICE_ID
#define IMAGE_WIDTH 1920
#define IMAGE_HEIGHT 1080
#define MEMORY_BASEADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR
#define UART_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID
#define UART_BASEADDR XPAR_XUARTPS_0_BASEADDR
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
s32 UartPsInit(XUartPs *UartPsInstPtr, XUartPsFormat* UartFormatPtr);
void GenPureColor(u8* DestAddr, u32 ImageWidth, u32 ImageHeight);
void GenColorBar(u8* DestAddr, u32 ImageWidth, u32 ImageHeight);
/************************** Variable Definitions *****************************/
XAxiVdma VdmaInst;
XUartPs UartInst;
int FrameBufferAddr = (MEMORY_BASEADDR + 0x02000000);
XUartPsFormat UartFormat = {
XUARTPS_DFT_BAUDRATE, // 115200
XUARTPS_FORMAT_8_BITS,
XUARTPS_FORMAT_NO_PARITY,
XUARTPS_FORMAT_1_STOP_BIT
};
/*****************************************************************************/
int main()
{
//
int Status;
u8* VdmaBufferAddr = (u8*)FrameBufferAddr;
char cmd;
// 串口初始化
Status = UartPsInit(&UartInst, &UartFormat);
if (Status != XST_SUCCESS) {
printf("UART Initialization Failed.\n");
return XST_FAILURE;
}
// 写入纯色图(用于确定RGB的字节位置)
// GenPureColor(VdmaBufferAddr, (u32)IMAGE_WIDTH, (u32)IMAGE_HEIGHT);
// 写入彩条图
GenColorBar(VdmaBufferAddr, (u32)IMAGE_WIDTH, (u32)IMAGE_HEIGHT);
//
Status = run_triple_frame_buffer(&VdmaInst, VDMA_DEVICE_ID, IMAGE_WIDTH, IMAGE_HEIGHT, FrameBufferAddr, 0, 0);
if (Status == XST_FAILURE) {
printf("VDMA Run Failed.\n");
}
//
printf("VDMA Control Ready (s=start, q=stop):\n");
while (1) {
if (XUartPs_IsReceiveData(UART_BASEADDR)) {
cmd = XUartPs_ReadReg(UART_BASEADDR, XUARTPS_FIFO_OFFSET);
if (cmd == 's') { // 启动VDMA
if (XAxiVdma_DmaStart(&VdmaInst, XAXIVDMA_READ) == XST_SUCCESS) {
printf("VDMA Start Succeeded.\n");
} else {
printf("VDMA Start Failed.\n");
}
}
else if (cmd == 'q') { // 停止VDMA
XAxiVdma_DmaStop(&VdmaInst, XAXIVDMA_READ);
printf("VDMA Stop Succeeded.\n");
}
}
usleep(10000); // 降低CPU占用
}
//
return 0;
}
/*****************************************************************************/
s32 UartPsInit(XUartPs *UartInstPtr, XUartPsFormat* UartFormatPtr)
{
//
s32 Status;
XUartPs_Config *UartConfigPtr;
// 查找UART配置
UartConfigPtr = XUartPs_LookupConfig(UART_DEVICE_ID);
if(NULL == UartConfigPtr)
{
return XST_FAILURE;
}
// 初始化UART
Status = XUartPs_CfgInitialize(UartInstPtr, UartConfigPtr, UartConfigPtr->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 设置UART数据格式
XUartPs_SetDataFormat(UartInstPtr, UartFormatPtr);
// 设置UART操作模式
XUartPs_SetOperMode(UartInstPtr, XUARTPS_OPER_MODE_NORMAL);
//
return XST_SUCCESS;
}
/*****************************************************************************/
void GenPureColor(u8* DestAddr, u32 ImageWidth, u32 ImageHeight)
{
// 禁用缓存(如果目标内存是非缓存区域)
Xil_DCacheDisable();
for (u32 y = 0; y < ImageHeight; y++) {
for (u32 x = 0; x < ImageWidth; x++) {
// 计算当前像素的内存位置(3字节/像素)
u32 PixelOffset = (y * ImageWidth + x) * 3;
u8* PixelAddr = DestAddr + PixelOffset;
// 写入RGB三个字节
PixelAddr[0] = 0x00;
PixelAddr[1] = 0x00;
PixelAddr[2] = 0xff;
}
}
// 如果需要,刷新缓存
Xil_DCacheFlushRange((INTPTR)DestAddr, ImageWidth * ImageHeight * 3);
//
return;
}
/*****************************************************************************/
void GenColorBar(u8* DestAddr, u32 ImageWidth, u32 ImageHeight)
{
// 定义8种颜色(R, G, B顺序)
const u8 color_bars[8][3] = {
{0x00, 0x00, 0x00}, // 黑
{0xff, 0xff, 0xff}, // 白
{0xff, 0x00, 0x00}, // 蓝
{0x00, 0xff, 0x00}, // 绿
{0x00, 0x00, 0xff}, // 红
{0xff, 0xff, 0x00}, // 青
{0xff, 0x00, 0xff}, // 紫
{0x00, 0xff, 0xff} // 黄
};
// 计算每个色条的宽度
u32 BarWidth = ImageWidth / 8;
// 禁用缓存(如果目标内存是非缓存区域)
Xil_DCacheDisable();
for (u32 y = 0; y < ImageHeight; y++) {
for (u32 x = 0; x < ImageWidth; x++) {
// 计算当前色条索引
u32 BarIndex = x / BarWidth;
// 计算当前像素的内存位置(3字节/像素)
u32 PixelOffset = (y * ImageWidth + x) * 3;
u8* PixelAddr = DestAddr + PixelOffset;
// 写入RGB三个字节
PixelAddr[0] = color_bars[BarIndex][0];
PixelAddr[1] = color_bars[BarIndex][1];
PixelAddr[2] = color_bars[BarIndex][2];
}
}
// 如果需要,刷新缓存
Xil_DCacheFlushRange((INTPTR)DestAddr, ImageWidth * ImageHeight * 3);
//
return;
}