文件IO · 代码纯享版 —— 第2天

本文介绍了如何使用mmap在Linux系统中进行内存映射操作,包括将图片显示在屏幕上、验证映射0kb文件的无效性以及通过mmap实现文件的上传和下载。此外,还展示了如何利用mmap显示BMP图片以及处理内存映射的像素数据。
摘要由CSDN通过智能技术生成

0010读取图片并在屏幕上绘图

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define LCD_H 480
#define LCD_W 800

#define LCD_SIZE (LCD_H * LCD_W)

#define LCD_FILE_PATH "/dev/fb0"

int main(int argc, char const *argv[])
{
    int lcd_fd = open(LCD_FILE_PATH, O_WRONLY);
    if (lcd_fd == -1)
    {
        perror("打开lcd文件失败!");
        return -1;
    }

    int bmp_picture_fd = open("./a1.bmp", O_RDONLY);
    if (bmp_picture_fd == -1)
    {
        perror("打开图片文件失败!");
        return -1;
    }
    // 移动到文件头存放图片宽度的数据之处,读取图片宽度数据
    lseek(bmp_picture_fd, 18, SEEK_SET);

    int picture_w, picture_h;
    if (read(bmp_picture_fd, &picture_w, sizeof(int)) == -1)
    {
        perror("读取图片宽度失败!");
        return -1;
    }
    if (read(bmp_picture_fd, &picture_h, sizeof(int)) == -1)
    {
        perror("读取图片高度失败!");
        return -1;
    }
    printf("图片的宽度为:%d,图片的高度为:%d\n", picture_w, picture_h);

    // 避开文件头,开始读取像素数据
    lseek(bmp_picture_fd, 54, SEEK_SET);
    char picture_color_data_arr[LCD_SIZE * 3];
    if (read(bmp_picture_fd, picture_color_data_arr, LCD_SIZE * 3) == -1)
    {
        perror("读取图片像素数据失败!");
        return -1;
    }

    //     由于bmp是1个像素数据3个字节,而屏幕是1个像素数据4个字节,
    // 所以需要对读取到的像素数据进行3字节转换成4字节,
    // 转换后的数据保存在新数组中。
    int new_picture_color_data_arr[LCD_SIZE];
    for (int y = 0; y < LCD_H; y++)
    {
        for (int x = 0; x < LCD_W; x++)
        {
            // 由于bmp图片的数据是逆序存放的,所以需要进行处理,使其变成顺序存放。
            // 1、bmp图片的像素数据是以BGR的方式存放,而屏幕的像素是以RGB的形式存放
            // 2、bmp的图片像素数据是从下往上排列,而屏幕像素是从上往下排列
            new_picture_color_data_arr[LCD_W * (479 - y) + x] =
                picture_color_data_arr[3 * (LCD_W * y + x) + 0] << 0 |
                picture_color_data_arr[3 * (LCD_W * y + x) + 1] << 8 |
                picture_color_data_arr[3 * (LCD_W * y + x) + 2] << 16;
        }
    }

    if (write(lcd_fd, new_picture_color_data_arr, LCD_SIZE * 4) == -1)
    {
        perror("写入像素数据失败!");
        return -1;
    }

    if (close(lcd_fd) == -1)
    {
        perror("关闭lcd文件失败!");
        return -1;
    }

    if (close(bmp_picture_fd) == -1)
    {
        perror("关闭图片文件失败!");
        return -1;
    }

    return 0;
}

  在secureCRT中的运行结果:
在这里插入图片描述
  在开发板上观察到的运行现象:
在这里插入图片描述

