在boot启动阶段,大家都知道他的主要目的就是引导uboot,uboot在引导内核,从而让整个系统运作起来。全志的boot阶段,对应平板这一块,它会驱动LCD,显示一些开机LOGO,这个过程很快,也就1-2秒钟的时间。然而对于车载行业应用来说,可能需要再boot阶段做一些事情。比如,机器冷启动,大家都知道android启动时间还是比较长的,那么怎么使得客户能快速的用上倒车影像的功能呢?这就需要动脑筋了。
/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/edsam49原创,转载请注明出处,谢谢!
/*****************************************************************************************************/
我们可以肯定的是不能等到系统启动完成,应该在最早我们能控制的地方增加我们的特定代码。倒车影像说白了,就是一个视频信号输入到系统,在LCD上再显示出来的事。但是如果要在boot阶段就把视频输入信号正常的显示在LCD上的话,我们需要做什么呢?
首先,我们需要做倒车信号的检测。倒车信号的检测一般来说都是一个GPIO的高低电平来评判,而直接连接倒车线的IC一般是单片机,单片机处理识别以后再告诉主控芯片,告诉的通道其实也就是控制一个GPIO,主控这边来监控这个GPIO的变化,通常是设置成中断模式来检测。全志平台最方便的就是驱动的高度可配置化,非常灵活。我们可以把倒车检测的GPIO配置放在配置文件里面,也就是sys_config.fex文件里面,放在这里面的话就很灵活,不同项目如果使用的IO脚步一样,轻松一配,不用改boot的代码,相当不错。简单示例代码如下:
ret = wBoot_script_parser_fetch("custom_design_cfg", "ReverseCarDetectGPIO", (int *)&gpio_reverse, sizeof(user_gpio_set_t)/4); if(!ret) { gpio_reverse.mul_sel = 0; //set input type gpio_handle = wBoot_GPIO_Request(&gpio_reverse, 1); if(gpio_handle) { gpio_value = wBoot_GPIO_Read_One_PIN_Value(gpio_handle, 0); } } if(1 == gpio_value) { //__inf("Reverse signal come\n"); return 1; } else { __inf("Not enter reverse!!!\n"); return 0; }
接下来我们就要做倒车显示处理了。做过全志平台的人都知道,里面有一个TVD的module来负责视频输入信号的处理。那么,我们就需要把TVD给打通。为了日后的管理方便,在boot里面这些驱动也都已经模块化,因此我们也要取其精华,把好的做法延续下去。就是单独做一个模块化的驱动。那怎么说呢?我教你,告诉你我也是从它已有的代码堆里模仿处理的,但是里面还是有几个地方要注意了,不要全抄了啊,免得老师发现你考试的卷子把别人的名字都抄过来了就不好了。
言归正传。搭框架,学样子。在boot1目录下的driver目录里,先增加一个tvd驱动的目录。把drv_de目录下的make.cfg、makefile、config.lds、magic.c先直接copy过来。先把make.cfg修改一下,主要是改名字,基本如下:
#定义生成的目标文件(输出/本地) LTARGET = drv_tvd.drv TARGET0 = $(WORKSPACEPATH)/wboot/bootfs/drv_tvd.drv TARGET1 = $(LICHEEPATH)/wboot/bootfs/drv_tvd.drv TARGET2 = $(SDKROOT)/pack/chips/sun7i/wboot/bootfs/drv_tvd.drv LOCALTARGET = __image.axf
Makefile写的比较好适配,不用修改。Config.lds这可得说到说到,哥在里面吃了亏的,当时也是直接拷过来的,后面再多个驱动一起运作的时候,工作不正常,查了半天才想起这个破绽,就是程序的加载地址。说出来了,感觉很容易是不是,没搞懂之前那还真不是太好找。就是0x42960000这一串数字,你得看其他模块的这个对应位置的值是多少,根据driver的大小,留一段给上一个程序。Tvd的如下:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) SECTIONS { . = 0x42960000; EGON2_MAGIC : { magic.o(.rodata) } .text : { *(.text) *(.rodata)} .data : { *(.data) } .bss : { *(.bss) } }
剩下的我们就得改magic.c这个文件了,在这个文件里面也就是依葫芦画瓢了。需要改的就是模块的ID编号,其他就是对应改改函数接口的名字,如下:
const eGon2_mod_t modinfo = { {'e','G','o','n','2','d','r','v'}, //.magic 0x01000000, //.version EGON2_EMOD_TYPE_DRV_TVD, //.mod id 0, //.入口地址,驱动(模块)应该填空 { //.mif &DRV_TVD_INIT, &DRV_TVD_EXIT, &DRV_TVD_OPEN, &DRV_TVD_CLOSE, &DRV_TVD_READ, &DRV_TVD_WRITE, &DRV_TVD_IOCTRL, &DRV_TVD_Standby } };
接下来,我们就该写驱动的实体了吧!也即是magic.c里面的各个函数。在这里可以先都只写一行打印信息,其他为空的函数。
格式就按下面的要求来就好了,如下:
struct eGon2_drv_func { int (* eGon2_init )(void ); int (* eGon2_exit )(void ); unsigned int (* eGon2_open )(unsigned int mid, void * open_arg ); int (* eGon2_close )(unsigned int hd ); unsigned int (* eGon2_read )(void *pdata, unsigned int size, unsigned int n , unsigned int hd ); unsigned int (* eGon2_write )(const void *pdata, unsigned int size, unsigned int n , unsigned int hd ); int (* eGon2_ioctl )(unsigned int hd , unsigned int cmd , signed int aux, void *pbuffer); int (* eGon2_standby)(unsigned int cmd ,void *pbuffer ); }; //eGon2里用到的模块数据结构,里面区分了是一个驱动还是一个应用 typedef struct _eGon2_mod_section { char magic[8]; //MAGIC字符,用于标识是eGON2的驱动/应用代码 unsigned int version; //版本数字 unsigned int mod_id; //模块的ID,每个elf都应该有一个模块ID,不论驱动还是应用,且各不相同 int (*main)(int argc, char **argv); //pcb里第一个任务的首地址 struct eGon2_drv_func demo_func; //驱动函数列表,列出了驱动所必要的7个函数 } eGon2_mod_t;
然后编译一下整个模块,可以加到boot的整体编译里面,也就是在boot目录下的Makefile 里面增加一行 “make -Cboot1/driver/drv_tvd -j8”即可。这样make一下,就会去编译这个新增加的模块,编译没有问题就会有一个二进制文件出来。
这样整个驱动模块的框架是已经搭建好了。可以在bootmain里面去调用这个驱动了。加载驱动也是用的boot框架代码里支持的wBoot_driver_install("c:\\drv_tvd.drv");, 当然还有其他ioctl,close这些接口,就不详细在此描述。本文只是重点讲述模块的生成。关于模块的使用,已经TVD怎么工作的,显示控制怎么管理,笔者计划在下一篇文章中再做叙述,敬请关注。