这里写目录标题
- LCD屏幕
- 简介
- 操作:打开屏幕
- 映射
- 如何让plcd指向屏幕首地址!
- BMP图片的解析
- 把一张BMP格式的图片显示在我们的开发板上
- 触摸板的相关操作
- 练习:获取屏幕坐标
- 线程进程
- 练习:创建广告播放的一个线程
- 音频播放
- 播放的方式!!
- 相关
LCD屏幕
简介
- 屏幕的分辨率是:800480,即屏幕是由800480个像素点组成的。
- 像素点:可以组成颜色的点!
- 每个像素点占32bit,由A R G B组成,各占8各bit
像素点 | A(透明度) | R(red) | G(green) | B(blue) |
---|---|---|---|---|
color | 00 | ff | 00 | 00 |
unsigned int color = 0x00ff0000;
- 这种模式使得我们可以把颜色数量化 。
操作:打开屏幕
- 屏幕的路径:“/dev/fb0”
int lcd_fd;lcd_fd = open("/dev/fb0",O_RDWR);if(lcd_fd == -1){ printf("open error\n"); return -1;}
映射
- 帧缓冲设备是linux为显存设备提供一个接口,把显存抽象后的一种设备!他允许上次应用程序在图像模式下直接对显示缓冲区进行读写!
int *plcd; //假设这个指针是指向lcd屏幕的首地址!*plcd = 0x00ff0000; //给第一个像素点写入红色*(plcd+1) = 0x00ff0000 ; //这是给lcd屏幕第二个像素点写入红色for(int y = 0 ; y < 480 ; y++){ for(int x = 0 ; x < 800 ; x++) { *(plcd + y * 800 + x) = 0x00ff0000; // color }}
//写一个画点的封装函数!void Lcd_Draw_point(int x , int y , int color){ if(x<800&&x>=0&&y<480&&y>=0) { *(plcd + y * 800 + x) = color; }}
如何让plcd指向屏幕首地址!
- mmap
NAME mmap, munmap - map or unmap files or devices into memory 映射 ,把屏幕的首地址,镜像给我们!!SYNOPSIS #include 头文件 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); void *addr : NULL 它是由系统分配 size_t length :映射的文件的大小 480*800*4 int prot : 权限 PROT_READ : 可读 PROT_WRITE : 可写 PROT_EXEC : 可执行 PROT_READ | PROT_WRITE | PROT_EXEC int flags : 属性 MAP_SHARED int fd : 文件描述符 off_t offset : 偏移量 , 0
返回值:
- 失败返回NULL
- 成功的话就返回该屏幕映射过来的首地址!
例子:
int *plcd = mmap(NULL , 480*800*4 , PROT_READ | PROT_WRITE | PROT_EXEC , MAP_SHARED , lcd_fd , 0);int munmap(void *addr, size_t length); void *addr : plcd 是我们映射lcd屏幕过来的首地址 size_t length :480*800*4
BMP图片的解析
把一张BMP格式的图片显示在我们的开发板上
- 打开图片
int bmp_fd ;bmp_fd = open("./1.bmp",O_RDWR);if(bmp_fd == -1){ printf("open bmp error\n"); return -1;}
- 图片的类型:bmp,jpg,png
- bmp 图片是一种最原始的,也是windows下面最常见一种图片,也是最基础的图片。它是没有经过任何算法处理的,或者说是没有经过任何算法压缩的,所以它bmp图片所占的内存是最大的!!!
- 没有 压缩 的图片 :意味着它保存每个像素点的值!
BITMAP头: 0x00 2字节 保存BMDIB: 0x12 4字节 表示是位图的宽度 位图的宽度:每一行有多少个像素点!
int width; lseek(bmp_fd,0x12,SEEK_SET); int r = read(bmp_fd,&width,4); if(r == -1) { printf("read error\n"); return -1; } printf("width == %d\n", width);
width > 0 :它的像素点的保存顺序就是从左边到右边 < 0 :则反之 0x16 4字节 表示是位图的高度 位图的高度:每一列有多少个像素点! high > 0 < 0 0x1C 两个字节 表示是位图的色深 short depth; depth == 24 //3个字节 保存的东西为 R G B A:默认为0 depth == 32 //四个字节 保存的东西为 A R G B 像素数组的大小:abs(width) * abs(high) * (depth/8) 0x36 像素数组:保存每个像素点
触摸板的相关操作
在开发板上任意坐标显示一张任意分辨率的bmp图片
触摸屏的路径名: “/dev/input/event0”
int touch_fd = open("/dev/input/event0",O_RDONLY); if(touch_fd == -1) { printf("open touch error\n"); return -1; }
NOTE:
触摸板文件跟其他文件不同的是,它的内容吧,不是用数组保存,而是用一个结构体!在linux系统下面,所以的输入事件(鼠标,键盘,触摸板。。。)都用一个结构体来表示的
struct input_event { struct timeval time; //输入事件的时间 _u16 type ; //事件的类型,如下: type == EV_KEY 表示这是一个按键事件 type == EV_REL 表示这是一个鼠标事件 type == EV_ABS 表示的是一个触摸板事件 type == EV_SYN 事件的分割标志 _u16 code; //要根据type的不同,它表示的含义也就不一样了 type == EV_KEY 表示这是一个按键事件 code 表示的是按键事件的键值 code == BTN_TOUCH type == EV_REL 表示这是一个鼠标事件 //这个暂时我们不讲!!因为我们用不到! type == EV_ABS 表示的是一个触摸板事件 code 有几个值 code == ABS_X 表示这是X轴的坐标 code == ABS_Y 表示这是Y轴的坐标 code == ABS_PRESSURE 表示这是给触摸板的压力 _u16 value; //要根据type和code的不同,它表示的含义就不一样 type == EV_KEY code == BTN_TOUCH value == 1 或者value == 0 表示按键按下/按键弹起 type == EV_ABS code == ABS_X value == x轴坐标 code == ABS_Y value == y轴坐标 code == ABS_PRESSURE value == 压力值 };
- NOTE:
应用程序,通过不断从输入设备文件,读取该结构体!
int xy_read(){ int x_read = -1; int y_read = -1; struct input_event ev; while(1) { read(); if(ev.type && ev.code) { } }}
练习:获取屏幕坐标
//获取坐标#include #include #include #include #include #include #include #include #include #define TOUCH_PATH "/dev/input/event0"int touch_fd;int xy_read(){ //1.打开触摸屏 touch_fd = open(TOUCH_PATH,O_RDONLY);//只读打开 if(touch_fd<0) { perror("open fail"); return 0; } int x_read = -1; int y_read = -1; //定义一个结构体叫ev struct input_event ev; //一直读取触摸板信息 while(1) { //读取屏幕 read(touch_fd, &ev, sizeof(struct input_event));//第三个参数ev if(ev.type == EV_KEY && ev.code == BTN_TOUCH) { if(ev.value == 1) { printf("down\n"); } else { printf("up\n"); } } if(ev.type == EV_ABS) //触摸事件 { if(ev.code == ABS_X) { x_read = ev.value; //x轴 } if(ev.code == ABS_Y)//y轴 { y_read = ev.value; } printf("(%d,%d)\n", x_read, y_read); } }}int main(){ xy_read(); close(touch_fd);}
线程进程
我们的一个main就是一个进程
并发:同时进行两个或者两个以上的执行任务
线程就是我们主函数的一个分支,是比进程更加小的活动的单位,执行分支。
线程和进程都是我们平时所说的并发的一种方式!
指令它必须在函数的内部,线程的指令也会封装在函数的内部, 那么我们封装线程的指令函数叫做线程函数!!!线程函数的原型:
typedef void * (*start_routine_t)(void *) 这样子的一个指针会指向我们的线程函数!!! 那么线程函数是该长什么样子呢?? 参数:void * 返回值:void *
- step1 :定义一个线程函数!!
void * display_adv(void *) { while(1) { //在你们的2048棋盘的旁边显示一个电子相册,用来播放小广告 //显示第一个广告 sleep(5); //显示第二个广告 sleep(5); } }
- step2: 开辟这个分支
- 在linux中pthread的接口函数!
它可以创建一个线程:pthread_create
每一个线程都有自己的ID,用来唯一标识一个线程的,也就是每个线程都有一个ID身份证!!
id的类型:pthread_t ;
声明一个id: pthread_t id;
- 在linux中pthread的接口函数!
NAMEpthread_create - create a new thread 创建一个新的线程SYNOPSIS #include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);pthread_t *thread : 指向你保存id号的那个空间!!!const pthread_attr_t *attr : 表示线程的属性,一般为NULL,采用的是默认的属性void *(*start_routine) (void *) : *start_routine : 指向它对应的线程函数我们上面开创线程的目的就是为了人这个线程去执行线程函数!!void * arg :其实它就是将要作为线程函数的实参传入!!!
Compile and link with -pthread.编译时加上-pthread
gcc xxx.c -o xxx -pthread
例子:
int main(){ pthread_t id; pthread_create(&id,NULL,&display_adv,NULL); while(1) { show(); }}
练习:创建广告播放的一个线程
pthrad_t ad_th_num; // 定义一个线程号 pthread_create(&ad_th_num,NULL,&ad,NULL); //线程任务函数 --- 广告 void * ad(void * arg) { while(1) { bmp_show(""); sleep(3); bmp_show(""); sleep(3); } }
音频播放
- 命令:madplay
- 查看开发板是否已经安装了madplay
which madplay - 拷贝到/bin/
- 给权限
- 查看开发板是否已经安装了madplay
播放的方式!!
madplay的用法:madplay 路径
madplay ./mp3/1.mp3
* system NAME system - execute a shell commandSYNOPSIS #include int system(const char *command); const char *command : 是一个字符串,然后整个字符串就是我们要执行的那个命令!
char cmd[100]; sprintf(cmd,"madplay ./mp3/1.mp3"); sprintf(cmd,"madplay %s" , p);
相关
上一篇:
粤嵌GEC6818-学习笔记1-基础篇https://blog.csdn.net/weixin_45735391/article/details/125350683
下一篇:
粤嵌GEC6818-学习笔记3-相关项目https://blog.csdn.net/weixin_45735391/article/details/125820496