0020验证映射0kb大小的文件无意义

   当映射一个0kb的稀疏文件时,会发生总线错误:

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    umask(0000);

	// 使用mmap映射时,记得给够可读可写权限,否则会提醒权限不够
    int text_fd = open("./text.txt", O_RDWR | O_CREAT, 0777);
    if (text_fd == -1)
    {
        perror("打开lcd文件失败!");
        return -1;
    }

    /* lseek(text_fd, 499, SEEK_SET);
    if (write(text_fd, "*", 1) == -1)
    {
        perror("写入数据失败!");
        return -1;
    } */

    char *mmap_start_p = (char *)mmap(NULL, 500, PROT_READ | PROT_WRITE, MAP_SHARED, text_fd, 0);
    if (mmap_start_p == (char *)MAP_FAILED)
    {
        perror("内存映射失败!");
        return -1;
    }

    char *string = "真是有趣的测试呢!";
    memset(mmap_start_p, 0, 500);
    memcpy(mmap_start_p, string, strlen(string));

    if (close(text_fd) == -1)
    {
        perror("关闭文本文件失败!");
        return -1;
    }
    if (munmap(mmap_start_p, 500) == -1)
    {
        perror("释放映射空间失败!");
        return -1;
    }

    return 0;
}

  在Linux上观察到的运行现象:
在这里插入图片描述
  代码更改为这样就可以成功运行:

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    umask(0000);

    // 使用mmap映射时,记得给够可读可写权限,否则会提醒权限不够
    int text_fd = open("./text.txt", O_RDWR | O_CREAT, 0777);
    if (text_fd == -1)
    {
        perror("打开lcd文件失败!");
        return -1;
    }

    lseek(text_fd, 499, SEEK_SET);
    if (write(text_fd, "*", 1) == -1)
    {
        perror("写入数据失败!");
        return -1;
    }

    char *mmap_start_p = (char *)mmap(NULL, 500, PROT_READ | PROT_WRITE, MAP_SHARED, text_fd, 0);
    if (mmap_start_p == (char *)MAP_FAILED)
    {
        perror("内存映射失败!");
        return -1;
    }

    char *string = "真是有趣的测试呢!";
    memset(mmap_start_p, 0, 500);
    memcpy(mmap_start_p, string, strlen(string));

    if (close(text_fd) == -1)
    {
        perror("关闭文本文件失败!");
        return -1;
    }
    if (munmap(mmap_start_p, 500) == -1)
    {
        perror("释放映射空间失败!");
        return -1;
    }

    return 0;
}

  在Linux上观察到的运行结果:
在这里插入图片描述
  在vscode中打开text.txt文件:
在这里插入图片描述

0030mmap实现将源文件写入到稀疏文件后再提取出来

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

#define EMPTY_FILE_PATH "./empty_file"
#define EMPTY_FILE_SIZE 1048576 * 10
#define MMAP_OFFSET 0
#define DOWNLOAD_MODE "下载"
#define UPLOAD_MODE "上传"
#define OPERATE_DATA_SIZE 1048576

typedef struct file_download_upload_inf
{
    int obj_file_fd;
    int empty_file_fd;
    char *mmap_file_poi;
    int obj_file_size;
    int download_size;
    int upload_size;
} FDUDI, *P_FDUDI;

P_FDUDI Demo_Init();
int Upload_File(P_FDUDI p_fdudi, const char *obj_file_path);
int Download_File(P_FDUDI p_fdudi, const char *obj_file_path);
int Demo_Free(P_FDUDI p_fdudi);

/**
    【业务逻辑】:
        1. 在函数开始处,分配FDUDI结构体的内存空间,用于存储相关信息。
        2. 检查内存分配是否成功,如果分配失败,则返回错误。
        3. 将分配的内存空间清零,以确保结构体的初始状态是整洁的。
        4. 使用access函数检查空洞文件是否存在,如果文件不存在,则进入以下步骤:
            设置文件创建权限掩码为0000,以确保文件创建时具有所需的权限。
            创建空洞文件,并以可读写的方式打开该文件。
            将文件指针设置到文件末尾,以便在后续写入数据时可以扩展文件大小。
            在文件末尾写入一个字节的数据,用于扩展文件大小。
        5. 如果空洞文件存在,则进入以下步骤:
            以可读写的方式打开已存在的空洞文件。
        6. 使用mmap函数将空洞文件映射到内存中,以便后续可以直接对内存进行读写操作。
            设置映射的内存地址为NULL,让系统自动选择合适的地址。
            设置映射的内存大小为空洞文件的大小。
            设置映射的内存权限为可读可写。
            设置映射的内存属性为共享映射,以便多个进程可以共享映射的内存。
            将空洞文件的文件描述符和映射的偏移量作为参数,完成映射操作。
        7. 如果映射失败,则返回错误。
        8. 返回指向FDUDI结构体的指针,表示初始化成功。

    【总结】:
        Demo_Init()函数的业务流程包括分配内存空间、检查空洞文件是否存在、创建或打开空洞文件、将空洞文件映射到内存空间中,并返回指向FDUDI结构体的指针。
 */
