软件环境:vivado 2017.4 硬件平台:XC7Z020
太丢脸了,实在太丢脸了,这篇日期是8月的文章,其实是我在8月的最后一天占了个坑,在9月才陆陆续续补上的,不然每月一更就破例了。出差在外果然是干什么都不方便啊。
本次!那就说说怎样在zynq7020上通过hdmi显示sd卡上的1080p图片。
因为这里只做图片显示输出,所以vdma只配置读通道就行。
接下来,video timing controller 功能类似于一个时序发生器,产生显示器输出所需要的时序信号,简化消隐区处理过程。第一页不使能探测模式。
第二页选择1080p。
axi4-stream to video out把axi4-stream的视频信号转成标准行场信号输出,选择独立时钟。
最后,1080p图像的hdmi时钟需要两个,1倍和5倍时钟,分别为148.5和742.5,如果是720p的图像,1倍和5倍的时钟则为74.25和371.25。
接下来是SDK部分,代码总共分为3部分,因为要注意的点还是比较多的,而且也比较零碎,所以分开来说,首先是main。
main.c
#include <stdio.h>
#include "xparameters.h"
#include "xsdps.h"
#include "xil_printf.h"
#include "ff.h"
#include "bmp.h"
#include "sleep.h"
#define H_STRIDE 1920
#define H_ACTIVE 1920
#define V_ACTIVE 1080
#define VIDEO_LENGTH (H_STRIDE*V_ACTIVE)
#define VDMA_BASEADDR XPAR_AXI_VDMA_0_BASEADDR
#define VIDEO_BASEADDR0 0x05000000
#define Buffer_Size 1920*1080*3
static FATFS SD_Card_Dev;
char *SD_Card_Path = "0:/";
u8 Original_Buf1[Buffer_Size] __attribute__ ((aligned(32)));
u8 Original_Buf2[Buffer_Size] __attribute__ ((aligned(32)));
u8 Original_Buf3[Buffer_Size] __attribute__ ((aligned(32)));
u8 Original_Buf4[Buffer_Size] __attribute__ ((aligned(32)));
u8 Show_Buf1[Buffer_Size] __attribute__ ((aligned(32)));
u8 Show_Buf2[Buffer_Size] __attribute__ ((aligned(32)));
u8 Show_Buf3[Buffer_Size] __attribute__ ((aligned(32)));
u8 Show_Buf4[Buffer_Size] __attribute__ ((aligned(32)));
void Xil_DCacheFlush(void);
void Show_BMP_Picture( const unsigned char * addr, u32 size_x, u32 size_y)
{
u32 x=0;
u32 y=0;
u32 r,g,b;
for(y=0;y<size_y;y++)
{
for(x=0;x<size_x;x++)
{
r = *(addr++);
g = *(addr++);
b = *(addr++);
Xil_Out32((VIDEO_BASEADDR0+((y*size_x)+size_x-x)*4),((r<<16)|(g<<8)|(b<<0)));
}
}
Xil_DCacheFlush();
}
void VDMA_init()
{
int i;
for(i=0;i<VIDEO_LENGTH;i++)
{
Xil_Out32(VIDEO_BASEADDR0+i*4,0);
}
Xil_DCacheFlush();
Xil_Out32((VDMA_BASEADDR + 0x000), 0x3);
Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0);
Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR0);
Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR0);
Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*4));
Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*4));
Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE);
}
int SD_init()
{
FRESULT result;
result = f_mount(&SD_Card_Dev,SD_Card_Path, 0);
if (result != 0) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
int main()
{
VDMA_init();
SD_init();
BMP_Picture_Processor((u8 *)"1.bmp" , Original_Buf1 ,Buffer_Size);
BMP_Picture_Processor((u8 *)"2.bmp" , Original_Buf2 ,Buffer_Size);
BMP_Picture_Processor((u8 *)"3.bmp" , Original_Buf3 ,Buffer_Size);
BMP_Picture_Processor((u8 *)"4.bmp" , Original_Buf4 ,Buffer_Size);
u32 i;
for(i = 0;i < Buffer_Size ;i++ )
{
Show_Buf1[i] = Original_Buf1[Buffer_Size-i-1];
Show_Buf2[i] = Original_Buf2[Buffer_Size-i-1];
Show_Buf3[i] = Original_Buf3[Buffer_Size-i-1];
Show_Buf4[i] = Original_Buf4[Buffer_Size-i-1];
}
while(1)
{
Show_BMP_Picture(Show_Buf1,1920,1080);
sleep(2);
Show_BMP_Picture(Show_Buf2,1920,1080);
sleep(2);
Show_BMP_Picture(Show_Buf3,1920,1080);
sleep(2);
Show_BMP_Picture(Show_Buf4,1920,1080);
sleep(2);
}
return 0;
}
main里需要说明的:
1.我有4张图需要显示,但是使用了8个数组,是因为从SD直接读取的内容是小端模式,指的是高位存的是低字节,低位是高字节,与我们使用习惯刚好是反的,所以除了4组存SD直接读取出来的图片信息外,另外4组就是为了把反了的图片翻过来,转正序用的。
2.也是由于1的关系,所以宏定义VIDEO_BASEADDR0预留的空间需要多一些,如果遇见无法正常显示,有可能是这里空间没留够,设定的值太小的问题。
3.aligned(32)是4字节对齐意思,r-g-b各占1字节,实际3字节就够,但4字节对齐存储传输,也方便与PL端传输接口对应。
4.show_bmp_picture根据存储图片数组内容,根据坐标显示rgb的,这个没啥好说的。
5.VDMA_init里,寄存器的定义和设置的值可以参考pg020_axi_vdma.pdf这个手册p15,可以看到,寄存器是小端模式,偏移也是4字节对齐的。
接下来说道代码里面提及的几个寄存器,首先是00h,控制寄存器,复位、锁相同步、帧存模式、运行状态都与此相关。
设定值03-运行、循环模式。
从5Ch~98h有最多32个寄存器可用于存放帧起始地址,32个寄存器划在两个bank上,根据MM2S_REG_INDEX来指定bank的值,每个bank上16个寄存器有相同的起始偏移地址,都是0x5C,举个例子,访问第1个寄存器时,MM2S_REG_INDEX给0,偏移地址0x5C,访问第17个寄存器时,MM2S_REG_INDEX给1,偏移地址0x5C。
58h,用于设定帧延迟和跨度,可以看出来,低2字节用于指定水平方向的跨度,[28:24]用于指定帧的延迟。
54h,与58h相似,用于指定水平方向尺寸,即每行多少字节需要传输,举个例子,1920*1080,如果每像素4字节,则应设置1920*4。
50h,用于设置垂直方向上总共有多少行数据,并启动VDMA传输,除此之外,当MM2S_VDMACR.RS=1时,对该寄存器写操作会将所有设定传给VDMA内部对应寄存器,所以,对于VDMA某通道配置时,必须,在最后设置50h寄存器。
bmp.c
#include "bmp.h"
#include "ff.h"
void Read_BMP_Header(uint8_t *header, BMP_HeaderTypeDef *bmp)
{
bmp->fileHeader.bfType = ((*header) << 8) | (*(header + 1));
header += 2;
bmp->fileHeader.bfSize = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) | ((*(header + 1)) << 8) | (*header);
header += 8;
bmp->fileHeader.bfOffBits = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) | ((*(header + 1)) << 8) | (*header);
header += 4;
bmp->infoHeader.bitSize = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) | ((*(header + 1)) << 8) | (*header);
header += 4;
bmp->infoHeader.biWidth = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) | ((*(header + 1)) << 8) | (*header);
header += 4;
bmp->infoHeader.biHeight = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) | ((*(header + 1)) << 8) | (*header);
header += 6;
bmp->infoHeader.biBitCount = ((*(header + 1)) << 8) | (*header);
header += 2;
bmp->infoHeader.biCompression = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) | ((*(header + 1)) << 8) | (*header);
header += 4;
bmp->infoHeader.biSizeImage = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) | ((*(header + 1)) << 8) | (*header);
header += 4;
bmp->infoHeader.biXPelsPerMeter = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) | ((*(header + 1)) << 8) | (*header);
header += 4;
bmp->infoHeader.biYPelsPerMeter = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) | ((*(header + 1)) << 8) | (*header);
}
void BMP_Picture_Processor(uint8_t *dir , uint8_t * buf ,uint32_t len)
{
FRESULT res;
FIL fsrc;
UINT br;
UINT ofs;
uint8_t buffer[1024];
BMP_HeaderTypeDef BMP_Header;
res = f_open(&fsrc, (const TCHAR*)dir, FA_READ); //打开要读取的文件
if(res == FR_OK) //如果打开成功
{
res = f_read(&fsrc, buffer, sizeof(buffer), &br); //读取BMP文件信息
Read_BMP_Header(buffer, &BMP_Header); //将BMP文件信息,按格式放进数组
ofs = BMP_Header.fileHeader.bfOffBits; //去掉文件信息才是像素数据
res = f_lseek(&fsrc, ofs);
if(res)
{
return 0;
}
res = f_read(&fsrc, buf, len, &br);
}
f_close(&fsrc); //关闭文件
}
bmp.c中主要包括两个函数,用Read_BMP_Header来对BMP格式图片的头信息进行解析,获取实际图像数据的起始点,然后通过f_lseek,定位到图像数据起始点,最后通过f_read读进buffer。
bmp.h
#ifndef _bmp_H
#define _bmp_H
#include <stdio.h>
typedef struct
{
uint16_t bfType; //文件类型,BMP格式为字符串BM
uint32_t bfSize; //图片大小,单位为KB
uint16_t bfReserved1; //保留位
uint16_t bfReserved2; //保留位
uint32_t bfOffBits; //从文件头到实际图像数据之间的字节偏移量
} BMP_FileHeaderTypeDef;
typedef struct
{
uint32_t bitSize; //BMP_InfoHeaderTypeDef结构体所需要的字节数
uint32_t biWidth; //图片宽度,像素位单位
int32_t biHeight; //图片高度,像素为单位。正为倒立,负为正向。
uint16_t biPlanes; //颜色平面数,总为1
uint16_t biBitCount; //比特数/像素。其值为:1、4、8、16、24或32
uint32_t biCompression; //数据压缩类型
uint32_t biSizeImage; //图像大小
uint32_t biXPelsPerMeter;//水平分辨率
uint32_t biYPelsPerMeter;//垂直分辨率
uint32_t biClrUsed; //颜色索引数
uint32_t biClrImportant; //重要颜色索引数
}BMP_InfoHeaderTypeDef;
typedef struct
{
BMP_FileHeaderTypeDef fileHeader; //文件头
BMP_InfoHeaderTypeDef infoHeader; //图片信息头
}BMP_HeaderTypeDef;
void Read_BMP_Header(uint8_t *header, BMP_HeaderTypeDef *bmp);
void BMP_Picture_Processor(uint8_t *dir , uint8_t * buf ,uint32_t len);
#endif
bmp.h中的结构体用于根据固定格式,即对应的字段长度,解析BMP图片头信息。
哦,对了,最后还有一点需要注意,这里的sd卡为fat文件系统,sdk中的bsp记得要勾选xilffs,否则无法读到文件。