张国伟 发表于 2024-6-21 13:18:18

粤嵌Linux GEC6818开发板实现电子相册

前言

最近学校要求使用粤嵌的开发板实现电子相册,具体的功能要有点击特定的区域实现上一张、下一张、自动播放图片、黑屏退出应用步伐、左右滑动切换图片相关功能。其中涉及到的知识点也比较多(文件IO、内存映射、触摸屏、bmp图片格式、进程、线程创建和同步、字符串操纵等)。为理清思路和复习去年学的Linux C应用编程知识,特写下此文举行回顾和总结。
先看看效果
   粤嵌Linux GEC6818开发板实现电子相册

整个工程文件和使用到的图片在下方链接
门牙会稍息 / 粤嵌GEC 6818开发板实现简易电子相册和音乐播放器 · GitCode
一:内存映射

存储映射 I/O(memory-mapped I/O)是一种基于内存区域的高级 I/O 操纵,它能将一个文件映射到进程所在空间中的一块内存区域中,当从这段内存中读数据时,就相当于读文件中的数据(对文件举行 read 操纵),将数据写入这段内存时,则相当于将数据直接写入文件中(对文件举行 write 操纵)。用到的两个函数是mmap和munmap,函数原型如下:
https://img-blog.csdnimg.cn/4e73107fadf4443b88985550d5ed8055.png
简言之addr设置为NULL的话内核会自动找一内存空间,大小就是length,粤嵌的屏是800*480的,以是length就是800*480*4,4代表一个像素点由四字节构成(ARGB),port参数设为PROT_READ、PROT_WRITE就是可读可写,flags形貌的是映射区的属性。
https://img-blog.csdnimg.cn/7a294709400647d68f718d74320d02f6.png
 二:BMP格式图片

https://img-blog.csdnimg.cn/f3ce5ae6f25a4469bb6a0208621ba6f9.png
 用hexdump检察一下bmp图片的数据(高度和宽度)
https://img-blog.csdnimg.cn/f3d0b7f8bfec49bb8056642f8d3cefb6.png
LCD屏显示BMP格式图片的函数
/**
*lcd屏显示bmp格式图片函数
*@param   pathname    图片名字
*@param   x0          图片在LCD上显示的x起点坐标
*@param   y0          图片在LCD上显示的y起点坐标
*@return    函数返回值
*/
int lcd_show_bmp(char *pathname, int x0, int y0)
{
        //1、打开设备文件
        int lcd = open("/dev/fb0", O_RDWR);
        if(-1 == lcd)
        {
                printf("lcd open error!\n");
                return -2;
        }
       
        //2、内存映射
        char *ptr =(char *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE,
                      MAP_SHARED, lcd, 0);
        if(NULL == ptr)
        {
                printf("mmap error!\n");
                return -3;
        }
        //清除背景色
        //bzero(ptr, 800*480*4);
       
        //打开图片
        int bmp = open(pathname, O_RDWR);
        if(-1 == bmp)
        {
                printf("bmp open error!\n");
                return -2;
        }
       
        //读取图片相关信息
        int bmp_size, bmp_width, bmp_height;
        //大小
        lseek(bmp, 2, SEEK_SET);
        read(bmp, &bmp_size, sizeof(bmp_size));
       
        //宽度和高度
        lseek(bmp, 18, SEEK_SET);
        read(bmp, &bmp_width, sizeof(bmp_width));
        read(bmp, &bmp_height, sizeof(bmp_height));
       
        //读取图片的颜色数据
        lseek(bmp, 54, SEEK_SET);
        char *bmp_buf = malloc(bmp_size);        //申请空间
        read(bmp, bmp_buf, bmp_width*bmp_height*3);

       
        //对颜色数据进行处理(上下颠倒以及数据混乱)
        int bmp_sum = 0;
        int lcd_sum = 0;
        for(int y=0; y+y0<480 && y<bmp_height; y++)
        {
                for(int x=0; x+x0<800 && x<bmp_width; x++)
                {
                        bmp_sum = 3*(x+((bmp_height-1-y)*bmp_width));
                        lcd_sum = 4*(800*(y+y0)+x+x0);
                       
                        //等号的坐标属于lcd屏幕, 等号的右边是bmp图像数据
                        ptr = bmp_buf; //蓝色数据
                        ptr = bmp_buf; //绿色数据
                        ptr = bmp_buf; //红色数据
                        ptr = bmp_buf; //透明度数据
                }
                //usleep(1);
        }
    //释放相关资源
        munmap(ptr, 800*480*4);
        free(bmp_buf);
        //3、关闭文件
        close(lcd);
        close(bmp);
}  三:input_event接收触摸屏上报值