P_FDUDI Demo_Init()
{
    // 1. 使用malloc函数申请FDUDI结构体大小的堆空间,并将其赋值给指针p_fdudi
    P_FDUDI p_fdudi = (P_FDUDI)malloc(sizeof(FDUDI));
    if (p_fdudi == (P_FDUDI)NULL)
    {
        // 如果申请堆空间失败,输出错误信息并返回-1
        perror("初始化程序时,申请堆空间失败!");
        return (P_FDUDI)-1;
    }

    // 2. 使用memset函数将p_fdudi指向的内存空间清零
    memset(p_fdudi, 0, sizeof(FDUDI));

    // 3. 检查空洞文件是否存在
    if (access(EMPTY_FILE_PATH, F_OK))
    {
        // 不存在则创建空洞文件
        umask(0000);
        if ((p_fdudi->empty_file_fd = open(EMPTY_FILE_PATH, O_RDWR | O_CREAT, 0777)) == -1)
        {
            perror("打开空洞文件失败!");
            return (P_FDUDI)-1;
        }

        // 将文件指针移动到文件末尾,并写入一个字节的数据,用于创建空洞文件
        lseek(p_fdudi->empty_file_fd, EMPTY_FILE_SIZE - 1, SEEK_SET);
        if (write(p_fdudi->empty_file_fd, "*", sizeof(char)) == -1)
        {
            perror("初始化程序时,写入数据到稀疏文件失败!");
            return (P_FDUDI)-1;
        }
    }
    else
    {
        // 存在则直接打开空洞文件
        if ((p_fdudi->empty_file_fd = open(EMPTY_FILE_PATH, O_RDWR)) == -1)
        {
            // 如果打开失败,输出错误信息并返回-1
            perror("程序初始化时,打开空洞文件失败!");
            return (P_FDUDI)-1;
        }
    }

    // 4. 使用mmap函数将空洞文件映射到内存空间,并将映射到的内存地址赋值给p_fdudi->mmap_file_poi
    if ((p_fdudi->mmap_file_poi = (char *)mmap(NULL,
                                               EMPTY_FILE_SIZE,
                                               PROT_READ | PROT_WRITE,
                                               MAP_SHARED,
                                               p_fdudi->empty_file_fd,
                                               MMAP_OFFSET)) == (char *)MAP_FAILED)
    {
        // 如果映射失败,输出错误信息并返回-1
        perror("映射空洞文件失败!");
        return (P_FDUDI)-1;
    }

    // 5. 返回指向FDUDI结构体的指针p_fdudi
    return p_fdudi;
}

/**
    【业务逻辑】:
        1. 使用memset函数将p_fdudi->mmap_file_poi指向的内存空间清零,以确保初始化状态。
        2. 使用open函数以只读方式打开需要上传的文件,获取文件的文件描述符obj_fd。
        3. 使用lseek函数将obj_fd指向的文件指针移动到文件末尾,并通过返回值获取文件大小,将文件大小保存在p_fdudi->obj_file_size中。
        4. 将字符串"ok"拷贝到new_mmap_poi指向的内存空间中,表示上传开始,将new_mmap_poi指针向后移动2个字节。
        5. 将p_fdudi->obj_file_size的内容拷贝到new_mmap_poi指向的内存空间中,表示上传文件大小,将new_mmap_poi指针向后移动sizeof(int)个字节。
        6. 使用lseek函数将obj_fd指向的文件指针移动到文件开头。
        7. 输出即将上传的文件的路径和大小。
        8. 进入循环,每次循环读取OPERATE_DATA_SIZE个字节的文件数据到file_data数组中。
            如果读取失败,输出错误信息并返回-1。
            如果读取结束,输出上传成功的信息,跳出循环。
            如果读取成功,将file_data中的数据拷贝到new_mmap_poi指向的内存空间中,将new_mmap_poi指针向后移动读取的字节数,增加上传字节数p_fdudi->upload_size。
        9. 使用close函数关闭obj_fd指向的文件。
        10. 返回0表示上传成功。

    【总结】:
        Upload_File()函数的业务流程包括清零内存空间、打开文件并获取文件大小、拷贝数据到内存空间、读取文件数据并写入内存空间,最后关闭文件。
 */
int Upload_File(P_FDUDI p_fdudi, const char *obj_file_path)
{
    // 1. 使用memset函数将p_fdudi->mmap_file_poi指向的内存空间清零
    memset(p_fdudi->mmap_file_poi, 0, EMPTY_FILE_SIZE);

    // 2. 使用open函数以只读方式打开需要上传的文件
    int obj_fd = open(obj_file_path, O_RDONLY);
    if (obj_fd == -1)
    {
        perror("打开需要上传的文件失败!");
        return -1;
    }

    // 3. 使用lseek函数获取需要上传的文件的大小,并将其保存在p_fdudi->obj_file_size中
    p_fdudi->obj_file_size = lseek(obj_fd, 0, SEEK_END);

    // 4. 将"ok"两个字节的数据拷贝到new_mmap_poi指向的内存空间中,并将new_mmap_poi指针向后移动2个字节
    char *new_mmap_poi = p_fdudi->mmap_file_poi;
    memcpy(new_mmap_poi, "ok", 2);
    new_mmap_poi += 2;

    // 5. 将p_fdudi->obj_file_size的内容拷贝到new_mmap_poi指向的内存空间中,并将new_mmap_poi指针向后移动sizeof(int)个字节
    memcpy(new_mmap_poi, &p_fdudi->obj_file_size, sizeof(int));
    new_mmap_poi += sizeof(int);

    // 6. 使用lseek函数将obj_fd指向的文件指针移动到文件开头
    lseek(obj_fd, 0, SEEK_SET);

    // 7. 输出即将上传的文件的路径和大小
    printf("即将上传的文件为:%s\n其大小为:%d\n\n", obj_file_path, p_fdudi->obj_file_size);

    // 8. 定义一个大小为OPERATE_DATA_SIZE的字符数组file_data,用于存储从文件中读取的数据
    char file_data[OPERATE_DATA_SIZE];
    while (1)
    {
        // 9. 使用memset函数将file_data数组清零
        memset(file_data, 0, OPERATE_DATA_SIZE);

        // 10. 使用read函数从obj_fd指向的文件中读取最多OPERATE_DATA_SIZE个字节的数据到file_data数组中
        int read_ret = read(obj_fd, file_data, OPERATE_DATA_SIZE);
        if (read_ret == -1)
        {
            perror("读取上传文件失败!");
            return -1;
        }
        else if (read_ret == 0)
        {
            printf("上传成功!总共上传字节数:%d\n", p_fdudi->upload_size);
            break;
        }
        else
        {
            // 如果读取成功,将file_data中的数据拷贝到new_mmap_poi指向的内存空间中,并将new_mmap_poi指针向后移动read_ret个字节
            memcpy(new_mmap_poi, file_data, read_ret);
            new_mmap_poi += read_ret;
            p_fdudi->upload_size += read_ret;
        }
    }

    // 11. 使用close函数关闭obj_fd指向的文件
    if (close(obj_fd) == -1)
    {
        perror("关闭上传文件失败!");
        return -1;
    }

    // 12. 返回0表示上传成功
    return 0;
}

