【JokerのZYNQ7020】BMP_SHOW_1080p。

软件环境: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,否则无法读到文件。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值