大家都知道在一些大型的操作系统上都有Framebuffer的概念,比如Linux,Windows等。
Framebuffer驱动程序是显示设备的一个抽象层,它使得图形系统和其他应用显示设备的程序与具体显示设备独立开来,增加了系统的可移植性。
在eCos中CYGPKG_IO_FRAMEBUF包提供了Framebuffer驱动的抽象层,它可以被应用程序或者其他包来使用。(本文原创转载请注明出处)
在eCos提供的IO层驱动函数中包含了线性framebuffer的操作函数,所谓线性framebuffer就是在LCD初始化以后分配一段固定的内存空间,我们对这段内存空间的读写直接相当于操作屏幕。
以MINI2440的QEMU平台为例,我们首先初始化好LCD,并且分配好一个事先准备的内存空间给Framebuffer,然后把一些基本的画点和画线函数用IO层的线性函数替代就基本完成了Framebuffer驱动。
先来看一下MINI2440 Framebuffer需要实现的结构体
红色 表示调用IO层次的内置函数
绿色 表示需要自己实现的LCD初始化函数
蓝色 表示需要的一些配置和LCD Framebuffer地址等
CYG_FB_FRAMEBUFFER(CYG_FB_fb0_STRUCT,
CYG_FB_fb0_DEPTH,
CYG_FB_fb0_FORMAT,
CYG_FB_fb0_WIDTH,
CYG_FB_fb0_HEIGHT,
CYG_FB_fb0_WIDTH,
CYG_FB_fb0_HEIGHT,
CYG_FB_fb0_BASE,
CYG_FB_fb0_STRIDE,
CYG_FB_fb0_FLAGS0,
CYG_FB_fb0_FLAGS1,
CYG_FB_fb0_FLAGS2,
CYG_FB_fb0_FLAGS3,
(CYG_ADDRWORD) &cyg_mini2440_fb0_lcd,
(CYG_ADDRWORD) 1,
(CYG_ADDRWORD) &fb0_mini2440,
(CYG_ADDRWORD) CYGMEM_REGION_lcd,
&mini2440_fb_on,
&mini2440_fb_off,
&mini2440_fb_ioctl,
&mini2440_fb_synch,
&cyg_fb_nop_read_palette,
&cyg_fb_nop_write_palette,
&cyg_fb_dev_make_colour_16bpp_true_565,
&cyg_fb_dev_break_colour_16bpp_true_565,
&cyg_fb_linear_write_pixel_16,
&cyg_fb_linear_read_pixel_16,
&cyg_fb_linear_write_hline_16,
&cyg_fb_linear_write_vline_16,
&cyg_fb_linear_fill_block_16,
&cyg_fb_linear_write_block_16,
&cyg_fb_linear_read_block_16,
&cyg_fb_linear_move_block_16,
0, 0, 0, 0
);
再看一下CYGMEM_REGION_lcd
个人认为向Framebuffer这样的设备驱动,应该在系统编译的时候直接指定一段内存,而不是定义一个大数组让编译器决定放到一个什么位置。
所以,我在memory配置文件中定义一个区域CYGMEM_REGION_lcd,把这个区域放在ram的最高地址处作为framebuffer需要的内存地址。
// eCos memory layout - Fri Oct 20 05:43:59 2000 // This is a generated file - do not edit #ifndef __ASSEMBLER__ #include <cyg/infra/cyg_type.h> #include <stddef.h> #endif #define CYGMEM_REGION_ram (0) #define CYGMEM_REGION_ram_SIZE (0x3600000) #define CYGMEM_REGION_ram_ATTR (CYGMEM_REGION_ATTR_R | CYGMEM_REGION_ATTR_W) #define CYGMEM_REGION_lcd (0x3600000) #define CYGMEM_REGION_lcd_SIZE (0xa0000) #define CYGMEM_REGION_lcd_ATTR (CYGMEM_REGION_ATTR_R | CYGMEM_REGION_ATTR_W) #define CYGMEM_REGION_sram (0x40000000) #define CYGMEM_REGION_sram_SIZE (0x1000) #define CYGMEM_REGION_sram_ATTR (CYGMEM_REGION_ATTR_R | CYGMEM_REGION_ATTR_W) #ifndef __ASSEMBLER__ extern char CYG_LABEL_NAME (__heap1) []; #endif #define CYGMEM_SECTION_heap1 (CYG_LABEL_NAME (__heap1)) #define CYGMEM_SECTION_heap1_SIZE (0x3600000 - (size_t) CYG_LABEL_NAME (__heap1))
LCD的初始化代码参考emboslab的
packages\devs\framebuf\arm\mini2440\current\src\mini2440fb.c
下面在讨论一下framebuffer驱动是如何初始化的
参考一下
packages\devs\framebuf\arm\mini2440\current\src\mini2440fb_init.cxx
eCos的framebuffer io层会通过一种宏代码调用含有framebuffer名字的c++初始化函数,比如我们的framebuffer名字是mini2440
那么系统最终会调用cyg_mini2440_fb_init这个函数,而cyg_mini2440_fb0_instantiate真是我们实现LCD初始化的入口。
#include <pkgconf/devs_framebuf_mini2440.h> #include <cyg/io/framebuf.h> class cyg_mini2440_fb_init { public: cyg_mini2440_fb_init(void) { #ifdef CYGPKG_DEVS_FRAMEBUF_MINI2440_FB0 cyg_mini2440_fb0_instantiate(&CYG_FB_fb0_STRUCT); #endif } }; static cyg_mini2440_fb_init cyg_mini2440_fb_init_object CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_DEV_CHAR);
在看一下eCos的Framebuffer是如何实现平台无关性的
首先eCos的Framebuffer是一个API层次,并非是设备文件,因此实现平台无关性采用了一个特殊的技巧。
先看一下结果,然后我们再看如何实现
ecos_install\include\cyg\io\framebufs\framebufs.h
#ifndef CYGONCE_IO_FRAMEBUFS_FRAMEBUFS_H # define CYGONCE_IO_FRAMEBUFS_FRAMEBUFS_H /* This is a generated file - do not edit! */ /* <cyg/io/framebufs/framebufs.h> should not be #include'd */ /* directly, instead use <cyg/io/framebuf.h> */ #include <cyg/io/framebufs/mini2440_fb.h> #define CYGDAT_IO_FRAMEBUF_DEFAULT_TEST_DEVICE fb0 #endif
以上这个文件是编译好的eCos生成的一个framebufs.h的头文件,我们看到这是一个自动生成的文件。
在这个文件中自动的加入了
#include <cyg/io/framebufs/mini2440_fb.h>
而这个头函数,正式我们对MINI2440的framebuffer的一些特殊设定,也有一些特别的函数。
当用户去应用framebuffer时,因为eCos已经把需要的头函数加到了一个共有头函数中,所以我们可以看作应用层无需再考虑目前
是在什么样的显示设备上。
那么这个头文件是怎么样自动加入进来的呢
首先我们需要写一个tcl脚本,这个脚本是从framebuffer的synth平台复制过来的。
packages\devs\framebuf\arm\mini2440\current\src\gen_mini2440fb.tcl
简单看几行, set_fb会根据framebufer的配置而更改,比如我们设置famebufer ID是0
那么
#define CYG_FB_fb[set fb]_STRUCT cyg_mini2440_fb[set fb]
就会展开成
#define CYG_FB_fb0_STRUCT cyg_mini2440_fb0
extern cyg_fb cyg_mini2440_fb[set fb]; #include CYGHWR_MEMORY_LAYOUT_H #define cyg_mini2440_fb0_base ((volatile cyg_uint8*)CYGMEM_REGION_lcd) externC void cyg_mini2440_fb0_instantiate(struct cyg_fb *fb); externC struct cyg_fb CYG_FB_fb0_STRUCT; #define CYG_FB_fb[set fb]_STRUCT cyg_mini2440_fb[set fb] #define CYG_FB_fb[set fb]_DEPTH $depth #define CYG_FB_fb[set fb]_FORMAT $format #define CYG_FB_fb[set fb]_WIDTH $width #define CYG_FB_fb[set fb]_HEIGHT $height #define CYG_FB_fb[set fb]_VIEWPORT_WIDTH $viewport_width #define CYG_FB_fb[set fb]_VIEWPORT_HEIGHT $viewport_height #define CYG_FB_fb[set fb]_BASE cyg_mini2440_fb[set fb]_base #define CYG_FB_fb[set fb]_STRIDE $stride #define CYG_FB_fb[set fb]_FLAGS0 $flags0 #define CYG_FB_fb[set fb]_FLAGS1 0 #define CYG_FB_fb[set fb]_FLAGS2 0 #define CYG_FB_fb[set fb]_FLAGS3 0
只有脚本是不够的,我们还需要在framebuffer驱动的cdl配置文件中去运行这个脚本
cdl_package CYGPKG_DEVS_FRAMEBUF_MINI2440 { display "MINI2440 board Framebuffer device driver" parent CYGPKG_IO_FRAMEBUF active_if CYGPKG_IO_FRAMEBUF hardware include_dir cyg/framebuf compile mini2440fb.c compile -library=libextras.a mini2440fb_init.cxx make -priority=1 { <PREFIX>/include/cyg/io/framebufs/mini2440_fb.h : \ <PACKAGE>/src/gen_mini2440fb.tcl \ <PREFIX>/include/pkgconf/devs_framebuf_mini2440.h tclsh {1}lt; $(PREFIX) }
有了以上的基础就会很容易的写一个eCos的Framebuffer驱动。
编译eCos的测试程序
Build->Tests
qemu-system-arm.exe -M mini2440 -kernel ecos_install\tests\io\framebuf\current\tests\fb -show-cursor -serial file:CON
qemu-system-arm.exe -M mini2440 -kernel ecos_install\tests\io\framebuf\current\tests\fbmacro -show-cursor -serial file:CON
有了这样一个Framebuffer接口就很容易移植不同的GUI在eCos上,经过评估选择一款合适于你产品的GUI。