嵌入式学习笔记--GEC6818--bmp图片显示

一、开发环境

1.ARM+linux

2.ubuntu16.04(安装了GCC,arm-linux-gcc 5.4.0)

安装方法ubuntu16.04
https://blog.csdn.net/qq_40592257/article/details/106296000

安装arm-linux-gcc 5.4.0

https://blog.csdn.net/weixin_42108484/article/details/84295214

3.GEC6818开发板

4、安装SecureCRT

二、bmp图片显示原理

1、bmp图片显示的格式

常见的图片格式有:bmp,jpg,png,gif

我们使用的是bmp图片
    计算机中用RGB表示每个像素点的颜色值
    bmp格式图片的编码方式:
    第一个:每个像素点占3个字节(24位二进制),分别存放的是BGR的数值(正常我们习惯的顺序RGB,但是bmp图片编码的时候是反的)
    第二个:lcd要求每个像素点占4个字节,多出来的一个字节(A存放的是透明度),也就是说lcd要求是ARGB
    第三个:bmp图片RGB的排列顺序并不是我们常规理解的RGB,而是BGR
    并且bmp图片像素点上下也是颠倒的
    第四个:bmp图片的宽所占的字节数如果不能被4整除,windows在保存的时候,会在每一行的后面添加垃圾数凑够4整除
    领悟道理:每种图片都有自己独特的编码方式,显示图片实际上先要了解图片的编码方式,然后使用文件IO配合算法解决图像显示的问题

2.分析bmp图片显示的思路
开发板的基本信息:

       cpu:  三星s5p6818芯片   基于ARM架构的

       ARM:英国一家半导体公司,设计芯片的内核和架构,自己不生产芯片
           高通
           联发科
           三星
           华为
       液晶屏: 7英寸,分辨率 800*480
思路:
(1)打开你要显示的bmp图片
打开lcd的驱动 –》文件IO可以对硬件设备进行操作
(2)读取bmp图片的像素点颜色值
(3)把读取的颜色值写入到lcd中
(4)关闭bmp,lcd驱动            
3.开发板的基本使用
设置字体大小:secure  CRT中的选项—》会话选项–》外观–》字体
(1)连接电脑,使用secure CRT软件通过串口线查看开发板系统中的内容
usb转串口线连接电脑,安装驱动
安装secureCRT软件,配置该软件(见day04图示)
(2)使用开发板
 第一:编译程序
 gcc  –》只适合编译得到X86平台的程序(电脑上运行)
 arm-linux-gcc(交叉编译工具) –》只适合编译得到ARM平台的程序(开发板上运行)

 arm-linux-gcc  hello.c   -o   hello

补充: file  文件名  //查看该文件的属性
交叉编译
程序在PC端编译,程序运行在开发板上运行(程序的编译和运行不在同一个平台)–》把这个过程叫做交叉编译
第二:下载程序到开发板
方法一:使用串口下载(下载速度慢)
    rx  要下载的文件名   回车 –》点击传输–》选择发送xmodem–》自动弹出对话框,你找到要下载的文件,双击自动下载、
    可以把文件打包之后下载(速度快)
    补充:  压缩(打包)      tar  -jcf  压缩包名字.tar.bz2   文件1  文件2  …..   
    tar  -zcf  压缩包名字.tar.gz     文件1  文件2  …..                                                                                             
    比如:  tar  -jcf  88.tar.bz2   1.bmp  2.bmp   3.bmp   
    解压               
    tar  -jxf  压缩包的名字.tar.bz2
    tar  -zxf  压缩包的名字.tar.gz
    常见的压缩包格式:两种bz2结尾和gz结尾
第三:安装交叉工具
第一步:解压压缩包
 tar  -xf   arm-linux-gnueabi-5.4.0.tar.xz
第二步:设置环境变量(为了让arm-linux-gcc可以在任何路径下使用)

环境变量:指的是系统的默认路径,你输入arm-linux-gcc,系统自动去这个路径下找到arm-linux-gcc
export  PATH=你刚才解压的arm-linux-gcc的路径:$PATH  //这样写是一次性,重启ubuntu窗口就失效了

