• Linux :LCD应用层编程


    LCD屏幕简单概述


     如上图所示,每个像素点都是由红绿蓝混色而成,目前市面上主流的屏幕每一个原色的色阶都是采用8位元来表示,所以每个像素点可以表示大约1600万中颜色,LCD屏幕由背光层,偏光层,电极层,液晶层,滤光片等组成,液晶是一种介于固体和液体之间的有机化合物,本身不会发光,而是作为一种可透光的物体,通过控制其偏转角度的大小,来控制透出光线的多少,通过控制不同原色透出光线的多少来表示出不同的颜色(需要通过滤光片)。

    如何操作LCD

     linux下一切皆文件,同样对于外部设备也是通过读写对应的文件来操作设备,对于LCD的操作有如下步骤:
      1.打卡LCD对应的设备文件;
      2.写入RGB颜色值;
      3.关闭文件。

    实验程序

    #define     LCD_PATH    "/dev/fb0"
    
    int main(int argc, char const *argv[])
    {
        // 打开lcd设备文件  /dev/fb0  --》 由驱动工程师完成
        int fd  = open(LCD_PATH , O_WRONLY);
        if (fd < 0 )
        {
           perror("open lcd error");
           return -1 ;
        }
        
        // 写入颜色值
        int color = 0x698b22 ;
        int i ;
        for ( i = 0; i < 800*480; i++)
        {
            write(fd , &color , sizeof(int));  
           
        }
        
        // 关闭文件
        close(fd);
        
        return 0;
    }
    

     由上程序可知,write向LCD设备写入RGB的值来控制LCD的显示。但是write函数需要进行一系列的系统调用才能将数据写入到设备文件,这会消耗大量的时间,实验现象也能看出屏幕从上而下,甚至从左到右的变化,实在是太慢了。使用系统调用mmap可以将文件映射至内存(进程空间),如此可以把对文件的操作转为对内存的操作,这点对于大文件或者频繁访问的文件尤其有用,提高了I/O效率。

    mmap

    mmap ( 建立内存映射 )
        头文件:
            #include <unistd.h>
            #include <sys/mman.h>
        定义函数:
            void *mmap(void *start, size_t length, int prot, 
                                    int flags, int fd, off_t offsize);
        参数分析:
           start --> 指向欲对应的内存起始地址, 通常设为 NULL, 代表让系统自动选定地址
           length --> 需要申请内存区域的大小
           prot --> 代表映射区域的保护方式有下列组合
                PROT_EXEC 映射区域可被执行
                PROT_READ 映射区域可被读取
                PROT_WRITE 映射区域可被写入
                PROT_NONE 映射区域不能存取
           flags -->会影响映射区域的各种特性
                MAP_FIXED 如果参数 start 所指的地址无法成功建立映射时, 则放弃映射, 不对地址做修正.通常不鼓励用此旗标.
                MAP_SHARED 对应射区域的写入数据会复制回文件内, 而且允许其他映射该文件的进程共享.
                MAP_PRIVATE 对应射区域的写入操作会产生一个映射文件的复制, 即私人的"写入时复制" (copyon write)对此区域作的任何修改都不会写回原来的文件内容.
                MAP_ANONYMOUS  建立匿名映射. 此时会忽略参数 fd, 不涉及文件, 而且映射区域无法和其他进程共享.
                MAP_DENYWRITE  只允许对应射区域的写入操作, 其他对文件直接写入的操作将会被拒绝.
                MAP_LOCKED  将映射区域锁定住, 这表示该区域不会被置换(swap:交换空间).
           fd --> 需要映射的文件的文件描述符
           offsize --> 偏移量, 一般设置为零,表示不需要偏移 文件与内存是一一对应的
           
       返回值:
           若映射成功则返回映射区的内存起始地址, 
           否则返回 MAP_FAILED(-1), 错误原因存于 errno 中.
    

    实验程序

    #define     LCD_W       800
    #define     LCD_H       480
    #define     LCD_SIZE    LCD_W*LCD_H*4
    
    #define     LCD_PATH    "/dev/fb0"
    
    int main(int argc, char const *argv[])
    {
       
        int fd  = open(LCD_PATH , O_RDWR);
        if (fd < 0 )
        {
           perror("open lcd error");
           return -1 ;
        }
    
        // 内存映射
        int * lcd_p = (int *)mmap(NULL ,   // 指向欲对应的内存起始地址, 通常设为 NULL, 代表让系统自动选定地址
                                LCD_SIZE,  // 显示器的大小
                                PROT_READ | PROT_WRITE,  // 配置内存区可读, 可写
                                MAP_SHARED,    // 设置内存区为共享*(对内存的任何操作都会被复制到文件中)
                                fd,        // 需要映射的文件
                                0);        // 偏移量
        if (MAP_FAILED ==  lcd_p)
        {
            perror("mmap error ");
            return -1 ;
        }
        
        // 写入颜色值
        int color = 0xFF3030 ;
        int i ;
        for ( i = 0; i < 800*480; i++)
        {
            *(lcd_p+i) = color ;
        }
       
        close(fd);
      
        return 0;
    }
    

     将此程序编译到开发板上运行时,肉眼看不到屏幕变化的过程,建立内存映射能够大大提高I/O的效率。

    实验程序

     以下程序是一个"跑马灯程序",将屏幕分成了不同颜色的八块,并按顺时针"跑动"。

    #define     LCD_W       800
    #define     LCD_H       480
    #define     LCD_SIZE    LCD_W*LCD_H*4
    
    #define     CYAN        0x00FFFF
    #define     PPuff       0xFFDAB9
    #define     RED         0xFF0000
    #define     YELLOW      0XFFFF00
    #define     BLUE        0x0000FF
    #define     INRED       0xEE6363
    #define     SNOW        0xFFFAFA
    #define     PURPLE      0xA020F0
    
    #define     LCD_PATH    "/dev/fb0"
    
    int color[8] = {CYAN, PPuff, RED, YELLOW, BLUE, INRED, SNOW, PURPLE};
    
    bool draw_point(int *address, int color, int x, int y) //画点函数:x,y用来表示坐标值
    {
        if (MAP_FAILED == address)
        {
            printf("draw_point msg:%s
    ", strerror(errno));  
            return false;
        }
    
        *(address + (x + (y*800))) = color; 
        return true;
    }
    
    bool draw_line(int *address, int color, int len, int x, int y) //画线函数:以坐标(x,y)为起点,画一条长度为len的横线
    {
        if (MAP_FAILED == address)
        {
            printf("draw_line msg:%s
    ", strerror(errno));        
            return false;
        }
        while (len--)
        {
            draw_point(address, color, x, y);
            address++;
        }
        return true;
    }
    
    bool draw_surface(int *address, int color, int high, int len, int x, int y)//画面函数:以坐标(x,y)为起点,画一个长为len,高high的面
    {
        if (MAP_FAILED == address)
        {
            printf("draw_surface msg:%s
    ", strerror(errno)); 
            return false;
        }
        while (high--)
        {
           draw_line(address, color, len, x, y);
           address = address + LCD_W;
        }
        return true;
    }
    
    int main(int argc, char const *argv[])
    {
        int fd  = open(LCD_PATH , O_RDWR);
        if (fd < 0 )
        {
           perror("open lcd error");
           return -1 ;
        }
    
        // 内存映射
        int * lcd_p = (int *)mmap(NULL ,   // 指向欲对应的内存起始地址, 通常设为 NULL, 代表让系统自动选定地址
                                LCD_SIZE,  // 显示器的大小
                                PROT_READ | PROT_WRITE,  // 配置内存区可读, 可写
                                MAP_SHARED,    // 设置内存区为共享*(对内存的任何操作都会被复制到文件中)
                                fd,        // 需要映射的文件
                                0);        // 偏移量
        if (MAP_FAILED ==  lcd_p)
        {
            perror("mmap error ");
            return -1 ;
        }
        
        int div_wide =  800 / 4; 
        int div_high =  480 / 2;
    
    
        int i = 0;
        int j = 0 ;
        // 将屏幕分为八块,写入不同颜色值,并让其顺时针“跑”起来
        while (1)
        {
           
            draw_surface(lcd_p, color[i++], div_high, div_wide, 0, 0);j++;
            if (7 <= i)
            {
                i = i % 8;
            }
            draw_surface(lcd_p, color[i++], div_high, div_wide, div_wide, 0);j++;
            if (7 <= i)
            {
                i = i % 8;
            }
            draw_surface(lcd_p, color[i++], div_high, div_wide, div_wide*2, 0);j++;
            if (7 <= i)
            {
                i = i % 8;
            }
            draw_surface(lcd_p, color[i++], div_high, div_wide, div_wide*3, 0);j++;
            if (7 <= i)
            {
                i = i % 8;
            }
            draw_surface(lcd_p, color[i++], div_high, div_wide, div_wide*3, div_high);j++;
            if (7 <= i)
            {
                i = i % 8;
            }
            draw_surface(lcd_p, color[i++], div_high, div_wide, div_wide*2, div_high);j++;
            if (7 <= i)
            {
                i = i % 8;
            }
            draw_surface(lcd_p, color[i++], div_high, div_wide, div_wide, div_high );j++;
            if (7 <= i)
            {
                i = i % 8;
            }
            draw_surface(lcd_p, color[i], div_high, div_wide, 0, div_high);j++;
            if (7 <= i) 
            {
                i = i % 8;
            }
    
            if (64 == j)//防止i,j无限增长
            {
                j = 0;
                i = 0;
            }
            usleep(160000);
    
        }
        close(fd);
        munmap(lcd_p, LCD_SIZE);
        
        return 0;
    }
    
  • 相关阅读:
    原型链的树形结构
    粗略讲一讲js的代码执行机制
    30天前端打卡整理记录
    简单模拟Vue的数据代理功能
    JavaScript语言精粹读后记录
    JavaScript巧用对象的引用解决三级联动
    es6新了解
    记录小程序开发的n个坑
    比拼人品-拼手气红包的JavaScript实现方式.
    一诺千金:微信小程序的wx.request 与Promise的结合使用
  • 原文地址:https://www.cnblogs.com/ding-ding-light/p/14162205.html
Copyright © 2020-2023  润新知