张国华, 朱世伟, 高常波
在众多的嵌入式系统中,尤其是一些人机交互较频繁的嵌入式系统,显示屏是一种应用较为广泛的输入输出设备。由于嵌入式系统功能的不同,选择的显示屏的种类和特性不尽一致,需要根据嵌入式设备的实际功能和特殊需要来选择相应特性的显示屏并实现其驱动程序。
通过专用显示芯片加外围电路的方式实现嵌入式系统显示硬件设计,但一般在系统设计时只考虑了操作系统内的显示驱动,在 uboot的启动和加载操作系统的过程[1]中没有考虑开机图片的显示,在这段长时间的系统加载过程中能够显示开机图片,为用户显示系统运行状态显的尤为重要。
文中以高温宽的 EL屏 el320.240为例,讲解uboot版本为1.1.2的linux驱动实现过程。
在linux下的显示驱动由uboot配置直接管理和分层调用。程序开发者只需要开发硬件和硬件相关的显示驱动就可以了。在linux uboot显示设计开发中,整个设计架构如图1所示[2]。
在以上框架中,内核配置与程序调用有关,首先要在内核配置的 config文件里定义 CONFIG_VIDEO_SED13806的宏;显示驱动是和LCD 硬件密切相关的,驱动开发者必须根据LCD的硬件的特性来计算出驱动程序所需的几个重要参数值。
图1 显示驱动框架
首先,要在include/configs/at91rm9200dk.h里面添加相应的宏。
#define CONFIG_VIDEO_SED13806
#define CONFIG_VIDEO_SED13806_16BPP
#define CONFIG_NEC_NL644BC20
然后,在 include/sed13806.h中修改 FRAME_BUFFER_OFFSET、TOTAL_SPACE_SIZE 和DEFAULT_VIDEO_MEMORY_SIZE等和硬件设计相关的空间配置。
#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 240
#define SED13806_REG_ADDR 0x30000000
#define FRAME_BUFFER_OFFSET 0x200000
#define TOTAL_SPACE_SIZE 0x1400000
#define DEFAULT_VIDEO_MEMORY_SIZE 0x140000
这段代码定义了驱动320×240 分辨率LCD 屏所需的值,并根据显示存储器的片选管脚确定显示驱动所需访问的寄存器出示地址和偏移量等。
硬件主要有核心处理模块、显示控制模块、电源转换模块、显示屏等几个部分组成,框图如图 2所示。
图2 硬件设计框
核心处理模块主要由核心处理芯片AT91RM9200及其外围电路组成,主要完成bootloader和操作系统的运行及加载并完成显示驱动和与显示驱动模块的交互,显示驱动模块完成显示控制接口和显示存储等功能,通过显示控制模块接口实现与显示屏的链接并将显示内容显示在显示屏上,电源转换模块完成各功能模块所需电源的转换和供电。
第一步,在drivers/sed13806.c里面,添加对于s1d13506需要的操作函数。这里主要是对video_hw_init这个函数进行修改,这个函数就是将要在uboot初始化过程中被调用来初始化s1d13506的函数[3]。
void *video_hw_init (void)
{
unsigned int *vm, i;
memset (&s1d13806, 0, sizeof (Graphic Device));
s1d13806.frameAdrs=sed13806.isaBase+FAME_BUFFER_OFFSET;
s1d13806.winSizeX = board_get_width ();
s1d13806.winSizeY = board_get_height ();
s1d13806.gdfIndex=GDF_16BIT_565 RGB;
s1d13806.gdfBytesPP = 2;
s1d13806.memSize = s1d13806.winSizeX *s1d13806.winSizeY * s1d13806.gdfBytesPP;
EpsonSetRegs (); /*设置s1d13506的寄存器*/
/*初始化显示内存 */
i = s1d13806.memSize/4;
vm = (unsigned int *)s1d13806.frameAdrs;
printf("i is %d, vm is %x ", i, vm);
while(i--) *vm++ = 0xffffffff;
}
以上这个函数基本上就完成了对于驱动lcd所需要的硬件s1d13506的设置了并清除了现实内内存。
第二步,在board/at91rm9200dk/at91rm9200dk.c添加board_video_init()函数。
首先,拷贝static const S1D_REGS init_regs[ ]数组和头文件,这个数组定义了s1d13506的寄存器配置,并用来初始化s1d13506的寄存器值。
然后,为board_video_init()函数赋值。
Unsigned int board_video_init (void)
{
(AT91PS_PIO)AT91C_BASE_PIOC->PIO_IDR = (unsigned int) (1 << 6);
(AT91PS_PIO)AT91C_BASE_PIOC->PIO_ASR = (unsigned int) (1 << 6);
(AT91PS_PIO)AT91C_BASE_PIOC->PIO_PDR = (unsigned int) (1 << 6);
AT91C_BASE_SMC2->SMC2_CSR[2] =(unsigned int)( (1 << 13) | (1 << 7) | (4 << 0) | (1 <<8) );
return (SED13806_REG_ADDR);
}
这样,对于board_video_init()的工作就结束了,而对于EpsonSetRegs也通过对寄存器数组的修改完成。最后将这个函数包含到 uboot的初始化函数数组中进行调用。
第三步,打开文件 lib_arm/board.c,找到start_armboot(),这个函数就是板卡进行调用的初始函数,里面有一段代码:
for (init_fnc_ptr = init_sequence; *init_fnc_ptr;++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
就是调用函数数组init_sequence来进行板卡初始化,找到该函数数组,将s1d13506的初始化函数video_hw_init添加进去,如下:
init_fnc_t *init_sequence[ ] = {
cpu_init,/* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
display_dram_config,
video_hw_init, /*zhs: init sed13506*/
#if defined(CONFIG_VCMA9)
checkboard,
#endif
NULL,
};
通过硬件设计和驱动代码的编写,完成了对uboot启动后使能lcd驱动电路的工作,接下来就是在uboot中来使用这部分电路和驱动显示图形界面了。
为了能显示开机 logo,可以使用 uboot的CONGIF_LOGO, CONFIG_SPALSH等机制来显示相关像素阵列,也可通过tftp或者cp.b来将要显示的数据拷贝到显示存储器相应位置进行显示。
首先准备一张准备显示的开机 logo,并通过图像取模工具进行取模,然后将取模阵列烧到nor flash中,片选内存的地址为0x10300000开始,图片的大小为 320x240=76800,为 0x12c00。使用命令 cp.b 10300000 30200000 12c00即可正确显示图片了。
通过上述计算结果不难看出,linux uboot下LCD 显示驱动程序的架构相对固定。一般情况下,只需要根据 LCD 屏的电气特性对其中某些参数进行调整。要完成对uboot的移植[4],关键是对linux代码和 uboot代码的熟悉,以及对芯片相关手册的熟悉,若不能很好地理解 LCD 屏用户手册中的参数意义,就很难正确得到linux uboot所需的LCD 显示参数,也就无法正确配置s1d13506寄存器,进而无法正确驱动LCD屏[5]。
[1] 霍东,张国华,亢硕.基于AT91RM9200系统的Boot loader设计[J].通信技术,2010,43(02):105-106,109.
[2] 马忠梅.ARM&Linux嵌入式系统教程[M].北京:北京航空航天大学出版社,2004:102-103.
[3] CORBET J,RUBINI A,Kroah-Hartman G.Linux设备驱动程序[M].魏永明,骆刚,姜君,译.出版地不详:中国电力出版社,2002:8-15.
[4] 龙怡翔,李海涛,胡薇.战术网络中基于策略的网络管理技术研究[J].信息安全与通信保密,2012(07):87-89.
[5] 陆平林.一种基于TMS320C6205 DSP的在线升级方法[J].通信技术,2012,45(02):132-134.