当前位置:首页 >> 信息与通信 >>

MTK新手教程


一、

开发环境设立 开发环境设立

以下表述中提到的相关压缩包或安装文件到华禹 ftp 服务器下载,部分文件要到群共享 里面下,那里是最新的。 华禹 ftp 服务器:IP:220.113.15.15,帐号为 study-bbs.com,密码为 study-bbs0304 P1300 的 QQ 群号: 15762255 http://www

.study-bbs.com/thread-27527-1-1.html P1300_Build_Guide.rar---------------------来自 ftp 服务器, “华禹\MTK 相关部分”目录下 手机开发板 C 语言开发视频.rar---------来自 ftp 服务器, “华禹\MTK 相关部分”目录下 huayu109_ads1.2.rar-------来自 ftp 服务器,“华禹\旋风 001 手机模块\1.工具类”目录下 huayu102_ADS12_Patch_Windows.rar-----------------------------------------------------------同上 huayu106_perlzip.rar--------------------------------------------------------------------------------同上 huayu108_Source Insight3.5.rar--------------------------------------------------------------------同上 huayu101_p1300 软件使用工具及开发指南.rar------------------------------------------------同上 huayu201_P1300_V1.7_Release.rar------------------------------------------------文件来自群共享 huayu203_P1300_V1.7_Release_patch.rar----------------------------------------文件来自群共享 FlashTool_v3.0844.00.rar------------------------------------------------------------文件来自群共享

1、开发主机要求及所需工具: 、开发主机要求及所需工具: 机要求及所需工具 操作系统: 操作系统:Windows 2000, WinXP. 推荐 Windows 2000 with SP2 or later. 编 译 器:ADS (Arm Developer Suite) v1.2 [Build 842] Perl 解析:ActivePerl, 推荐使用 ActivePerl 5.6.1 版本 解析: 仿真工具: 仿真工具:MTK PC Simulator 跟踪工具: 跟踪工具:Catcher_L1 v3.10.01,从串口输出调试信息,只做普通 UI 应用的话,大 致不会用到,用 PC 仿真工具就好了 烧录工具: 烧录工具:FlashTool_UI v3.1.05 或 FlashTool_v3.0844.00 其 他:PL2303_Driver_XP2K v204102,这是下载线用到的 USB 串口驱动,以

前机器上装过 PL2303 驱动的就不必再装了? 2、开发环境建立过程: 、开发环境建立过程: 新建一个目录, 英文的好了, 否则有些工具不认识中文路径, 这里以 E:\MTK_P1300 为例说明, 下面我用文字和图片简单说明下安装过程, 更详细的安装过程可以看压缩文 件“手机开发板 C 语言开发视频.rar”中的 Flash 文件“P1300_Build_Env.swf”。 1) 安装 ADS1.2 解压文件 huayu109_ads1.2.rar 到某个目录,比如说解压到“E:\MTK_P1300”,解压 完后运行“E:\MTK_P1300\ADS1.2\Setup.exe”,一路按下一步,最后安装 License 时选

定文件“E:\MTK_P1300\ADS1.2\CRACK\ license.dat”,然后接着一路下一步直到安装 完。安装完后删掉目录“E:\MTK_P1300\ADS1.2\”,节省磁盘空间。 2) 安装 ADS 补丁 运行 huayu102_ADS12_Patch_Windows.rar 压缩文件中的 ADS_Patch_Window.exe, 这是个自解压的压缩文件,选 Unzip 解压到上一步 ADS 的安装目录(缺省是 C:\Program Files\ARM\ADSv1_2),解压过程中如果问到是否覆盖,全部选覆盖好了。 3) 安装脚本解释器 Perl 直接运行压缩包 huayu106_perlzip.rar 里面扩展名为 msi 的安装文件, 缺省安装就好, 一路 Next,呵呵。 上述几步完成后,编译环境基本建立,此时可以开一个 dos 窗(开始菜单->运行-> 输入 cmd->确定)看一下,安装正常应该能顺利执行以下命令,如下图:

注意看版本号,应该是 ADS1.2 [Build 842],不是的话可能没打补丁,或补丁打的位置 不对,请看第 2 步 ADS 补丁部分的说明并重新打一次,直到版本号对为止,哈哈。

这是 perl 解释器的运行画面。 注 1:上述安装过程会自动创建编译环境所需的环境变量,如果发现编译不正常了,可 : 能是安装了其他编译工具导致冲突,这时建议查看下系统环境变量,把 Perl 和 ADS 的安装 目录调到最前面,同时从 path 中去掉可能会产生冲突的编译工具链的路径(例如 winavr),如 下图所示(我是安装到 D 盘滴,缺省是 C 盘,不过我 C 盘几乎被我塞满了):

如果嫌改 path 麻烦(因为要用到其他编译工具链的时候还得改回去),还有一种办 法,就是改 make.bat 批处理,具体见注 4 中相关说明。 注 2: 如果安装 ADS 时,改变了默认安装路径,需要修改源码中的设置,源码目录 : 树解压过程见模拟器编译环节相关介绍。 make\Option.mak ---------------------------------ifeq ($(strip $(COMPILER)),ADS) DIR_ARM = c:\progra~1\arm\adsv1_2 # 修改这里

DIR_ARM := $(strip $(DIR_ARM)) DIR_TOOL DIR_ARMLIB DIR_ARMINC endif -----------------------------------------------比如改装到 D 盘了,这里把红色部分“c”改成“d”就好了 = $(DIR_ARM)\bin = $(DIR_ARM)\lib = $(DIR_ARM)\include

4)

代码编辑环境 运行压缩文件 huayu108_Source Insight3.5.rar 中的安装文件安装即可。 至于编辑器,

这个看个人喜好了,不过 Source Inside 看代码蛮方便的。 5) PC 机模拟器 模拟器 MTK PC Simulator 是用来在 PC 上仿真调试用的,要安装 VC6 SP6(同时要 安装 Uuicode 静态和动态库, 没有装 Unicode 库的可以用 “huayu103_MTK 模拟器 DLL 补丁.zip”中的库,拷到系统目录“%windir%\system32”下即可)。我机器上的 VC 是有 装 Unicode 库的,没装过 Unicode 库的如果在模拟器编译或运行中出现问题,建议重装 下 VC6,安装时勾选 Unicode 库,并打上相关补丁到 SP6。 模拟器能模拟真机的大部分行为, 这样能给调试带来很大便利, 不需要每次改动都 要烧录。 模拟器要从源码编译,解压压缩包“huayu201_P1300_V1.7_Release.rar”里面的文 件“HUAYU_P1300_V1.7_Release.rar”到目录“E:\MTK_P1300”,此时会出现目录 “E:\MTK_P1300\P1300_V1.7_Release”,这就是 P1300 的代码目录树了;接着打上最 新的补丁,解压压缩包“huayu203_P1300_V1.7_Release_patch.rar”里面的压缩文件 “HUAYU_P1300_V1.7_Release_patch.rar”到目录 “E:\MTK_P1300\P1300_V1.7_Release”,解压时选择全覆盖以替换被修改过的文件, 打补丁之前对目录树里面的文件进行修改过的,需要手动再加上去。 编译 PC 仿真器之前需要先把刚才上面得到的目录树 build 一次,开一个 dos 窗, 盘符和目录转到代码目录树对应的盘符和目录,例如: E: cd E:\MTK_P1300\P1300_V1.7_Release\ make new 注 3:第一次 make 的时候要用“make new”,make new 的时间比较长(慢的机器 : 搞不好要 2 小时以上),编译过程中间会出现一些文件找不到的信息,只要编译过程没 意外终止,那些信息可以忽略。编译日志文件位置: build\NEOTEL25_06B\log\,要查看 编 译 过 程 有 啥 问 题 就 看 这 个 目 录 下 的 文 件 了 ; 生 成 的 bin 文 件 位 置 : build\NEOTEL25_06B\*.bin,下载烧录就是烧 bin 文件。 这里顺带介绍下 build 的命令格式: make new make resgen make remake make viewlog xxx make 清除后重新编译整个项目 重新生成资源 重新编译链接项目 查看模块 xxx 的编译日志 查看可以用 make 选项帮助信息

注 4:如果机器上装了其他 gcc 编译工具链(比如 winavr 之类)的话可能要手动改下 : Path , 否 则 编 译 会 出 错 , 修 改 Path 的 方 法 见 注 1 , 也 可 以 修 改 批 处 理 文 件

“E:\MTK_P1300\P1300_V1.7_Release\make.bat”,在“perl make2.pl %*”之前加上下 面两句: set PERL5LIB=D:\perl\lib set PATH=D:\perl\bin;d:\progra~1\arm\adsv1_2\bin;E:\MTK_P1300\P1300_V1.7_Release\tools ;c:\windows\system32;c:\windows;c:\windows\system 注意,上面的路径是我机器上的,不是缺省安装路径, 注意,上面的路径是我机器上的,不是缺省安装路径,要根据自己的实际安装路 径做修改。 径做修改。 下 面 开 始 编 译 PC 仿 真 器 了 , 用 VC6 打 开 工 程 文 件