/**
    【业务逻辑】:
        1. 定义下载掩码数组dowmload_mask,并将p_fdudi->mmap_file_poi指向的内存空间中的前两个字节拷贝到dowmload_mask中。
        2. 将new_mmap_poi指针向后移动2个字节。
        3. 如果下载掩码不为"ok",则输出无下载文件的信息,并返回0。
        4. 将new_mmap_poi指向的内存空间中的sizeof(int)个字节的数据拷贝到p_fdudi->obj_file_size中,并将new_mmap_poi指针向后移动sizeof(int)个字节。
        5. 输出即将下载的文件大小和路径。
        6. 设置文件创建权限掩码为0000,以确保文件创建时具有所需的权限。
        7. 使用open函数创建或打开文件,并获取文件描述符obj_fd。
        8. 定义一个大小为OPERATE_DATA_SIZE的字符数组file_data,用于存储将要写入文件的数据。
        9. 循环将内存空间中的数据写入文件,每次循环写入OPERATE_DATA_SIZE个字节的数据,直到写入完成。
            清零file_data数组,并将new_mmap_poi指向的内存空间中的数据拷贝到file_data数组中。
            使用write函数将file_data中的数据写入obj_fd指向的文件中。
            将new_mmap_poi指针向后移动OPERATE_DATA_SIZE个字节,增加下载字节数p_fdudi->download_size。
        10. 如果文件大小不是OPERATE_DATA_SIZE的整数倍,处理剩余的数据。
            计算剩余数据的字节数。
            将new_mmap_poi指向的内存空间中的数据拷贝到file_data数组中。
            使用write函数将file_data中的数据写入obj_fd指向的文件中。
            增加下载字节数p_fdudi->download_size。
        11. 输出下载成功的信息,包括总共下载的字节数。
        12. 使用close函数关闭obj_fd指向的文件。
        13. 返回0表示下载成功。

    【总结】:
        Download_File()函数的业务流程包括拷贝掩码、判断下载掩码、拷贝文件大小、创建或打开文件、循环写入文件数据、处理剩余数据,最后关闭文件。
 */
