本人是一名双非一本自动化专业在读大三学生,因学习过程总是冗长乏味,故想把自己所学一字一句记录下来,由于尚在入门阶段,与周围大佬差距甚大,有所不足之处,欢迎指正批评。
该项目是在Ubuntu/C语言环境下,通过运行一块虚拟的 800*400*4 的LCD屏幕,首先利用mmap内存映射简化文件访问和处理过程,通过内存指针在内存中访问文件内容,进行读取或写入操作。
void init_lcd()
{
//1. 打开LCD设备文件
int fd = open("/dev/ubuntu_lcd", O_RDWR);
if(fd == -1)
{
perror("open");
return ;
}
//2. 映射
plcd = mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
}
该项目显示图片格式仅支持bmp图片,通过函数实现读取bmp文件信息(文件宽 高 深度 像素数组大小)并且做出打印在LCD屏幕的操作。
//显示 像素数组
void draw_image(int x0,int y0,int width, int height , const unsigned char image[])
{
int i,j,k=0;
int color;
for(i=height;i>=0;i--)
for(j=0;j<width;j++)
{
color = image[k]|(image[k+1]<<8)|(image[k+2]<<16);
k = k+3;
draw_point(j+x0, i+y0, color);
}
}
//读"1.bmp" 文件,获取 w,h,depth
int draw_bmp(int x0,int y0, const char *bmpfile)
{
unsigned char buf[4];
int fd = open(bmpfile,O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}
read(fd, buf, 2);
if(buf[0]=='B' && buf[1]=='M')
{
printf("It's bmpfile\n");
}
else
return -1;
//1. 读出文件的 宽,高,色深:才能知道像素数组的大小
lseek(fd, 0x12,SEEK_SET);
read(fd, buf, 4);//宽为4字节
int width = (buf[3]<<24)|(buf[2]<<16)|(buf[1]<<8)|buf[0];
//2. 读出文件的 高
lseek(fd, 0x16,SEEK_SET);
read(fd, buf, 4);
int height = (buf[3]<<24)|(buf[2]<<16)|(buf[1]<<8)|buf[0];
//3. 读出文件的 色深
lseek(fd, 0x1c,SEEK_SET);
read(fd, buf, 2);
int depth = (buf[1]<<8)|buf[0]; //24
// 4:像素数组大小: width * height *depth/8
// 读 像素数组
unsigned char *image = (unsigned char*)malloc(width * height *depth/8);
lseek(fd, 54, SEEK_SET); // bmp 信息head占了 54个字节
read(fd, image, width * height *depth/8);
draw_image(x0,y0, width, height, image);
}
为了实现简易人机交互,我添加了触摸屏模块,利用函数实现,用于从触摸屏设备 /dev/ubuntu_event
中读取触摸事件的坐标信息,并返回一个包含 x 和 y 坐标的结构体 pos
。
//读取触摸屏信息函数
struct pos getPos()
{
struct pos p;
p.x = -1;
p.y = -1;
int fd = open("/dev/ubuntu_event", O_RDONLY);
if(fd < 0)
{
perror("open");
return p;
}
// 读触摸屏驱动文件:可以读到一个 事件的结构体
struct input_event ev;
while(1)
{
read(fd, &ev, sizeof(ev));
//printf("type=%d, code=%d, value=%d\n", ev.type, ev.code, ev.value);
if(ev.type==3 && ev.code==0)
{
//printf("x = %d ", ev.value);
p.x = ev.value;
}
else if(ev.type==3 && ev.code==1)
{
//printf("y = %d\n", ev.value);
p.y = ev.value;
close(fd);
return p;
}
}
}
随后进入项目核心部分通过链表尾插法将要显示的图片储存起来,当按下LCD屏幕指定区域后,利用单向循环链表实现显示下一张图片、返回上一张图片的功能,此外,我利用fork创建一个子进程,在子进程中实现播放音乐(也可使用线程实现)。部分主循环函数代码如下:
p = getPos();
printf("x = %d, y = %d\n", p.x, p.y);
if (p.x > 0 && p.x <= 100 && p.y > 0 && p.y < 480)
{
if (head != NULL && current_node != NULL)
{
struct Node* prev_node = NULL;
struct Node* temp_node = head;
while (temp_node && temp_node->next != current_node)
{
temp_node = temp_node->next;
}
prev_node = temp_node;
if (prev_node != NULL)
{
current_node = prev_node;
}
else
{
struct Node* temp_node = head;
while (temp_node->next != NULL)
{
temp_node = temp_node->next;
}
current_node = temp_node;
}
displayImage(current_node);
}
}
else if (p.x >= 700 && p.x < 800 && p.y > 0 && p.y < 480)
{
if (current_node != NULL && current_node->next != NULL)
{
current_node = current_node->next;
}
else
{
current_node = head;
}
displayImage(current_node);
}
pid_t pid = fork();
if (pid == 0)
{ // 子进程
if (system("madplay mp3/duvet.mp3") == -1)
{
perror("system");
}
exit(0);
}
if (p.x >= 750 && p.y >= 0 && p.y <= 50)
{
// 释放内存
struct Node* temp;
while (head != NULL)
{
temp = head;
head = head->next;
free(temp);
}
break;
}
具体实现的视频如下:
ubuntu简易电子相册
📢写在最后
博主只是一名想从事嵌入式linux开发的小白,创作不易,之后还会不定时分享我学习的项目经验。
- 🎉感谢关注🎉