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上观察到的运行现象:
在开发板上观察到的运行现象: