![]() |
中国DOS联盟-- 联合DOS 推动DOS 发展DOS --联盟域名:www.cn-dos.net 论坛域名:www.cn-dos.net/forum |
| 游客 | 登录 | 注册 | 会员 | 搜索 | 中国DOS联盟 |
|
中国DOS联盟论坛 现在时间是 2026-08-01 18:37 |
共 48,037 主题排行 / 350,122 发帖 / 今日 2 篇 / 48,249 会员排行 |
| DOS开发编程 & 发展交流 (开发室) » Seekfor eDOS v1.0移植指南 |
| 可打印版本 3,347 / 0 |
| 第1楼 seekfor | 发表于 2006-11-18 21:29 |
| 荣誉版主 发帖 23 积分 57 | |
|
Seekfor eDOS v1.0移植指南 Seekfor eDOS v1.0移植非常方便,有的时候甚至只需要更改几个头文件中的宏定义即可.在kernel代码中,所有和硬件相关的功能都做成了全局变量和函数指针的形式,而且所有的初始化都是在main.c中.因此,要成功移植eDOS到不同的硬件系统,只需要更改main.c中部分功能定义即可!
第一章 Seekfor eDOS v1.0的硬件需求 <1> Seekfor eDOS v1.0支持哪些系列的CPU? eDOS需要运行在冯.诺依曼体系的CPU上,这种CPU数据和指令地址共用,因此可以把ROM中的代码拷贝到RAM(SRAM或者SDRAM)中运行.这种CPU典型的如ARM和X86. <2> Seekfor eDOS v1.0需要什么样的硬件设备条件? 要成功移植eDOS到新的硬件环境中,新环境至少需要提供4M以上的RAM,能提供一个正常工作的stdin设备(如CPU的UART,键盘等)和一个正常工作的stdout设备(如CPU的UART,CRT,LCD等).stdin是eDOS的输入设备,stdout是eDOS的输出设备,因此,这两个设备工作不正常都会导致eDOS无法运行shell.另外,硬件环境还应该提供如NAND flash,SD卡之类的存储器设备,实现数据的掉电保存(如果没有这些设备,eDOS可以利用RAM来虚拟一个存储器,这会消耗部分RAM). <3>Seekfor eDOS v1.0的RAM内存映射 用eDOS for S3C2410为例:RAM开始地址是0x30000000,动态内存大小设置为8M,则RAM空间的占用情况如下: 0x30000000~0x301fffff(2M空间)保留为eDOS内存空间,移植代码包括RO,RW,ZI必须在2M范围内(如果移植确实还需要比较大的RAM空间的话,建议用malloc()获取指针而不是用静态数组的方法) 0x30200000~0x309fffff(8M空间),这是动态内存区,这个区域大小可以在移植eDOS的时候在main.c或者dos_cfg.h中指定大小. 0x30a00000~RAM最顶端,地址低端可能是用户的内存驻留程序(TSR程序),紧接着就是exe文件和com文件的加载地址,每次加载一个exe文件,都是先把所有的代码copy到这个地址,然后初始化RW段和ZI段. 第二章 Seekfor eDOS v1.0的软件架构 eDOS v1.0提供给用户一个eDOS.a(库文件)和main.c(初始化文件),以及对应eDOS内核代码的*.h文件.用户需要提供初始化文件(如S3C2410的2410init.s等),用来完成C环境设置,中断设置和堆栈设置(在ARM下,一般用标准的2410init.s,44b0init.s等即可完成),设置完成后直接调用或跳转到main.c()中的main()函数即可. 请看eDOS for S3C2410的工程设置图: kernel下只有eDOS.a和main.c两个文件,S3C2410下有2410init.s等众多文件.事实上这些文件基本上是设备驱动文件,和移植有关的文件只有2410init.s和chips.c.2410init.s实现硬件环境初始化并跳转到main.c中的main(), chips.c通过调用eDOS的API添加所有的设备到系统中.其中的2410init.s直接采用标准2410开发板中的代码,只不过把”BL Main”代码更改为”BL main”即可. 简单的来说,移植eDOS需要以下几步: (1) 编写初始化文件(如上例的2410init.s),实现硬件环境初始化,并跳转到main()执行 (2) 编写和硬件相关的设备驱动程序,关键是编写好stdin和stdout驱动程序 (3) 在DOS_cfg.h中重新定义必要的功能需求(如硬件中无NAND flash等存储器设备,则必须定义SUPPORT_RAM=1来加入一个RAM虚拟盘) 先看一下main.c中的代码: int main() { /*必须先设置好系统参数*/ SYSPARAMS_init(); /*在这里调用和CPU相关的硬件初始化等*/ #ifdef CHIPS_init CHIPS_init(); #endif /*如果用户希望支持RAM磁盘,则初始化一个RAM盘到系统中*/ #if(SUPPORT_RAM==1) RAM_init(); #endif /*如果用户系统包含LCD等点阵显示类驱动器,则挂接一个LCD:设备到系统中(在CHIPS_init()中必须先初始化)*/ #if(SUPPORT_GRAPHICS==1) Device_install("LCD:",WRITEABLE,(DEVICE_OPEN)Device_CommonOpen,(DEVICE_CLOSE)Device_CommonClose,(DEVICE_READ)0,(DEVICE_WRITE)LCD_Write,(DEVICE_ESCAPE)LCD_Escape); #endif /*自定义STDIO使用的设备*/ STDIO_init(); #if(SUPPORT_SHELL==1) shell(); #else printf("Warnning:no SHELL!Please adds your code here!\r\nSystem Halted!\r\n"); while(1); #endif return 0; } 以上代码分别执行如下过程: (1) SYSPARAMS_init()初始化系统参数,有些参数必须设置正确如ram_start_addr(RAM开始地址)等 (2) CHIPS_init()是用户自定义的初始化函数,CHIPS_init实际上是DOS_cfg.h中的一个宏 (3) RAM_init()实现把一个固定大小的RAM区域虚拟为一个磁盘驱动器,如果用户在CHIPS_init()中加入了别的磁盘驱动器,可以关闭SUPPORT_RAM实现小的RAM需求 (4) Device_install(…..)实现安装一个一个LCD驱动器到系统中,这个LCD驱动已经在eDOS内部集成,用户只需要在CHIPS_init()中通过调用initgraph()设置好写点和读点的函数指针即可实现LCD输出. (5) STDIO_init()实现加载stdin,stdout,stderr驱动.因此在这之前,所有的设备驱动都应该安装完毕(一般在CHIPS_init()中完成即可) (6) 最后,如果使用eDOS的shell功能,则跳到shell()中执行,否则需要自己在这里添加代码实现其他功能. Main.c中的代码除非用户需要添加功能,否则一般不需要更改.要实现自定义功能,只需要在DOS_cfg.h中定义必要的宏即可.下面详细分析一下dos_cfg.h中的宏. 用户可以自定义的部分有: (1) #define SUPPORT_RAM 1 #if(SUPPORT_RAM==1) #define RAM_BYTES_PER_SECTOR 512 #define RAM_SECOTR_TOTAL 4096 /*RAM参数:FAT16,512字节每扇区,首扇区从0扇区开始,共4096个扇区(2M空间)*/ #define RAM_PARAMS {0,RAM_BYTES_PER_SECTOR,0,RAM_SECOTR_TOTAL,0,RAM_read,RAM_write,0} #endif 以上几行代码实现是否在eDOS中加入RAM盘,以及RAM盘的规格,这里的含义是支持RAM盘,RAM盘是512字节/扇区,共4096个扇区,消耗的RAM是512*4096=2M字节.(注,一般在硬件系统不含NAND flash,SD,HDD等存储设备的时候才打开这个开关而且这时候必须打开这个开关),这些宏在main.c中的RAM_init()中使用. (2) /*stdin,stdout,stderr使用的设备*/ #define STDOUT_USING "LCD:" #define STDIN_USING "COM1:" #define STDERR_USING "COM1:” 这几个定义是确定标准输入输出使用哪一个设备.必须使用在Device_install()中成功安装的设备驱动名(这个工作一般在CHIPS_init()中完成)!这几个宏在main.c中的STDIO_init()中使用! (3) /*控制台程序*/ #define CONSOLE_USING CONSOLE_OTHER 当stdout是UART并且在PC上显示时,定义这个宏用来表示上位机程序用dnw.exe还是Windows的超级终端程序,如果stdout不是UART(如上例是LCD)则不定义都行.值在stdio.h中有定义,该宏在main.c中的SYSPARAMS_init()中使用! (4) /*RAM空间开始地址*/ #define RAM_START_ADDR 0x30000000 定义硬件设备的内存开始地址,这个值必须和硬件环境相同,否则默认是0x00000000,这会导致错误. 该宏在main.c中的SYSPARAMS_init()中使用! (5) /*动态分配RAM大小,8M Bytes*/ #define MALLOC_SIZE (1024*1024*8) 定义系统使用的动态空间大小,默认是64K空间,一般最少定义在1M,看系统需求而定. 该宏在main.c中的SYSPARAMS_init()中使用! (6) /*执行代码是否可以load到任意空间运行*/ #define REALLOCATABLE 0 定义CPU是否支持把exe文件load到任意地址运行,0=不支持,1=支持,ARM一般是0,X86可以是1*/ (7) /*CPU初始化,如果不需要,屏蔽此宏定义即可*/ #define CHIPS_init S3C2410_init 定义硬件相关的初始化函数名称,移植时一般都在这个函数里面加载所有的设备驱动程序. (8) /*定义和上位机通讯的fread(),fwrite()函数*/ #define CONNECT_fwrite COM1_Write #define CONNECT_fread COM1_Read 定义和PC通讯的两个函数,如果不定义则remotedisks.exe无法联机! (9) #define EXECUTE_CALLBACK CACHE_CLEAR 这是定义在exec()函数执行一个exe或com文件前调用的一个函数指针.在ARM下因为使用了指令cache,所以在运行exe,com文件前必须清空指令cache,否则程序运行不正常,当然也可以设置一个函数来做其他的工作! (10) /*和CPU有关的API重定义,如果CPU不能实现对应的功能,则把宏定义屏蔽即可*/ #define getdate RTC_getdate #define gettime RTC_gettime #define setdate RTC_setdate #define settime RTC_settime #define enable _enable #define disable _disable #define getvect _getvect #define setvect _setvect 这些是CPU特殊功能API,不定义也可以,但会导致对应的API执行无功能! 从以上代码可以看出,要移植eDOS到不同的硬件环境,所做的工作非常少,只需要更改部分宏定义,加入部分设备驱动即可!下面详细叙述一下eDOS的设备驱动格式和安装方法. 第三章 Seekfor eDOS v1.0的设备驱动格式 eDOS一个重要的功能就是硬件设备的文件化,标准化.eDOS内部区分两种文件,一种是磁盘文件,另外一种是设备文件.设备文件是虚拟的文件,可以通过fopen()打开驱动,用fread()读驱动信息或数据,用fwrite()写数据给驱动,用fclose()关闭驱动等.一般情况下,设备文件需要一个特殊的文件名(名称虽然可以任意,但为了表示和磁盘文件的区别,一般名称设置为”XXXX:”的形式,如LCD驱动名称是”LCD:”,键盘驱动是”KBD:”等). <1>设备文件有哪些属性? 参考device.h中的DEVICE_LIST结构: typedef struct { char name[32];/*这是设备名称*/ int mode;/*bit0,可读,bit1,可写,bit6,可共享读,bit7,可共享写,其他位用户可自定义*/ int nest; DEVICE_OPEN lpfnOpen;/*fopen()调用*/ DEVICE_CLOSE lpfnClose;/*fclose()调用*/ DEVICE_READ lpfnRead;/*fread()调用*/ DEVICE_WRITE lpfnWrite;/*fwrite()调用*/ DEVICE_ESCAPE lpfnEscape;/*Device_Escape()调用*/ void*user_data; void*prev; void*next; }DEVICE_LIST; 以上结构中有注释的就是设备属性,事实上,安装一个设备就是把这些属性写入一个DEVICE_LIST结构并且连接到系统设备列表的过程.用户调用fopen()打开一个设备的时候,会通过文件名在设备列表中查找设备,如果找到则调用该设备的lpfnOpen()实现对该设备的初始化功能,fread(),fwrite()和fclose()依此类推.因此,要安装一个设备,最重要的就是写好这几个函数,然后直接用系统API Device_install()安装即可! 先看一下这几个函数指针的原型: typedef void*(*DEVICE_OPEN)(char*name,char*mode);/*name:驱动设备名称,mode:文件打开模式,可能是读/写,或者读写模式*/ typedef int (*DEVICE_CLOSE)(void*p);/*p:设备文件指针*/ typedef int (*DEVICE_READ)(void*buffer,int size);/*buffer:接收缓冲区,size:要读的数据长度*/ typedef int(*DEVICE_WRITE)(void*buffer,int size);/*buffer:写入的缓冲区地址,size:写入的长度*/ typedef int(*DEVICE_ESCAPE)(int code,void*user_data);/*设备直接通讯功能,code和user_data定义和设备驱动解释相关*/ 现在我们假设成功安装了一个名字叫”TFT:”的设备,则使用该设备的方法是: FILE*p=fopen(“TFT:”,”w”);/*写方式打开该设备,此时会自动调用TFT的lpfnOpen()函数*/ fwrite(“This is a demo program!\r\n”,25,1,p);/*写数据给TFT设备,这时是调用TFT的lpfnWrite()函数,只要lpfnWrite()能正确把数据显示到屏幕上,这行代码就是显示”This is a demo program!”字符串到屏幕上*/ fclose(p);/*关闭TFT设备,此时调用TFT的lpfnClose(),调用后p不再有效*/ 从上例可以看出,要写一个设备驱动程序,只要正确写好该设备对应的lpfnOpen(),lpfnRead(),lpfnWrite()和lpfnClose()即可.实际上,很多设备都可以直接用系统共用的Device_CommonOpen()和Device_CommonClose()参数来设置lpfnOpen和lpfnClose,因此,设计设备驱动程序,就是如何写好lpfnRead()和lpfnWrite()了! 下面举两个设备驱动的例子,一个是输入设备,一个是输出设备: (1) 2410的按键驱动,这是一个输入设备,所以只需要写lpfnOpen(),lpfnClose和lpfnRead()即可.假定该按键接在GPG.5. 首先,编写lpfnOpen(). void*KBD_Open(char*name,char*mode)/*参数是fopen()自动传递的*/ { FILE*p=(FILE*)Device_CommonOpen(name,mode);/*必须先调用Device_CommonOpen()获取一个FILE*指针*/ if(!p) return (void*)0;/*返回NULL表示内存不足*/ rGPGCON&=~((1<<12)|(1<<13));/*GPG.5设置为输入状态*/ return (void*)p;/*返回p指针即可*/ } 注意,因为这里需要设置IO口状态,因此定义自己的KBD_Open(),如果不需要做任何初始化,完全可以在Device_install()的时候用Device_CommonOpen参数.但如果使用了自己的XXX_Open(),则必须保证调用一次Device_CommonOpen()并且直接返回该返回值(如上例) 接着,编写lpfnRead() int KBD_Read(void*buff,int size)/*参数是fread()自动传递*/ { char*ptr=(char*)buff; while(size--) ptr++=rGPGDAT&0x20;/*读取size次IO口状态到buff*/ return 0;/*返回0表示读取没有出错*/ } 然后编写lpfnClose(): 事实上如果不需要更改IO口状态的话,可以用Device_CommonClose()代替这个函数,但如果定义了,必须调用Device_CommonClose()一次! int KBD_Close(FILE*p)/*fclose()自动传递*/ { rGPGCON|=3<<12;/*恢复输出状态*/ return Device_CommonClose(p); } 最后,调用Device_install()安装该驱动: Device_install(“KBD:”/*驱动名*/,READABLE|SHARE_READ/*可读可共享读*/,(DEVICE_OPEN)KBD_Open,(DEVICE_CLOSE)KBD_Close,(DEVICE_READ)KBD_Read,(DEVICE_WRITE)0,(DEVICE_ESCAPE)0);/*因为”KBD:”不支持Write和Escape功能,则lpfnWrite和lpfnEscape都是0*/ 这样,一个名为”KBD:”的驱动设备就安装到了eDOS系统中.如何使用该设备读按键呢?过程如下: unsigned char key; FILE*p=fopen(“KBD:”,”r”);/*读打开设备*/ fread(&key,1,1,p);/*从p中读一个字节*/ fclose(p);/*关闭*/ if(!key) printf(“KEY pressed!\r\n”); else printf(“KEY released!\r\n”); (2) 2410的LED输出驱动,LED接在GPB.0 void*LED_Open(char*name,char*mode) { FILE*p=(FILE*)Device_CommonOpen(name,mode); if(!p) return (void*)0; rGPBCON&=~0x0f; rGPBCON|=0x05;/*IO口初始化为输出*/ return(void*)p; } int LED_Write(void*buff,int size) { BYTE*ptr=(BYTE*)buff; while(size--) { rGPBDAT&=~0x01; if(*ptr++) rGPBDAT|=0x01; } return 0; } 调用系统API安装(注意,没有自定义lpfnClose,则这里用Device_CommonClose代替): Device_install(“LED:”,WRITEABLE,(DEVICE_OPEN)LED_Open,(DEVICE_CLOSE)Device_CommonClose,(DEVICE_READ)0,(DEVICE_WRITE)LED_Write,(DEVOCE_ESCAPE)0); 使用驱动程序”LED:”来实现500ms闪烁功能的代码如下: void main() { unsigned char i; FILE*p=fopen(“LED:”,”w”); while(1) { i=0; fwrite(&i,1,1,p); delay(500); i=1; fwrite(&i,1,1,p); delay(500); } } 由此可见,eDOS仅仅提供了一个通用的驱动程序通信的机制,具体实现什么功能是在XXX_read(),XXX_write()中实现了.但是,stdout因为是console的输出驱动,所有stdout的驱动必须支持printf()中的转意字符如’\r’,’\n’,’\t’!因此,在stdout的驱动中,必须逐个字符分析fwrite()传来的buffer信息,具体请参考下列伪代码: int LCD_Write(char*buffer,int size) { while(size--) { if(*buffer==’\r’) { } else if(*buffer==’\n’) { } else if(*buffer==’\t’) { } else if(*buffer==’\b’) { } else ShowText(*buffer); Buffer++; } return 0; } Seekfor eDOS v1.0内部已经包含了一个名为”LCD:”的驱动,该驱动可以直接操作点阵输出的显示设备.只需要提供画点和读点函数给initgraph()即可!一般在CHIPS_init()通过如下方式调用: #define LCD_WIDTH 640 #define LCD_HEIGHT 480 int GetPixel(int x,int y) { /*加入具体的代码实现读点操作*/ } void SetPixel(int x,int y,int color) { /*加入实际的代码实现画点操作*/ } void LCD_init() { /*进行必要的硬件初始化,如设置寄存器等工作*/ initgraph(GetPixel,SetPixel,(LCD_WIDTH)|(LCD_HEIGHT<<16)); } 这样,eDOS的图形库功能即可使用,同时如果把DOS_cfg.h中的STDOUT_USING定义为”LCD:”,则eDOS的输出定义在了LCD上,所有的信息都在LCD上显示了! 第四章 如何安装FAT驱动器 FAT驱动器的安装需要使用FAT_install()和FAT_format()两个函数,这两个API都用到了一个结构FORMAT_PARAMS: typedef struct { BOOL bFAT32;/*0=FAT16,1=FAT32*/ DWORD dwBytesPerSector;/*每扇区字节数,一般是512,和硬件可Erase的单位有关如K9F1208是按block擦除的,所以这个参数应该是16384*/ DWORD dwStart;/*开始扇区号,一般是0即可*/ DWORD dwLength;/*总的扇区数目*/ SECTOR_ERASE lpfnErase;/*硬件相关擦除函数*/ SECTOR_READ lpfnRead;/*硬件相关读一个扇区函数*/ SECTOR_WRITE lpfnWrite;/*硬件相关写一个扇区函数*/ void user_data;/*用户数据,可以自动传递到lpfnErase,lpfnRead,lpfnWrite中使用*/ }FORMAT_PARAMS; 和其他设备驱动一样,FAT驱动也是主要是写好lpfnErase(),lpfnRead(),lpfnWrite()即可.具体情况参考main.c中的RAM盘的驱动!一般情况下,安装一个FAT驱动的步骤如下: FORMAT_PARAMS nand_fmt={0,16384,0,4096,NAND_erase,NAND_read,NAND_write,0};/*设置NAND参数*/ if(!FAT_install(nand_fmt.dwStart,nand_fmt.lpfnErase,nand_fmt.lpfnRead,nand_fmt.lpfnWrite,nand_fmt.user_data)) { FAT_format((DRIVER_INFO*)0,&nand_fmt); FAT_install(nand_fmt.dwStart,nand_fmt.lpfnErase,nand_fmt.lpfnRead,nand_fmt.lpfnWrite,nand_fmt.user_data); } 代码先尝试直接安装一个NAND驱动(如果上次格式化过则FAT_install()返回1而不是0),如果安装不成功可能是没有格式化则调用FAT_format()格式化一次,格式化完成后再次调用FAT_install()安装驱动,如果FAT成功安装,则eDOS下的磁盘驱动将有效!(盘符和调用FAT_install()的顺序有关,第一个是’C’,第二个是’D’,其他依此类推) 感谢所有关心和支持Seekfor eDOS v1.0的同道和朋友,你们的支持就是我最大的动力! 欢迎和我联系讨论eDOS方面问题 QQ:82054357 MSN:sfrad32@hotmail.com Mail:Seek_For@163.com |
|
|
[ 联系联盟系统管理团队 -
中国DOS联盟 -
标准版 ] Sponsored by ifanr Inc | © 2001–2023 |