https://img-blog.csdnimg.cn/650630c159594f2aa16dbd0b56497bb4.png
用hexdump下令检察触摸屏上报值
https://img-blog.csdnimg.cn/517de483fff143ee8f8aa92baa6e04c5.png
https://img-blog.csdnimg.cn/e023000a73ca4f1394832cf05c8c2088.png
 此实验用到input子体系中的type、code(下面的图片泉源Linux内核中的input.h文件)
https://img-blog.csdnimg.cn/c5cb1ca69dbc4ddf836b2f5bd6551721.png
https://img-blog.csdnimg.cn/8784ff045a304a2a8f46c43d5af30950.png
https://img-blog.csdnimg.cn/9f78d50edcb24345a064c0917dbe7ac3.png
获取触摸屏坐标返回值函数
/**
*获取触摸屏坐标返回值函数
*param    NULL
*return   NULL
*/
void get_touch()
{
    //打开设备文件
    int touch_fd = open("/dev/input/event0", O_RDONLY);
    if(-1 == touch_fd)
    {
      printf("event0 open error!\n");
    }

    while(1)
    {
      read(touch_fd, &ts, sizeof(ts));
          //获取X、Y坐标
      if(EV_ABS == ts.type) //判断是否为触摸屏事件
      {
            if(ABS_X == ts.code)//判断是否为x轴数据
            {
                ts_x = ts.value;
                flag_x = 1;       
            }
            else if(ABS_Y == ts.code) //判断是否为y轴数据
            {
                ts_y = ts.value;
                flag_y = 1;
            }
      }

        if(EV_KEY == ts.type)
        {
                //刚触碰的坐标/长按时
                if(ts.code == BTN_TOUCH && ts.value == 1)
                {
                        old_x = ts_x;
                        first_press_flag = 1;
                }
        }
       
        if(flag_x == 1 && flag_y == 1 && first_press_flag == 1)
      {
                flag_x = 0;
                flag_y = 0;
                pthread_mutex_lock(&mutex);
                flag_x_y = 1;
                //黑色底板才需要执行如下操作
                ts_x = ts_x*800/1024;
                ts_y = ts_y*480/600;
                pthread_mutex_unlock(&mutex);
                pthread_cond_signal(&cond);
                //break;                  
      }

    if(EV_KEY == ts.type)
        {
                //松开
                if(ts.code == BTN_TOUCH && ts.value == 0)
                {
                        //从左到右的滑动
                        if(((ts_x > old_x) && (ts_x - old_x > 150) && (ts_x < 600)))
                        {
                                right_left_slide_flag = 1;
                                slider_right = 1;                       
                                old_x = 300;
                        }       
                        //从右到左的滑动
                        else if(((old_x > ts_x) && (old_x - ts_x > 150) && (old_x < 600)))
                        {
                               
                                right_left_slide_flag = 1;
                                slider_left = 1;
                                old_x = 300;
                        }
                }
        }

    }
    //4、关闭文件
    close(touch_fd);
} 四:创建线程,处理惩罚触摸屏坐标数据

