嵌入式Linux学习——Framebuffer应用
LCD的基本操作原理
- 驱动程序设置好LCD控制器:根据LCD的参数设置LCD控制器的时序、信号极性;根据LCD分辨率、bpp分配Framebuffer。
- APP通过 ioctl获得LCD分辨率、BPP
- APP通过 mmap 映射Framebuffer,在Framebuffer中写入数据
1.地址和RGB格式
fb_base 为使用mmap 后获得的地址,要确定 LCD中(x,y)坐标的地址由下面公式计算得到
(x,y)像素起始地址 = fb_base +(xres * bpp / 8)*y + x * bpp / 8
其中 xres表示 每行像素的个数。
bpp表示像素元素的表示格式,RGB三元素(红绿蓝),如下图有RGB888、RGB565、RBG555等格式。
RGB888就表示 红绿蓝每个颜色都用 8位来表示,颜色位占用低24位,高8为表示透明度,但一般的LCD不支持。
RGB888就是32BPP,对24BPP,硬件上为了方便处理,Framebuffer也是用32位来表示。
16BPP,常用的为RGB565。
2.涉及的API函数
2.1 open函数
头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
函数原型
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
说明:
pathname 表示打开文件的路径
Flags表示打开文件的方式,常用的有6种。
Mode表示创建文件的权限,使用O_CREATE时有效。
返回值:打开成功返回文件描述符,失败返回-1。
参数 | 打开方式 |
---|---|
O_RDWR | 可读可写 |
O_RDONLY | 只读 |
O_WRONLY | 只写 |
O_APPEND | 若文件有内容,新写入的内容会接续到原内容后面 |
O_TRUNC | 如果文件原本有内容,原来的内容会被丢弃、截断 |
O_CREATE | 文件不存在,创建并打开它,与O_EXCL结合使用 |
O_EXCL | 没有文件创建文件时,有这个文件时会报错提醒,返回 -1 |
2.2 ioctl函数
ioctl(输入输出控制)是一个系统调用,用于设备驱动程序和用户空间之间的特殊控制操作。它允许程序通过特定的命令与设备进行交互,通常用于设置设备参数或获取设备状态。
函数原型
int ioctl(int fd, unsigned long request, ...);
fd: 设备文件描述符,通过 open 函数获得。
request: 控制命令,用于指定要执行的操作。
…: 可选参数,通常是指向数据的指针,用于传递命令相关的信息。
常见用法
设备控制:设置或获取设备状态,例如配置串口参数、获取设备统计信息等。
网络接口控制:在网络编程中,ioctl 常用于配置网络接口,例如设置IP地址或获取网络接口信息。
示例
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int fd;
int result;
// 打开设备文件
fd = open("/dev/mydevice", O_RDWR);
if (fd == -1) {
perror("open");
return 1;
}
// 执行ioctl操作(假设IOCTL_CMD是一个定义的命令)
result = ioctl(fd, IOCTL_CMD, NULL);
if (result == -1) {
perror("ioctl");
close(fd);
return 1;
}
// 关闭设备文件
close(fd);
return 0;
}
注意事项
命令定义:request 参数通常是通过宏定义的命令,例如 IOCTL_CMD,这些宏定义可能在设备驱动的头文件中找到。
权限问题:确保你有足够的权限来执行特定的 ioctl 操作。
设备依赖:ioctl 操作依赖于具体的设备驱动程序,因此不同设备支持的命令和操作可能有所不同。
ioctl 提供了与设备进行细粒度控制的能力,但它的具体实现和使用方式取决于设备的驱动程序。
2.3 mmap函数
mmap 是一个系统调用,用于将文件或设备的内容映射到进程的虚拟地址空间中,从而允许程序直接访问这些内容。它通常用于实现高效的文件 I/O 操作和共享内存。
函数原型
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数解释
- addr: 映射的起始地址,通常设置为 NULL 让系统选择。
- length: 映射的字节数。
- prot: 内存保护标志,如 PROT_READ(可读)、PROT_WRITE(可写)。
- flags: 映射选项,如MAP_PRIVATE(私有映射)、MAP_SHARED(共享映射)。
- fd: 文件描述符,指向要映射的文件或设备。
- offset: 文件的偏移量,必须是页面大小的整数倍。
示例
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd;
void *mapped;
size_t length = 4096; // 映射的字节数(例如1页大小)
// 打开文件
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
// 映射文件到内存
mapped = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapped == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
// 使用映射的内存(例如打印内容)
printf("Mapped content: %s\n", (char *)mapped);
// 解除映射
munmap(mapped, length);
// 关闭文件
close(fd);
return 0;
}
常见标志和选项
PROT_READ: 允许读取。
PROT_WRITE: 允许写入。
MAP_PRIVATE: 创建私有映射,写入时不会影响原文件。
MAP_SHARED: 创建共享映射,写入时会影响原文件。
注意事项
页面对齐: offset 必须是页面大小的整数倍。
错误处理: mmap 可能失败,通常返回 MAP_FAILED。
权限管理: 根据需要设置适当的 prot 和 flags 选项。
mmap 允许高效的文件访问和进程间通信,但需要注意正确管理映射区域的权限和生命周期。
3. 流程分析
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
static int fd_fb;
static struct fb_var_screeninfo var; /* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;
/**********************************************************************
* 函数名称: lcd_put_pixel
* 功能描述: 在LCD指定位置上输出指定颜色(描点)
* 输入参数: x坐标,y坐标,颜色
* 输出参数: 无
* 返 回 值: 会
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/05/12 V1.0 zh(angenao) 创建
***********************************************************************/
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
switch (var.bits_per_pixel)
{
case 8:
{
*pen_8 = color;
break;
}
case 16:
{
/* 565 */
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
*pen_16 = color;
break;
}
case 32:
{
*pen_32 = color;
break;
}
default:
{
printf("can't surport %dbpp\n", var.bits_per_pixel);
break;
}
}
}
int main(int argc, char **argv)
{
int i;
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fb_base == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}
/* 清屏: 全部设为白色 */
memset(fb_base, 0xff, screen_size);
/* 随便设置出100个为红色 */
for (i = 0; i < 100; i++)
lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
munmap(fb_base , screen_size);
close(fd_fb);
return 0;
}