这里写目录标题
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 <sys/mman.h>
头文件
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字节 保存BM
DIB:
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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/input.h>
#include <unistd.h>
#include <string.h>
#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的接口函数!
NAME
pthread_create - create a new thread
创建一个新的线程
SYNOPSIS
#include <pthread.h>
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 command
SYNOPSIS
#include <stdlib.h>
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