就像在linux系统一样,eCos把lcd设备抽象为framebuf设备,并提供了framebuf抽象层,使得用户GUI程序不依赖于具体的frambuf设备,增加了用户GUI程序的独立性、可移植性,便于移植和使用第三方开发的GUI系统。
本章节主要介绍如何实现eCos中的LCD驱动,并展示了一个简单的测试用例。
LCD硬件
如上图所示,stm32板使用FSMC来驱动LCD,16 bits数据总线,液晶屏大小:400X240,自带LCD控制器(型号为:SPFD5420A)和触摸屏(型号为:TSC2046)。
LCD驱动源码下载
LCD驱动组成
LCD驱动主体由stm32fb.c源文件实现,整个LCD驱动主要由LCD初始化、LCD底层函数、framebuf实例、framebuf IO操作内联文件及其头文件自动生成文件等4部分组成。下面分别介绍。
LCD初始化
LCD的初始化位于stm32fb_init.cxx文件中,如下:
#include <pkgconf/devs_framebuf_stm32.h> #include <cyg/io/framebuf.h> extern "C" void _cyg_stm32_fb_instantiate(struct cyg_fb *); class _stm32_fb_init { public: _stm32_fb_init(void) { #ifdef CYGPKG_DEVS_FRAMEBUF_STM32_FB0 _cyg_stm32_fb_instantiate(&cyg_stm32_fb0); #endif } }; static _stm32_fb_init _stm32_fb_init_object CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_DEV_CHAR); //------------------------------------------------------------------------- // End of file
这里定义了一个非常简单的初始化类:_stm32_fb_init,它仅包含一个构造函数,主要启动LCD初始化的实体部分:_cyg_stm32_fb_instantiate()。
底下定义了这个类的静态对象_stm32_fb_init_object。后面有其属性:CYGBLD_ATTRIB_INIT_PRI。也就是说,eCos启动的时候,会执行这个类的构造函数,从而启动LCD的初始化。
_cyg_stm32_fb_instantiate()函数 这是LCD初始化实体部分,主要初始化FSMC的时序、FSMC所用IO的引脚配置、LCD寄存器的初始化等。 在前期调试的时候,由于没有对FSMC所用IO的引脚进行配置,导致LCD初始化失败,这点需要注意。如果初始化成功了,可以看到LCD会有反应的。LCD寄存器的初始化是直接拷贝安富莱开发板上带的例子。
LCD底层函数
LCD底层主要实现如LCD寄存器读写、LCD像素点读写、LCD坐标设定、LCD显示窗口设置等,对应stm32fb_lcd.inl源文件,它是一个内联文件。之所以用内联文件实现,这是因为不仅LCD驱动主体需要使用它,而且framebuf IO操作内联文件也需要使用这些LCD底层操作函数。
在LCD底层,定义了一个LCD结构体:
// Internal structure for the platform dependent operations typedef struct cyg_stm32_lcd_t { cyg_uint32 base; /* base address, N/A */ cyg_uint32 width; /* width */ cyg_uint32 height; /* height */ cyg_uint32 pwm_io; /* IO controlling the LCD backlight */ } cyg_stm32_lcd_t; // Declare the LCD 0 screen externC cyg_stm32_lcd_t cyg_stm32_lcd_0; #define CYG_PLF_FB_fb0_BASE &cyg_stm32_lcd_0
cyg_stm32_lcd_0定义在stm32fb.c文件中,作为framebuf的私有数据。LCD初始化的时候会对这个变量进行初始化的。
注意宏:CYG_PLF_FB_fb0_BASE,它定义为cyg_stm32_lcd_0的地址。
framebuf实例:即 struct cyg_fb结构体对象
eCos中每个framebuf都对应着一个struct cyg_fb定义的对象,从另一方面说,LCD驱动的编写就是围绕这个结构体展开的,主要实现这个结构体中的相关成员函数。定义一个framebuf采用工具宏:CYG_FB_FRAMEBUFFER。
下面是本LCD驱动中framebuf的定义:
#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
- stm32_fb_on()函数:打开LCD设备,相当于open方法;
- stm32_fb_off ()函数:关闭LCD设备,相当于close方法;
- stm32_fb_ioctl() 函数:LCD设备的IO控制,相当于ioctl方法;
上面3个函数不难理解,可能需要关注的是上面代码中FB_STM32_MAKE_FN宏定义的部分。 实际上,这些是一些LCD基本操作的实现,比如画点、画横线、画竖线、填充块等操作,对应eCos的framebuf IO层接口,供应用程序调用。这些函数分为Landscape(宽屏)、Portrait(竖屏)2类。编译时会根据图形配置工具中LCD宽(width)、高(height)的设置自动编译链接相应类别的函数。如果你设置的是宽屏,则不会链接竖屏类别的函数。通过这种实现,提高了程序的适应性和移植性。
看下2个简单的LCD基本操作函数(分别对应画点和画横线函数):
// Landscape void stm32_fb_write_pixel_landscape_16(cyg_fb* fb, cyg_ucount16 x, cyg_ucount16 y, cyg_fb_colour colour) { stm32_fb_write_pixel_landscape_16_inl(fb->fb_base, fb->fb_stride, x, y, colour); } void stm32_fb_write_hline_landscape_16(cyg_fb* fb, cyg_ucount16 x, cyg_ucount16 y, cyg_ucount16 len, cyg_fb_colour colour) { stm32_fb_write_hline_landscape_16_inl(fb->fb_base, fb->fb_stride, x, y, len, colour); }
这些基本操作函数,实际上使用的是内联文件中定义的内联函数。由于篇幅限制,这部分内容详见下节 LCD驱动第4部分内容:framebuf IO操作内联文件及其头文件自动生成文件。