一、开发环境
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;
}