阻塞式 I/O 的优点在于可以或许提升 CPU 的处理惩罚服从,当自身条件不满足时,进入阻塞状态,交出 CPU资源,将 CPU 资源让给别人使用;而非阻塞式则是抓紧使用 CPU 资源,譬如不断地去轮训,这样就会导致该步伐占用了非常高的 CPU 使用率!我这里是想得到触摸点坐标之后再做相关的操纵,当没有按下触摸屏的时间,相关线程就会阻塞挂起,节约资源,线程同步中使用互斥锁和条件变量就可以实现。
左右滑动线程处理惩罚函数:
void *right_left_slide_func(void *arg)
{
        printf("enter right_left_slide_func\r\n");
        while(1){
                if(right_left_slide_flag == 1){
                        if(slider_left == 1){
                                touch_flag--;
                                if(touch_flag <= 0)
                                        touch_flag = BMP_MAX_NUMBER;
                                lcd_show_bmp(bmp_path, 0, 0);
                                slider_left = 0;
                                right_left_slide_flag == 0;
                        }
                        else if(slider_right == 1){
                                touch_flag++;
                                if(touch_flag > BMP_MAX_NUMBER)
                                        touch_flag = 1;
                                lcd_show_bmp(bmp_path, 0, 0);
                                slider_right = 0;
                                right_left_slide_flag == 0;
                        }
                        right_left_slide_flag == 0;
                }       
        }
} 右侧选项框线程处理惩罚函数:
void *area_switch(void *arg)
{
        printf("enter area_switch\r\n");
        while(1){
                pthread_mutex_lock(&mutex);

                while(flag_x_y == 0)
                        pthread_cond_wait(&cond, &mutex);
                while(flag_x_y == 1){
                        //上一张
                        if((ts_y > 0 && ts_y < 120) && (ts_x > 600))
                        {
                                touch_flag--;
                                if(touch_flag <= 0)
                                        touch_flag = BMP_MAX_NUMBER;
               
                                lcd_show_bmp(bmp_path, 0, 0);
                                ts_x = 0;
                                ts_y = 0;
                        }
                        //下一张
                        else if((ts_y > 120 && ts_y < 240) && (ts_x > 600))
                        {
                                touch_flag++;
                                if(touch_flag > BMP_MAX_NUMBER)
                                        touch_flag = 1;
               
                                lcd_show_bmp(bmp_path, 0, 0);
                                ts_x = 0;
                                ts_y = 0;
                        }
                        //幻灯片
                        else if((ts_y > 240 && ts_y < 360) && (ts_x > 600))
                        {
                                printf("click slider photo\r\n");
                                if(slider_flag == 0){
                                        slider_flag = 1;
                                }
                                else{
                                        slider_flag = 0;
                                }
                                printf("slider_flag = %d\r\n", slider_flag);
                                ts_x = 0;
                                ts_y = 0;
                        }
                        //息屏
                        else if((ts_y > 360 && ts_y < 480) && (ts_x > 600))
                        {
                                char command[] = "kill -9 ";
                                char str;
                                sprintf(str, "%d", pid);
                                strcat(command, str);
                                printf("command = %s\r\n", command);
                                //方式一:显示一张黑色的图片
                                lcd_show_bmp("black.bmp", 0, 0);
                                system(command);
                        }
                        else{
                                flag_x_y = 0;
                                break;
                        }
                        flag_x_y = 0;
                }

                pthread_mutex_unlock(&mutex);
        }
       
}  自动播放图片线程处理惩罚函数:
void *slider_func(void *arg)
{
        printf("enter slider_func : %d\r\n", pthread_self());
        while(1){
                if(slider_flag == 1){
                        printf("enter slider_func\r\n");
                        touch_flag++;
                        if(touch_flag > BMP_MAX_NUMBER)
                                touch_flag = 1;
                        lcd_show_bmp(bmp_path, 0, 0);
                        sleep(1);
                }
        }
} 五:Main函数

main函数就是一些线程、互斥锁、条件变量的创建和接纳
int main()
{
        lcd_show_bmp("choice.bmp", 600, 0);
        lcd_show_bmp(bmp_path, 0, 0);
        pthread_t tid, tid_area_switch, tid_right_left_slide;
        int ret = 0;
       
        pthread_create(&tid, NULL, slider_func, NULL);
        pthread_create(&tid_area_switch, NULL, area_switch, NULL);
        pthread_create(&tid_right_left_slide, NULL, right_left_slide_func, NULL);

        pthread_mutex_init(&mutex, NULL);
        pthread_cond_init(&cond, NULL);
       
        printf("main thread = %ld\r\n", pthread_self());
        pid = getpid();
        printf("pid = %d\r\n", pid);

        while(1){
                get_touch();
        }
       
       
        pthread_join(tid, NULL);
        pthread_join(tid_area_switch, NULL);
        pthread_join(tid_right_left_slide, NULL);
        pthread_cond_destroy(&cond);
        exit(0);

} 六:完整代码

