2019年7月25日星期四
一. linux系统IO应用实例 -> LCD液晶屏幕
1. 在linux下,一切都是文件。 -> 连LCD液晶屏幕都是文件。
既然LCD液晶是文件,那么文件名是什么? --> 硬件设备文件去/dev下寻找。
/dev/ttySAC0 -> 拓展外接串口1
/dev/ttySAC1 -> 拓展外接串口2
/dev/ttySAC2
/dev/ttySAC3
/dev/fb0 -> LCD液晶屏幕
/dev/input/event0 -> 触摸屏
2. 液晶LCD参数:
1)屏幕尺寸:7寸
2)分辨率:蓝色:800*480 黑色:1024*600
什么是分辨率? --> 指的就是屏幕中像素点的总个数。
3)每一个像素点都是由RGB组成,那么每一个像素点中有多少个字节?
[root@GEC6818 /]# cat /sys/class/graphics/fb0/bits_per_pixel -> 32 -> 每一个像素点都是32位 -> 4字节
像素点排布: ARGB
3. 举例子。
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//1. 打开LCD液晶屏幕
int lcd = open("/dev/fb0",O_WRONLY);
//2. 写入颜色到LCD中
int color = 0x00FF00FF;
write(lcd,&color,4);
//3. 关闭文件
close(lcd);
return 0;
}
===========================================================
练习1:显示全屏紫色。
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//1. 打开LCD液晶屏幕
int lcd = open("/dev/fb0",O_WRONLY);
//2. 写入颜色到LCD中
int color = 0x00FF00FF;
int i;
for(i=0;i<800*480;i++)
{
write(lcd,&color,4);
}
//3. 关闭文件
close(lcd);
return 0;
}
或者
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//1. 打开LCD液晶屏幕
int lcd = open("/dev/fb0",O_WRONLY);
//2. 写入颜色到LCD中
int color[800*480];
int i;
for(i=0;i<800*480;i++)
{
color[i] = 0x00FF00FF;
}
write(lcd,color,800*480*4);
//3. 关闭文件
close(lcd);
return 0;
}
===========================================================
练习2:横向彩虹。红黄蓝绿...
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//1. 打开LCD液晶屏幕
int lcd = open("/dev/fb0",O_WRONLY);
//2. 写入颜色到LCD中
int color[800*480];
int i;
for(i=0;i<800*160;i++)
{
color[i] = 0x00FF0000;
} //i=800*160
for( ;i<800*320;i++)
{
color[i] = 0x0000FF00;
}
for( ;i<800*480;i++)
{
color[i] = 0x000000FF;
}
write(lcd,color,800*480*4);
//3. 关闭文件
close(lcd);
return 0;
}
二.文件IO的处理方式
write() -> 访问文件,得到文件描述符fd -> 往文件描述符中写入数据 write(fd);
内存映射 -> 访问文件,得到文件描述符fd -> 根据文件描述符去内存空间中申请一片内存空间,得到地址p -> 用户只需要操作地址p上的数据 -> 文件就会有相对应的变化
一般,对于LCD液晶屏幕,大多数都是使用内存映射方式来进行数据IO。
内存映射方式:
1. 根据文件申请文件描述符fd。
int fd = open("xxxx");
2. 如何根据文件描述符fd申请内存空间? -> mmap() -> man 2 mmap
功能: map files or devices into memory
使用格式:
#include
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr:
NULL -> 系统自动分配空间 99.99%
不为NULL -> 手动分配空间 0.01%
length: 内存映射的空间的长度 -> 例如:lcd就填800*480*4
prot: 保护权限
PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
如果有多个权限,则使用"|"连接在一起,例如: PROT_READ|PROT_WRITE
flags:
MAP_SHARED -> 公开
MAP_PRIVATE -> 私有
fd: 进行映射的文件描述符
offset: 文件的偏移量
返回值:
成功: 指向该内存空间的起始地址
失败: (void *)-1
3. 拷贝数据到空间中 -> memcpy() -> man 3 memcpy
NAME
memcpy - copy memory area -> 拷贝数据到内存空间中
#include
void *memcpy(void *dest, const void *src, size_t n);
dest:目标地址 -> 地址上空间必须足够大。
src:需要拷贝的东西
n:需要拷贝的字节数
返回值:
成功: 指向dest的地址
失败: NULL
4. 撤销映射 -> munmap() -> man 2 munmap
使用格式:
#include
int munmap(void *addr, size_t length);
addr:内存的起始地址
length:需要撤销的映射长度
返回值:
成功:0
失败:-1
练习3:使用内存映射的方式显示满屏紫色。
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char*argv[])
{
//1. 打开文件
int lcd;
lcd = open("/dev/fb0",O_RDWR);
if(lcd < 0)
printf("open lcd error!\n");
//2. 申请内存映射
char *p = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd,0);
if(p == (void *)-1)
printf("mmap error!\n");
//3. 往内存空间中填充颜色
int color = 0x00FF00FF;
int i;
for(i=0;i<800*480*4;i+=4)
{
memcpy(&p[i],&color,4); 等价于: &p[i] = &*(p+i) = p+i
}
//4. 撤销映射
munmap(p,800*480*4);
//5. 关闭文件
close(lcd);
return 0;
}
练习4:将char *修改为int *
char *p = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd,0);
if(p == (void *)-1)
printf("mmap error!\n");
修改为:
int *p = (int *)mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd,0);
if(p == (void *)-1)
printf("mmap error!\n");
for(i=0;i<800*480*4;i+=4)
{
memcpy(&p[i],&color,4); 等价于: &p[i] = &*(p+i) = p+i
}
修改为:
for(i=0;i<800*480;i++)
{
memcpy(p+i,&color,4);
}
练习5: 使用内存映射方式来显示横向彩虹 红黄蓝绿...
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char*argv[])
{
//1. 打开文件
int lcd;
lcd = open("/dev/fb0",O_RDWR);
if(lcd < 0)
printf("open lcd error!\n");
//2. 申请内存映射
int *p = (int *)mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd,0);
if(p == (void *)-1)
printf("mmap error!\n");
//3. 往内存空间中填充颜色
int red_color = 0x00FF0000;
int green_color = 0x0000FF00;
int blue_color = 0x000000FF;
int i;
for(i=0;i<800*160;i++)
{
memcpy(p+i,&red_color,4);
}
for(;i<800*320;i++)
{
memcpy(p+i,&green_color,4);
}
for(;i<800*480;i++)
{
memcpy(p+i,&blue_color,4);
}
//4. 撤销映射
munmap(p,800*480*4);
//5. 关闭文件
close(lcd);
return 0;
}
三. linux系统IO应用 ---> 触摸屏
1. linux下,一切都是文件。 -> 连触摸屏也是文件。
触摸屏设备文件名字: /dev/input/event0
2. 熟悉两个关于触摸屏专业术语。
1)事件
当一些外接设备(鼠标、键盘、WIFI、触摸屏)接入到嵌入式平台时,这些外接设备的状态发生改变,例如:鼠标的左键按了一下,键盘的R键松开,WIFI有人连接进来,触摸屏被滑动了。这个动作就称之为事件。也就是点击鼠标10次,就发生了20次事件(按下/松开都是属于一个事件)
2)输入子系统
专门用于处理事件的值,当一些事件发生了,输入子系统就会把该事件的值计算出来。
3. 触摸屏原理
见“触摸屏原理.jpg”
4. 分析读取触摸屏坐标/压力的过程。
1)访问设备,得到对应的文件描述符。
2)确定设备数据来源
外部 -> 设备 -> write() -> 例如: LCD液晶屏幕
设备 -> 外部 -> read() -> 例如: 触摸屏
3)确定读取出来的数据是什么类型?
究竟是char?int?结构体?数组?指针?
4)分析处理数据
5)关闭设备文件,回收资源。
5. 写例子尝试读取坐标
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//1. 打开文件
int fd;
fd = open("/dev/input/event0",O_RDONLY);
if(fd < 0)
printf("open error!\n");
//2. 读取文件数据
char buf[50] = {0};
while(1)
{
read(fd,buf,sizeof(buf));
printf("%s\n",buf);
}
close(fd);
return 0;
}
结果: 都是乱码!
四. 究竟输入子系统从驱动发来的数据上计算出来的结果是什么样子?
其实是一个结构体来的,这个结构体专门是用于描述刚才发生事件的情况。这个结构体是被定义在一个头文件中:/usr/include/linux/input.h
struct input_event {
struct timeval time; -> 事件发生的时间 -> 一般不用!
__u16 type; -> 事件的类型
__u16 code; -> 事件的编码 -> 对事件类型做进一步的描述!
__s32 value; -> 事件的值
};
输入子系统中事件类型有哪些? -> Event types
#define EV_KEY 0x01 -> 键盘、触摸屏压力设备
#define EV_ABS 0x03 -> 触摸屏坐标
输入子系统压力编码:
#define BTN_TOUCH 0x14a -> 触摸屏压力事件
#define ABS_X 0x00 -> X轴坐标
#define ABS_Y 0x01 -> Y轴坐标
输入子系统值:
压力值: 按下:1 松开:0
X坐标: 0~800 0~1024
Y坐标: 0~480 0~600
练习6: 当你的手点击屏幕松开时,就打印一句话"your hand leave lcd!"
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//1. 打开文件
int fd;
fd = open("/dev/input/event0",O_RDONLY);
if(fd < 0)
printf("open error!\n");
//2. 读取文件数据
struct input_event buf;
while(1)
{
read(fd,&buf,sizeof(buf)); //如果手已经触摸屏幕,那么buf就会有内容!
if(buf.type == EV_KEY && buf.code == BTN_TOUCH && buf.value == 0)
{
printf("your hand leave lcd!\n");
}
}
close(fd);
return 0;
}
练习7:当你的手在滑动时,把手的位置的坐标打印出来
(100,200)
(100,201)
(101,201)
...
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
struct input_event buf;
int x,y
fd = open("/dev/input/event0",O_RDONLY);
while(1)
{
read(fd,&buf,sizeof(buf));
if(buf.type == EV_ABS && buf.code == ABS_X)
{
x = buf.value;
}
if(buf.type == EV_ABS && buf.code == ABS_Y)
{
y = buf.value;
}
printf("(%d , %d)\n",x,y);
}
close(fd);
return 0;
}
练习8:点击屏幕左边松手,打印一次"left"!
点击屏幕右边松手,打印一次"right"!