- A+
所属分类:linux技术
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 (NULL == 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 (NULL == address) { printf("draw_point msg:%sn", 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 (NULL == address) { printf("draw_line msg:%sn", 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 (NULL == address) { printf("draw_surface msg:%sn", 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 (NULL == 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); return 0; }