永久设置:把刚才那句话写入到/etc/bash.bashrc的最后面即可
比如:export  PATH=/home/ubuntu/usr/local/arm/5.4.0/usr/bin:$PATH

4. 显示bmp出现的问题

           (1)bmp错乱

                     原因:bmp每个像素点3字节,但是lcd每个像素点4字节

                     解决方法(编程):C语言的按位或,配合左移,实现数据的拼接   

                                     二进制:   1101 0101

                                                     1010 1101<<8  位或

                                                     1101 1010<<16

                                                     0000 0000<<24

                                  0000 0000  1101 1010 1010 1101 1101 0101  结果

触摸屏读写坐标

==================

    新款开发板(黑色): 坐标是1024*600 --》代码去修正了

    1. 思路:

                第一步: 打开触摸屏的驱动   /dev/input/event0

                第二步: 读取触摸屏的坐标

                第三步: 关闭触摸屏

    2. 输入子系统模型

               概念:linux把所有输入类型设备(键盘,鼠标,触摸屏)的驱动和应用程序抽象为统一的模型(输入子系统模型)

                         linux系统中定义一个头文件跟输入子系统模型有关  /usr/include/linux/input.h                   

               重要的结构体:

                        struct input_event {

	           struct timeval time; //事件响应的时间

	          __u16 type;  //事件类型,区分不同的输入设备,比如:EV_KEY 键盘事件   EV_ABS 触摸事件  

	          __u16 code; //按键   X,Y坐标ABS_X    ABS_Y

	          __s32 value; //键值  坐标值

                       };   





内存映射(把硬件设备在内存中对应的首地址告诉你)

===============

   1.相关的接口函数

             #include <sys/mman.h>

            void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

                    返回值:成功  返回映射得到的首地址

                                 失败  NULL

                       参数:addr --》一般设置为NULL,表示由操作系统自动给我分配硬件对应的首地址

                                 length --》你要映射的内存地址的长度,字节  比如:液晶屏大小是  800*480*4

                                 prot --》PROT_READ  可读

                                               PROT_WRITE 可写

                                 flags --》你映射的内存是否可以被其它程序共享

                                               MAP_SHARED 可以共享

                                               MAP_PRIVATE 不可以共享

                                 fd --》你要映射的那个硬件设备的文件描述符

                                 offset --》一般设置为0,表示地址偏移



           int munmap(void *addr, size_t length)   //解除映射

                     参数:addr --》你之前映射得到的首地址

                               length --》地址的长度 

三、循环显示bmp图片,可以点击上一张,下一张

main.c

#include"doublecycle.h"
#include"myhead.h"
#define BMP5 "5.bmp"
#define BMP1 "1.bmp"
#define BMP2 "2.bmp"
#define BMP3 "3.bmp"
#define BMP4 "4.bmp"
int show_xy(int *xy)
{
	int tsfd;
	//定义输入子系统模型有关的结构体变量
	struct 	input_event myevent;
	
	//打开触摸屏驱动
	tsfd=open("/dev/input/event0",O_RDWR);
	if(tsfd==-1)
	{
			perror("open failed!\n");
			return -1;
	}

	int flag=0;
	while(1)
	{
		//读取触摸屏的坐标
		read(tsfd,&myevent,sizeof(myevent));
		if(myevent.type==EV_ABS)
		{
			if(myevent.code==ABS_X)
			{
				flag++;
				xy[0]=myevent.value/1.28;
				//printf(" < %d,",xy[0]);
			}
			if(myevent.code==ABS_Y)
			{
				flag++;
				xy[1]=myevent.value/1.25;
				//printf("%d > \n",xy[1]);
				
			}
			
		}
		if(flag==2)
		{
			break;
		}
			
	}
	close(tsfd);
	return 0;
}

int showbmp_mmap(char argv[50])
{
	int bmpfd;
	int lcdfd;
	
	
	int height;//高度
	int length;//长度
	
	//printf("The pirture's big \n length: height:\n");
	//scanf("%d%d",&length,&height);//scanf里面最好不要加换行符

	//打开你要显示的200*100大小的bmp 
	bmpfd=open(argv,O_RDWR);
	if(bmpfd==-1)
	{
		perror("打开图片失败!\n");
		return -1;
	}
	//打开lcd驱动
	lcdfd=open("/dev/fb0",O_RDWR);
	if(lcdfd==-1)
	{
		perror("打开lcd失败!\n");
		return -1;
	}
	lseek(bmpfd,18,SEEK_SET);
	read(bmpfd,&length,4);
	read(bmpfd,&height,4);
	lseek(bmpfd,54,SEEK_SET);//跳过头信息
	//定义一个数组,依据图片的大小
	char bmpbuf[height*length*3];
	if((length*3)%4!=0)//长度*3不能被4整除
	{
		//读取bmp图片的RGB数据
		for(int i=0;i<height;i++)
		{
			
			read(bmpfd,&bmpbuf[i*length*3],length*3);
	
			
			lseek(bmpfd,4-(length*3)%4,SEEK_CUR);
			
		}
		
		
	}
	else
	{
		//读取bmp图片的RGB数据
		read(bmpfd,bmpbuf,height*length*3);
	}
	
	int lcd_buf[800*480];//RGB变ABGR存放的地方
	int i=0;
	int x,y;
	
	//图片居中显示200*100
	for(y=(480-height)/2;y<(240+height/2);y++)
		for(x=((800-length)/2);x<((400+length/2));x++)
		{
			//RGB变ABGR
			lcd_buf[800*y+x]=(0x00<<24)+(bmpbuf[3*i+2]<<16)+(bmpbuf[3*i+1]<<8)+(bmpbuf[3*i+0]);
			i++;
		}
		
	int lcd_buf1[800*480];//上下颠倒
	bzero(lcd_buf1,800*480*4);
	/*
	for(x=0;x<800;x++)
		for(y=0;y<480;y++)
		{
			lcd_buf1[800*(480-y-1)+x]=lcd_buf[800*y+x];
		}
		*/
	
	//居中显示
	for(x=((800-length)/2);x<((400+length/2));x++)
		for(y=(480-height)/2;y<(240+height/2);y++)
		{
			lcd_buf1[800*(480-y-1)+x]=lcd_buf[800*y+x];
		}
	
	//write(lcdfd,lcd_buf1,800*480*4);
	
	//映射内存
	int *mmap_bmp = mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcdfd, 0);
	for(int i=0;i<480;i++)
		for(int j=0;j<800;j++)
			*(mmap_bmp+i*800+j)=lcd_buf1[800*i+j];
	munmap(mmap_bmp, 800*480*4);
	//lcd_buf[i] =(0x00<<24) + (bmpbuf[3*i+2]<<16) + (bmpbuf[3*i+1]<<8) + (bmpbuf[3*i+0]);
	//关闭
	
	close(bmpfd);
	close(lcdfd);
	return 0;
}
int main()
{
	struct siglelist *mylist=list_init();
	struct siglelist *data_n=list_init();
	stpcpy(mylist->names,BMP5);
	stpcpy(data_n->names,BMP1);
	insert_tail(data_n,mylist);
	stpcpy(data_n->names,BMP2);
	insert_tail(data_n,mylist);
	stpcpy(data_n->names,BMP3);
	insert_tail(data_n,mylist);
	stpcpy(data_n->names,BMP4);
	insert_tail(data_n,mylist);
	int xy[2]={0,0};
	char bmp_box[50];
	showbmp_mmap(BMP5);
	struct siglelist *p=mylist;
	/* show_xy(xy);
	printf("< %d,%d >\n",xy[0],xy[1]); */
	//show_xy(xy);
	
	while(1)
	{
		show_xy(xy);
		printf("< %d,%d >\n",xy[0],xy[1]);
		if(xy[0]>0&&xy[0]<150)
		{
			
			p=p->next;
			strcpy(bmp_box,p->names);
			showbmp_mmap(bmp_box);
			
		
		}
		if(xy[0]>600&&xy[0]<800)
		{
			
			p=p->pre;
			strcpy(bmp_box,p->names);
			showbmp_mmap(bmp_box);
			
		}
	}
		

	
	return 0;
}

doublecycle.h

#ifndef _MYHEAD_H
#define _MYHEAD_H

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <linux/input.h>
#include <errno.h>
#include <dirent.h>