int Download_File(P_FDUDI p_fdudi, const char *obj_file_path)
{
    // 1. 定义下载掩码数组dowmload_mask,并将p_fdudi->mmap_file_poi指向的内存空间中的前两个字节拷贝到dowmload_mask中
    char dowmload_mask[3] = "\0";
    char *new_mmap_poi = p_fdudi->mmap_file_poi;
    memcpy(dowmload_mask, new_mmap_poi, 2);

    // 2. 将new_mmap_poi指针向后移动2个字节
    new_mmap_poi += 2;
    // 3. 如果下载掩码不为"ok",则输出无下载文件的信息,并返回0
    if (strcmp(dowmload_mask, "ok") != 0)
    {
        printf("无下载文件\n");
        return 0;
    }

    // 4. 将new_mmap_poi指向的内存空间中的sizeof(int)个字节的数据拷贝到p_fdudi->obj_file_size中,并将new_mmap_poi指针向后移动sizeof(int)个字节
    memcpy(&p_fdudi->obj_file_size, new_mmap_poi, sizeof(int));
    new_mmap_poi += sizeof(int);

    // 5. 输出即将下载的文件大小和路径
    printf("即将下载数据为 %d 个字节数据,保存到 %s 文件中\n", p_fdudi->obj_file_size, obj_file_path);

    // 6. 设置文件创建权限掩码为0000,以确保文件创建时具有所需的权限
    umask(0000);
    // 7. 使用open函数创建或打开文件,并获取文件描述符obj_fd
    int obj_fd = open(obj_file_path, O_WRONLY | O_CREAT | O_EXCL, 0777);
    if (obj_fd == -1)
    {
        perror("下载文件的文件描述符创建失败!");
        return -1;
    }

    // 8. 定义一个大小为OPERATE_DATA_SIZE的字符数组file_data,用于存储将要写入文件的数据
    char file_data[OPERATE_DATA_SIZE];
    // 9. 循环将内存空间中的数据写入文件,每次循环写入OPERATE_DATA_SIZE个字节的数据,直到写入完成
    for (int lp = 0; lp < p_fdudi->obj_file_size / OPERATE_DATA_SIZE; lp++)
    {
        // 清零file_data数组,并将new_mmap_poi指向的内存空间中的数据拷贝到file_data数组中
        memset(file_data, 0, OPERATE_DATA_SIZE);
        memcpy(file_data, new_mmap_poi, OPERATE_DATA_SIZE);

        // 使用write函数将file_data中的数据写入obj_fd指向的文件中
        if (write(obj_fd, file_data, OPERATE_DATA_SIZE) == -1)
        {
            perror("写入数据失败!");
            return -1;
        }
        // 将new_mmap_poi指针向后移动OPERATE_DATA_SIZE个字节,增加下载字节数p_fdudi->download_size
        new_mmap_poi += OPERATE_DATA_SIZE;
        p_fdudi->download_size += OPERATE_DATA_SIZE;
    }

    // 10. 如果文件大小不是OPERATE_DATA_SIZE的整数倍,处理剩余的数据
    if (p_fdudi->obj_file_size % OPERATE_DATA_SIZE != 0)
    {
        // 计算剩余数据的字节数
        int skip = p_fdudi->obj_file_size % OPERATE_DATA_SIZE;
        // 将new_mmap_poi指向的内存空间中的数据拷贝到file_data数组中
        memcpy(file_data, new_mmap_poi, skip);

        // 使用write函数将file_data中的数据写入obj_fd指向的文件中
        if (write(obj_fd, file_data, skip) == -1)
        {
            perror("写入剩余数据失败!");
            return -1;
        }
        // 增加下载字节数p_fdudi->download_size
        p_fdudi->download_size += skip;
    }

    // 11. 输出下载成功的信息,包括总共下载的字节数
    printf("下载成功!总共下载 %d 个字节数据。\n", p_fdudi->download_size);

    // 12. 使用close函数关闭obj_fd指向的文件
    if (close(obj_fd) == -1)
    {
        // 如果关闭文件失败,输出错误信息并返回-1
        perror("关闭下载文件失败!");
        return -1;
    }
    // 13. 返回0表示下载成功
    return 0;
}

/**
    【业务逻辑】:
        1. 使用close函数关闭空洞文件描述符p_fdudi->empty_file_fd。
        2. 使用munmap函数释放映射空间p_fdudi->mmap_file_poi。
        3. 使用free函数释放p_fdudi指向的内存空间。
        4. 返回0表示释放成功。

    【总结】:
        Demo_Free()函数的业务流程包括关闭空洞文件描述符、释放映射空间和释放内存空间。
 */
int Demo_Free(P_FDUDI p_fdudi)
{
    if (close(p_fdudi->empty_file_fd) == -1)
    {
        perror("关闭空洞文件失败!");
        return -1;
    }

    if (munmap(p_fdudi->mmap_file_poi, EMPTY_FILE_SIZE) == -1)
    {
        perror("释放映射空间失败!");
        return -1;
    }

    free(p_fdudi);

    return 0;
}

int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        printf("命令行参数有误!");
        return -1;
    }

    P_FDUDI p_fdudi = Demo_Init();
    if (p_fdudi == (P_FDUDI)-1)
    {
        printf("初始化失败!");
        return -1;
    }

    if (strcmp(argv[1], DOWNLOAD_MODE) == 0)
    {
        Download_File(p_fdudi, argv[2]);
    }
    else if (strcmp(argv[1], UPLOAD_MODE) == 0)
    {
        Upload_File(p_fdudi, argv[2]);
    }
    else
    {
        printf("输入命令有误(非“上传”“下载”),请重新输入\n");
    }

    if (Demo_Free(p_fdudi) == -1)
    {
        printf("释放资源失败!\n");
        return -1;
    }

    return 0;
}

  在Linux上观察到的运行现象:
