分享

Linux :LCD应用层编程

 行者花雕 2022-05-18 发布于北京


 如上图所示,每个像素点都是由红绿蓝混色而成,目前市面上主流的屏幕每一个原色的色阶都是采用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\n", 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\n", 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\n", 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;
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多