中国DOS联盟论坛

中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --

联盟域名:www.cn-dos.net  论坛域名:www.cn-dos.net/forum
DOS,代表着自由开放与发展,我们努力起来,学习FreeDOS和Linux的自由开放与GNU精神,共同创造和发展美好的自由与GNU GPL世界吧!

游客:  注册 | 登录 | 命令行 | 会员 | 搜索 | 上传 | 帮助 »
作者:
标题: Seekfor eDOS v1.0移植指南 上一主题 | 下一主题
seekfor
荣誉版主





积分 57
发帖 23
注册 2006-10-28
状态 离线
『楼 主』:  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

2006-11-18 21:29
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复

请注意:您目前尚未注册或登录,请您注册登录以使用论坛的各项功能,例如发表和回复帖子等。


可打印版本 | 推荐给朋友 | 订阅主题 | 收藏主题



论坛跳转: