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
|
|