“E:\MTK_P1300\P1300_V1.7_Release\plutommi\mmi\ PC_Simulator.dsw”,然后开始编 译,编译时间比较长,具体看机器配置了,这时又可以干点别的啥了,呵呵。 因为仿真器工程涉及文件较多,编译费时,建议编译过程中电脑上少开窗口,特别 是网络类的,如 QQ,有时会发现开 QQ 后,编译过程中 VC 会挂死,呵呵。有可能是 开 QQ 后防火墙过滤网络数据占用较多系统资源, 如果发现 VC 编译特别慢或干脆挂死, 可以重启动一下电脑,只开必要的窗口,然后开始编译。 如果编译过程中发现怪异的问题,如: incomingstringiddef.h(120) : error C2059: syntax error : 'constant' 或者 error C2065: 'STR_CM_REDIAL' : undeclared identifier 之类 这个时侯你可能需要看一下你 VC 相关路径的设置顺序,如下图:

把 VC 原本的头文件路径调整到最前面,我刚开始编译的时候 SDK 的头文件在前 面,编译总是通不过,改一下就好了,库也一样都调整下比较保险,呵呵。 6) Tracer 跟踪工具 解压文件 P1300_Build_Guide.rar 到 E:\MTK_P1300,然后在文件管理器进入目录 “E:\MTK_P1300\P1300_Build_Guide”,接着解压 Catcher_L1_v3.10.01.zip 到当前目录 下 的 Catcher_L1_v3.10.01 子 目 录 , 进 入 子 目 录 Catcher_L1_v3.10.01 , 发 送 一 个 Catcher.exe 的快捷方式到桌面。 在需要用到 TRACE 的时候,在代码中使用函数 void kal_prompt_trace(module_type mod_id, const kal_char *fmt,...); 打印需要查看的 信息,使用 Catcher 跟踪查看。 这个有点类似 Linux 的 Kernel Debug 工具,具体使用方法见文档《cather 使用手 册.doc》,这个文档可在压缩包“huayu101_p1300 软件使用工具及开发指南.rar”里面找到。

7)

烧录工具 解压文件 “E:\MTK_P1300\P1300_Build_Guide\ FlashTool_UI_exe_v3.1.05.zip” 到当

前目录,然后进入目录“E:\MTK_P1300\P1300_Build_Guide\FlashTool_v3.1.05”,发送一 个 Flash_tool.exe 的快捷方式到桌面好了。 还有个下载工具 FlashTool_v3.0844.00.rar,也是不用安装,解压后直接用的,据说 烧录速度比上面那个快。 具体使用方法见文档《flash_tool_MT 平台使用教程.doc》,这个文档可在压缩包 “huayu101_p1300 软件使用工具及开发指南.rar”里面找到。

8)

USB 串口下载线驱动 运行压缩文件 “E:\MTK_P1300\P1300_Build_Guide\ PL2303_Driver_XP2K_v204102.zip” 里面的可 执行文件进行安装,一路下一步,这里不再赘述。

一、

Hello World

上面一节我们建立好了开发环境, 现在是时候实战一把了, 在进行实质性的项目开发之 前,我们先来了解下 P1300 上写程序的一般性做法。从“Hello World”开始吧,有句笑话 讲:“会编程就是会 Hello World,编程高手就是会写很多个 Hello World”,虽然是句笑话, 但是细想也是有道理的,麻雀虽小五脏俱全嘛,掌握了框架,剩下的就无非是些编程技巧的 问题了。这和建房子一个道理,地基打的深不深,框架建的是否合理,决定房屋总体质量好

不好,其他就是装修的功夫了,当然还有水电线路的铺设,这涉及到习惯和技巧了,设计不 好会漏水漏电,哈哈。扯远了,下面开始 Hello World。 下面为描述上的方便,我们将 Hello World 称为一个模块。首先建立新的模块目录,之 后把模块相关文件都放到这个目录下统一进行管理, 新的模块一般放到 plutommi\MMI 下面, 这里我们新建一个目录“HelloWorld”,然后在模块目录“HelloWorld”下再建三个子目录: “Inc”、“Src”和“Res”,分别用来存放模块的头文件、源文件和资源文件,目录结构 如下图所示:

接 着 我 们 在 Inc 下 创 建 几 个 头 文 件 : HelloWorldGprot.h , HelloWorldProt.h, HelloWorldTypes.h, HelloWorldDefs.h, 再在 Src 下创建一个源文件:HelloWorld.c,关于文 件名和函数名等的命名标准每个公司及个人各有不同, 统一就好, 否则在进行大的项目开发 时彼此协同会遇到问题,有时候弄不好光这些问题就能折腾你半天甚至数日或数月,呵呵。 上面几个文件的作用我大致讲下: HelloWorldGprot.h 接口就不要放这里了 HelloWorldProt.h HelloWorldTypes.h HelloWorldDefs.h HelloWorld.c 模块内部接口,供模块内部调用的函数原型在此申明 本模块用到的一些常量、自定义数据类型、结构的定义 本模块用到的资源 ID 定义 模块功能函数的实现部分 模块对外接口,供模块外部调用的函数原型在此申明,模块内部

头文件 HelloWorldGprot.h 的内容大致如下: /*************************************************************************/ #ifndef __HELLOWORLD_GPROT_H__

#define __HELLOWORLD_GPROT_H__ #include "PixtelDataTypes.h" #include "HelloWorldTypes.h"

extern */ #endif

void mmi_HelloWorld_entry(void);/* 模块入口,理解成 dos 程序的 main 好了

/* __HELLOWORLD_GPROT_H__ */

/*************************************************************************/

头文件 HelloWorldProt.h 的内容的大致如下: /*************************************************************************/ #ifndef __HELLOWORLD_PROT_H__

#define __HELLOWORLD_PROT_H__

#include "HelloWorldGprot.h"

extern void mmi_HelloWorld_entry(void); /* 本模块主界面入口例程 */ extern void mmi_HelloWorld_exit(void); /* 本模块主界面退出例程 */

#endif

/* __HELLOWORLD_PROT_H__ */

/*************************************************************************/ 其他几个文件的内容因模块功能而异,这里先略过,后面再讲。 紧接着,我们要修改的相关系统文件,使这个模块成为整个项目的一部分,需要修改的 系统文件如下: make\plutommi\plutommi.inc make\plutommi\plutommi.pth make\plutommi\plutommi.lis 所有 mmi 部分的头文件所在目录的相对路径列表 所有 mmi 部分的源文件所在目录的相对路径列表 所有 mmi 部分的源文件(相对路径)列表

在上述 3 个文件里面分别加上我们模块的对应内容,就现在讲的 HelloWorld 而言,所 加内容如下: make\plutommi\plutommi.inc 文件追加一行“plutommi\mmi\HelloWorld\Inc” make\plutommi\plutommi.pth 文件追加一行“plutommi\mmi\HelloWorld\Src” make\plutommi\plutommi.lis 文件追加一行 “plutommi\mmi\HelloWorld\Src\HelloWorld.c” 再接着,我们需要增加一个开关,以决定这个模块是否成为最终发布版本的一部分,这 实际上是一个编译开关, 需要修改系统文件及相关的模块源文件, 需要修改的系统文件包括:

plutommi\Customer\CustResource\PLUTO_MMI\MMI_featuresPLUTO.h , 这 里 我 们 用 名字为“__MMI_HELLOWORLD_ENABLED__”这个开关好了,在这个文件里面追加 一行: #define __MMI_HELLOWORLD_ENABLED__,同时在相关源文件里面用: #ifdef #endif 来框住相关代码行就 OK 了,当不打算将这个模块发布的时候,注释掉“#define __MMI_HELLOWORLD_ENABLED__”这一句重新 make 一次就好了。 下面讲具体实现的部分, 为了简化描述, 我们暂时先将我们的模块入口挂接到主菜单入 口处(后面讲到资源的部分可将入口移到某个新建的子菜单下),修改下文件 “plutommi\mmi\mainmenu\mainmenusrc\MainMenu.c”中的 goto_main_menu 函数,如下所 示红色字体部分: void goto_main_menu(void) { #ifdef __MMI_HELLOWORLD_ENABLED__ __MMI_HELLOWORLD_ENABLED__

mmi_HelloWorld_entry(); return; #else … // 此处为之前 goto_main_menu 的代码 #endif } 同时我们需要在文件 MainMenu.c 中包含头文件 HelloWorldGprot.h, 所以再在这个文件 的头文件包含代码块(通常是文件的顶部区域)追加这一句:#include "HelloWorldGprot.h"。

下面是 HelloWorld.c 的内容: /*************************************************************************/ #include "stdC.h" #include "MMI_Features.h" /* 编译开关会出现在这个由 make update 生成的文件里面 */ #include "L4Dr.h" #include "L4Dr1.h"

#include "AllAppGprot.h"

#include "FrameworkStruct.h" #include "GlobalConstants.h" #include "EventsGprot.h" #include "mmiappfnptrs.h" #include "HistoryGprot.h"

#include "HelloWorldProt.h" #include "HelloWorldTypes.h" #include "HelloWorldDefs.h"

#include "MainMenuDef.h"

#include "wgui_categories.h"