//定义结构体表示单链表
struct siglelist
{
	int data;//学号
	char names[10];//姓名
	int weight;//体重
	int high;//身高
	int grade[3];//成绩,语数英
	struct siglelist *pre;
	struct siglelist *next;
};
struct siglelist *list_init();//封装链表的初始化
int insert_tail(struct siglelist *data_n,struct siglelist *head);
int insert_mid(int newdata,int olddata,struct siglelist *data_n,struct siglelist *head);
int show_list(struct siglelist *head);
int delete_list(int olddata, struct siglelist *head);
#endif

myhead.h

#ifndef _MYHEAD_H
#define _MYHEAD_H

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/mman.h>//内存映射mmap()
#include <linux/input.h>
#include <errno.h>
#include <dirent.h>
#include <strings.h>
//输入子系统
#include<linux/input.h>

#endif

doublecycle.c

#include"doublecycle.h"

//封装链表的初始化
struct siglelist *list_init()
{
	struct siglelist *myhead=malloc(sizeof(struct siglelist));
	myhead->next=myhead;
	myhead->pre=myhead;
	return myhead;
}
//尾部插入
int insert_tail(struct siglelist *data_n,struct siglelist *head)
{
	//找到链表的尾部
	struct siglelist *p=head;
	while(p->next!=head)
	{
		p=p->next;
	}
	//准备好新的节点
	struct siglelist *newnode=malloc(sizeof(struct siglelist));
	//写入数据
	newnode->data=data_n->data;
	strcpy(newnode->names,data_n->names);
	newnode->weight=data_n->weight;
	newnode->high=data_n->high;
	for(int i=0;i<3;i++)
	{
		newnode->grade[i]=data_n->grade[i];
	}
	newnode->next=NULL;
	newnode->pre=NULL;
	//p和newnode建立连接
	p->next=newnode;
	newnode->pre=p;
	//newnode和head链接
	newnode->next=head;
	head->pre=newnode;
}

//中间插入,把newdata学号插入到olddata学号的后面
int insert_mid(int newdata,int olddata,struct siglelist *data_n,struct siglelist *head)
{
	int flag=0;
	//找到olddata所在的节点
	struct siglelist *p=head;
	while(p->next!=head)
	{
		p=p->next;
		if(p->data==olddata)
		{
			flag++;
			break;
		}
			
	}
	if(flag==0)
	{
		printf("选择要插入的学号不存在\n");
		return -1;
	}
	//准备新节点
	struct siglelist *newnode=malloc(sizeof(struct siglelist));
	newnode->data=data_n->data;
	strcpy(newnode->names,data_n->names);
	newnode->weight=data_n->weight;
	newnode->high=data_n->high;
	for(int i=0;i<3;i++)
	{
		newnode->grade[i]=data_n->grade[i];
	}
	newnode->next=NULL;
	newnode->pre=NULL;
	//newnode和p->next链接
	p->next->pre=newnode;
	newnode->next=p->next;
	//newnode和p链接
	p->next=newnode;
	newnode->pre=p;
}

//打印链表
int show_list(struct siglelist *head)
{
	struct siglelist *p=head;
	printf("学号\t 姓名\t 体重\t 身高\t 语文\t 数学\t 英语\n");
	while(p->next!=head)
	{
		p=p->next;
		
		printf("%d\t %s\t %d\t %d\t %d\t %d\t %d\n",p->data,p->names,p->weight,p->high,p->grade[0],p->grade[1],p->grade[2]);
		
	}
}
//删除链表(可以删除重复的数据)
int delete_list(int olddata, struct siglelist *head)
{
	int n = -1;	//标志位

	//遍历找到要删除节点所在
	struct siglelist *p = head->next;
	while(p!=head)
	{
		if(p->data==olddata)
			{
				
				p->next->pre=p->pre;
				p->pre->next=p->next;
				p=p->next;
				n++;
				
			}
		else
		{
			p=p->next;
		}
		
	}
	

	if(n<0)
	{
		printf("链表中不存在数据%d\n", olddata);
	}
	else
	{
		printf("删除了%d个数据\n", n+1);
	}
	
	return 0;
}

 

    原文作者:qq_40592257
    原文地址: https://blog.csdn.net/qq_40592257/article/details/107578011
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