在这里插入图片描述

0040使用mmap进行内存映射显示图片

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define LCD_DEV_PATH "/dev/fb0"
#define LCD_PIEXL_SIZE 4
#define BMP_PIEXL_SIZE 3
#define MMAP_OFFSET 0

typedef struct lcd_dis_inf
{
    int lcd;
    int *mmap_star_poi;
    int lcd_w;
    int lcd_h;
} LCD_INF_STR, *LCD_INF_POI;

LCD_INF_POI Lcd_Dis_Init();

LCD_INF_POI Lcd_Dis_Init()
{
    LCD_INF_POI lcd_inf_poi = (LCD_INF_POI)malloc(sizeof(LCD_INF_STR));
    if (lcd_inf_poi == (LCD_INF_POI)NULL)
    {
        perror("申请堆空间失败!");
        return (LCD_INF_POI)-1;
    }

    memset(lcd_inf_poi, 0, sizeof(LCD_INF_STR));

    lcd_inf_poi->lcd_w = 800;
    lcd_inf_poi->lcd_h = 480;

    if ((lcd_inf_poi->lcd = open(LCD_DEV_PATH, O_RDWR)) == -1)
    {
        perror("打开lcd失败!");
        return (LCD_INF_POI)-1;
    }

    if ((lcd_inf_poi->mmap_star_poi = mmap(NULL,
                                           lcd_inf_poi->lcd_w * lcd_inf_poi->lcd_h * LCD_PIEXL_SIZE,
                                           PROT_READ | PROT_WRITE,
                                           MAP_SHARED,
                                           lcd_inf_poi->lcd,
                                           MMAP_OFFSET)) == MAP_FAILED)
    {
        perror("映射失败!");
        return (LCD_INF_POI)-1;
    }
    return lcd_inf_poi;
}

/**
    【业务逻辑】:
        1. 在函数开始处,打开图片。
        2. 检查图片是否成功打开,如果打开失败,则输出错误信息并返回-1。
        3. 获取图片的宽度和高度。
        4. 读取图片的宽度和高度,如果读取失败,则输出错误信息并返回-1。
        5. 输出即将显示的图片的路径、宽度和高度。
        6. 设置读取图片像素点时的偏移量。
        7. 计算每行像素点的字节数。
        8. 为存储像素点的数组分配内存。
        9. 读取图片的像素点数据,如果读取失败,则输出错误信息并返回-1。
        10. 将读取到的像素点数据存储到映射的内存中。
        11. 关闭图片。
        12. 检查关闭图片是否成功,如果关闭失败,则输出错误信息并返回-1。
        13. 返回0,表示函数执行成功。

    【参数说明】:
        - lcd_inf_poi:指向LCD_INF_POI结构体的指针,用于存储LCD相关信息。
        - bmp_path:指向字符串的指针,表示图片的路径。

    【总结】:
        该函数的业务流程包括打开图片、获取图片的宽度和高度、读取图片的像素点数据并存储到映射的内存中,最后关闭图片。
 */
