基于ZYNQ的SD卡的访问

参考文献

[1].V3学院

项目简述

现在FPGA板卡上面常用的存储设备是SD卡,也经常将BOOT文件下载到SD卡中启动。那么这篇文章我们将讲解使用API函数来进行SD卡的读写,当然使用纯FPGA写Verilog也是可以读写SD卡,遵循SPI协议,后面的文章我们会进行相应的介绍,这篇文章先来讲解简单的C语言访问SD卡。

工程描述:ZYNQ利用FAT文件系统对SD卡些一张图片。

本次实验所用到的软硬件环境如下:
1、VIVADO 2019.1
2、米联客MZ7015FA开发板

FAT文件架构简述

本片博客我们将利用开源的FAT文件系统架构,介绍及维护这个系统的网址如下:FAT。打开这个网址,我们便可以看到如下信息:
在这里插入图片描述
点开每一个函数都有详细的参数说明及例子:
在这里插入图片描述
在这里插入图片描述
同学们可以主要通过上面的例子进行相应的学习,了解每个函数的功能。这里注意上面的介绍非常详细,尤其是示例工程。

那么学习到这里的同学或许会问,我们明白了上面函数的作用,那么我们如何将上面的系统框架引用到ZYNQ中呢?因为我们直接写相应的FAT中的函数,ZYNQ肯定不会识别的,这里我们将给出详细的操作步骤。
首先右击相应工程的bsp文件,选择Board Support Package Setting
在这里插入图片描述
然后点击相应的xilffs
在这里插入图片描述
然后重新生成相应的bsp文件即可。接下来我们对常用的函数进行简单的介绍:

//挂载的函数
   
   //The f_mount fucntion registers/unregisters filesystem object to the FatFs module.
FRESULT f_mount (
  FATFS*       fs,    /* [IN] Filesystem object */
  const TCHAR* path,  /* [IN] Logical drive number */
  BYTE         opt    /* [IN] Initialization option */
);

//The f_open function opens a file.
FRESULT f_open (
  FIL* fp,           /* [OUT] Pointer to the file object structure */
  const TCHAR* path, /* [IN] File name */
  BYTE mode          /* [IN] Mode flags */
);

//The f_write writes data to a file.
FRESULT f_write (
  FIL* fp,          /* [IN] Pointer to the file object structure */
  const void* buff, /* [IN] Pointer to the data to be written */
  UINT btw,         /* [IN] Number of bytes to write */
  UINT* bw          /* [OUT] Pointer to the variable to return number of bytes written */
);

//The f_read function reads data from a file.
FRESULT f_read (
  FIL* fp,     /* [IN] File object */
  void* buff,  /* [OUT] Buffer to store read data */
  UINT btr,    /* [IN] Number of bytes to read */
  UINT* br     /* [OUT] Number of bytes read */
);


//The f_sync function flushes the cached information of a writing file.
FRESULT f_sync (
  FIL* fp     /* [IN] File object */
);

//The f_close function closes an open file.
FRESULT f_close (
  FIL* fp     /* [IN] Pointer to the file object */
);

这里需要特别注意f_sync函数,是将
至于上面函数中每个参数的作用,同学们可以打开博客中上面的连接进行学习,上面的介绍非常详细。

bmp图像数据格式

bmp图像主要由下面结构组成:
1、文件头结构
2、信息头结构
3、颜色表
4、像素数据
对于bmp格式的24位真彩图,上面的颜色表信息为空,因为24位不需要颜色表的映射。至于每个结构里面具体的信息,我们给出相应的头文件,可以学习上面的注释。

#ifndef _BMP_IMG_H
#define _BMP_IMG_H
#include "xsdps.h"
#include "ff.h"
#endif
typedef unsigned short int WORD;
//typedef unsigned int DWORD;
typedef int LONGG;
typedef unsigned char BYTE;

/*********** *********** *********** *********** *********** *********** ***********
* definition :struct
* Description :位图文件头
*********** *********** *********** *********** *********** *********** ***********/
#pragma pack(1)/////////////////将结构体中成员按n字节对齐
typedef struct tagBITMAPFILEHEADER
{
    WORD bfType;////////////////文件类型,必须为BM
    DWORD bfSize;///////////////指定文件大小,以字节为单位(3-6字节,低位在前)
    WORD bfReserved1;///////////文件保留字,必须为0
    WORD bfReserved2;///////////文件保留字,必须为0
    DWORD bfOffBits;////////////从文件头到实际位图数据的偏移字节数(11-14字节,低位在前)
}BITMAPFILEHEADER;
/*********** *********** *********** *********** *********** *********** ***********
* definition :struct
* Description :位图信息头
*********** *********** *********** *********** *********** *********** ***********/
typedef struct tagBITMAPINFOHEADER
{
	DWORD biSize;///////////////本结构所占用字节数,为40。注意:实际操作中则有44,这是字节补齐的原因
	LONGG biWidth;///////////////位图的宽度,以像素为单位
	LONGG biHeight;//////////////位图的高度,以像素为单位
	WORD biPlanes;//////////////目标设备的级别,必须为1
	WORD biBitCount;////////////每个像素所需的位数,1(双色),4(16色),8(256色)16(高彩色),24(真彩色)或32之一
	DWORD biCompression;////////位图压缩类型,0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
	DWORD biSizeImage;//////////位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位
	LONGG biXPelsPerMeter;///////位图水平分辨率,每米像素数
	LONGG biYPelsPerMeter;///////位图垂直分辨率,每米像素数
	DWORD biClrUsed;////////////位图实际使用的颜色表中的颜色数,若该值为0,则使用颜色数为2的biBitCount次方
	DWORD biClrImportant;///////位图显示过程中重要的颜色数,若该值为0,则所有的颜色都重要
}BITMAPINFOHEADER;
#pragma pack()//////////////////取消自定义字节方式
/*********** *********** *********** *********** *********** *********** ***********
* definition :struct
* Description :调色板
*********** *********** *********** *********** *********** *********** ***********/
typedef struct tagRGBQUAD
{
	BYTE rgbBlue;///////////////蓝色的亮度(0-255)
	BYTE rgbGreen;//////////////绿色的亮度(0-255)
	BYTE rgbRed;////////////////红色的亮度(0-255)
	BYTE rgbReserved;///////////保留,必须为0
}RGBQUAD;

