MTK 功能机 创建我的应用

开始

  • Hello,World
main()
{
    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 mmi_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));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值