#include "Unicodexdcl.h"

/* 模块入口 */ void mmi_HelloWorld_entry(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

/* 强制退出当前屏幕,之后进入到我们的模块了 */ /* 上电缺省是 idle 屏幕,现进入 MAIN_MENU_SCREENID 屏 */ /* 注意看第二个参数,这个是当我们模块被强制退出时执行的一些操作 */ EntryNewScreen(MAIN_MENU_SCREENID, mmi_HelloWorld_exit, NULL, NULL);

/* 关掉屏幕顶部的状态条,我们要用整个屏幕 */ entry_full_screen();

/* 擦除当前背景 */ clear_screen();

/* 移动文本输出光标 */ gui_move_text_cursor(50, 100);

/* 设置字体颜色 */ gui_set_text_color(UI_COLOR_RED);

/* 输出文本到显示缓冲, 注意是 Unicode 编码 */ gui_print_text(L"Hello, World");

/* 刷新屏幕显示,MMI 用的是双缓冲绘图方式,而且需要显式刷新 */ gui_BLT_double_buffer(0, 0, UI_device_width - 1, UI_device_height - 1);

/* 注册一个按键处理,右软键弹起时返回到之前被我们强制退出的模块 */ SetKeyHandler(GoBackHistory, KEY_RSK, KEY_EVENT_UP); #endif }

/* 模块出口 * 当我们的模块被其他模块强制退出时会执行这个函数, * 这个函数的常见写法,包括: * * 1、模块已申请资源的释放(如果需要的话),这一步可选 2、手动把自己压栈到窗口(实际是整个屏)堆栈里面,

* * * */

便于强制我们退出的模块执行完后重新把我们叫出来 不像 Window 的窗口管理是自动压栈的,Pluto MMI 需要手动压栈 3、其他一些清理动作

void mmi_HelloWorld_exit(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

history currHistory; S16 nHistory = 0;

currHistory.scrnID = MAIN_MENU_SCREENID; currHistory.entryFuncPtr = mmi_HelloWorld_entry; pfnUnicodeStrcpy( (S8*)currHistory.inputBuffer, (S8*)&nHistory);

AddHistory(currHistory); #endif } /*************************************************************************/ 编码的部分基本上就是这些了,然后我们开始 make,这次要用“make update”, 基于上述对相关系统文件的改动,make update 会自动将刚才的模块加入到整个项目中 去,同时也会更新仿真器 VC 工程文件,如下图所示,HelloWorld 模块的相关文件已经 被自动加入到 MMI 库里面了:

后续的 make, 如果是不涉及到系统相关文件的修改, “make remake” 用 就可以了, 以节省编译时间,因为 make 一次太耗时间了。 注意:对于仿真程序的编译,“make update”似乎缺少一些处理,导致可能还需 注意 要手工在文件“plutommi\mmi\GlobalSimulatorPathDef”中追加一行,以便模块的头文 件能被顺利找到,如下所示: /I ".\HelloWorld\Inc" 好,现在编译仿真程序开始看下效果,编译 (make update 会清除掉仿真程序之前 编译的中间文件,这样仿真程序会整个重新编译) 完后运行仿真器程序,如下图所示, 点选 File->Network Simulator 启动仿真,启动后手机模拟器进入 Idle 画面,点选手机模 拟画面上的“菜单”,就进入到我们的 Hello World 了,而不是之前的主菜单,然后点 右按钮,画面回到 Idle 画面。

至此,Hello World 基本完成,这是一个相对完整的模块框架,在此基础之上我们 可以逐步开始进入真正有意义的项目开发了。 三、 使用资源

我们知道, 很多时候我们需要应付来自不同国家和地区客户的需求, 他们使用的语言编 码不一样,但实际上对产品功能的要求是基本相同的;此外即便是同一种语言,客户的个性 化需求(例如外观换皮肤)也要求我们有相应手段来处理和产品业务逻辑关系不是很密切的 产品表现手段,大多数时候,这些东西涉及到:字符串、图标、图片、菜单、字库、主题、 声音等,也就是我们常说的资源,做过多语言版本软件的朋友应该对此深有体会。一般来讲 VC 使用资源动态库来解决这个问题,那么在 MTK 平台上一般是怎么解决的呢,我们一起 来看下。 1、资源 ID 、 在进入实际编码前,让我们先来看看 MTK 平台是如何管理资源的。MTK 平台大 致将资源分为以下几类:字符串、菜单、图片、字库、主题、声音、屏幕,所有资源统 一编码管理,对于同一类型的资源,每个资源有全系统唯一的编号,也就是资源 ID, 字符串资源和其他资源稍有不同, 同一个语义的不同语言版本字符串共用同一个字符串 资源 ID,系统自动根据当前语言设定提取对应语言版本的字串。此外,屏幕是一种平

常我们 Windows 开发中不多见的一种资源,这里我们可以简单的理解成 Windows 的窗 口句柄好了。为了不让系统各个模块的资源 ID 彼此重叠从而导致混乱,系统提供专门 的宏来处理,并将相关的定义统一放到同一个文件里面便于管理。 具体来讲,这个文件是“plutommi\mmi\Inc\MMIDataType.h”,在开始添加实际资 源之前,我们需要大致估计一个安全值,这个值代表我们可能用到的资源最大数,注意 这个最大数并不是我们用到的所有资源的总数, 而是某一类资源, 因为不同类型的资源 其资源 ID 是允许重复的,比如说我们总共要用到 20 个字符串,10 张图片,1 个菜单 项,那么这个最大数是 20,一般会再留一些可扩展的空间,就 HelloWorld 模块而言, 最大数取 100 足够了(当然了,这个数后面还可以再改的)。最大值取好后,我们就可以 着手向系统申报我们的资源 ID 空间了,修改文件“plutommi\mmi\Inc\MMIDataType.h” 如下所示红色字体部分,这样系统就预留了 100 个全系统唯一的资源 ID 给我们了,注 意,是每一种资源都有 100 个可用的资源 ID。 typedef enum { …… RESOURCE_BASE_RANGE(MAIN_MENU, RESOURCE_BASE_RANGE(HELLOWORLD, …… } RESOURCE_BASE_ENUM; …… /************************************************************************* * Main Menu **************************************************************************/ #define MAIN_MENU_BASE ((U16) RESOURCE_BASE_MAIN_MENU) 600), 100),

#define MAIN_MENU_BASE_MAX((U16) RESOURCE_BASE_MAIN_MENU_END) RESOURCE_BASE_TABLE_ITEM(MAIN_MENU)

/************************************************************************* * HelloWorld **************************************************************************/ #define HELLOWORLD_BASE #define HELLOWORLD_BASE_MAX ((U16) RESOURCE_BASE_HELLOWORLD) ((U16)

RESOURCE_BASE_HELLOWORLD_END) RESOURCE_BASE_TABLE_ITEM(HELLOWORLD)

…… 好,现在我们有自己的资源 ID 空间了,之前我们有暂借 MAIN_MENU 的屏幕 ID 资源, 现在用回我们自己的, 增加相关定义到 HelloWorldDefs.h, 现在 HelloWorldDefs.h 看起是这样: /************************************************************************/ #ifndef __HELLOWORLD_DEFS_H__

#define __HELLOWORLD_DEFS_H__

typedef enum { SCR_HELLOWORLD = HELLOWORLD_BASE + 1, }SCREENID_LIST_HELLOWORLD;

#endif

/* __HELLOWORLD_DEFS_H__ */

/************************************************************************/ 接着修改模块入口函数 mmi_HelloWorld_entry,见红色字体部分,如下所示: EntryNewScreen(SCR_HELLOWORLD, mmi_HelloWorld_exit, NULL, NULL); 屏幕 ID 资源是一种最简单的资源,就是一个数值,定义完就可以使用了,其他资源相 对复杂,需要有一个生成的过程,下面我们接着看资源的生成。

2、资源的生成 、 限于篇幅,这里我们主要讲常用的 3 种资源:字符串、图片和菜单。首先,我们来看这 些资源是如何被生成的,后面我们再讲如何使用这些资源。MTK 平台的资源是通过一个叫 mtk_resgenerator.exe 的程序生成的,这个程序位于目录 plutommi\Customer\ResGenerator 下, 是个临时生成的可执行文件,每次项目相关资源被修改,该程序都要重新编译,实际上这个 程序是由一系列需要自有资源的模块根据一定的资源生成规则将各自的资源生成代码组合 而成的。 资源生成相关的代码统一放在同一个目录下面, 因此我们要给自己的模块添加资源, 只需要在这个目录下建立自己的资源生成.c 文件,并按照指定的生成规则编写代码即可。 具体来讲,这个目录就是 plutommi\Customer\CustResource\PLUTO_MMI\Res_MMI。注 注 意,这个目录下不要放其他不相关的.c 或.cpp 源文件,因为编译脚本会自动将这个目录下的 所有源文件作为 mtk_resgenerator.exe 的一部分来处理。

好,下面开始资源生成相关的编码。我们继续接着在 HelloWorld 模块上讲好了,首先 在上面说的目录下面创建一个文件:Res_HelloWorld.c,这个文件的内容大致框架如下: /*************************************************************************/ #include "StdC.h"

#ifdef DEVELOPER_BUILD_FIRST_PASS 编译开关框住 #include "PopulateRes.h" #include "MMI_features.h" #include "GlobalMenuItems.h" #include "HelloWorldDefs.h"

// 注意, 注意, 后面资源生成相关代码要用这个

void PopulateHelloWorldRes(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

…… // 这里是资源生成部分的代码,基本上都是些宏,后面讲到具体资源的时候我们 再讲 #endif }

#endif

/* DEVELOPER_BUILD_FIRST_PASS */

/*************************************************************************/ 接着, 我们还需要修改系统文件 plutommi\Customer\ResGenerator\Makefile, 以便编译系 统能顺利找到我们定义资源 ID 的头文件"HelloWorldDefs.h",在 PLUTO_INC =这一项里面 追加一行:-I "../../MMI/HelloWorld/Inc",注意 注意,追加新行的时候别忘了在上一行尾部添上续 注意 行符号“\”。 再接着, 我们要把我们的资源生成函数 “PopulateHelloWorldRes” 放到合适的地方调用, 这 样 才 能 生 成 我 们 要 的 资 源 , 这 里 我 们 需 要 修 改 另 外 一 个 系 统 .c 源 文 件 : “plutommi\mmi\Resource\PopulateRes.c”,需要修改的见红色字体部分,如下所示: …… extern void PopulateMmiapiRes(void);

extern void PopulateHelloWorldRes(void); ……

void PopulateResData(void) { …… #ifdef DOWNLOAD_MCU /* Added, 20071218 */ PRINT_INFORMATION(("Populating MCU Download Resource\n")); Populate_MCUDownload(); #endif

#ifdef

__MMI_HELLOWORLD_ENABLED__

PRINT_INFORMATION(("Populating HelloWorld Resource\n")); PopulateHelloWorldRes(); #endif …… } 对 于字 符串资 源的 生成, 我们 还需要 修改 另外一 个系 统 .c 源 文件 ,这个 文件 是 “plutommi\Customer\ResGenerator\readexcel.c”,这个文件是专门用来处理字符串资源的, mtk_resgenerator.exe 会调用这个部分来单独处理字符串相关的部分。 需要修改的见红色字体 部分,如下所示: …… #include "SettingDefs.h" #ifdef __MMI_HELLOWORLD_ENABLED__

#include "HelloWorldDefs.h" #endif …… 只加上面 3 句就好,主要是让字串资源生成程序能根据我们的资源 ID 定义头文件 “HelloWorldDefs.h”辨识到我们的字符串资源。

1)

字符串资源的生成

首先在我们的资源 ID 定义头文件"HelloWorldDefs.h"里面加入字符串 ID 的定义, 如下所示红色字体部分: typedef { STR_HELLOWORLD_HELLO = HELLOWORLD_BASE + 1, }STRINGID_LIST_HELLOWORLD; 好,现在我们有一个全系统唯一的字符串资源 ID 了,让我们给这个字符串资源 ID 设定具体的文本内容,这需要修改到一个系统文本文件: “plutommi\Customer\CustResource\PLUTO_MMI\ref_list.txt” ,注意,这个文本文 件是 Unicode 编码的,可以用 UltraEdit 打开,不过最好用 Excel 打开,这样便于编辑修 改,这个文件的格式如下: 第 一 列 : 字 符 串 资 源 ID , 这 里 我 们 只 加 了 一 个 , 也 就 是 我 们 上 面 定 义 的 “STR_HELLOWORLD_HELLO”; 第二列:字符串资源所属模块,这个是便于设计者辨识资源归属的,资源生成程序 并没用到,最好和当前的模块号同名,便于识别和管理; 第三列:该字串的最大长度,取所有语言中文本长度最长的一个 第四列:字串描述,这一列可以随意 第五列及以后各列分别对应各个不用语言的字串 按照上述格式,我们增加追加一条,如下红色字体所示: STR_HELLOWORLD_HELLO 好,世界 你好,世界 接着, 我们要在我们的资源生成函数 PopulateHelloWorldRes 中增加一行, 这样才能 将上面的资源真正变成最终系统资源的一部分,一条宏指令就搞定了,如下红色字体所 示: ADD_APPLICATION_STRING2(STR_HELLOWORLD_HELLO, “HelloWorld”); 这个宏的第一个参数是字符串 ID, 第二个参数是当前系统语种对应的字串内容为空 的时候的替代值,第三个参数是串的描述,可以随意。好,至此我们就成功的向系统中 添加了一个字符串资源,而且是包含多语种支持的。 “Hello, World”, HelloWorld 20 Hello World "Hello,World" 你 enum

2)

图片资源的生成 同样,第一步我们向资源 ID 定义头文件"HelloWorldDefs.h"里面加入图片 ID 的定

义,如下所示红色字体部分: typedef enum

{ IMG_HELLOWORLD = HELLOWORLD_BASE + 1, }IMAGEID_LIST_HELLOWORLD; 接着我们把设计好的图片放到系统约定的目录, 一般来讲系统是按照屏幕分辨率来 分 类 存 放 图 片 资 源 的 , 对 于 P1300 320*240 的 屏 幕 分 辨 率 来 讲 , 这 个 目 录 是 “plutommi\Customer\Images\PLUTO240X320\MainLCD”,在这个目录下我们建一个名 字为“HelloWorld”的子目录,之后把我们模块要用到的图片通通都放到这个子目录下 以便于管理。我们增加一个小图片 HelloWorld.bmp 到这个子目录下,后面我们将这个 小图片作为菜单项的图标,如下所示:

再接着,同样我们需要在资源生成函数 PopulateHelloWorldRes 中增加一行,这也 是一个宏,如下所示红色字体部分: ADD_APPLICATION_IMAGE2(IMG_HELLOWORLD, CUST_IMG_PATH"\\\\MainLCD\\\\HelloWorld\\\\HelloWorld.bmp", "HelloWorld"); 这个宏第一个参数是图片资源 ID,第二个参数是图片存储路径(CUST_IMG_PATH 对应“plutommi\Customer\Images\PLUTO240X320”),第三个参数是资源描述,这样一 个图片资源就加好了, 下面接着看菜单资源的添加, 这一部分稍稍繁琐一点(主要是相对 应的宏参数比较多)。 注 意 : HelloWorld 子 目 录 以 及 相 关 文 件 实 际 上 是 要 加 入 到 压 缩 文 件 “plutommi\Customer\Images\PLUTO240X320\image.zip”中(放到 MainLCD 目录下), “plutommi\Customer\Images\PLUTO240X320\MainLCD” 这个目录是由 image.zip 生成的 临时目录。

3)

菜单资源的生成 我们都知道,菜单是一种树形的结构,一个菜单项一般有以下几个要素: 1、 父菜单项 2、 子菜单项 3、 菜单项的文本提示 4、 菜单项文本提示前面的小图标 5、 菜单项对应的动作 第 5 个涉及到菜单资源的使用, 我们放到后面再讲, 下面就前面几项讲讲如何向添

加一个新的菜单项。

首先确定我们新的菜单项的位置, 我们放到工具菜单下好了, 就是主菜单进去后那 个小推车对应的菜单。 第 一 步 : 我 们 要 定 义 一 个 全 系 统 唯 一 的 菜 单 ID , 这 需 要 在 系 统 文 件 “plutommi\mmi\Inc\GlobalMenuItems.h”中名字为 GLOBALMENUITEMSID 的枚举型 定义里面增加一行,如下红色字体所示: enum GLOBALMENUITEMSID { IDLE_SCREEN_MENU_ID = 1, …… MENU_ID_HELLOWORLD, /************************************************************* * Add new menuitems definitions before here *************************************************************/

/* Add All Menus defines above MAX_MENU_ITEMS_VALUE Only */ …… } 从上面我们可以看出, 菜单 ID 并没有用到我们之前提到的 HELLOWORLD_BASE 到 HELLOWORLD_BASE_MAX 之间的数值,而是重新启用另外一套数值空间,没关 系,只要能保证同一种资源每一个都有全系统唯一编号就好了。 第二步: 第二步:找到工具菜单的定义部分,将 MENU_ID_HELLOWORLD 作为其中的 一部分,这实际上是要修改主菜单的相关资源,主菜单的资源定义文件是 “plutommi\Customer\CustResource\PLUTO_MMI\Res_MMI\Res_MainMenu.c”,打开这 个文件,找到 OrganizerMenu 的定义部分,增加我们的相关定义,如下所示红色部分: typedef enum { …… #if defined(__MMI_EBOOK_READER__) ENUM_EBOOK, #endif

#if defined(__MMI_HELLOWORLD_ENABLED__) ENUM_ID_HELLOWORLD,

#endif

ORG_ENUM_TOTAL } OrganizerMenu; 对于 OrganizerMenu 这个定义的修改,主要意义在于 ORG_ENUM_TOTAL,这个 值实际上是对应菜单的子菜单数, 系统会引用到这个值, 我们往枚举定义 OrganizerMenu 里面增加一项,这个值自动加 1,也就是对应菜单的子菜单数比以前大 1。 第三步:把我们的菜单 ID 作为工具箱菜单的一个子菜单,在对应位置插入一行, 如下红色字体所示: #if defined(__MMI_VERSION_2__) void PopulateMainMenuRes(void) { …… /* oganizer */ ADD_APPLICATION_MENUITEM((MAIN_MENU_ORGANIZER_MENUID,I DLE_SCREEN_MENU_ID, ORG_ENUM_TOTAL + 1, // fun and games menu …… #if defined(__MMI_EBOOK_READER__) MAIN_MENU_EBOOK_MENUID, #endif #if defined(__MMI_HELLOWORLD_ENABLED__) MENU_ID_HELLOWORLD, #endif …… } 通过上面三步我们已经将新的菜单项成功插入到工具箱菜单, 也就是说我们解决了 菜单几个元素中的“父菜单项”的问题,接着我们来看看其他几项元素。这里我们介绍 下 ADD_APPLICATION_MENUITEM 这个宏,刚才往工具箱菜单插入我们的菜单项时 已经接触到这个宏了,生成菜单资源用的就是这个宏,接着我们就会用到。这个宏的参 数个数是可变的,其参数具体含义如下: 参数 1:菜单项本身的 ID,就我们的例子而言是 MENU_ID_HELLOWORLD 参数 2:菜单项的父菜单 ID,这里是 MAIN_MENU_ORGANIZER_MENUID /* Max 0224 */

参数 3:本菜单子菜单个数,假定为 N 好了,这个参数之后是变参部分,随后的 N 个参数为各个子菜单的 ID 参数 4+N:菜单是否显示,一般为 SHOW 参 数 5+N: 菜 单 其 他 属 性 , 可 以 是 这 些 值 的 组 合 : NONMOVEABLE, MOVEABLEWITHOARENT, MOVEABLECROSSPARENT, INSERTABLE,

SHORTCUTABLE,一般我们就用 SHORTCUTABLE 就好了。 参数 6+N: 下级菜单的显示风格,以下风格任选其一就好: DISP_LIST—列表显示,常见风格 DISP_MATRIX—矩阵显示,如九格宫、十二格宫,主菜单一般用这种风格 DISP_CIRCULAR_3D—循环 3D 显示,只有主菜单用到 DISP_PAGE—翻页格式,每个菜单项一页,一般也是主菜单才会用到 DISP_FIXED_GRID—很少用到,可忽略 参数 7+N:菜单项显示文本对应的字串资源 ID 参数 8+N:菜单项对应的小图标的资源 ID 好,根据上述参数说明,我们在函数 PopulateHelloWorldRes 中加入菜单生成宏, 如下红色字体所示: ADD_APPLICATION_MENUITEM( (MENU_ID_HELLOWORLD, MAIN_MENU_ORGANIZER_MENUID, 0, /* 我们没有子菜单 */ SHOW, SHORTCUTABLE, DISP_LIST, STR_HELLOWORLD_HELLO, IMG_HELLOWORLD)); 至此,我们完成菜单项资源的生成部分 注:ADD_APPLICATION_MENUITEM 这个宏的参数实际上是一个,上面那些 参数要用括号“()”括起来。 3、资源的使用 、 上一小节我们分别完成了字符串资源、 图片资源以及菜单资源的生成部分, 这一小 节我们来看如何的使用这些资源: 1) 字符串资源的使用 ) 字符串资源的使用比较简单,用函数 GetString 就可以了,如下红色所示,我 们修改下之前输出字符的操作: gui_print_text((UI_string_type)GetString(STR_HELLOWORLD_HELLO)); /* 要加右括号 */ /* 要加左括号 */

