整个LCD驱动主要由LCD初始化、LCD底层函数、framebuf实例、framebuf IO操作内联文件及其头文件自动生成文件等4部分组成。上节介绍了LCD驱动的前面3个部分。
本节介绍LCD驱动的framebuf IO操作内联文件及其头文件自动生成文件这个部分。本章最后会展示一个简单的应用测试程序。
CDL文件
LCD驱动的CDL文件是framebuf_stm32.cdl,相比其它设备的CDL文件,它稍微显得有点复杂。我们需要注意下面这一段代码:
active_if { CYGPKG_DEVS_FRAMEBUF_STM32_FB0} make -priority=1 { <PREFIX>/include/cyg/io/framebufs/stm32_fb.h : \ <PACKAGE>/src/gen_stm32fb.tcl \ <PREFIX>/include/pkgconf/devs_framebuf_stm32.h tclsh $< $(PREFIX) } compile stm32fb.c compile -library=libextras.a stm32fb_init.cxx
这段CDL代码大概的意思是会执行gen_stm32fb.tcl脚本,产生一个stm32_fb.h的头文件。这从驱动编译后可以在生成目录树中看到这个头文件。
tcl脚本文件
gen_stm32fb.tcl脚本文件有点复杂,它是一个tcl语言脚本,主要用于自动生成一个stm32_fb.h的头文件,生成后,这个头文件会自动包含到<cyg/io/framebufs/framebufs.h>头文件中,而它又会被<cyg/io/framebuf.h>头文件包含。
gen_stm32fb.tcl脚本文件有点多,下面截取其一部分:
extern cyg_fb cyg_stm32_fb[set fb]; #define CYG_FB_fb[set fb]_STRUCT cyg_stm32_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]_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 #ifdef CYG_PLF_FB_fb[set fb]_BASE # define CYG_FB_fb[set fb]_BASE (void *) CYG_PLF_FB_fb[set fb]_BASE #else # define CYG_FB_fb[set fb]_BASE 0 #endif #if CYG_FB_fb[set fb]_WIDTH > CYG_FB_fb[set fb]_HEIGHT # define CYG_FB_fb[set fb]_ORIENTATION landscape # else # define CYG_FB_fb[set fb]_ORIENTATION portrait #endif #if CYG_FB_fb[set fb]_WIDTH == CYG_FB_fb[set fb]_HEIGHT # error \"Framebuffer width and height cannot be configured equal\" #endif
上面代码中,“set_fb”取0,因此上面的“fb[set fb]”实际上等效于“fb0”。等效后再看这些代码,是不是很熟悉?它就是上节我们用宏CYG_FB_FRAMEBUFFER定义LCD设备对象时一些使用的定义,看下面:
#define FB_STM32_MAKE_FN1(_fn_, _orientation_, _suffix_) \ stm32_fb_ ## _fn_ ## _ ## _orientation_ ## _ ##_suffix_ #define FB_STM32_MAKE_FN( _fn_, _orientation_, _suffix_) \ FB_STM32_MAKE_FN1(_fn_, _orientation_, _suffix_) #define FB_STM32_COLOUR_FN1(_fn_, _suffix_) cyg_fb_dev_ ## _fn_ ## _ ## _suffix_ #define FB_STM32_COLOUR_FN( _fn_, _suffix_) FB_STM32_COLOUR_FN1(_fn_, _suffix_) #ifdef CYGPKG_DEVS_FRAMEBUF_STM32_FB0 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_VIEWPORT_WIDTH, CYG_FB_fb0_VIEWPORT_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) 0, (CYG_ADDRWORD) 0, (CYG_ADDRWORD) 0, (CYG_ADDRWORD) 0, &stm32_fb_on, &stm32_fb_off, &stm32_fb_ioctl, &cyg_fb_nop_synch, &cyg_fb_nop_read_palette, &cyg_fb_nop_write_palette, &FB_STM32_COLOUR_FN(make_colour, 16bpp_true_565), &FB_STM32_COLOUR_FN(break_colour, 16bpp_true_565), FB_STM32_MAKE_FN(write_pixel, CYG_FB_fb0_ORIENTATION, CYG_FB_fb0_DEPTH), FB_STM32_MAKE_FN(read_pixel, CYG_FB_fb0_ORIENTATION, CYG_FB_fb0_DEPTH), FB_STM32_MAKE_FN(write_hline, CYG_FB_fb0_ORIENTATION, CYG_FB_fb0_DEPTH), FB_STM32_MAKE_FN(write_vline, CYG_FB_fb0_ORIENTATION, CYG_FB_fb0_DEPTH), FB_STM32_MAKE_FN(fill_block, CYG_FB_fb0_ORIENTATION, CYG_FB_fb0_DEPTH), FB_STM32_MAKE_FN(write_block, CYG_FB_fb0_ORIENTATION, CYG_FB_fb0_DEPTH), FB_STM32_MAKE_FN(read_block, CYG_FB_fb0_ORIENTATION, CYG_FB_fb0_DEPTH), FB_STM32_MAKE_FN(move_block, CYG_FB_fb0_ORIENTATION, CYG_FB_fb0_DEPTH), 0, 0, 0, 0 ); #endif
如果上节没弄明白这些定义来自哪里,到这里应该就可以看得懂了。 原来它们定义在由gen_stm32fb.tcl脚本生成的stm32_fb.h头文件中。
再看gen_stm32fb.tcl脚本文件的下面:
if { $landscape == 1 } { append data \ " # define CYG_FB_fb0_WRITE_PIXEL(_x_, _y_, _colour_) \\ CYG_MACRO_START \\ stm32_fb_write_pixel_landscape_16_inl(CYG_FB_fb0_BASE, \\ CYG_FB_fb0_STRIDE, \\ _x_, _y_, _colour_); \\ CYG_MACRO_END # define CYG_FB_fb0_READ_PIXEL(_x_, _y_) \\ stm32_fb_read_pixel_landscape_16_inl(CYG_FB_fb0_BASE, \\ CYG_FB_fb0_STRIDE, \\ _x_, _y_); # define CYG_FB_fb0_WRITE_HLINE(_x_, _y_, _len_, _colour_) \\ CYG_MACRO_START \\ stm32_fb_write_hline_landscape_16_inl(CYG_FB_fb0_BASE, \\ CYG_FB_fb0_STRIDE, \\ _x_, _y_, _len_, _colour_); \\ CYG_MACRO_END # define CYG_FB_fb0_WRITE_VLINE(_x_, _y_, _len_, _colour_) \\ CYG_MACRO_START \\ stm32_fb_write_vline_landscape_16_inl(CYG_FB_fb0_BASE, \\ CYG_FB_fb0_STRIDE, \\ _x_, _y_, _len_, _colour_); \\ CYG_MACRO_END
这里定义了一些函数的宏,在定义LCD设备对象时,LCD基本操作函数也是使用这些函数的。它们都是framebuf IO操作的内联函数。这些定义的宏都会原封不动的呈现到stm32_fb.h头文件中。
framebuf IO操作内联文件
stm32fb.inl文件中定义了framebuf IO操作的内联函数。
// The always_inline attribute must be a applied to a declaration, not a // definition, so combine the two via a single macro. #define CYG_FB_DEVS_FRAMEBUF_INLINE_FN(_type_, _name_, _args_) \ static __inline__ _type_ _name_ _args_ __attribute__((__always_inline__)); \ static __inline__ _type_ _name_ _args_ /* 设置显示窗口 WINDOWS */ # define CYG_FB_DEFAULT_WINDOW_AREA( _fbaddr_ ) \ CYG_MACRO_START \ cyg_stm32_lcd_t * _lcd = (cyg_stm32_lcd_t *)_fbaddr_; \ if ( _lcd->width > _lcd->height ) { \ lcd_set_disp_win(_lcd, 0, 0, _lcd->width, _lcd->height); \ } else { \ lcd_set_disp_win(_lcd, 0, 0, _lcd->height, _lcd->width); \ } \ CYG_MACRO_END // ---------------------------------------------------------------------------- // LCD in landscape mode CYG_FB_DEVS_FRAMEBUF_INLINE_FN(void, stm32_fb_write_pixel_landscape_16_inl, (void* _fbaddr_, cyg_ucount16 _stride_, cyg_ucount16 _x_, cyg_ucount16 _y_, cyg_fb_colour _colour_)) { lcd_put_pixel(_x_,_y_,_colour_); } CYG_FB_DEVS_FRAMEBUF_INLINE_FN(cyg_fb_colour, stm32_fb_read_pixel_landscape_16_inl, (void* _fbaddr_, cyg_ucount16 _stride_, cyg_ucount16 _x_, cyg_ucount16 _y_)) { return lcd_get_pixel(_x_,_y_); } CYG_FB_DEVS_FRAMEBUF_INLINE_FN(void, stm32_fb_write_hline_landscape_16_inl, (void* _fbaddr_, cyg_ucount16 _stride_, cyg_ucount16 _x_, cyg_ucount16 _y_, cyg_ucount16 _len_, cyg_fb_colour _colour_)) { cyg_uint32 pixels; lcd_set_cursor(_x_,_y_); /* 先横向扫描,再纵向扫描 */ lcd_write_ir(LCDR_GRAM); for (pixels = 0; pixels < _len_; pixels++) { lcd_write_ram(_colour_); } }
目前,stm32fb.inl文件中只实现了landscape(宽屏) LCD的一些操作IO,portrait(竖屏)LCD操作还没有实现。
到现在,应该大概明白LCD驱动的架构是怎样实现的了。像stm32fb.inl文件中实现的内联函数,以及stm32fb_lcd.inl中LCD底层函数,这些都会被gen_stm32fb.tcl脚本自动生成到stm32_fb.h头文件中。
最终,应用程序中只需包含<cyg/io/framebuf.h>头文件,就可以使用framebuf提供的API了。从这层来看,eCos中LCD应用程序与LCD驱动实际是一种API调用关系,只是被framebuf IO层封装化了。当然,这种封装是必要的,它实现了LCD应用程序与具体LCD设备的独立性。
应用实例
注意:要使用framebuf,需要包含CYGPKG_IO_FRAMEBUF软件包。在实际使用中发现,如果要使用标准颜色值,还需要包含FILE IO包:CYGPKG_IO_FILEIO,否则下面代码中宏定义的颜色会不起作用。
#include <unistd.h> #include <fcntl.h> #define STRING1(_a_) # _a_ #define STRING(_a_) STRING1(_a_) // The colours used by this test code. Default to the standard palette // but if running on a true colour display then adjust. static cyg_ucount32 colours[16] = { CYG_FB_DEFAULT_PALETTE_BLACK, CYG_FB_DEFAULT_PALETTE_BLUE, CYG_FB_DEFAULT_PALETTE_GREEN, CYG_FB_DEFAULT_PALETTE_CYAN, CYG_FB_DEFAULT_PALETTE_RED, CYG_FB_DEFAULT_PALETTE_MAGENTA, CYG_FB_DEFAULT_PALETTE_BROWN, CYG_FB_DEFAULT_PALETTE_LIGHTGREY, CYG_FB_DEFAULT_PALETTE_DARKGREY, CYG_FB_DEFAULT_PALETTE_LIGHTBLUE, CYG_FB_DEFAULT_PALETTE_LIGHTGREEN, CYG_FB_DEFAULT_PALETTE_LIGHTCYAN, CYG_FB_DEFAULT_PALETTE_LIGHTRED, CYG_FB_DEFAULT_PALETTE_LIGHTMAGENTA, CYG_FB_DEFAULT_PALETTE_YELLOW, CYG_FB_DEFAULT_PALETTE_WHITE }; #define BLACK colours[0x00] #define BLUE colours[0x01] #define GREEN colours[0x02] #define CYAN colours[0x03] #define RED colours[0x04] #define MAGENTA colours[0x05] #define BROWN colours[0x06] #define LIGHTGREY colours[0x07] #define DARKGREY colours[0x08] #define LIGHTBLUE colours[0x09] #define LIGHTGREEN colours[0x0A] #define LIGHTCYAN colours[0x0B] #define LIGHTRED colours[0x0C] #define LIGHTMAGENTA colours[0x0D] #define YELLOW colours[0x0E] #define WHITE colours[0x0F] #define FRAMEBUF fb0 #define DEPTH CYG_FB_DEPTH(FRAMEBUF) #define WIDTH CYG_FB_WIDTH(FRAMEBUF) #define HEIGHT CYG_FB_HEIGHT(FRAMEBUF) #define GRID_X_SIZE 400 #define GRID_Y_SIZE 240 #define GRID_ABSOLUTE_X_POS 1 #define GRID_ABSOLUTE_Y_POS 19 #define GRID_STEP 25 #define MAX_X (GRID_X_SIZE + GRID_ABSOLUTE_X_POS) #define MAX_Y (GRID_Y_SIZE + GRID_ABSOLUTE_Y_POS) static void reset_colours_to_true(void) { #if (CYG_FB_FLAGS0_TRUE_COLOUR & CYG_FB_FLAGS0(FRAMEBUF)) int i; for (i = 0; i < 16; i++) { colours[i] = CYG_FB_MAKE_COLOUR(FRAMEBUF, cyg_fb_palette_vga[i + i + i], cyg_fb_palette_vga[i + i + i + 1],cyg_fb_palette_vga[i + i + i + 2]); // diag_printf("colours[%02d] = 0x%06X\n", i, colours[i]); } #endif } void lcd_init(void * data) { int i; if (CYG_FB_ON(FRAMEBUF)) { diag_printf("Open frambuf %s failed!\n", STRING(FRAMEBUF)); return; } diag_printf("Frame buffer %s, ", STRING(FRAMEBUF)); diag_printf("Depth %d, width %d\n", DEPTH, WIDTH); // 打开背光 cyg_fb_ioctl_backlight backlight; backlight.fbbl_current = 1; i = sizeof(backlight); CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_BACKLIGHT_SET, &backlight, &i); if (CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_TRUE_COLOUR) { reset_colours_to_true(); } // 白色背景 CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0, WIDTH, HEIGHT, WHITE); // 画一条横线 CYG_FB_WRITE_HLINE(FRAMEBUF, 5, 20, 100, RED); // 填充一个区域 CYG_FB_FILL_BLOCK(FRAMEBUF, 5, 30, 100, 20, YELLOW); // 一条竖线 CYG_FB_WRITE_VLINE(FRAMEBUF, 1, 1, 100, BLUE); // 一条竖线 CYG_FB_WRITE_VLINE(FRAMEBUF, 120, 1, 100, GREEN);