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;
}