MTK 功能机 创建我的应用
2016-08-15 15:33
393 查看
开始
Hello,Worldmain() { printf("Hello,World"); }
程序入口
嵌入式操作系统的应用程序通常是与整个系统固定在一起。MMI可以看成一个大的程序,我们写的小程序就是大程序的分支。将自己写的程序在大程序中添加新的入口。目前先借用已有的程序入口goto_main_menu,主菜单的入口函数。
void mmi_myapp_entry(void) { //我们的程序由此开始 }
void goto_main_menu(void) { //将主菜单切换为我们的程序 mmi_myapp_entry(); return; ...... }
打印文本
显示文本串的函数原型如下: void(*gui_print_text)(UI_string_type_text); 所以我们的程序显示字符串代码如下: void mmi_myapp_entry(void) { //先清屏,去掉之前显示的内容 clear_screen(); //设置文本输出起始位置(文本属性的设置是对于整个系统,因此每次输出文本时都需要重新设置) gui_move_text_cursor(50,100); //设置文本颜色 gui_set_text_color(UI_COLOR_RED); //L表示强制将字符串以Uinicode编码输入 gui_print_text(L"Hello,World"); //需要重新刷新屏幕才能显示,参数表示刷新整个屏幕 gui_BLT_double_buffer(0,0,UI_device_width-1,UI_device_height-1); }
屏幕
手机的屏幕类似于windows中的窗口概念,即一个应用程序在某个状态时的显示模式及交互方式,但我们的屏幕是独占整个显示系统及交互系统,任何时候只能由一个屏幕来控制。一个程序可能由多个屏幕组成,类似于windows中一个程序可能有多个窗口。当前我们的写的程序一直只有一个屏幕显示。新的屏幕
前面我们已经将Hello,World在屏幕上输出了,但是只要稍微等上一会,就会发现屏幕上多出了一些主页面上的东西显示出来了,这是因为没有退出上一个程序。
void mmi_myapp_entry(void) { //退出上一个程序,避免局部可能显示在新的页面中 EntryNewScreen(MAIN_MENU_SCREENID,NULL,NULL,NULL); //去掉状态栏 entry_full_screen(); clear_screen(); gui_move_text_cursor(50,100); gui_set_text_color(UI_COLOR_RED); gui_print_text(L"Hello,World"); gui_BLT_double_buffer(0,0,UI_device_width-1,UI_device_height-1); }
屏幕历史
当我们的程序在运行时,如果有新的程序出来(如插上充电器时的提示框),会强制退出我们自己的程序,但新的程序结束后会发现直接返回了Idle。为了避免这个问题,系统建立了一套屏幕历史管理机制。我们只需要在调用EntryNewScreen时传入我们新的屏幕的ID以及入口函数,那么系统下次调用EntryNewScreen是会我们的屏幕加入历史记录,当新的屏幕退出后,系统会将我们的屏幕从历史中弹出并显示。
EntryNewScreen的函数原型 U8 EntryNewScreen(U16 newscrnID,FuncPtr newExitHandler,FuncPtr newEntryHandler,void *peerBuf) 参数1:新显示屏幕的序号 参数2:屏幕退出时会调用的函数 参数3:新屏幕的入口函数 参数4:暂不使用 void mmi_myapp_entry(void) { EntryNewScreen(MAIN_MENU_SCREENID,NULL,mmi_myapp_entry,NULL); entry_full_screen(); clear_screen(); gui_move_text_cursor(50,100); gui_set_text_color(UI_COLOR_RED); gui_print_text(L"Hello,World"); gui_BLT_double_buffer(0,0,UI_device_width-1,UI_device_height-1); }
手动加入历史
当EntryNewScreen的第三个参数为空时,系统就不会自动加入历史中,我们也可以在mmi_myapp_exit中手动添加进历史
void mmi_myapp_exit(void) { history currHistory; S16 nHistory=0; currHistory.scrnID = MAIN_MENU_SCREENID; currHistory.entryFuncPtr = mmi_myapp_entry; pfnUnicodeStrcpy((S8*)currHistory.inputBuffer,(S8*)&nHistory); AddHistory(currHistory); } void mmi_myapp_entry(void) { EntryNewScreen(MAIN_MENU_SCREENID,mmi_myapp_exit,NULL,NULL); entry_full_screen(); clear_screen(); gui_move_text_cursor(50,100); gui_set_text_color(UI_COLOR_RED); gui_print_text(L"Hello,World"); gui_BLT_double_buffer(0,0,UI_device_width-1,UI_device_height-1); }
返回最近的屏幕
有进入就有退出,退出屏幕也需要手动执行。我们通常用GoBackHistory通知系统将历史中最后一次显示的屏幕弹出来。
void mmi_myapp_entry(void) { EntryNewScreen(MAIN_MENU_SCREENID,NULL,mmi_myapp_entry,NULL); entry_full_screen(); clear_screen(); gui_move_text_cursor(50,100); gui_set_text_color(UI_COLOR_RED); gui_print_text(L"Hello,World"); gui_BLT_double_buffer(0,0,UI_device_width-1,UI_device_height-1); //我们通常将右软键设为返回最近显示的屏幕 SetKeyHandler(GoBackHistory,KEY_RSK,KEY_EVENT_UP); }
程序
新程序为了将自己的程序规范化,我们需要将自己的程序独立出来。
1. 代码独立:就是将程序代码放到单独的文件中。
2. 数据独立:就是资源独立(下一部分介绍)。
在修改之前,我们先将自己的程序命名为”MyApp”。
添加程序文件
一般新加的MMI程序都放到plutommi\MMI下面,创建如下目录: 程序总目录 plutommi\MMI\MyApp 源文件目录 plutommi\MMI\MyApp\MyAppSrc MyAppSrc.c是本程序的主源文件,需要将主函数mmi_myapp_entry和mmi_myapp_exit从MainMenu.c中转移到此处。 头文件目录 plutommi\MMI\MyApp\MyAppInc MyAppProt.h---放本程序所有函数声明,但此头文件只被本程序的源文件所加载 #ifndef _MYAPPPORT_H; #define _MYAPPPORT_H; #include "MyAppGprot.h" extern void mmi_myapp_exit(void); extern void mmi_myapp_entry(void); #endif/*_MYAPPPORT_H*/ MyAppTypes.h---用来放本程序所需的类型,结构,常量定义。 MyAppGprot.h---也是用来放函数声明,但此头文件是被别的程序加载的,此文件所声明的都是对外的接口。 #ifndef _MYAPPGPORT_H; #define _MYAPPGPORT_H; #define "PixtelDataTypes.h" #include "MyAppTypes.h" extern void m e4a4 mi_myapp_entry(void); #endif/*_MYAPPPORT_H*/ MyAppDefs.h---用来放本程序的资源ID定义。 typedef enmu { SCR_MYAPP_MAIN=MYAPP_BASE+1, }SCREENID_LIST_MYAPP;
将文件加入项目
文件需要使用ARM编译器,为了将文件加入项目,必须手动将新文件路径添加到以下几个表文件中:
修改make\plutommi\下的三个文件夹: 1. plutommi.li:此文件用来指明MMI所要编译的所有源文件。在文件中添加 plutommi\MMI\MyApp\MyAppSrc\MyAppSrc.c 2. plutommi.inc:此文件用来指明MMI所有头文件所在目录(因为源文件中加载头文件时都没有路径,所以需要在此申明) plutommi\MMI\MyApp\MyAppInc 3. plutommi.pth:此文件用来指明MMI所有源文件所在目录。 plutommi\MMI\MyApp\MyAppSrc
程序开关
为了尽量精简最终生成的烧录程序,我们一般都会给每个小程序加上自己的编译开关,并将自己程序所有的代码都包含进编译开关。
MMI的编译开关一般都放到文件plutommi\Customer\CustResource\PLUTO_MMI_featuresPLUTO.h中,按照如下方式添加:
/************************** [Application]:MyApp **************************/ #define _MMI_MYAPP_ 如下所示,我们一般也会将入口加入编译开关: #include "MyAppGprot.h" void goto_main_menu(void) { #ifdef _MMI_MYAPP_ //将主菜单切换成我们的程序: mmi_myapp_entry(void)(); return; #endif/*_MMI_MYAPP_*/ }
资源
资源介绍通常将程序使用的数据分为动态数据与静态数据两种,动态数据即程序运行时才能知道的数据,一般是由程序动态生成。而静态数据是固定的,在编译时即可将其转换成其他二进制数据,保存到最终烧录的文件中,静态数据也称为资源。
常见资源类型:字串,图像,菜单,字库,主题,声音,以及某些程序单独使用的资源。 添加新的程序一般只会修改其中三种:字串,图像,菜单。 添加一项资源通常分为三步:原料,ID,装载。 原料:原材料,如图像就是准备一张新图,字串就是各种语言的Unicode编码。 ID:资源项的别名,程序只能通过ID来获取资源(ID一般定义在XXDefs.h中). 装载:装载在编译目标烧录文件之前就会被执行,其目的有两个:一是将原材料转换成二进制数据,二是生成将ID与二进制数据联系起来的映射表。 资源装载预编译程序是plutommi\Custommer\ResGenerator\mtk_resgenerator.exe,这个程序在每次编译目标烧录文件之前临时编译生成的。下面的修改基本与这个程序有关。
添加文件
在plutommi\Custommer\CustResource\PLUTO_MMI\Res_MMI下创建一个新文件: plutommi\Custommer\CustResource\PLUTO_MMI\Res_MMI\Res_Myapp.c 并在文件中添加一个函数PopulateMyAppRes: #include "StdC.h" #ifdef DEVELOPER_BUILD_FIRST_PASS ... void PopulateMyAppRes(void) { } #endif/*DEVELOPER_BUILD_FIRST_PASS*/ 此文件用在预编译时装载资源,每个程序都有自己的资源装载文件,这些文件与plutommi\Custommer\ResGenerator\mtk_resgenerator.exe一起生成mtk_resgenerator.exe并在Windows下被执行。
修改Makefile,PopulateRes.c,readexcel.c
修改Makefile: 在文件plutommi\Custommer\ResGenerator\Makefile中添加如下两行: -I"../../MMI/MainMenu/MainMenuInc" \ -I"../../MMI/MyApp/MyAppInc" \ 此文件是资源装载预编译程序的Makefile。 修改PopulateRes.c: 在plutommi\MMI\Resource\PopulateRes.c ... extern void PopulateMainDemoRes(void); extern void PopulateMyAppRes(void); ... void PopulateResData(void) { ... PRINT_INFORMATION(("Populating Main Menu Resource\n")); PopulateMainMenuRes(); PRINT_INFORMATION(("Populating MyApp Resource\n")); PopulateMyAppRes(); ... } mtk_resgenerator.exe在执行时会呼叫到这里面的PopulateResData。 修改readexcel.c 在plutommi\Customer\ResGenerator\readexcel.c(找不到此文件,可省略该步骤) ... #include "SettingDefs.h" #ifdef _MMI_MYAPP_ #include "MyAppDefs.h" #endif /*_MMI_MYAPP_*/ ... 字符串资源有自己单独的装载预编译程序readexcel.exe.此程序在mtk_resgenerator.exe呼叫完后会被接着生成并执行。
资源ID
在加ID之前先为本程序添加一个基础ID,因所有的程序资源ID都是各自为政个定义各的,但是这些ID又不能冲突(每种类型的资源ID都是在同一个取值空间),所以我们就用这些基础ID将每个程序ID取值隔离开来。
基础ID统一定义在plutommi\MMI\Inc\MMIDataType.h: ... typef enmu { ... RESOURCE_BASE_RANGE(MAIN_MENU,600), //新增基础ID RESOURCE_BASE_RANGE(MY_APP,100). ... }RESOURCE_BASE_ENUM; ... /*************************************** *Main Menu ***************************************/ #define MAIN_MENU_BASE ((U16)RESOURCE_BASE_MAIN_MENU) #define MAIN_MENU_BASE_MAX ((U16)RESOURCE_BASE_MAIN_MENU_END) RESOURCE_BASE_TABLE_ITEM(MAIN_MENU) /*************************************** *MyApp ***************************************/ #define MYAPP_BASE ((U16)RESOURCE_BASE_MYAPP) #define MYAPP_BASE_MAX ((U16)RESOURCE_BASE_MYAPP_END) RESOURCE_BASE_TABLEITEM(MYAPP) ... 重点是在RESOURCE_BASE_RANGE(MYAPP,100)这里的100表示我们的程序ID定义不会超过100个(是任何一种类型的资源ID数量都不会超过100,不是所有加起来)。 还有一种资源是跟屏幕历史控制有关,即前面所讲的屏幕的序号,前面没有定义就用的MainMenu的屏幕序号,下面我们就给自己的程序加上屏幕序号(也定义在MyAppDefs.h中): typedef enmu { SCR_MYAPP_MAIN=MYAPP_BASE+1, }SCREENID_LIST_MYAPP; 下面将我们主程序改过来: void mmi_myapp_entry(void) { EntryNewScreen(SCR_MYAPP_MAIN,NULL,mmmi_myapp_entry,NULL); ... }
字串资源
将字串”Hello,World”转移到资源中去,并为其添加多国语言版本。字串ID
先在MyAppDefs.h中添加字串ID: typedef enum { STR_MYAPP_HELLO=MYAPP_BASE+1, }STRINGID_LIST_MYAPP;
字串资源
在plutommi\Customer\CustResource\PLUTO_MMI\ref_list.txt中添加一行 (注意添加字符串资源文件时参考已有的字串再进行添加) 此文件中将字串资源与ID对应起来
字串装载
在函数PopulateMyAppRes中添加一行: void PopulateMyAppRes(void) { //字串ID,,默认显示,字串描述 ADD_APPLICATION_STRING2(STR_MYAPP_HELLO,"Hello,world","MyApp."); } 宏ADD_APPLICATION_STRING2用来装载字串
字串读取
使用函数GetString可将字串资源读取出来: void mmi_myapp_entry(void) { ..... gui_print_text((UI_string_type)GetString(STR_MYAPP_HELLO)); ..... }
菜单资源
添加新的菜单项,新菜单放在[MainMenu]->[Organizer]->[Hello,World]菜单项ID
//所有菜单的ID都放在头文件plutommi\MMI\Inc\Global\MenuItems.h enum GLOBALMENUITEMSID { IDLE SCREEN_MENU_ID = 1; ..... ..... //加入自己的菜单ID MENU_ID_MYAPP_HELLO, MENU_ID_DEVAPP_START, MENNU_ID_DEVAPP_END = MENU_ID_DEVAPP_START + 100, MAX_MENU_ITEMS_VALUE, MENU_ITEM_END }; //新菜单ID必须放在MAX_MENU_ITEMS_VALUE之前
菜单加载
首先,我们需要将MENU_ID_MYAPP_HELLO加入到Organizer的下级列表中,按照如下方式修改Res_MainMenu.c(main menu及main menu下一级子菜单都在此文件中加载):
typedef enum { #if defined(_MMI_CALENDAR_) ORG_ENUM_CALRNDAR, #endif #if defined(_MMI_TODOLIST) ORG_ENUM_TODOLIST, #endif ORG_ENUM_ALRAM, #ifdefined(_MMI_WORLD_CLOCK_) ORG_ENUM_WORLDCLOCK, #endif #ifdef _MMI_MESSAGES_CLUB_ ORG_ENUM_SERVICE, #endif //添加如下代码 #ifdef _MMI_MYAPP_ MENU_ENUM_MYAPP_HELLO, #endif/*_MMI_MYAPP_*/ ORG_ENUM_TOTAL }OrganizerMenu; ..... ..... #if defined(_MMI_VERSION_2_) void PopulateMainMenuRes(void) { ..... /*organizer*/ //用来装载菜单资源 //参数1:新加菜单ID,参数2:新菜单上一级菜单ID,参数3:子菜单总个数,参数4到参数N:每个子菜单项ID //参数N+1:隐藏属性,一般设为SHOW,参数N+2:菜单项转移属性,参数N+3:下级菜单的显示风格,参数N+4:此菜单项显示文本串ID,参数N+5:此菜单的小图标ID ADD_APPLICATION_MENUITEM((MAIN_MENU_ORGANIZER_MENUID,IDLE_SCREEN_MENU_ID,ORG_ENUM_TOTAL, #if defined(_MMI_CALENDAR_) ORGANIZER_CALENDAR_MENU, #endif #if defined(_MMI_TODOLIST_) ORGANIZER_TODOLIST_MENU, #endif ORGANIZER_ALARM_MENU, #if defined(_MMI_WORLD_CLOCK_) ORGANIZER_WORLDCLOCK_MENU, #endif #ifdef _MMI_MESSAGES_CLUB_ EXTRA_SHORTCUTS_MENUID, #endif //添加如下代码 #ifdef _MMI_MYAPP_ MENU_ID_MYAPP_HELLO, #endif SHOW, MOVEABLEWITHINPARENT|INSERTABLE, DISP_LIST, MAIN_MENU_ORGANIZER_TEXT, MAIN_MENU_ORGANIZER_ICON )); ..... } 然后加载MENU_ID_MYAPP_HELLO本身: void PopulateMyAPpRes(void) { ADD_APPLICATION_STRING2(STR_MYAPP_HELLO,"Hello,World","MyApp."); ADD_APPLICATION_MENUITEM((MENU_ID_MYAPP_HELLO,MAIN_MENU_ORGANIZER_MENUID,0,SHOW,SHORTCUTABLE,DISP_LIST,STR_MYAPP_HELLO,0)) } //MENU_ID_MYAPP_HELLO没有下级菜单,所以第三个参数设为0
由于每个菜单项的行为都由菜单项自己控制,系统只能进行高亮显示时发通知过来。每个菜单项都要在开机时告知系统将由哪个函数来接受通知,SetHiliteHandler就是用来做此事的(参数1:菜单ID,参数2:接受通知的函数指针)。我们通常会为每个程序建一个初始化函数,此函数只在开机时运行一次,如下面代码所示的mmi_myapp_init,在此函数中我们会将本程序所有菜单都注册一遍。
在菜单项接受通知函数中,我们通常所做的只有一件事,更改左右软键的响应函数。左软键必须修改,否则菜单项无法进入下一级菜单,右软键可有可无(一般程序都是GOBackHistory)。
修改见MyAppSrc.c .... void mmi_myapp_hilite_hello(void) { SetLeftSoftkeyFunction(mmi_myapp_entry,KEY_EVENT_UP); } void mmi_myapp_init(void) { SetHiliteHandler(MENU_ID_MYAPP_HELLO,mmi_myapp_hilite_hello); } //同时这些函数要在头文件中声明 MyAppProt.h ...... extern void mmi_myapp_hilite_hello(void); ...... MyAppGprot.h ..... extern void mmi_myapp_init(void); ..... //需要开机初始化函数mmi_myapp_init,在MMITask.c进行如下修改 ..... #ifdef _MMI_MYAPP_ #include "MyAppGprot.h" #endif ..... void InitAllApplications(void) { ..... #ifdef _MMI_MYAPP_ mmi_myapp_init(); #endif ... }
图像资源
图像ID先在MyAppDefs.h中添加字串ID: typedef enum { IMG_MYAPP_HELLO = MYAPP_BASE+1, }IMAGEID_LIST_MYAPP;
新加目录并加入图片
通常在plutommi\Customer\Images\目录下 找到对应屏幕尺寸(例如176X220),找到主屏文件夹MainLCD,创建子文件夹MyApp plutommi\Customer\Images\PLUTO176X220\MainLCD\MyApp 在该文件夹下添加新图片IMG_MYAPP.bmp
装载图片
宏ADD_APPLICATION_IMAGE2用来加载图像资源 void PopulateMyAppRes(void) { ..... //参数1:图像ID,参数2:图像路径(路径前加宏CUSTIMG_PATH在运行时自动转换为相应的图像根目录plutommi\Customer\Images\PLUTO176X220\,参数3:对图像的描述) ADD_APPLICATION_IMAGE2(IMG_MYAPP_HELLO,CUSTIMG_PATH"\\\\MainLCD\\\\MyApp\\\\IMG_MyApp.bmp","Hello World"); ..... }
将图片作为菜单ICON
void PopulateMyAppRes(void) { ..... //将最后一个参数改为图像资源ID ADD_APPLICATION_MENUITEM((MENU_IDMY_APP_HELLO,MAIN_MENU_ORGANIZER_MENUID,0,SHOW,SHORTCUTABLE,DISP_LIST,STR_MYAPP_HELLO,IMG_MYAPP_HELLO)); }
相关文章推荐
- 原型图工具功能比拼1
- CTF入门指南1(Capture the flag)
- malloc了多个结构体长度的内存后,怎样操作每个结构体
- 线段树总结!
- 这是个巧妙的表达式: a ˆ= b ˆ= a ˆ= b 它不需要临时变量就可 以交换 a 和 b 的值。
- 快速幂(模板)
- ThinkPHP 遇到问题:无法加载数据库驱动: Think\Db\Driver\
- java的命名规范
- poj 3190 Stall Reservations(区间贪心,优先队列)
- 数字生成游戏_纪中2570_bfs
- [图解tensorflow源码] 入门准备工作
- CodeForce 510 B
- 验证签名机制——java示例
- 【985系列】985的买饮料难题
- iptables 详解
- 命名空间、闭包、上下文管理器、鸭子类型与多态(10)
- spring入门(一)
- Modular Programming with JavaScript-Packt Publishing 2016(读书笔记)
- 实例:ABAP权限对象设计与权限检查的实现(详细)
- 二分图最大匹配模板