#include <stdio.h>#include <fcntl.h>#include <sys/stat.h>#include <sys/types.h>#include <unistd.h>#include <string.h>#include <sys/mman.h>#include <stdlib.h>#include <linux/input.h>#include <pthread.h>#include <unistd.h>#define BMP_MAX_NUMBER 4char bmp_path = {"1.1.bmp", "1.2.bmp", "1.3.bmp", "1.4.bmp"};int slider_flag = 0;pid_t pid;pthread_mutex_t mutex;pthread_cond_t cond;//自界说函数:LCD屏幕显示bmp图片//pathname:必要打开的图片路径//x0:存放图片显示的x轴出发点//y0:存放图片显示的y轴出发点int lcd_show_bmp(char *pathname, int x0, int y0){        //1、打开装备文件        int lcd = open("/dev/fb0", O_RDWR);        if(-1 == lcd)        {                printf("lcd open error!\n");                return -2;        }                //2、内存映射        char *ptr =(char *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE,                       MAP_SHARED, lcd, 0);        if(NULL == ptr)        {                printf("mmap error!\n");                return -3;        }        //清除背景致        //bzero(ptr, 800*480*4);                //打开图片        int bmp = open(pathname, O_RDWR);        if(-1 == bmp)        {                printf("bmp open error!\n");                return -2;        }                //读取图片相关信息        int bmp_size, bmp_width, bmp_height;        //大小        lseek(bmp, 2, SEEK_SET);        read(bmp, &bmp_size, sizeof(bmp_size));                //宽度和高度        lseek(bmp, 18, SEEK_SET);        read(bmp, &bmp_width, sizeof(bmp_width));        read(bmp, &bmp_height, sizeof(bmp_height));                //读取图片的颜色数据        lseek(bmp, 54, SEEK_SET);        char *bmp_buf = malloc(bmp_size);        //申请空间        read(bmp, bmp_buf, bmp_width*bmp_height*3);                //对颜色数据举行处理惩罚(上下颠倒以及数据混乱)        int bmp_sum = 0;        int lcd_sum = 0;        for(int y=0; y+y0<480 && y<bmp_height; y++)        {                for(int x=0; x+x0<800 && x<bmp_width; x++)                {                        bmp_sum = 3*(x+((bmp_height-1-y)*bmp_width));                        lcd_sum = 4*(800*(y+y0)+x+x0);                                                //等号的坐标属于lcd屏幕, 等号的右边是bmp图像数据                        ptr = bmp_buf; //蓝色数据                        ptr = bmp_buf; //绿色数据                        ptr = bmp_buf; //红色数据                        ptr = bmp_buf; //透明度数据                }                //usleep(1);        }        munmap(ptr, 800*480*4);        free(bmp_buf);        //3、关闭文件        close(lcd);        close(bmp);}int touch_flag = 1;int ts_x, ts_y;int old_x = 300;int slider_left = 0;int slider_right = 0;int flag_x_y = 0;int right_left_slide_flag = 0;struct input_event ts;int flag_x = 0, flag_y = 0, first_press_flag = 0;//获取触摸屏坐标//自界说函数:获取触摸屏的坐标void get_touch(){    //1、打开装备文件    int touch_fd = open("/dev/input/event0", O_RDONLY);    if(-1 == touch_fd)    {      printf("event0 open error!\n");    }    while(1)    {      read(touch_fd, &ts, sizeof(ts));        //3、分析数据      if(EV_ABS == ts.type) //判断是否为触摸屏变乱      {            if(ABS_X == ts.code)//判断是否为x轴数据            {                ts_x = ts.value;                flag_x = 1;                    }            else if(ABS_Y == ts.code) //判断是否为y轴数据            {                ts_y = ts.value;                flag_y = 1;            }      }        if(EV_KEY == ts.type)        {                //刚触碰的坐标/长按时                if(ts.code == BTN_TOUCH && ts.value == 1)                {                        old_x = ts_x;                        first_press_flag = 1;                }        }                if(flag_x == 1 && flag_y == 1 && first_press_flag == 1)         {                flag_x = 0;                flag_y = 0;                pthread_mutex_lock(&mutex);                flag_x_y = 1;                //黑色底板才必要执行如下操纵                ts_x = ts_x*800/1024;                ts_y = ts_y*480/600;                 pthread_mutex_unlock(&mutex);                pthread_cond_signal(&cond);                //break;                            }    if(EV_KEY == ts.type)        {                //松开                if(ts.code == BTN_TOUCH && ts.value == 0)                {                        //从左到右的滑动                        if(((ts_x > old_x) && (ts_x - old_x > 150) && (ts_x < 600)))                        {                                right_left_slide_flag = 1;                                slider_right = 1;                                                        old_x = 300;                        }                                //从右到左的滑动                        else if(((old_x > ts_x) && (old_x - ts_x > 150) && (old_x < 600)))                        {                                                                right_left_slide_flag = 1;                                slider_left = 1;                                old_x = 300;                        }                }        }    }    //4、关闭文件    close(touch_fd);}void *right_left_slide_func(void *arg)
{
        printf("enter right_left_slide_func\r\n");
        while(1){
                if(right_left_slide_flag == 1){
                        if(slider_left == 1){
                                touch_flag--;
                                if(touch_flag <= 0)
                                        touch_flag = BMP_MAX_NUMBER;
                                lcd_show_bmp(bmp_path, 0, 0);
                                slider_left = 0;
                                right_left_slide_flag == 0;
                        }
                        else if(slider_right == 1){
                                touch_flag++;
                                if(touch_flag > BMP_MAX_NUMBER)
                                        touch_flag = 1;
                                lcd_show_bmp(bmp_path, 0, 0);
                                slider_right = 0;
                                right_left_slide_flag == 0;
                        }
                        right_left_slide_flag == 0;
                }       
        }
}void *area_switch(void *arg)
{
        printf("enter area_switch\r\n");
        while(1){
                pthread_mutex_lock(&mutex);

                while(flag_x_y == 0)
                        pthread_cond_wait(&cond, &mutex);
                while(flag_x_y == 1){
                        //上一张
                        if((ts_y > 0 && ts_y < 120) && (ts_x > 600))
                        {
                                touch_flag--;
                                if(touch_flag <= 0)
                                        touch_flag = BMP_MAX_NUMBER;
               
                                lcd_show_bmp(bmp_path, 0, 0);
                                ts_x = 0;
                                ts_y = 0;
                        }
                        //下一张
                        else if((ts_y > 120 && ts_y < 240) && (ts_x > 600))
                        {
                                touch_flag++;
                                if(touch_flag > BMP_MAX_NUMBER)
                                        touch_flag = 1;
               
                                lcd_show_bmp(bmp_path, 0, 0);
                                ts_x = 0;
                                ts_y = 0;
                        }
                        //幻灯片
                        else if((ts_y > 240 && ts_y < 360) && (ts_x > 600))
                        {
                                printf("click slider photo\r\n");
                                if(slider_flag == 0){
                                        slider_flag = 1;
                                }
                                else{
                                        slider_flag = 0;
                                }
                                printf("slider_flag = %d\r\n", slider_flag);
                                ts_x = 0;
                                ts_y = 0;
                        }
                        //息屏
                        else if((ts_y > 360 && ts_y < 480) && (ts_x > 600))
                        {
                                char command[] = "kill -9 ";
                                char str;
                                sprintf(str, "%d", pid);
                                strcat(command, str);
                                printf("command = %s\r\n", command);
                                //方式一:显示一张黑色的图片
                                lcd_show_bmp("black.bmp", 0, 0);
                                system(command);
                        }
                        else{
                                flag_x_y = 0;
                                break;
                        }
                        flag_x_y = 0;
                }

                pthread_mutex_unlock(&mutex);
        }
       
}void *slider_func(void *arg)
{
        printf("enter slider_func : %d\r\n", pthread_self());
        while(1){
                if(slider_flag == 1){
                        printf("enter slider_func\r\n");
                        touch_flag++;
                        if(touch_flag > BMP_MAX_NUMBER)
                                touch_flag = 1;
                        lcd_show_bmp(bmp_path, 0, 0);
                        sleep(1);
                }
        }
}int main()
{
        lcd_show_bmp("choice.bmp", 600, 0);
        lcd_show_bmp(bmp_path, 0, 0);
        pthread_t tid, tid_area_switch, tid_right_left_slide;
        int ret = 0;
       
        pthread_create(&tid, NULL, slider_func, NULL);
        pthread_create(&tid_area_switch, NULL, area_switch, NULL);
        pthread_create(&tid_right_left_slide, NULL, right_left_slide_func, NULL);

        pthread_mutex_init(&mutex, NULL);
        pthread_cond_init(&cond, NULL);
       
        printf("main thread = %ld\r\n", pthread_self());
        pid = getpid();
        printf("pid = %d\r\n", pid);

        while(1){
                get_touch();
        }
       
       
        pthread_join(tid, NULL);
        pthread_join(tid_area_switch, NULL);
        pthread_join(tid_right_left_slide, NULL);
        pthread_cond_destroy(&cond);
        exit(0);

} 总结

以上就是本文的全部内容, 希望可以或许帮助到你。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 粤嵌Linux GEC6818开发板实现电子相册