/*********** *********** *********** *********** *********** *********** ***********
* Function Name :printInfo
* Description :输出文件信息
*********** *********** *********** *********** *********** *********** ***********/
void printInfo(BITMAPFILEHEADER fileHeader,BITMAPINFOHEADER infoHeader);
/*********** *********** *********** *********** *********** *********** ***********
* Function Name :printInfo
* Description :输出画板信息
*********** *********** *********** *********** *********** *********** ***********/
void printPalette(RGBQUAD *rgbPalette,int sizeOfPalette);
//int ReadBmp(BITMAPFILEHEADER fileHeader,BITMAPINFOHEADER infoHeader,RGBQUAD *rgbPalette/*给它去别名一起变化*/,void *img[5000],char *FileName);
int SaveBmp(BITMAPFILEHEADER fileHeader,BITMAPINFOHEADER infoHeader,RGBQUAD *rgbPalette,u8 *img,char *FileName);

特别注意以下函数限制:
在这里插入图片描述
这是为了让结构体之间的成员按照字节递增的顺序排列,如果没有这句约束,结构体再内存中的排列有可能是跳跃的。
所谓ZYNQ写24位真彩图片到SD卡,其实就是先写文件头、再写信息头、最后写图像数据。详情请查看下面的代码进行学习。

PS端设计

因为我们全部的工作量都是在PS端设计的,所以我们这里不再浪费空间给出相应的PL端设计。所以这里直接给出相应的代码,代码说白了就是从示例工程中学习得来的。代码如下:

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "ff.h"
#include "bmp_img.h"
int main()
{
	BITMAPFILEHEADER filh;
	BITMAPINFOHEADER infh;
	filh.bfOffBits = 54;
	filh.bfReserved1 =0;
	filh.bfReserved2 =0;
	filh.bfSize = 54 + 32*32*3;
	filh.bfType = 0x4d42;

	infh.biBitCount = 24;
	infh.biClrImportant = 0;
	infh.biClrUsed =0;
	infh.biCompression =0;
	infh.biHeight =32;
	infh.biPlanes =1;
	infh.biSize = 40;
	infh.biSizeImage = 32*32*3;
	infh.biWidth =32;
	infh.biXPelsPerMeter =0;
	infh.biYPelsPerMeter =0;

	FATFS fs;
	FIL fil;
	u8 *databuf = (u8 *)0x2000000;
	TCHAR *path ="0:/";
	TCHAR *filename ="abc.bmp";
	FRESULT res;
	UINT write_byte_nums;
	int r,c,i;
	for(r=0;r<32;r++){
		for(c=0;c<32;c++){
			for(i=0;i<3;i++){
				if(r<16){
					databuf[r*32*3+c*3+i] = 255;
				}
				else{
					databuf[r*32*3+c*3+i] = 0;
				}
			}
		}
	}
	res = f_mount(&fs,path,0);
	if(res != FR_OK){
		return res;
	}
	res = f_open(&fil,filename,FA_CREATE_ALWAYS | FA_WRITE);
	if(res != FR_OK){
			return res;
		}
	res = f_write(&fil,&filh,sizeof(BITMAPFILEHEADER),&write_byte_nums);
	if(res != FR_OK){
				return res;
		}
	res = f_write(&fil,&infh,sizeof(BITMAPINFOHEADER),&write_byte_nums);
	if(res != FR_OK){
				return res;
		}
	res = f_write(&fil,databuf,32*32*3,&write_byte_nums);
	if(res != FR_OK){
			return res;
		}
	res = f_sync(&fil);
	if(res != FR_OK){
			return res;
		}
	f_close(&fil);

    print("write 256 bytes to abc.bin \n\r");

    return 0;
}

上面的代码特别简单,相信同学们可以学会,但是学这个的时候需要特别注意bmp图像的数据格式。

下板测试结果

从上面的代码中我们可以看出,我们写了一副32*32的bmp图片到SD卡。然后将SD卡在电脑上读出,观察图片是否正确。
在这里插入图片描述
上图证明了我们实验的正确性。

总结

创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章