整个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);

