ZYNQ基础—-使用SD卡保存图片(3)


使用ZYNQ向SD卡中写入测试彩条

??使用在ZYNQ中有两个A9的arm核,在PS部分可以像单片机那样去访问一些常用的接口。并且Xilinx已经为这些接口的使用提供了库可以在SDK中直接使用。想要使用SD卡存储图片,首先需要在ZYNQ核中打开SD卡设置。

搭建最小系统

??在ZYNQ核中,打开SD卡,然后导出bit文件到sdk中,接下来就可以在PS端来完成调用前面所介绍的函数,从而实现向SD卡中写入数据的操作。
在这里插入图片描述

在SDK中使用FATFS文件系统

??Xilinx已经将FATFS进行了移植,在使用的时候只需要打开所支持的库即可。在创建好工程后点击BSP设置,就可以看到其中所支持的库,需要勾选xilffs。
在这里插入图片描述

头文件

??在头文件中,需要将ff.h这个头文件进行引用,然后再头文件中需要使用到文件头和信息头的结构体。数据类型WORD等等其实都是基本数据类型的重定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*********** *********** *********** *********** *********** *********** ***********
* 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()//////////////////取消自定义字节方式

??main函数中需要定义这两个结构体的对象,并且在main函数中对这两个结构体进行赋值。本次我做的是一个1024*768大小的图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    //文件头结构体赋值
    bmpFile_H.bfType = 0x4d42;  // 文件类型为BMP
    bmpFile_H.bfSize = 54 + 1024*768*3; // 文件大小存储1024*768大小的图像数据
    bmpFile_H.bfReserved1 = 0;
    bmpFile_H.bfReserved2 = 0;
    bmpFile_H.bfOffBits = 54; //图像数据相较于文件头的偏移地址

    //信息头结构体赋值
    bmpInfo_H.biSize = 40; //信息头大小
    bmpInfo_H.biWidth = 1024; //BMP图像宽
    bmpInfo_H.biHeight = 768; //BMP图像高度
    bmpInfo_H.biPlanes = 1; //目标设备级别
    bmpInfo_H.biBitCount = 24; //像素为24位真彩色
    bmpInfo_H.biCompression = 0; //不压缩
    bmpInfo_H.biSizeImage = 1024 * 768 * 3; //位图的大小
    bmpInfo_H.biXPelsPerMeter = 0;//水平分辨率
    bmpInfo_H.biYPelsPerMeter = 0;//垂直分辨率
    bmpInfo_H.biClrImportant = 0;//色彩权重
    bmpInfo_H.biClrUsed = 0 ; //使用到的颜色

写入数据函数

??然后声明两个函数,一个函数用于向SD卡中写入数据,一个用于产生测试图像。向SD中写入数据的函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*********** *********** *********** *********** *********** *********** ***********
*功能:保存图片
*parameter: @ bInfoHeader: BMP文件信息头
*parameter: @ bFileHeader:BMP文件头
*parameter: @ imageSrc :图像数据源
*parameter: @ fileName :图像要保存的文件
*return : 返回值判断图片是否被正确写入
*********** *********** *********** *********** *********** *********** ***********/
int writeSdCard(
        BITMAPFILEHEADER * bFileHeader,
        BITMAPINFOHEADER * bInfoHeader,
        u8 * imageSrc,
        char * fileName
        )
{
    FATFS fatFS; //FATFS 文件结构体对象
    const char * path = "0:/";//逻辑分区的指针编号
    FIL fil;
    UINT numWriteBytes;

    FRESULT fileOK;
    fileOK = f_mount(&fatFS, path, 0); //挂载
    fileOK = f_open(&fil,fileName, FA_CREATE_ALWAYS|FA_WRITE);//创建文件并对文件进行写入
    fileOK = f_lseek(&fil, 0); //指针指向文件开始的位置
    fileOK = f_write(&fil, bFileHeader, sizeof(BITMAPFILEHEADER), &numWriteBytes);//写入文件头
    fileOK = f_write(&fil, bInfoHeader, sizeof(BITMAPINFOHEADER), &numWriteBytes);//写入信息头
    fileOK = f_write(&fil, imageSrc , 1024*768*3 + 256, &numWriteBytes);//写入图像数据
    fileOK = f_sync(&fil); //同步
    fileOK = f_close(&fil);//关闭
    if (fileOK != FR_OK )
    {
        return XST_FAILURE;
    }
    return XST_SUCCESS;
}

产生测试图像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*********** *********** *********** *********** *********** *********** ***********
*功能:产生测试图像
*parameter: @ imageSrc 测试图像的指针
*********** *********** *********** *********** *********** *********** ***********/
void genTestImage(u8 * imageSrc)
{
    for (int idxRow = 0; idxRow < 768; idxRow++)
    {
        for (int idxCol = 0; idxCol < 1024; idxCol++)
        {
            if ( idxCol >= 0 && idxCol < 341)
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0x00;
            }
            else if (  idxCol >= 341 && idxCol < 682)
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0x00;
            }
            else if ( idxCol >= 682 && idxCol < 1024)
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0xFF;
            }
        }
    }
}

将SD卡插入到开发板

??将SD卡插入到开发板后,就可以运行程序,然后就可以将程序运行起来,程序运行结束后,可以将SD插入到电脑上看到SD卡中出现了一幅bmp的图像。
在这里插入图片描述
??打开可以看到产生了测试的图像。虽然出现了图像,但是图像的与期望是不一样的,是相反的,图像彩条应该是RGB排列,但是现在得到的结果是BGR排列。这是与内部数据排列有关。
在这里插入图片描述
??通过更改测试图像的数据后可以看到图像数据确实发生了变化,可以看到BMP图像的存储,可以看到是由左下角开始的。需要注意在SD卡中存储的数据是低位在前,高位在后,因此在程序中设计的想要的每个像素RGB排列变成了BGR排列。并且可以看到,在SD中存储的图片在显示的时候,第一行变成了最后一行。最后一行变成了第一行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*********** *********** *********** *********** *********** *********** ***********
*功能:产生测试图像
*parameter: @ imageSrc 测试图像的指针
*********** *********** *********** *********** *********** *********** ***********/
void genTestImage(u8 * imageSrc)
{
    for (int idxRow = 0; idxRow < 768; idxRow++)
    {
        for (int idxCol = 0; idxCol < 1024; idxCol++)
        {
            if ( idxRow>=0 && idxRow <200 && idxCol >= 682 && idxCol < 1024)
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0xFF;
            }
            else if ( idxCol >= 0 && idxCol < 341)
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0x00;
            }
            else if (  idxCol >= 341 && idxCol < 682)
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0x00;
            }
            else
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0xFF;
            }
        }
    }
}

在这里插入图片描述
??经过一番debug之后,最终解决了这些问题,显示的颜色顺序也就都正常了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void genTestImage(u8 * imageSrc)
{
    for (int idxRow = 0; idxRow < 768; idxRow++)
    {
        for (int idxCol = 0; idxCol < 1024; idxCol++)
        {
            if ( idxRow>=500 && idxRow <768 && idxCol >= 682 && idxCol < 1024)
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0xFF;
            }
            else if ( idxCol >= 0 && idxCol < 341)
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0xFF;
            }
            else if (  idxCol >= 341 && idxCol < 682)
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0x00;
            }
            else
            {
                *(imageSrc + (1024*idxRow + idxCol)*3) = 0xFF;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 1) = 0x00;
                *(imageSrc + (1024*idxRow + idxCol)*3 + 2) = 0x00;
            }
        }
    }

在这里插入图片描述


参考:V3学院尤老师的哦