姓名
班级
学号
指导老师目录
第一章概述 (1)
第二章MiniGUI在PC机上的安装 (1)
2.1所需源码准备 (2)
2.2编译安装 (2)
2.3运行样例程序 (4)
2.4遇到的问题 (5)
第三章MiniGUI在ARM上的移植 (5)
3.1交叉编译环境的建立 (5)
3.2交叉编译移植所需源码 (5)
3.3修改移植所需的配置文件 (8)
3.4MiniGUI移植到开发板 (8)
3.5交叉编译器的改进和完善 (9)
第四章MiniGUI串口传输界面设计 (10)
4.1MiniGUI设计知识简介 (10)
4.2串口传输知识简介 (13)
4.3程序整体框架设计 (14)
4.4重要程序解析 (15)
第五章程序调试 (20)
5.1接收测试方法 (21)
5.2发送测试方法 (21)
5.6设计的程序界面实际运行效果 (21)
第六章设计总结及改进 (21)
6.1设计总结 (21)
6.2设计改进 (22)
第七章心得体会 (22)
第八章参考文献 (22)
第九章附录 (22)一、
概述
一、概述
1、设计目的
1、进一步巩固嵌入式系统的基本知识;
2、掌握嵌入式应用系统的基本结构;
3、掌握嵌入式系统开发环境建立和使用;
4、掌握嵌入式系统基本驱动设计、调试和编译移植方法;
5、学习Linux下界面的设计开发
2、设计任务
1、掌握嵌入式系统开发环境建立和使用;
2、掌握嵌入式系统基本驱动、应用程序的设计调试和编译移植方法;
3、MiniGUI在PC上的安装、移植
4、Linux串口编程与MiniGUI界面编程
5、串口传输数据(位图)并在TQ2440的LCD上显示
6、编写设计说明书(方案的确定、设计环节的考虑及必要说明
等)及设备的使用说明
3、设计硬件平台
1、PC机VMware Workstation S3C2440开发板
MiniGUI在PC机上的安装
二、
二、MiniGUI1、所需源码的准备
编译安装MiniGUI需要用到源码包libminigui-1.6.10.tar.gz、资源源码包minigui-res-1.6.10.tar.gz、样例源码包mg-samples-1.6.10.tar.gz、以及图像引擎源码qvfb-1.1.tar.gz。所需的这些的源码均可以在飞漫公司官网上下载。
2、编译安装
在根目录下建一个目录存放所需的源码包
#cd/
#mkdir MiniGUI
库文件的编译安装
libminigui-1.6.10.tar.gz库文件的编译安装
(1)、、libminigui-1.6.10.tar.gz
(1)
#tar zxvf libminigui-1.6.10.tar.gz
#cd libminigui-1.6.10
#./configure//根据配置文件生成相应的Makefile
#make
#make install
然后把目录切换到/etc修改ld.so.conf
#cd/etc
#vim ld.so.conf
修改如下:
include ld.so.conf.d/*.conf
/usr/local/lib//在此添加
(2)、、minigui-res-1.6.10.tar.gz资源的安装
(2)
#tar zxvf minigui-res-1.6.10.tar.gz
#cd minigui-res-1.6.10
#make install(3)mg-samples-1.6.10.tar.gz演示样例的编译
#tar zxvf mg-samples-1.6.10.tar.gz
#cd mg-samples-1.6.10
#./configure//生成Makefile文件
#make
图像引擎qvfb-1.1.tar.gz的编译安装
(4)图像引擎
(4)
#tar zxvf qvfb-1.1.tar.gz
#cd qvfb-1.1
#./configure//生成Makefile文件
#make
#make install
#qvfb&//测试
如果安装成功,则会出现如下界面
然后#cd/usr/local/etc
#vim MiniGUI.cfg//修改配置文件
因为已有的配置符合要求,所以不必修改。但须注意[qvfb]
defaultmode=0x480-16bpp/注意这一栏
display=0
然后选择file下的Configure进行配置,如下图配置
图中的配置须与MiniGUI.cfg文件的配置一致,否则运行样例程序时会出现如下错误
3、运行样例程序如helloworld
#cd mg-samples-1.6.10/src
#./helloworld
出现下图所示界面,则表示MiniGUI在PC机上安装成功
4、遇到的问题
在编译libminigui 时遇到
查阅了很多资料都没有解决该问题的方法,于是将libminigui-1.6.10整个目录删除,重新解压源码包,重新配置编译。问题得到了解决
三、三、MiniGUI
MiniGUI 在ARM 板上的移植1、交叉编译环境的建立
(1)(1)交叉编译器交叉编译器arm-linux-gcc arm-linux-gcc的安装
的安装将EABI-4.3.3_EmbedSky_20091210.tar.bz2复制到根目录下#tar zxvf EABI-4.3.3_EmbedSky_20091210.tar.bz2
解压后会在opt 目录下生成EmbedSky 文件,交叉编译器就安装在该目录下。修改全局配置文件,将交叉编译器的bin 所在的路径添加进去,以便全局使用arm-linux-gcc 命令
#gedit /etc/profile
#source /etc/profile //刷新全局配置文件
至此,交叉编译环境就算建立好了
2、交叉编译移植所需源码
在根目录下新建一个目录:#mkdir MiniGUI-arm
#cd MiniGUI-arm
#mkdir nfsroot
库文件的交叉编译
(1)
(1)、、libminigui-1.6.10.tar.gz
libminigui-1.6.10.tar.gz库文件的交叉编译
#tar zxvf libminigui-1.6.10.tar.gz
#cd ibminigui-1.6.10
#cp../libcorss.sh.//将libcorss.sh脚本复制到该目录下
修改configure和libcorss.sh。具体修改如下图
#vim configure
//在文件开头处添加
#vim libcorss.sh
其中的--enable选项的no都改为yes,否则在开发板上移植samples 中带有图片的程序都将无法显示图片。--prefix所指向的目录为所要移植到开发板的目录即/MiniGUI-arm/nfsroot.
#./libcorss.sh
等待编译安装成功后会在nfsroot目录下生成etc、include、lib等目录
(2)、、minigui-res-1.6.10.tar.gz资源的交叉编译安装
(2)#cd minigui-res-1.6.10
#vim configure.linux//修改该文件
TOPDIR=/MiniGUI-arm/nfsroot//修改这里
prefix=$(TOPDIR)/usr/local
#make install//也可以将recorss.sh脚本复制到该目录。然后./recorss.sh
成功的话可以看到在nfsroot目录下生成了一个usr的目录
演示样例的交叉编译
mg-samples-1.6.10.tar.gz演示样例的交叉编译
(3)、、mg-samples-1.6.10.tar.gz
(3)
#cd mg-samples-1.6.10
#vim configure//修改Configure
//在文件开头处添加
#cp../simplecorss.sh.//将implecorss.sh脚本复制到该目录下
#vim simplecorss.sh//修改implecorss.sh
将脚本中prefix都指向/MiniGUI-arm/nfsroot
#./simplecorss.sh
#cd src//进入src目录下查看交叉编译的可执行程序
#file helloworld//查看helloworld的文件信息
若图中的intel80386为ARM则说明交叉编译成功,否则编译失败。若下载开发板上运行时会报错
#rm-f*.c
#rm-f*.o
#rm-f Makefile//删除不需要移植的文件
3、修改移植所需的MiniGUI.cfg文件
#cd/MiniGUI-arm/nfsroot/etc
#vim MiniGUI.cfg
图中的defaultmode必须为800*480为开发板LCD的尺寸,输入引擎为console,操作时可以用鼠标。用触摸程序则无响应。
至此移植所需的文件准备完毕!
4、MiniGUI移植到ARM开发板
1、在宿主机上建立超级终端,连接到S3C2440开发板
2、将MiniGUI.cfg下载到开发板上的/etc目录下
3、将/MiniGUI-arm/nfsroot的lib打包后下载到开发板的/usr下
#cd/MiniGUI-arm/nfsroot
#tar cvf lib.tar lib#cp lib.tar/mnt/hgfs/linux//将打包的lib.tar复制到共享目录
在超级终端下,进入/usr目录解压
#cd/usr
#tar zxvf lib.tar
#rm-f lib.tar
于是在开发板的/usr/lib下有了交叉编译后的库文件
4、将/MiniGUI/nfsroot/usr/local/lib的minigui打包下载到开发板的/usr/local/lib下,没有local/lib目录可以自己建一个
#cd/MiniGUI/nfsroot/usr/local/lib
#tar cvf minigui.tar minigui
#co minigui.tar/mnt/hgfs/linux//复制到共享目录
在超级终端下
#cd/usr/local/lib
#tar zxvf minigui.tar
#rm-f minigui.tar
于是在开发板/usr.local/lib有了交叉编译后的资源文件
5、运行helloworld程序
在开发板上显示helloworld的程序界面,成功移植了MiniGUI
5、交叉编译器的改进和完善为了能在开发板正常显示应用程序中的图片和用arm-linux-gcc顺利编译自己的应用程序,需在交叉编译器的include和lib目录下添加minigui的头文件和库文件。否则在交叉编译的应用程序时会报错,提示相应的头文件找不到和API函数未定义。自己解决的方法:再复制一份libminigui-1.6.10.tar.gz到/opt下
#tar zxvf libminigui-1.6.10.tar.gz
#cd libminigui-1.6.10
#cp../libcorss.sh.//复制libcorss.sh脚本到该目录下
#vim configure
//在文件开头处添加
#vim libcorss.sh
./libcorss.sh
图中的--enable选项的no改为yes,prefix指向交叉编译lib和include所在的目录。
这样可以顺利的用arm-linux-gcc命令交叉编译自己的应用程序以及在开发板上显示带有jpeg,png,gif格式的图片
MiniGUI串口传输及界面设计
四、
四、MiniGUI
1、MiniGUI设计知识简介
MiniGUI是由北京飞漫公司的一款针对嵌入式设备的、跨操作系统的图形界面支持系统的自由软件。正是因为MiniGUI是一个图形界面支持系
统,所以,通常的GUI编程概念均适用于MiniGUI编程;例如:窗口和事件驱动编程。
程序C文件的构成
(1)
(1)程序
(1.1)头文件
MiniGUI应用程必须包含的四个头文件 minigui.h:包含全局的和通用的接口函数以及某些杂项函数的定义; gdi.h:包含MiniGUI绘图函数的接口定义; window.h:包含窗口有关的宏,数据类型,数据结构的定义及函数接口声明; 在使用预定义控件时,还必须包含 control.h:包含libminigui中所有内建控件的接口定义 (1.2)程序入口 一个C程序的入口为main函数,而一个MiniGUI应用程序的入口为MiniGUIMain函数;原型如下 int MiniGUIMain(int argc,const char*argv[]) main函数已经在MiniGUI的函数库中定义了,该函数在进行一些MiniGUI的出始化工作之后调用MiniGUIMain函数。参数argc和argv 与C程序main函数的参数argc和argv的含义是一样的,分别为命令行参数个数和参数字符串数组指针。 (1.3)消息循环 消息循环是响应事件动作的纽带,MiniGUI会为每个MiniGUI应用程序维护一个消息队列,在事件发生后,MiniGUI将事件转换为消息,并将消息放入应用程序的消息队列中。而应用程序的任务就是执行消息循环,不断从消息队列中取出消息进行处理。消息循环代码如下: GetMessage函数从应用程序的消息队列取出消息,并填充MSG消息结构的各项属性。TranslateMessage函数把击键消息转换为MSG_CHAR消息,然后直接发往窗口过程函数;DispatchMessage函数最终把消息发往该消息的目标窗口的过程函数进行处理,然后在又返回到DispatchMessage函数中,最终回到应用程序代码中,从而实现消息的循环。 (1.4)程序退出 当用户单击应用的关闭按钮时,系统会向应用程序发送MSG_CLOSE,在应用程序的过程函数中作相应的处理,如:销毁窗口或对话框,结束消息循环即可退出应用程序,并释放相应的系统资源。 窗口和对话框及基本控件的编程 (2)窗口和对话框及基本控件的编程 (2) (2.1)主窗口的创建和显示 MiniGUI应用程序的初始界面一般为一个主窗口,通过函数CreateMainWindow创建,其参数为一个指向MAINWINCREATE结构的指针,返回值为所创建主窗口的句柄。MAINWINCREATE结构描述主窗口的属性,在创建主窗口之前需要先设置窗口的各项属性。在创建完主窗口还需要调用ShowWindow函数才能把所创建的窗口显示在屏幕上MAINWINCREATE原型具体可参见《MiniGUI编程指南》 (2.2)对话框及基本控件的编程 在MiniGUI中,对话框是一类特殊的窗口,这种主窗口只关注与用户的交互,即向用户提供输出信息和为用户提供输入。可通过对话框模版DLGTEMPLATE来建立对话框,指定控件数目;利用CTRLDATA结构来定义对话框中所需的控件,并用数组表示。然后将DLGTEMPLATE结构的controls指针指向所定义的控件数组,实现两者的关联。 CTRLDATA函数原型 DLGTEMPLATE函数原型 MiniGUI程序的编译链接 (3)MiniGUI (3) 将MiniGUI程序编译为MiniGUI-Threads模式,则采用如下的编译命令 —l选项指定生成应用程序所要连接的库。编译为MiniGUI-Threads模式时需要链接pthread库,minigui库为MiniGUI的核心库,编译需作链接,而jpeg,png,z库为MiniGUI内部所依赖的函数库,以支持jpeg,png格式的图片。 2、串口传输知识简介串口传输分为同步传输(USRT)和异步传输(UART)两种,而异步通信串口是嵌入式系统中最常用的通信设备;所谓异步传输是指异步通信的数据传输过程中,接收时钟与发送时钟是不同步的,在发送端和接收端都有自己的时钟和相同的速度约定。异步传输的数据是以帧方式传输的。常用的物理层标准为RS232。在进行数据传输之前,须先设定好传输的波特率,数据位,停止和校验位及数据流控制等信息。 3、程序整体框架设计 1、界面设计整体样式框图 2、实际界面图4、重要程序解析 1、对话框及其控件代码 //定义对话框 static DLGTEMPLATE DlgInitProgress= { WS_BORDER|WS_CAPTION, WS_EX_NONE, 0,30,0,420, "MiniGUI Serial Port 0,0, 12,NULL, }; //定义控件 static CTRLDATA CtrlInitProgress[]= { {"static {"button {"button {"button {"button {"button {"button {"combobox {"button {"button {"mledit {"ScrollWnd }; 2、窗口过程代码 static int GonesWinProc(HWND hWnd,int message,WPARAM wParam,LPARAM lParam) { HDC hdc; static HWND hStaticCtrl0,hButtonCtrl0,hmenu; switch(message) {case MSG_INITDIALOG: {int i; for(i=IDC_STATIC_SETBR+1;i SendDlgItemMessage(hWnd,IDL_COMDATA,CB_ADDSTRING,0,(LPARAM)comdata [i]); SetNotificationCallback(GetDlgItem(hWnd, IDL_COMDATA),CommoboxProc);} {hScrollwnd=GetDlgItem(hWnd,IDC_SCROLLWND); SendMessage(hScrollwnd,SVM_SETCONTAINERPROC,0,(LPARAM)ImageViewPro c); SendMessage(hScrollwnd,SVM_SETCONTRANGE,bitmap1.bmWidth,bitmap1.bm Height);} return1; case MSG_PAINT:hdc=BeginPaint(hWnd); FillBoxWithBitmap(hdc,250,0,110,230,&bitmap0); EndPaint(hWnd,hdc); return0; case MSG_CLOSE:UnloadBitmap(&bitmap0); UnloadBitmap(&bitmap1); DestroyMainWindow(hWnd); PostQuitMessage(hWnd); return0; break; case MSG_COMMAND:switch(wParam) {case IDC_BUTTON_QUIT: UnloadBitmap(&bitmap0); UnloadBitmap(&bitmap1); DestroyMainWindow(hWnd); PostQuitMessage(hWnd); return0; break; case IDC_SUBMENU_ABOUT: MessageBox(hWnd,"Developer is Gones whose StuID is020******* break;} case MSG_CREATE:if(LoadBitmap(HDC_SCREEN,&bitmap0, "handsome.jpg"))return1; return0; } return DefaultDialogProc(hWnd,message,wParam,lParam); } 3、串口设置代码 //设置波特率 int set_speed(int fd,int speed) { int i; int status; struct termios options; bzero(&options,sizeof(options)); tcgetattr(fd,&options);//得到当前串口的参数 for(i=0;i if(speed==name_arr[i]) { tcflush(fd,TCIOFLUSH); cfsetispeed(&options,speed_arr[i]); //设置输入波特率 cfsetospeed(&options,speed_arr[i]); //设置输出波特率 status=tcsetattr(fd,TCSANOW,&options); printf("status=%d\\n if(status!=0) { perror("tcsetattr fd"); return(-1); } } options.c_cflag|=(CLOCAL|CREAD);//使能接收并使能本地状态 tcflush(fd,TCIOFLUSH); } return0; } //打开串口函数int OpenDevice(char*Dev) { int fd=open(Dev,O_RDWR);//打开你选择的端口 if(-1==fd) { perror("Can't Open Serial Port");//设备打开失败 return-1; } else { printf("Open Serial Port:%s\\n return fd; } } //程序界面的串口设置代码 static void my_notif_proc(HWND hwnd1,int id,int nc,DWORD add_data) { char READ_BAUDRATE[8]; char speed[8][8]={"115200 int name_arr[8]={115200,57600,38400,19200,9600,4800,2400,300}; char READ_PORT[8]; char PORT[2][8]={"串口0 Char dev[2][12]={"/dev/tq2440_serial0 char READ_DATABIT[8]; char DATABIT[2][8]={"8 char DATALEN[2]={8,7}; char databits; char READ_STOPBIT[8]; char STOPBIT[2][8]={"1 char STOPLEN[2]={1,2}; char stopbits; char READ_PARITY[8]; char PARITYBIT[4][8]={"None char PARITYSTYLE[4]={'N','O','E','S'}; char parity; int i,j,nwrite; char str[1024]; pthread_t thread_id; hwnd=hwnd1; switch(id) { case IDC_BUTTON_OPEN: /*选择端口*/ GetWindowTextGetDlgItem(GetParent(hwnd1),IDC_BOX_PORT),READ_PORT,sizeof( READ_PORT)); for(i=0;i<2;i++) for(j=0;PORT[i][j]==READ_PORT[j]&&j<6;j++) { if(j==5){ fd=OpenDevice("/dev/tq2440_serial0"); } } /*设置波特率*/ GetWindowText(GetDlgItem(GetParent(hwnd1),IDC_BOX_BAUDRATE),READ_BAUDRAT E,sizeof(READ_BAUDRATE)); for(i=0;i<8;i++) for(j=0;speed[i][j]==READ_BAUDRATE[j]&&j<8;j++) { if(j==2) {set_speed(fd,name_arr[i]);} } /*设置数据位、停止位、奇偶检验*/ GetWindowText(GetDlgItem(GetParent(hwnd1),IDC_PROMPT_DATABIT),READ_DATAB IT,sizeof(READ_DATABIT)); for(i=0;i<8;i++) for(j=0;DATABIT[i][j]==READ_DATABIT[j]&&j<8;j++) {if(j==1) {databits=DATALEN[i];} GetWindowText(GetDlgItem(GetParenthwnd1),IDC_PROMPT_STOPBIT),READ_STOPBI T,sizeof(READ_STOPBIT)); for(i=0;i<8;i++) for(j=0;STOPBIT[i][j]==READ_STOPBIT[j]&&j<8;j++) { if(j==1) { stopbits=STOPLEN[i]; } } GetWindowText(GetDlgItem(GetParent(hwnd1),IDC_PROMPT_PARITYBIT),READ_PAR ITY,sizeof(READ_PARITY)); for(i=0;i<8;i++) for(j=0;PARITYBIT[i][j]==READ_PARITY[j]&&j<8;j++) { if(j==2) { parity=PARITYSTYLE[i]; } } set_Parity(fd,databits,stopbits,parity); if(pthread_create(&thread_id,NULL,(void*)ReadThread,NULL)!=0) 4、串口读写代码//创建读串口线程 if(pthread_create(&thread_id,NULL,(void*)ReadThread,NULL)!=0) printf("ReadThread_create is failed!\\n"); break; case IDC_BUTTON_CLOSE:close(fd); printf("Close Serial Port\\n"); break; case IDC_BUTTON_SEND: GetWindowText(GetDlgItem(GetParent(hwnd1),IDC_SEND_CHARS),str,1024); //printf("%s\\n nwrite=write(fd,str,strlen(str)); if(nwrite==-1) {printf("Write Serial Port Failed\\n"); close(fd); } break; case IDC_BUTTON_0:ButtonDown(IDC_BUTTON_0);break; case IDC_BUTTON_1:ButtonDown(IDC_BUTTON_1);break; case IDC_BUTTON_2:ButtonDown(IDC_BUTTON_2);break; case IDC_BUTTON_3:ButtonDown(IDC_BUTTON_3);break; case IDC_BUTTON_4:ButtonDown(IDC_BUTTON_4);break; case IDC_BUTTON_5:ButtonDown(IDC_BUTTON_5);break; case IDC_BUTTON_6:ButtonDown(IDC_BUTTON_6);break; case IDC_BUTTON_7:ButtonDown(IDC_BUTTON_7);break; case IDC_BUTTON_8:ButtonDown(IDC_BUTTON_8);break; case IDC_BUTTON_9:ButtonDown(IDC_BUTTON_9);break; default:break; } } /*读串口线程*/ void ReadThread(void*arg) { int nread; char readdata[1024]; char str[1024]; while(1) {sleep(1); memset(&readdata,0,1024); nread=read(fd,readdata,1024); if(nread==-1) {printf("Read Serial Port Failed\\n");} GetWindowText(GetDlgItem(GetParent(hwnd),IDC_RECIEVE_CHARS),str,st rlen(str)); memcpy(&str[strlen(str)],&readdata,strlen(readdata)); SetWindowText(GetDlgItem(GetParent(hwnd),IDC_RECIEVE_CHARS),str);}}程序调试 五、程序调试 五、1、接收测试方法 检查ARM开发板与PC机串口连接是否良好,在宿主机超级终端输入发送的数据,可在开发板上MiniGUI应用上显示即说明接收成功 2、发送测试方法 在开发板MiniGUI应用程序中预先定义发送的数据,在按下"Send"之后发送数据。在宿主机的超级终端下显示预定义的数据即可说明发送成功3、设计的程序界面在开发板上的运行效果 设计总结及改进 六、设计总结及改进 六、 1、总结 MiniGUI在PC机上的安装以及移植到ARM开发板的交叉编译。两者过程的本质是一样的,只不过是所用的编译工具不同以及运行的环境不同而已。在PC机上安装成功之后,移植便可照此思路。在ARM开发板上的嵌入式操作系统中类似的复制MiniGUI所需的资源和环境(可理解为路径)即在PC机/usr/lib目录下的文件交叉编译后移植到ARM开发板上的/usr/lib路径下;PC机/usr/local/lib路径下的文件交叉编译后同样移植到ARM开发板的/usr/local/lib路径下;唯一需要特别修改的就是MiniGUI.cfg文件。因为该文件配置了MiniGUI的图像引擎和输入引擎。在PC机通常采用qvfb作为图像引擎和输入引擎,而在开发板则不同。不同的开发板有不同的显示设备和输入输出驱动。所以,该文件的正确配置直接影响MiniGUI程序界面能否在开发板上显示。同样将MiniGUI.cfg文件下载到开发板的/etc目录下; 使用交叉编译器编译应用程序时,需将MiniGUI应用程序所需的头文件和库文件交叉编译到交叉编译器[include,lib]的目录下,避免使用arm-linux-gcc命令编译时报错,同时也要带上相应的链接库,如-lpthread,-lminigui,-ljpeg,-lpng,-lz; 文件传输可以采用串口或者NFS服务。实际因为要上网查阅资料,故采用传输较慢的串口传输。使用串口传输时可以将需要下载的多个文件一起打包,然后下载,这样可以避免文件的遗漏或者丢失损坏。 2、改进 界面开发有待完善,实现更好的人机交互,符合人的操作习惯;程序有待优化,尽量减少内存的占用,提高程序运行的稳定性。 心得体会 七、 七、心得体会 通过此次课程设计,实际动手操作了内核,文件系统,应用程序的移植。对Linux操作系统以及程序的编译链接运行有了进一步的认识。初步了解掌握了Linux下MiniGUI图形用户界面的开发。因为在前期的移植和交叉编译过程遇到很多问题,导致后面重要的串口驱动没能开发出来。总之,学到很多知识! 参考文献 八、参考文献 八、 系统开发技术详解--杨水清,张剑,施云飞》《ARM嵌入式Linux系统开发技术详解 《嵌入式Linux 设备驱动开发详解,华清远见嵌入式培训李俊》Linux设备驱动开发详解,华清远见嵌入式培训 编程指南》 《MiniGUI MiniGUI编程指南》 附录 九、 九、附录下载本文