在应用层与内核之间传递少量数据时,采用copy_from_usr,copy_to_usr。但是如果是传输图片这种大量数据时需使用mmap方法(在内核空间中申请一段内存作为显存,然后将这段内存的物理地址与应用层的地址进行映射)。
1)打开设备文件/dev/fb0
2)获取设备信息——需要包含头文件#include
3)mmap做映射
4)向framebuffer填充数据
#include
void *mmap (void *addr, size_t length, int prot, int flags, int fd, off_t offset);
@addr 指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。
@len 映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。
@prot 参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可执行), PROT_NONE(不可访问)。
@flags 由以下几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。
@fd 即将映射到进程空间的文件描述字,一般为open()返回值,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信)。
@offset 一般设为0,表示从文件头开始映射, 代表偏移量。
#include
#include
#include
#include
#include
#include
#include
//设备名宏定义
#define FBDEVICE "/dev/fb0"
// 旧开发板
//#define WIDTH 800
//#define HEIGHT 480
// 新开发板
#define WIDTH 1024
#define HEIGHT 600
//颜色宏定义
#define WHITE 0xffffffff // test ok
#define BLACK 0x00000000
#define RED 0xffff0000
#define GREEN 0xff00ff00 // test ok
#define BLUE 0xff0000ff
#define GREENP 0x0000ff00 // 一样,说明前2个ff透明位不起作用
//全局变量 指向mmap映射空间
unsigned int *pfb = NULL;
//背景填充函数
void draw_back(unsigned int width, unsigned int height, unsigned int color)
{
unsigned int x, y;
for (y=0; y<height; y++)
{
for (x=0; x<width; x++)
{
*(pfb + y * WIDTH + x) = color;//从左到右,从上到下
}
}
}
//划线函数
void draw_line(unsigned int color)
{
unsigned int x, y;
for (x=50; x<600; x++)
{
*(pfb + 200 * WIDTH + x) = color;
}
}
int main(void)
{
int fd = -1, ret = -1;
struct fb_fix_screeninfo finfo = {0};
struct fb_var_screeninfo vinfo = {0};
// 第1步:打开设备
fd = open(FBDEVICE, O_RDWR);
if (fd < 0)
{
perror("open");
return -1;
}
printf("open %s success.\n", FBDEVICE);
// 第2步:获取设备的硬件信息
ret = ioctl(fd, FBIOGET_FSCREENINFO, &finfo);//FBIOGET_FSCREENINFO 获取不可变的硬件信息
if (ret < 0)
{
perror("ioctl");
return -1;
}
printf("smem_start = 0x%x, smem_len = %u.\n", finfo.smem_start, finfo.smem_len);
ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);//FBIOGET_VSCREENINFO 获取可变的硬件信息
if (ret < 0)
{
perror("ioctl");
return -1;
}
printf("xres = %u, yres = %u.\n", vinfo.xres, vinfo.yres);
printf("xres_virtual = %u, yres_virtual = %u.\n", vinfo.xres_virtual, vinfo.yres_virtual);
printf("bpp = %u.\n", vinfo.bits_per_pixel);
// 修改驱动中屏幕的分辨率
vinfo.xres = 1024;
vinfo.yres = 600;
//虚拟空间大小=1024*1200 ——> 双缓冲区
vinfo.xres_virtual = 1024;
vinfo.yres_virtual = 1200;
ret = ioctl(fd, FBIOPUT_VSCREENINFO, &vinfo);//FBIOPUT_VSCREENINFO 设定可变的硬件信息
if (ret < 0)
{
perror("ioctl");
return -1;
}
// 再次读出来检验一下
ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);//FBIOGET_VSCREENINFO 获取可变的硬件信息
if (ret < 0)
{
perror("ioctl");
return -1;
}
printf("修改过之后的参数:\n");
printf("xres = %u, yres = %u.\n", vinfo.xres, vinfo.yres);
printf("xres_virtual = %u, yres_virtual = %u.\n", vinfo.xres_virtual, vinfo.yres_virtual);
printf("bpp = %u.\n", vinfo.bits_per_pixel);
// 第3步:进行mmap
unsigned long len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;//映射字节数=长*宽(实际长宽的两倍)*像素bit数/8
printf("len = %ld\n", len);
pfb = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (NULL == pfb)
{
perror("mmap");
return -1;
}
printf("pfb = %p.\n", pfb);
draw_back(WIDTH, HEIGHT, WHITE);//背景填充
draw_line(RED);//划线
close(fd);
return 0;
}
gcc编译,烧录测试
1、logo的png格式图片(图片分辨率不得大于屏幕分辨率),并将其命令为logo.png(目的在于方便pngtopnm指令)
2、在终端下执行以下命令
pngtopnm logo.png | ppmquant -fs 224 | pnmtoplainpnm > logo_x210_clut224.ppm
3、生成的ppm文件拷贝到/kernel/drivers/video/logo/目录下进行替换。
4、编译内核 make -j4
(1)修改fbmem.c的471行可以修改Logo显示在屏幕中间:
image.dx = (info->var.xres - logo->width)/2;
image.dy = (info->var.yres - logo->height)/2;
(2)如果图片显示不出来可能有以下原因:
a、内核里framebuffer的分辨率不对,可以在mach-x210.c的225行修改(1024 * 600);
b、只能在左上角显示,CONFIG_FRAMEBUFFER_CONSOLE宏可能被选中(该宏不需要勾选,可以现在.config里面查看,确定后,make menuconfig 去除Device Drivers -> Graphics support -> Console display driver support -> Framebuffer Console Support)
1、Boot显示开机logo的代码定位 uboot\drivers\video\logo.c
2、更改变量xboot_logo的赋值。
1、下载GIMP(linux平台)
2、将准备的图片(分辨率不要太高)通过GMIP导出C文件
3、将导出的C文件代码替换xboot_logo的值。
补:目前还没有测试过GIMP生成C数组