注意,GetString 前面有个类型转换,这个数据类型定义在“gui_data_types.h” 注意 里面,因此需要在 HelloWorld.c 中包含此头文件。

2) 图片资源的使用 ) 实际上我们在用宏 ADD_APPLICATION_MENUITEM 生成菜单的使用已经 用过了,更多的使用实例这里不再赘述,读者可自行寻找相关代码查看使用方法, 基本上都是将图片资源 ID 作为一个参数传入某个函数或者宏。

3) 菜单资源的使用 ) 实际上就是给菜单项设定一个响应动作, 使之转到我们模块的入口, 设置菜单 项响应动作的函数是 SetHiliteHandler, 设置的动作只需要在系统上电后初始化的部 分执行一次就可以了, 这里我们为模块新增一个函数, 这个函数专门处理我们模块 上电初始化的部分,如下红色字体所示我们在 HelloWorld.c 中增加一下部分: void mmi_HelloWorld_hilite(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

SetLeftSoftkeyFunction(mmi_HelloWorld_entry, KEY_EVENT_UP); #endif }

void mmi_HelloWorld_init(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

SetHiliteHandler(MENU_ID_HELLOWORLD, mmi_HelloWorld_hilite); #endif } 这实际上要用两个函数, 由这两个函数共同完成菜单点击通知的接收和转入我们模 块主入口的动作。 mmi_HelloWorld_init 这 个 函 数 是 一 个 外 部 接 口 , 我 们 把 申 明 放 到 HelloWorldGprot.h 中;mmi_HelloWorld_hilite 这个函数是内部接口,我们把申明放到 HelloWorldProt.h 中。然后我们把 mmi_HelloWorld_init 放入到函数 InitAllApplications

中 调 用 已 完 成 我 们 模 块 的 初 始 化 设 定 , 修 改 文 件 “plutommi\mmi\Framework\Tasks\TasksSrc\MMITask.c”,如下红色字体部分所示: …… #ifdef __MMI_HELLOWORLD_ENABLED__

#include “HelloWorldGprot.h” #endif …… void InitAllApplications(void) { …… #ifdef __MMI_HELLOWORLD_ENABLED__

mmi_HelloWorld_init(); #endif …… } 现在我们的菜单就成了整个的一部分了, 因为之前我们是暂借主菜单的入口来挂接 模块的,现在把之前的挂接去掉,去掉如下红色字体部分: void goto_main_menu(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

mmi_HelloWorld_entry(); return; #else …… #endif } 好,现在来编译一把看看效果,还是用“make update”吧,下面是效果图:

下载测试: 下载测试: 我用的 FlashTool_v3.0844.00, 据说下载速度要快些, 运行这个软件, “Download 设定 Agent ” 文 件 ( 用 FlashTool_v3.0844.00 目 录 下 的 “ MTK_AllInOne_DA.bin ” ) 和 “Scatter-loading File”文件(build\NEOTEL25_06B\ scatNEOTEL25_06B.txt),设定 “Scatter-loading File”文件后,下载到 ROM 的 bin 文件会自动选上刚刚编译出来的那 个,如果要改成下载其他 bin 文件,点选 ROM(注意是点选到 ROM 这 3 个字母上, 不是勾选前面的框框, 框框必须是打钩的, 否则 bin 文件不会下下去) “Authentication , File”貌似可以不理,如下图所示:

将 USB 下载线连上电脑(注意 注意是下载线,不是普通 USB 线,USB 下载线在接电 注意 脑的那一头封装了一个 PL2303 的转换芯片,这部分的个头比普通 USB 线大),此时 电脑上会出现一个新的串口, 可以用设备管理器看一下新的串口是串口几, 我的电脑是 COM3,如下图所示:

接着,如下图所示,设置下载工具的相关选项:

好, 准备下载了, 下载前先关手机(比较彻底的做法是将电池拿下), 将手机接上下载线, 然后点 下载,点完 之后软件下边出现下载进度条,装上电池,一直按住手机开机键直到下 载进度条开始动为止。 下载完屏幕上会弹出一个 OK 的图,表示已经成功完成了软件的下载,如下图:

开机试下看,看到 HelloWorld 了,运行结果和仿真器一样(如果不一样,你可能需 要 make remake 一下),不过开机时会提示 Midlet 找不到,这是因为系统默认开机时执 行指定的 Java 程序,但是这个 Java 程序系统里没找见。在设置->话机设置->Java 运行 时设置,手动关闭“自动运行 Java 程序”就行了(或者你有做好的 Java 程序,选定一 个),更多的关于 Java 方面的东西,请参考华禹相关资料。 下载的 Baudrate 设置如下所示,最高可以到 921600,建议设置到这个速率,下载 快: 四、 MMI 进阶

关于 MMI 的更多细节,大家可以看压缩包“huayu501_MTK 界面开发说明.rar”里面的 8.htm~20.htm,里面讲的很详细,我这里就不再重复了,呵呵。后面我将重点放到与设备硬 件的操作上。 五、 操作硬件

通常我们用 P1300 这类的模块做产品,大致都会在上面再扩展一些自己的硬件,通过 串口扩展是一种不错的想法, 这是一种非常常见的机对机通讯方式, 几乎所有的芯片都有串 口通讯能力,下面我们来看看 P1300 上如何进行 Rs232 串口通讯的。 为了简化描述和突出主题,我们这里将以 P1300 的 USB 串口 (就是接 USB 下载线的那 个口,实际上是串口 1)为例进行说明,通讯的对象是电脑主机(具体地说是电脑上的一个 串口工具,这样可确保通讯的另外一端是正常工作的,便于调试)。 1、串口的初始化 、 在 MTK 平台上,串口的操作和常见的操作系统不太一样,串口句柄并不是以文件 形式的出现,而是有点类似单片机的做法,即每个串口有固定的编号,串口的初始化不 需要有类似 Windows/Unix 等打开设备文件的操作(实际上是有 UART_Open 这个函数 的,不过我们没有用到,大概是底层硬件初始化会用到,这里我们简单的理解成不需要 Open 就好了),下面我们对照实际的初始化代码逐句讲解初始化所需要做的事情,如下 所示: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 我们要用到的串口, uart_port1 是枚举型数值,usb 串口的编号 #define HELLO_WORLD_UART_PORT uart_port1 // 定义我们的应用所属系统的哪一个模块 // 这里是 MMI 模块,实际上我更愿意称 MMI 为一个子系统,将我们的应用称为模块 #define MOD_HELLO_WORLD MOD_MMI // 手机接 USB 下载线的地方

// 数据接收 Buffer 大小 #define MAX_ECHO_PACKET_LEN 128

// 睡眠模式句柄 static kal_uint8 ghSleepMode;

// 我们要用到的串口之前的占用者 static module_type gnOrigUartOwner; static kal_bool gbUartInitialized = KAL_FALSE;

static void init_uart(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

if(gbUartInitialized) { return; }

ghSleepMode = L1SM_GetHandle(); // 禁止休眠,休眠后串口收发会有问题 // 问题:如何做到串口唤醒? FIXME 问题:如何做到串口唤醒? L1SM_SleepDisable(ghSleepMode);

// 记录我们要用的串口的当前占有者 gnOrigUartOwner = UART_GetOwnerID(HELLO_WORLD_UART_PORT);

// 下面我们申明要占用这个串口了 UART_SetOwner(HELLO_WORLD_UART_PORT, MOD_HELLO_WORLD); // 以上对串口占用的申明,我理解更多是一种编程者之间的约定 // 当发现串口的占用者不是本程序所属模块时,说明我们申明占用之后 // 又有其他模块申明占用同一个串口,此时如果收到数据应不予处理 // 当然,也有可能同一模块的其他程序申明占用同一串口

// 这需要在设计应用时统一协调,避免数据发生混乱

// 设置波特率,缺省的起停位和校验为:8,n,1,即 8 个数据位,1 个停止位,无校 验 UART_SetBaudRate(HELLO_WORLD_UART_PORT, MOD_HELLO_WORLD); // 其 他 串 口 设 定 ( 如 起 停 位 、 校 验 等 ) 使 用 函 数 UART_ReadDCBConfig 和 UART_SetDCBConfig // 详细参数见结构体 UARTDCBStruct UART_BAUD_115200,

// 注册一个事件钩子函数,当串口(任何)有数据到达时,我们的钩子函数将被调用 // 注意,同一种事件同时只能注册一个钩子函数,因此: // 据, // // 就必须由当前的钩子函数代为处理 实际上我觉得系统底层可以改一下, 改成 Windows 钩子的方式, 可以挂多个, 如果在我们的程序处理串口的同时还有其他程序要读取和处理(任何)串口数

能够依次调用或跳过 SetProtocolEventHandler(mmi_HelloWorld_uart_readyToRead_ind_handler, MSG_ID_UART_READY_TO_READ_IND);

gbUartInitialized = KAL_TRUE; #endif }

2、从串口读数据 、 这里有两个相关的函数,第一个是上面初始化部分提到的钩子函数 mmi_HelloWorld_uart_readyToRead_ind_handler,另外一个是读函数,函数体如下所示: ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void mmi_HelloWorld_uart_readyToRead_ind_handler(void *msg) {

#ifdef

__MMI_HELLOWORLD_ENABLED__ uart_rtr_ind =

uart_ready_to_read_ind_struct* (uart_ready_to_read_ind_struct*)msg;

// 检查一下端口以及所有者信息,不匹配的数据略过 // 注意,作为例子程序,我们只是查看和处理我们关心的数据 // 其他与此同时需要监听和处理串口数据的程序,须由相关的代码完成代理的工 作 if( KAL_FALSE == gbUartEchoStarted || ||

HELLO_WORLD_UART_PORT != uart_rtr_ind->port

MOD_HELLO_WORLD != UART_GetOwnerID(uart_rtr_ind->port) ) { // 如果底层能做成钩子链,这里就可以调用钩子链的下一个钩子了 return; }

gwLenUartBuffer

=

read_from_uart(gabyUartBuffer,

sizeof(gabyUartBuffer),

HELLO_WORLD_UART_PORT, MOD_HELLO_WORLD);

// 呼叫数据处理部分 uart_echo_process(); #endif }

static U16 read_from_uart(U8 *pbyBuf, U16 wLenMax, UART_PORT hPort, module_type hOwner) { U16 wLenAvail; U16 wLenRead; U16 wLenRet = 0; U8 byStatus = 0;

#ifdef

__MMI_HELLOWORLD_ENABLED__

// 收取数据,超出最大包长的数据将简单丢弃,这一层需要具体的应用协议做相 应处理 while( (wLenAvail = UART_GetBytesAvail(hPort) > 0 wLenMax) ) { if (wLenAvail + wLenRet > wLenMax) { wLenAvail = wLenMax - wLenRet; } && wLenRet <

wLenRead = UART_GetBytes(hPort, (kal_uint8 *)(pbyBuf + wLenRet), (kal_uint16)wLenAvail, &byStatus, hOwner); wLenRet += wLenRead; }

读完之后, // 读完之后,清除接收 Buffer UART_ClrRxBuffer(hPort, hOwner); #endif

return wLenRet; }

3、向串口写数据 、 static U8 write_to_uart(U8 *pbyBuf, U16 wLenBuf, UART_PORT hPort, module_type hOwner) { U16 wSent= 0; U8 bRet = FALSE;

#ifdef

__MMI_HELLOWORLD_ENABLED__

注意: // 发送前清 FIFO 和 Buffer,注意:这一步必须做,否则收发会有问题 注意 这一步必须做, UART_Purge(hPort, RX_BUF, hOwner); UART_Purge(hPort, TX_BUF, hOwner); // 清除设备输入 FIFO // 清除设备输出 FIFO

UART_ClrTxBuffer(hPort, hOwner); // 清除发送 Buffer UART_ClrRxBuffer(hPort, hOwner); // 清除接收 Buffer

wSent = UART_PutBytes(hPort, (kal_uint8 *)pbyBuf, (kal_uint16)wLenBuf, hOwner); #endif

if (wSent == wLenBuf) { bRet = TRUE; }

return bRet ; }

4、关闭串口 、 关闭串口实际上是将相关的设置改动恢复到之前的状态,不需要有 close 的动作, 如下所示: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void exit_uart() { #ifdef __MMI_HELLOWORLD_ENABLED__

if(gbUartInitialized) { // 恢复成原有的端口占用者

UART_SetOwner(HELLO_WORLD_UART_PORT, gnOrigUartOwner);

(kal_uint8)

// 允许休眠 L1SM_SleepEnable(ghSleepMode);

gbUartInitialized = KAL_FALSE; } #endif } 5、下载测试 、 Make remake 一下->下载程序到手机->关闭下载工具->运行串口工具, 打开 USB 下 载线对应的串口,设定为 115200,8N1->手机开机(此时下载线不要拔下,我们用的就是 这个下载线测试的)->手机工具菜单->Hello World->手机左键启动串口 Echo(启动后再 按左键停止串口 Echo)。 串口的部分在仿真器上模拟不了, 链接的时候也会提示找不到相关函数符号。 注 1: : 注 2:在 HelloWorld 的串口 Echo 停止工作时我们也能偶尔看到串口会原样回显, : 这应该是其他程序在处理,不是我们的 HelloWorld,我们的 HelloWorld 会将输入转成 全大写再回显。 注 3:使用手机的 USB 下载口当作串口时,要用 USB 下载线进行连接,同时要注 : 意相关的工程设置为以下: -------------TST Config < 无 > < 115200 > PS Config < UART 1 > < 115200 > -------------设置方法: 待 机 界面 下( 拨号 )输入 *#3646633# ( 进入 工 程 模式 ) -> 设备 ->"Set UART"->"UART Setting"

程序运行后的电脑端截图如下:

我采用 1ms 定时发送“Hello, World!\r\n”, 每毫秒 15 字节的速度,接收比发送多,这 是因为启动 Echo 时,我有送一个串“Hello World Uart Echo Example Started! \r\n”(40 字节) 过来,不过接收多出的部分应该不止 10 个字节(1928380 – 1928370),应该是 40 个字节, 数据似乎有部分丢失(刚好 2 个包, 15 * 2 = 30 = 40 - 10),应该是电脑端发送太快的原因 (115200 / (8 + 1) / 1000 = 12.8 字节 毫秒,小于 15,而且还有数据处理和 echo 的时间 字节/毫秒 毫秒, 的时间),这 , 样的响应速度算相当不错了。 6、完整的 C 程序 、 下面贴出比较完整的 HelloWorld.c 程序,供大家参考,由于时间的关系,开发心得 暂时到此为止,感谢华禹公司及其兄弟公司的支持,感谢各位网友! ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "stdC.h" #include "MMI_Features.h" /* 编译开关会出现在这个由 make update 生成的文件 里面 */ #include "L4Dr.h" #include "L4Dr1.h"

#include "AllAppGprot.h" #include "FrameworkStruct.h" #include "GlobalConstants.h" #include "EventsGprot.h" #include "mmiappfnptrs.h" #include "HistoryGprot.h"

#include "HelloWorldProt.h" #include "HelloWorldTypes.h" #include "HelloWorldDefs.h"

#include "MainMenuDef.h"

#include "wgui_categories.h"

#include "Unicodexdcl.h" #include "gui_data_types.h"

#include "Uart_sw.h"

// 我们要用到的串口, uart_port1 是枚举型数值,usb 串口的编号 #define HELLO_WORLD_UART_PORT uart_port1 方 // 定义我们的应用所属系统的哪一个模块 // 这里是 MMI 模块,实际上我更愿意称 MMI 为一个子系统,将我们的应用称为 模块 #define MOD_HELLO_WORLD MOD_MMI // 手机接 USB 下载线的地

// 数据接收 Buffer 大小 #define MAX_ECHO_PACKET_LEN 128

// 外部函数申明,没有对应头文件,手动加吧 extern module_type UART_GetOwnerID(UART_PORT port); extern void UART_ClrTxBuffer(UART_PORT port, module_type ownerid); extern void UART_ClrRxBuffer(UART_PORT port, module_type ownerid);

// 本程序内部使用的函数申明 static void init_uart(void); static void mmi_HelloWorld_uart_readyToRead_ind_handler(void *msg); static U16 read_from_uart(U8 *pbyBuf, U16 wLenMax, UART_PORT hPort,

module_type hOwner); static U8 write_to_uart(U8 *pbyBuf, U16 wLenBuf, UART_PORT hPort,

module_type hOwner); static void exit_uart(); static void start_uart_echo(void); static void uart_echo_process(void); static void stop_uart_echo(void);

// 睡眠模式句柄 static kal_uint8 ghSleepMode;

// 我们要用到的串口之前的占用者 static module_type gnOrigUartOwner; static kal_bool gbUartInitialized = KAL_FALSE;

static kal_bool static U16 static U8

gbUartEchoStarted = KAL_FALSE; gwLenUartBuffer = 0; gabyUartBuffer[MAX_ECHO_PACKET_LEN];

static void init_uart(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

if(gbUartInitialized) { return; }

ghSleepMode = L1SM_GetHandle(); // 禁止休眠,休眠后串口收发会有问题 // 问题:如何做到串口唤醒? FIXME L1SM_SleepDisable(ghSleepMode);

// 记录我们要用的串口的当前占有者 gnOrigUartOwner = UART_GetOwnerID(HELLO_WORLD_UART_PORT);

// 下面我们申明要占用这个串口了 UART_SetOwner(HELLO_WORLD_UART_PORT, MOD_HELLO_WORLD); // 以上对串口占用的申明,我理解更多是一种编程者之间的约定 // 当发现串口的占用者不是本程序所属模块时,说明我们申明占用之后 // 又有其他模块申明占用同一个串口,此时如果收到数据应不予处理 // 当然,也有可能同一模块的其他程序申明占用同一串口 // 这需要在设计应用时统一协调,避免数据发生混乱

// 设置波特率,缺省的起停位和校验为:8,n,1,即 8 个数据位,1 个停止位, 无校验 UART_SetBaudRate(HELLO_WORLD_UART_PORT, UART_BAUD_115200, MOD_HELLO_WORLD); // 其他串口设定(如起停位、校验等)使用函数 UART_ReadDCBConfig 和 UART_SetDCBConfig // 详细参数见结构体 UARTDCBStruct

// 注册一个事件钩子函数,当串口(任何)有数据到达时,我们的钩子函数将被 调用 // 注意,同一种事件同时只能注册一个钩子函数,因此: // 口数据, // // 就必须由当前的钩子函数代为处理 实际上我觉得系统底层可以改一下,改成 Windows 钩子的方式,可以挂 如果在我们的程序处理串口的同时还有其他程序要读取和处理(任何)串

多个,能够依次调用或跳过 SetProtocolEventHandler(mmi_HelloWorld_uart_readyToRead_ind_handler, MSG_ID_UART_READY_TO_READ_IND);

gbUartInitialized = KAL_TRUE; #endif }

static U16 read_from_uart(U8 *pbyBuf, U16 wLenMax, UART_PORT hPort, module_type hOwner) { U16 wLenAvail; U16 wLenRead; U16 wLenRet = 0; U8 byStatus = 0;

#ifdef

__MMI_HELLOWORLD_ENABLED__

// 收取数据,超出最大包长的数据将简单丢弃,这一层需要具体的应用协议 做相应处理 while( (wLenAvail = UART_GetBytesAvail(hPort) > 0 wLenMax) ) { if (wLenAvail + wLenRet > wLenMax) { wLenAvail = wLenMax - wLenRet; } && wLenRet <

wLenRead = UART_GetBytes(hPort, (kal_uint8 *)(pbyBuf + wLenRet), (kal_uint16)wLenAvail, &byStatus, hOwner); wLenRet += wLenRead; }

// 读完之后,清除接收 Buffer UART_ClrRxBuffer(hPort, hOwner); #endif

return wLenRet; }

static U8 write_to_uart(U8 *pbyBuf, U16 wLenBuf, UART_PORT hPort, module_type hOwner) { U16 wSent= 0; U8 bRet = FALSE;

#ifdef

__MMI_HELLOWORLD_ENABLED__

// 发送前清 FIFO 和 Buffer,注意:这一步必须做,否则收发会有问题 UART_Purge(hPort, RX_BUF, hOwner); UART_Purge(hPort, TX_BUF, hOwner); // 清除设备输入 FIFO // 清除设备输出 FIFO

UART_ClrTxBuffer(hPort, hOwner); // 清除发送 Buffer UART_ClrRxBuffer(hPort, hOwner); // 清除接收 Buffer

wSent = UART_PutBytes(hPort, (kal_uint8 *)pbyBuf, (kal_uint16)wLenBuf, hOwner); #endif

if (wSent == wLenBuf) { bRet = TRUE; }

return bRet ; }

static void exit_uart() { #ifdef __MMI_HELLOWORLD_ENABLED__

if(gbUartInitialized) { // 恢复成原有的端口占用者 UART_SetOwner(HELLO_WORLD_UART_PORT, gnOrigUartOwner); (kal_uint8)

// 允许休眠

L1SM_SleepEnable(ghSleepMode);

gbUartInitialized = KAL_FALSE; } #endif }

static void mmi_HelloWorld_uart_readyToRead_ind_handler(void *msg) { #ifdef __MMI_HELLOWORLD_ENABLED__ uart_rtr_ind =

uart_ready_to_read_ind_struct* (uart_ready_to_read_ind_struct*)msg;

// 检查一下端口以及所有者信息,不匹配的数据略过 // 注意,作为例子程序,我们只是查看和处理我们关心的数据 // 其他与此同时需要监听和处理串口数据的程序,须由相关的代码完成代理 的工作 if( KAL_FALSE == gbUartEchoStarted || ||

HELLO_WORLD_UART_PORT != uart_rtr_ind->port

MOD_HELLO_WORLD != UART_GetOwnerID(uart_rtr_ind->port) ) { // 如果底层能做成钩子链,这里就可以调用钩子链的下一个钩子了 return; }

gwLenUartBuffer

=

read_from_uart(gabyUartBuffer,

sizeof(gabyUartBuffer),

HELLO_WORLD_UART_PORT, MOD_HELLO_WORLD);

// 呼叫数据处理部分 uart_echo_process(); #endif }

static void start_uart_echo(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

S8 strHello[] = "Hello World Uart Echo Example Started!\r\n";

if(gbUartEchoStarted) { return; }

init_uart();

write_to_uart((kal_uint8*)strHello, HELLO_WORLD_UART_PORT, MOD_HELLO_WORLD);

(kal_uint16)strlen(strHello),

gbUartEchoStarted = KAL_TRUE;

SetKeyHandler(stop_uart_echo, KEY_LSK, KEY_EVENT_UP); #endif }

static void uart_echo_process(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

U8 i;

// 稍稍处理一下,这样就能看出是否确实是我们的程序在处理数据 for(i = 0; i < gwLenUartBuffer; i++) { if(gabyUartBuffer >= 'a' && gabyUartBuffer <= 'z') { gabyUartBuffer -= 0x20; } }

// 回显 write_to_uart(gabyUartBuffer, gwLenUartBuffer, HELLO_WORLD_UART_PORT, MOD_HELLO_WORLD); #endif }

static void stop_uart_echo(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

S8 strBye[] = "Hello World Uart Echo Example Stop!\r\n";

if(gbUartEchoStarted) { write_to_uart((kal_uint8*)strBye, HELLO_WORLD_UART_PORT, MOD_HELLO_WORLD); (kal_uint16)strlen(strBye),

gbUartEchoStarted = KAL_FALSE;

SetKeyHandler(start_uart_echo, KEY_LSK, KEY_EVENT_UP); }

exit_uart(); #endif }

gdi_handle hAnimation; void stop_play_anim(void) { gdi_anim_stop(hAnimation); }

/* 模块入口 */ void mmi_HelloWorld_entry(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

S32 x, y, w, h; color colorText = {255, 255, 128, 100}; color colorFill = {207, 252, 109, 100}; color colorShadow = {166, 201, 81, 100}; stFontAttribute tFont = {0}; U8 dotted_line_bitvalues[] = {1, 0, 1, 0, 1, 0, 1}; UI_filled_area tFiller = {0}; static color g_colors[3] = {{255, 0, 0}, {0, 255, 0}, {0, 0, 255}}; static U8 perc[2] = {50, 50}; gradient_color gc = {g_colors, perc, 3};

tFont.size = LARGE_FONT; tFont.italic = 1;

/* 强制退出当前屏幕,之后进入到我们的模块了 */ /* 上电缺省是 idle 屏幕,现进入 MAIN_MENU_SCREENID 屏 */ /* 注意看第二个参数,这个是当我们模块被强制退出时执行的一些操作 */ EntryNewScreen(SCR_HELLOWORLD, mmi_HelloWorld_exit, NULL, NULL); gui_lock_double_buffer();

/* 关掉屏幕顶部的状态条,我们要用整个屏幕 */ entry_full_screen();

/* 擦除当前背景 */ clear_screen();

tFiller.flags

=

UI_FILLED_AREA_TYPE_GRADIENT_COLOR

|

UI_FILLED_AREA_VERTICAL_FILL | UI_FILLED_AREA_DOUBLE_BORDER; tFiller.border_color = UI_COLOR_GREEN; tFiller.gc = &gc; gui_draw_filled_area(0, 0, UI_device_width - 1, UI_device_height - 1, &tFiller);

/* 设置字体颜色 */ gui_set_text_color(colorText/*UI_COLOR_RED*/); gui_set_font(&tFont); gui_set_text_border_color(UI_COLOR_GREEN); gui_measure_string((UI_string_type)GetString(STR_HELLOWORLD_HELLO), &w, &h); x = (UI_device_width - w) >> 1; y = UI_device_height - ((UI_device_height - h) >> 2);

gui_draw_rectangle(x - 7, y - 7, x + w + 7, y + h + 7, UI_COLOR_RED); gui_fill_rectangle(x - 6, y - 6, x + w + 6, y + h + 6, colorFill); gui_line(x - 4, y + h + 4, x + w + 4, y + h + 4, colorShadow); gui_line(x - 5, y + h + 5, x + w + 5, y + h + 5, colorShadow); gui_line(x - 6, y + h + 6, x + w + 6, y + h + 6, colorShadow); gui_line(x + w + 4, y - 4, x + w + 4, y + h + 4, colorShadow); gui_line(x + w + 5, y - 5, x + w + 5, y + h + 5, colorShadow); gui_line(x + w + 6, y - 6, x + w + 6, y + h + 6, colorShadow); /* 移动文本输出光标 */ gui_move_text_cursor(x, y);

/* 输出文本到显示缓冲, 注意是 Unicode 编码 */ // gui_print_text((UI_string_type)GetString(STR_HELLOWORLD_HELLO)); gui_print_bordered_text((UI_string_type)GetString(STR_HELLOWORLD_HELL O)); gdi_draw_line_style(x, y + h + 2, x + w + 2, y + h + 2, gdi_act_color_from_rgb(100, 255, 0, 0), sizeof(dotted_line_bitvalues), dotted_line_bitvalues);

// 显示图片 gdi_image_get_dimension_id(MAIN_MENU_MATRIX_ORGANIZER_ICON, &w, &h); x = (UI_device_width - w) >> 1; y = (UI_device_height - h) >> 1; gdi_image_draw_id(x, 10, MAIN_MENU_MATRIX_ORGANIZER_ICON);

// 显示动画 gdi_image_get_dimension_id(MAIN_MENU_MATRIX_ORGANIZER_ANIMATIO N, &w, &h); x = (UI_device_width - w) >> 1;

y = (UI_device_height - h) >> 1; gdi_anim_draw_id(x, 10 + h,

MAIN_MENU_MATRIX_ORGANIZER_ANIMATION, &hAnimation);

gui_unlock_double_buffer(); /* 刷新屏幕显示,MMI 用的是双缓冲绘图方式,而且需要显式刷新 */ gui_BLT_double_buffer(0, 0, UI_device_width - 1, UI_device_height - 1);

/* 注册一个按键处理,右软键弹起时返回到之前被我们强制退出的模块 */ SetKeyHandler(GoBackHistory, KEY_RSK, KEY_EVENT_UP); // SetKeyHandler(stop_play_anim, KEY_LSK, KEY_EVENT_UP); SetKeyHandler(start_uart_echo, KEY_LSK, KEY_EVENT_UP); #endif }

/* 模块出口 * 当我们的模块被其他模块强制退出时会执行这个函数, * 这个函数的常见写法,包括: * * * * * */ void mmi_HelloWorld_exit(void) { #ifdef __MMI_HELLOWORLD_ENABLED__ 1、模块已申请资源的释放(如果需要的话),这一步可选 2、手动把自己压栈到窗口(实际是整个屏)堆栈里面,

便于强制我们退出的模块执行完后重新把我们叫出来 不像 Window 的窗口管理是自动压栈的,Pluto MMI 需要手动压栈
3、其他一些清理动作

history currHistory; S16 nHistory = 0;

currHistory.scrnID = MAIN_MENU_SCREENID; currHistory.entryFuncPtr = mmi_HelloWorld_entry; pfnUnicodeStrcpy( (S8*)currHistory.inputBuffer, (S8*)&nHistory);

AddHistory(currHistory);

stop_uart_echo(); #endif }

void mmi_HelloWorld_hilite(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

SetLeftSoftkeyFunction(mmi_HelloWorld_entry, KEY_EVENT_UP); #endif }

void mmi_HelloWorld_init(void) { #ifdef __MMI_HELLOWORLD_ENABLED__

SetHiliteHandler(MENU_ID_HELLOWORLD, mmi_HelloWorld_hilite); #endif }


相关文章:
MTK_10a教程
MTK教程 2页 5财富值 MTK新手教程 51页 2财富值 MTK新手教程 39页 5财富值 MTK富贵教程 108页 2财富值 MTK 10A资源添加 4页 1财富值 MTK智能机刷机教程 ...
【刷机步骤, 菜鸟必备】最详细例子移动叔叔ROM3G包
刷机教程] 【刷机步骤, 菜鸟必备】最详细例子:移动叔叔 ROM*3G 包 [复制链接...《msxml6》 MTK_USB_Driver 目录下的《InstallDriver》 备份 IMEI: 刷机容易...
MTK DVD 代码学习 新手上路
MTK DVD 代码学习 新手上路_调查/报告_表格/模板_实用文档。DVD 新手 ABC 写...XBOX360自制中文教程 28页 免费 DVD构造及流程 33页 免费 ©2015 Baidu 使用...
手机软件安装步骤解析(新手必看)
手机软件安装步骤解析(新手必看)_信息与通信_工程科技...首先第一步:确定你的手机是否支持 MRP 或 MTK ...BlackBerry入门教程---黑... 7页 免费©...
DSA新手指南
DSA2010 版新手指南 一、些基本介绍 1、 DSA 名词解释 1)DSA 的名称:驾驶...MTK 的应该设为 9 baund=9600 DISP=驾驶安全预警与导航系统 VER=DSA 2、...
PL2303数据线自制MTK系列刷机线图文教程
PL2303数据线自制MTK系列刷机线图文教程_信息与通信_工程科技_专业资料。PL2303数据...可以参考下面的教程自制刷机数据 线: 一线通 ,PL2303刷机线,新手自制刷机线刷...
跑酷新手训练计划
所有关节力量训练参照跑酷 公社关节力量教程:http://v.youku.com/v_show/id_XMTkzNzA3MjYw.html 6.训练完之后也要把拉伸动作重新做一遍,加速恢复之用。 7....
善领DSA_新手指南
善领DSA_新手指南_解决方案_计划/解决方案_实用文档。善领DSA_新手指南 ...MTK 的应该设为 9 baund=9600 DISP=驾驶安全预警与导航系统 VER=DSA 2、...
mtk刷机线制作
mtk刷机线制作_信息与通信_工程科技_专业资料。mtk刷机线制作 丢了自我! 主页...sid=542 这个教程里面提到的什么超能一线通`就是狗屎`糊弄不会做线的新手` 一...
测量电压查找MTK主板RX TX接线端刷机教程(新手必看)
测量电压查找MTK主板RX TX接线端刷机教程(新手必看)_信息与通信_工程科技_专业资料。测量电压查找MTK主板RX TX接线端刷机教程测量电压查找 MTK 主板 RX TX 接线端...
更多相关标签:
mtk刷机教程 | mtk刷机工具教程 | mtkflashtool强刷教程 | mtk线刷教程 | mtk平台刷机教程 | mtk写串号教程 | 移动叔叔mtk刷机教程 | mtkdroidtools教程 |