int Lcd_Dising(LCD_INF_POI lcd_inf_poi, const char *bmp_path)
{
    // 打开指定路径下的图片文件
    int bmp = open(bmp_path, O_RDONLY);
    if (bmp == -1)
    {
        perror("打开图片失败!");
        return -1;
    }

    // 读取图片的宽度和高度信息
    lseek(bmp, 18, SEEK_SET);
    int bmp_w, bmp_h;
    if (read(bmp, &bmp_w, sizeof(int)) == -1)
    {
        perror("读取图片宽度失败");
        return -1;
    }
    if (read(bmp, &bmp_h, sizeof(int)) == -1)
    {
        perror("读取图片高度失败");
        return -1;
    }
    printf("读取到图片的名称为:%s, 宽为:%d,高为:%d\n", bmp_path, bmp_w, bmp_h);

    // 定位到图片数据的起始位置
    lseek(bmp, 54, SEEK_SET);

    // 计算每行像素点字节对齐的字节数
    int skip;
    if (bmp_w * 3 % 4 == 0)
    {
        skip = 0;
    }
    else
    {
        skip = 4 - (bmp_w * 3 % 4);
    }

    // 读取图片的像素点数据
    char rgb[bmp_w * bmp_h * BMP_PIEXL_SIZE + bmp_h * skip];
    if (read(bmp, rgb, bmp_w * bmp_h * BMP_PIEXL_SIZE + bmp_h * skip) == -1)
    {
        perror("读取图片像素点失败!");
        return -1;
    }

    // 将图片的像素点数据写入LCD屏幕的内存映射区域
    for (int y = 0, n = 0; y < bmp_h; y++)
    {
        for (int x = 0; x < bmp_w; x++, n += 3)
        {
            /** 其实也相当于(800 * y + )
             *  lcd_inf_poi->mmap_star_poi相当于LCD屏幕的内存映射区域的起始地址不用管
             *  lcd_inf_poi->lcd_w * (bmp_h - 1 - y) + x可看成:(800 * y + x)
             *  至于(bmp_h - 1 - y),那是因为位图的行是从下到上的,跟屏幕的从上到下不一样,需要进行校正
             */
            *(lcd_inf_poi->mmap_star_poi +
              lcd_inf_poi->lcd_w * (bmp_h - 1 - y) + x) =
                rgb[n] << 0 |
                rgb[n + 1] << 8 |
                rgb[n + 2] << 16;
        }
        // 跳过本行字节对齐部分,下一个循环直接进入下一行
        // 如果不跳过字节对齐这部分,那么下一行颜色数据将会写入到本行中
        n += skip;
    }

    // 关闭图片文件
    if (close(bmp) == -1)
    {
        perror("关闭图片失败!");
        return -1;
    }
    return 0;
}

/**
 * 【业务逻辑】:
 *  1. 在函数开始处,关闭显示器。
 *  2. 检查关闭显示器是否成功,如果失败,则打印错误信息并返回-1。
 *  3. 释放映射空间。
 *  4. 检查释放映射空间是否成功,如果失败,则打印错误信息并返回-1。
 *  5. 释放lcd_inf_poi指向的内存空间。
 *  6. 返回0。
 *
 * 【参数说明】:
 *  - 参数1:lcd_inf_poi,指向LCD_INF_POI结构体的指针。
 * 
 * 【注意事项】:
 *  - 注意事项1:需要确保lcd_inf_poi指向的内存空间是通过malloc函数分配的,否则会导致错误。
 *
 * 【总结】:
 *  该函数的业务流程包括关闭显示器、释放映射空间和释放内存空间。
 *   如果关闭显示器或释放映射空间失败,则会打印错误信息并返回-1,否则返回0表示执行成功。
 */
int Lcd_Dis_Free(LCD_INF_POI lcd_inf_poi)
{
    if (close(lcd_inf_poi->lcd) == -1)
    {
        perror("关闭lcd文件失败!");
        return -1;
    }

    if (munmap(lcd_inf_poi->mmap_star_poi,
               lcd_inf_poi->lcd_w * lcd_inf_poi->lcd_h * LCD_PIEXL_SIZE) == -1)
    {
        perror("释放映射空间失败!");
        return -1;
    }

    free(lcd_inf_poi);

    return 0;
}

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("命令行参数错误!\n");
        return -1;
    }

    LCD_INF_POI lcd_inf_poi = Lcd_Dis_Init();
    if (lcd_inf_poi == (LCD_INF_POI)-1)
    {
        printf("显示初始化失败!\n");
        return -1;
    }

    if (Lcd_Dising(lcd_inf_poi, argv[1]) == -1)
    {
        printf("显示图片失败!\n");
        return -1;
    }

    if (Lcd_Dis_Free(lcd_inf_poi) == -1)
    {
        printf("释放资源失败!\n");
        return -1;
    }

    return 0;
}

  在secureRTC上观察到的运行现象:
在这里插入图片描述
  在开发板上观察到的运行现象:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值