粤嵌GEC6818-学习笔记2-屏幕相关及音频播放

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;
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 ./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

猜你喜欢

转载自blog.csdn.net/weixin_45735391/article/details/125452935