CE 学习资料(转别人的博客)

来源:互联网 发布:黄金现货软件 编辑:程序博客网 时间:2024/04/28 15:42

作者:xing_dragon

转自:http://blog.csdn.net/xing_dragon/article/details/6227231


2010年7月30日

PB的Built Options介绍

Buffer tracked events in RAM

启用RAM缓冲事件跟踪(OSCaptere.exe实现)

Enable CE Target Control Support

为 OS 设计启用 CE 目标控制支持会为您的 OS 设计启用目标控制支持,并且启用内核独立传输层 (KITL)。

Enable Eboot Space in Memory

配置Config.bib文件中预留内存空间,允许在启动过程中操作系统可以读取boot loader存储的数据.

Enable Event Tracking during Boot

开启事件跟踪子系统

Enable Full Kernel Mode

可使线程运行在内核模式。注意:选择该模式会使系统较脆弱,但是性能会有所提高

Enable Kernel Debugger

通过启用对内核调试器的支持,您可以调试基于您的 OS 设计创建的运行库映像;若选上该选项,调试器值为0,否则为1;

Enable KITL

内核独立传输层(Kernel Independent Transport Layer — KITL),为 OS 设计启用完全内核模式可以提高运行库映像中的系统性能,要建立宿主机和目标机间的通讯就必须选择该选项。取消该项也会同时取消被选定的“Enable CE Target Control Support ”选项;

Enable Profiling

将Windows CE的有关内核的信息以日志的形式装入平台镜像中

Enable Ship Build

这是一个有条件编译的标志,设置它表示OS会提供详细的调试信息来帮助调试。(这个选项只在release设置才显示的,而Debug下是没有这个选项的)

Flush tracked events to Release Directory

将事件也放进release目录,同时开启事件跟踪功能

Run-time Image Can be Larger than 32 MB

通过使运行库映像能够大于 32 MB,您可以具有更大的运行库映像。如果最终的运行库映像需要 32 MB 以上的空间,否则生成过程可能无法成功完成。

Use XCOPY instead of links to populate release directory

用Xcopy将所需的文件复制到Release目录(如:BSP,系统组件等)

posted @ 2010-07-30 23:57 jiege 阅读(10) | 评论(0) | 编辑
WINCE4.2到5.0(2)中断服务差异

共同的内核函数:
OEMInterruptHandler():中断服务例程ISR
OEMInterruptEnable():
OEMInterruptDisable():
OEMInterruptDone():顾名思意,应用层在调用InterruptEnable、InterruptDisable、InterruptDone函数时会调用这几个内核函数

4.2下的OEMInterruptHandler()函数会跟据转进来的硬件IRQ中断号返回对应的SYSINTR逻辑中断号,也就是说如果需要修改两个中断号对应关系,可以直接在这个函数中修改,这里的这种对应、映射关系称之为静态映射。OEMInterruptEnable()...三个函数传进来的参数是SYSINTR逻辑中断号,可以根据OEMInterruptHandler()函数中的映射关系,对硬件中断IRQ来进行相应的操作。这几个函数分别位于platform/bsp/kernel/hal/cfw.c与bsp/kernel/hal/arm/armint.c文件中。换句话说4.2下的中断可以称之为静态映射,也就是不能在运行时的应用/驱动层将硬件与逻辑IRQ进行映射。

5.0下的OEMIntrruptHandler()函数位于:platform/common/src/arm/samsung/s3c2410x/intr/intr.c中。注意,他不像4.2中的该函直接将参数传递进来的硬件IRQ送出对应的逻辑SYSIRQ,而是调用OALIntrTranslateIrq()函数进行映射。OALIntrTranslateIrq()函数位于platform/common/src/common/intr/base/map.c文件中。这个函数只是简单的将以Irq参数为下标的数组中找出SYSIRQ,然后返回:
UINT32 OALIntrTranslateIrq(UINT32 irq)
{
    UINT32 sysIntr = SYSINTR_UNDEFINED;
    if (irq>=OAL_INTR_IRQ_MAXIMUM) goto cleanUp;
    sysIntr = g_oalIrq2SysIntr[irq];
    return sysintr;
    ...
}
看这里的g_oalIrq2SysIntr数组,还有一个与之对应的g_oalSysIntr2Irq数组,从名字上可以看出功能。这两个数组也定义在map.c文件中,是通过OALIntrMapInit()函数进行初始化的。这里也可以进行静态映射,使用OALIntrStaticTranslate这个函数,它的实现过程就是填充数组:
OALIntrStaticTranslate(UINT32 sysIntr, UINT32 irq)
{
    ...
    g_oalSysIntr2Irq[sysIntr] = irq;
    g_oalIrq2SysIntr[irq] = sysIntr;
    ...
}
看一下流程:OEMInit()=>OALIntrInit()=>OALIntrMapInit()这个就是对两个数组进行初始化=>BSPIntrInit()可以在这个函数里用OALIntrStaticTranslate()函数进行静态映射。

5.0下的OEMInterruptEnable()...这三个函数位于platform/common/src/common/INTR/common/oem.c文件小,大致流程是这样的:
OEMInterruptEnable()=>OALIntrTranslateSysIntr()这个函数是在将逻辑中断号转换成硬件的系统中断号,与OALIntrTranslateIrq()函数功相反,位于同一个文件中。=>OALIntrEnableIrq()这个函数先调用BSPIntrEnableIrq()使得BSP有机会先做处理,这个函数与OEMIntrruptHandler()位于同一个文件中,在platform/common/src/arm/samsung/s3c241x/intr/intr.c文件中。
OEMInterruptEnable()=>OALIntrTranslateSysIntr()=>OALIntrEnableIrqs()=>BSPIntrEnableIrq()
OEMInterruptDisable()=>OALIntrTranslateSysIntr()=>OALIntrDisableIrqs()=>BSPIntrDisableIrq()
OEMInterruptDone()=>OALIntrTranslateSysIntr()=>OALIntrDoneIrqs()=>BSPIntrDoneIrq()

总结:
    跟4.2的区别,实际上主要是5.0可以/是在应用/驱动层使用OALIntrRequestSysIntr()动态请求/映射,而4.2好像不可以,至少我没找到。4.2的方法更简单明了,5.0的好处更明显,更改驱动等代码时无须动内核函数。
    上面5.0使用到的几个函数分别位于下面的文件中:
    platform/common/src/arm/samsung/s3c241x/intr/intr.c
        OALIntrDisableIrqs()
        OALIntrDoneIrqs()
        OALIntrEnableIrqs()
        OALIntrInit()
        OALIntrRequestIrqs()
        OEMInterruptHandler()

    platform/smdk410/src/kernel/oal/init.c
        OEMInit()

    platform/smdk2410/src/kernel/oal/intr.c
        BSPIntrActiveIrq()
        BSPIntrDisableIrq()
        BSPIntrDoneIrq()
        BSPIntrEnableIrq()
        BSPIntrInit()
        BSPIntrRequestIrqs()

    platform/common/src/common/intr/base/map.c
        OALIntrMapInit()
        OALIntrReleaseSysIntr()
        OALIntrRequestSysIntr()
        OALIntrStaticTranslate()
        OALIntrTranslateIrq()
        OALIntrTranslateSysIntr()

贴两个网上找到的资料,都是5.0的:

WinCE有两种私有的中断表,一种是物理中断——中断请求(IRQs),另一种是逻辑中断——SYSINTR值。WinCE必须将一个物理中断和一个逻辑中断关联起来。

物理中断号定义在platform/c8090/pubic/csp/arm/intel/pxa27x/inc/Bulverde_intr.h。

逻辑中断号定义在platform/c8090/platform/mainstoneii/src/inc/Bsp_cfg.h。

两个中断表定义在platform/c8090/platform/src/common/intr/base/map.c

static UINT32 g_oalSysIntr2Irq[SYSINTR_MAXIMUM];

static UINT32 g_oalIrq2SysIntr[OAL_INTR_IRQ_MAXIMUM];

OAL_INTR_IRQ_MAXIMUM定义在platform/c8090/common/src/inc/oal_intr.h,

#define OAL_INTR_IRQ_MAXIMUM 64

该值表示物理中断——IRQs的最大值,现在最大只支持到64。

SYSINTR_MAXIMUM定义在wm522/public/common/oak/inc/nkintr.h

#define SYSINTR_DEVICES 8

#define SYSINTR_MAX_DEVICES 64

#define SYSINTR_MAXIMUM (SYSINTR_DEVICES+SYSINTR_MAX_DEVICES)

OEM Adaptation Layer——OAL初始化函数是OEMInit(),它是WinCE的OAL层初始化函数,在基本初始化完成之后,由内核调用,定义在:

platform/c8090/platform/mainstoneii/src/kernel/oal/init.c。

在这里调用中断初始化函数:OALIntrInit(),该函数定义在:

platform/c8090/platform/common/src/arm/intel/pxa27x/intr/intr.c中,该函数首先调用函数OALIntrMapInit(),初始化前面提到的两个数组表g_oalSysIntr2Irq和goalIrq2SysIntr。该函数定义在platform/c8090/platform/common/src/common/intr/base/map.c,源码如下:

for (i = 0; i < SYSINTR_MAXIMUM; i++) {

g_oalSysIntr2Irq[i] = OAL_INTR_IRQ_UNDEFINED;

}

for (i = 0; i < OAL_INTR_IRQ_MAXIMUM; i++) {

g_oalIrq2SysIntr[i] = SYSINTR_UNDEFINED;

}

然后调用函数BSPIntrInit()将物理中断和逻辑中断关联起来,该函数定义在:

platform/c8090/platform/mainstoneii/src/kernel/oal/intr.c中。

关联代码例子如下:

OALIntrStaticTranslate(SYSINTR_PMIC, IRQ_GPIO0);

OALIntrStaticTranslate(SYSINTR_OHCI, IRQ_USBOHCI);

OALIntrStaticTranslate(SYSINTR_TOUCH, IRQ_GPIOXX_WM9712);

OALIntrStaticTranslate(SYSINTR_TOUCH_CHANGED, IRQ_OSMR1);

OALIntrStaticTranslate(SYSINTR_KEYPAD, IRQ_KEYPAD);

前面都是逻辑中断,后面是物理中断。OALIntrStaticTranslate函数定义在:

platform/c8090/platform/common/src/common/intr/base/map.c,源码如下:

if (irq < OAL_INTR_IRQ_MAXIMUM && sysIntr < SYSINTR_MAXIMUM) {

g_oalSysIntr2Irq[sysIntr] = irq;

g_oalIrq2SysIntr[irq] = sysIntr;

}

OAL是位于WindowsCE内核与目标设备硬件之间的一个代码层,用于实现windowCE与目标设备硬件之间的通信。为了实现内核与硬件之间最基本的通信功能,OEM必须实现一些必要的功能,同时为了适合不同的硬件配置与操作系统功能,OEM有必要的选择实现一些其他的功能。
在OAL开发过程中,OEM需要实现下列主要功能或函数:
.Startup函数;
.调试串口;
.OEMInit函数;
.系统计时器;
.中断处理;
.内核的输入/输出(ioctl);
.KITL。
下面给出微软公司提供的各个功能的函数。
/***************************中断处理****************************/
./platform/Common/src/common/intr/base/map.c
The file implement simple table/array based mapping between IRQ and SYSINTR
which is suitable for most OAL implementations.
这个文件主要是用来定义IRQ和SYSIRQ的映射关系。
主要函数如下:
OALIntrMapInit();//此函数由OALInterruptInit调用来初始化IRQ和SYSIRQ的映射,只是简单的映射数组的初始化;初始化为未定义类型。
VOID OALIntrStaticTranslate(UINT32 sysIntr, UINT32 irq); //此函数建立IRQ和SYSINTR的静态映射,大多数情况下都不可能用到,只有在SYSINTR_RTC_ALARM和过时的设备驱动的时候会用到。
BOOL OALIntrTranslateSysIntr(UINT32 sysIntr, UINT32 *pCount, const UINT32 **ppIrqs);//此函数将SYSINTR映射为它相应的IRQ,主要用在OEMInterruptXXX中对于给定SYSINTR得到其IRQs。
UINT32 OALIntrTranslateIrq(UINT32 irq);//此函数将IRQ映射为相应的SYSINTR。
UINT32 OALIntrRequestSysIntr(UINT32 count, const UINT32 *pIrqs, UINT32 flags);//此函数为给定的IRQ分配一个新的SYSINTR,如果此IRQ没有静态映射,就创建一个。
BOOL OALIntrReleaseSysIntr(UINT32 sysIntr);//此函数释放一个SYSINTR,并且消除存在的静态映射。
问题:
SYSINTR于IRQ的映射关系是不是一对一?BOOL OALIntrTranslateSysIntr(UINT32 sysIntr, UINT32 *pCount, const UINT32 **ppIrqs)使用的是地址来进行映射,而UINT32 OALIntrTranslateIrq(UINT32 irq)使用值来进行映射,目前我认为应该是一对一的关系来映射的。
感想:
微软的程序写的很缜密,设计的很合理,能让我学到不少编程的方法。映射关系的编程可以参考微软的中断映射表。

posted @ 2010-07-30 23:54 jiege 阅读(31) | 评论(0) | 编辑
一键同步启动屏保锁定计算机

一键同步启动屏保锁定计算机

当我们离开时,一般都会希望同时锁定计算机和执行屏幕保护程序,要达到这一点还真不是一件轻而易取的事情。为了满足大家的要求,经过对计算机锁定和启动屏保的深入研究,终于找到了切实可行的办法。
进入系统目录C:/Winnt/System32(Windows 2000)或C:/Windows/SYstem32(Windows XP),然后找到扩展名为scr的文件,这类文件都是屏幕保护程序。双击即可运行,在选择好该类程序后将其在当前位置复制一份并将其扩展名改为exe,例如ssstars.exe。
做好这项工作之后,就可以打开记事本,在里面输入如下内容:
@echo off
%windir%/system32/ssstars.exe /s  //运行系统目录下的ssstars.exe文件
%windir%/system32/rundll32.exe user32.dll,LockWorkStation //锁定计算机
@exit

 

(%windir%为系统路径-by Simba)
将该文件保存为1.bat(可以起一个有意义的名字),再右击该文件选择“发送到”菜单中的“桌面快捷方式”,最后打开新创建快捷方式的属性窗口,在“快捷键”中为该快捷方式的运行指定一个快捷键,这样我们在需要的时候只需要按下该快捷键即可同步启动屏保和锁定计算机。
需要注意的是如果是Windows XP,需要进入控制面板中打开用户账户项,单击“更改用户登录或注销的方式”,然后将“使用欢迎屏幕”项取消,这样才能达到锁定的目的。

posted @ 2010-07-30 23:36 jiege 阅读(170) | 评论(0) | 编辑
WinCE中nandflash驱动开发介绍

先来谈一下flash,flash是一种非易失存储器,一般flash存储设备分为Nandflash和Norflash。这两种flash各有优缺点。在读写速度上,norflash的读速度快一些,nandflash的写速度会快一些。Nandflash的容量一般都比Norflash大很多,而且相比价格比较便宜。但是Norflash支持XIP,而nandflash不支持,而且Nandflash可能有坏块。相关的比较,网上很多文章都有介绍,这里就说这么多了。

这里介绍nandflash驱动,在WinCE中,有专门针对flash存储设备驱动的支持,一般传统采用FAL+FMD的架构。在WinCE最新的版本中,也就是Windows CE6.0 R2中,还支持MDD+PDD的架构。在FAL+FMD架构中,FAL层由微软来实现,我们需要实现FMD层的相关接口函数。在MDD+PDD的架构中,MDD替换了原来架构中的FAL,而PDD相当于原来的FMD,只要实现PDD层就可以了。如果你的系统已经升级到WinCE6.0 R2,那么你应该可以在/WINCE600/Public/COMMON/OAK/DRIVERS目录下面找到这两种架构驱动的源代码。 由于MDD+PDD的架构在WinCE6.0 R2中才有支持,本人也没有实现过。所以这里只介绍基于FAL+FMD架构下,nandflash驱动的开发,这也是目前大家都采用的开发flash驱动的架构。

如上面所说,我们需要实现FMD层的相关接口,下面来介绍一下各个接口函数:

1. PVOID FMD_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut): 这个是Flash设备的初始化函数。在WinCE启动的时候,要加载Flash驱动时,首先调用这个函数对flash设备进行初始化。如果你的系统中有nandflash的controller,那么你需要在这里对你的nandflash controller进行初始化。如果没有的话,你需要针对你的硬件设计进行相关的片选,时序等进行配置。返回一个handle表示成功,这个handle将被FMD_Deinit(..)函数用到,如果返回NULL表示失败。

2. BOOL FMD_Deinit(PVOID hFMD): 这个函数在nandflash驱动卸载的时候被调用,参数就是FMD_Init函数返回的Handle.一般在这个函数里面,你可以释放一些用到的资源,然后关闭nandflash controller。

3. BOOL FMD_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors): 这个函数用于读nandflash的一个扇区。对于nandflash来说,分大page和小page,大page是2048个bytes一页,小page是512个bytes一页。所以大page每个扇区有2048 bytes,小page每个扇区有512 bytes。

    startSectorAddr: nandflash物理扇区的起始地址,对于nandflash来说,就是nandflash中从哪个page开始。

    pSectorBuff:扇区数据buffer,从nandflash中读出的每一个扇区的数据都存放在这个buffer中。

    pSectorInfoBuff:扇区信息buffer,一般每个扇区的信息会被保存在nandflash的带外数据中,针对小page,带外数据有16 bytes,大page有64 bytes。从nandflash的带外数据将该扇区的相关信息读出来,存放在这个buffer中。

    dwNumSectors:读取多少个扇区,对于nandflash来说相当于读取多少个page。

4. BOOL FMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors): 该函数用于写nandflash的一个扇区。参数和上面的FMD_ReadSector的参数意思一样,就不多说了。

5. BOOL FMD_EraseBlock(BLOCK_ID blockID): 该函数用于擦出nandflash的一个block,参数为要擦除nandflash的block地址,也就是第几个block。

6. DWORD FMD_GetBlockStatus(BLOCK_ID blockID): 该函数获得nandflash中某一个block的状态。参数为nandflash的block地址。由于nandflash中可能有坏块,所以针对nandflash,这个函数首先会检查当前块是否是坏块,这个一般通过读取当前block的第0个page和第1个page的带外数据。对于小page nandflash一般是读取第5个byte,对于大page nandflash一般读取第0个byte,如果不为0xff表示该块是坏块。当然,至于具体该读哪个byte,最好还是看一下所用nandflash的datasheet,确认一下,不同的厂家可能有所不同。如果发现该块是坏块,应该返回BLOCK_STATUS_BAD。如果不是坏块,需要读取这个块的起始扇区的扇区信息。如果读该扇区信息出错,应该返回BLOCK_STATUS_UNKNOWN,否则,判断独到的信息,返回相应结果。

7. BOOL FMD_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus): 该函数设置nandflash某个block的状态,第一个参数是nandflash的block地址,第二个是要设置的状态。在这个函数中,首先检查dwStatus是不是BLOCK_STATUS_BAD,如果是就对nandflash作坏块标记,然后返回FALSE。如果不是,就将dwStatus写到该block的第0个page的扇区info中。这个函数和上面的函数正好是相反的。

8. BOOL FMD_GetInfo(PFlashInfo pFlashInfo): 该函数用于返回flash的信息。其中pFlashInfo是一个包含flash信息的结构。

    pFlashInfo->flashType:flash的类型,对于nandflash来说,应该是NAND。

    pFlashInfo->wDataBytesPerSector:一个扇区多少个bytes,对于大page是2048,对于小page是512。

    pFlashInfo->dwNumBlocks:flash中总共有多少个block,查一下所用的nandflash的datasheet就知道了。

     pFlashInfo->wSectorsPerBlock:每个block中包含多少个扇区。

    pFlashInfo->dwBytesPerBlock:每个block中包含多少个bytes。

9. VOID FMD_PowerDown()和VOID FMD_PowerUp(): 这两个函数用于电源管理。FMD_PowerDown()用于关闭flash设备电源,FMD_PowerUp()用于恢复flash设备电源。根据你所用处理器和相关硬件环境,去实现这两个函数。不实现也不会影响nandflash的使用。

10. BOOL FMD_OEMIoControl(..): 就像很多的IOControl函数一样,根据不同的case,实现相应的功能。针对nandflash来说,这里面的case不一定都需要实现。事实上,如果什么都没有实现,也不影响nandflash的使用。在WinCE的文档中,定义了一些需要实现的case,你可以实现,也可以不去实现。

对于nandflash来说,实现上述函数就可以了。在nandflash出厂的时候,厂家已经对nandflash中的坏块进行了标记。所以第一次对nandflash操作的时候,不要随便擦除nandflash,因为这样可能会把坏块标记擦掉,这样你就判断不出哪个块是坏块了。

关于ECC校验,目前很多处理带有nandflash controller,而且nandflash controller带有硬件ECC功能。如果没有硬件ECC,也可以使用软件ECC,软ECC的代码可以在/WINCE600/PUBLIC/COMMON/OAK/DRIVERS/BLOCK/MSFLASHFMD/ECC下找到。一般来说,ECC校验会对512个BYTE产生3个字节的校验码,也就是说对小PAGE来说,每个PAGE有3个字节的ecc校验码;对于大PAGE来说,有12个字节。这些校验码应该在写扇区数据的时候,被写在扇区的带外数据里面。当读扇区数据时,会先把数据读出来,然后根据这些数据计算ecc,再和读出来的ecc进行比较,如果一致,则表示正确。

posted @ 2010-07-30 23:34 jiege 阅读(18) | 评论(0) | 编辑
wince下如何加载驱动(摘录)

设备管理器是Windows CE.Net设备管理的核心机构,它主要负责跟踪、维护系统的设备信息并对设备资源进行调配。(在%WINCEROOT%/PRIVATE/WINCEOS/COREOS /DEVICE/LIB里可以看到Windows CE设备管理器的代码)

设备管理器在Windows CE中主要表现为Device.exe的文件,Device.exe在系统启动的时候通过注册表里面的HKEY_LOCAL_MACHINE/Init/"Launch20"=“Device.exe"加载(Windows CE启动时分别执行[HKEY_LOCAL_MACHINE/init]键下所有子键列出的程序)设备管理器是用户级别的程序,在基于Windows CE的平台上在不停地运行着。设备管理器虽然不是内核的一部分,但是它是与内核、注册表和流接口驱动程序有相互影响的单独部分。设备管理器完成以下任务:

1)在系统启动时或收到用户添加外围设备的信息时初始化驱动程序的加载

2)向内核注册特定的文件名,该文件名把应用程序使用的流I/O函数映射到流接口驱动程序的那些函数的实现。

3)通过从外围设备获得即插即用标示符,或激活一个检查子程序来发现可以处理该设备的驱动程序,为外围设备找到合适的驱动程序。

4)通过读写注册值加载跟踪驱动程序。

5)当不再需要设备时,负责卸载驱动程序。

在系统启动时初始化流驱动程序的加载。加载流驱动程序有三种方法。

第一种加载类型是在系统启动的时候进行的。当Winows CE的平台启动的时候,启动设备管理器。设备管理器从注册表的HKEY_LOCAL_MACHINE/Drivers/RootKey下面加载入口点,通常RootKey的值都被设置为Drivers/BuiltIn。然后设备管理器通过/RootKey提供的入口点开始读取HKEY_LOCAL_MACHINE /Drivers/Builtin健的内容,并加载已列出的流接口驱动程序。

第二种加载的类型是在设备管理程序自动检测外围设备设备与基于Windows CE平台的连接时进行的。PC卡是自动检测设备最常见的类型,因为在用户插入PC卡时PC卡插槽控制程序就通知Windows CE。在用户把PC卡插入插槽时,设备管理程序调用槽驱动程序(这是一个内部设备管理程序)寻找即插即用标示符。然后,设备管理程序检查HKEY_LOCAL_MACHINE/Drivers/PCMCIA健已得到与即插即用标示符所匹配的子键。如果有一个子键存在,该子键就加载键值列表里的这个驱动程序。如果没有匹配的子键,设备管理器就调用HKEY_LOCAL_MACHINE/Drivers/PCMCIA/Detect键中列表的所有侦测函数。如果有一个函数返回一个值,那么设备管理程序就加载并初始化那个流接口驱动程序。

第三种加载类型是当设备管理器程序不能够自动检测或加载某一种驱动程序的时候,一般这种情况大多数出现在串行设备上,因为Windows CE不能自动检测到串行设备。这个时候的可以使用系统提供的函数ActivateDeviceEx函数来加载驱动程序。下面介绍一下ActivateDeviceEx这个函数。

ActivateDeviceEx用于加载驱动程序,事实上Windows CE的实现通过StartOneDriver函数把一个具体的驱动程序挂接到系统中的,不过这个函数是一个纯粹的内部函数。它的实现是通过ActivateDeviceEx来实现的。在PB的%WINCEROOT%/PRIVATE/WINCEOS/

COREOS/DEVICE/LIB/目录下的文件device.c中可以找到ActivateDeviceEx函数的实现。它的代码部分如下:

HANDLE FS_ActivateDeviceEx(

LPCTSTR lpszDevKey,

LPCREGINI lpReg,

DWORD cReg,

LPVOID lpvParam

) 设备管理器是Windows CE.Net设备管理的核心机构,它主要负责跟踪、维护系统的设备信息并对设备资源进行调配。(在%WINCEROOT%/PRIVATE/WINCEOS/COREOS /DEVICE/LIB里可以看到Windows CE设备管理器的代码)

设备管理器在Windows CE中主要表现为Device.exe的文件,Device.exe在系统启动的时候通过注册表里面的HKEY_LOCAL_MACHINE/Init/"Launch20"=“Device.exe"加载(Windows CE启动时分别执行[HKEY_LOCAL_MACHINE/init]键下所有子键列出的程序)设备管理器是用户级别的程序,在基于Windows CE的平台上在不停地运行着。设备管理器虽然不是内核的一部分,但是它是与内核、注册表和流接口驱动程序有相互影响的单独部分。设备管理器完成以下任务:

1)在系统启动时或收到用户添加外围设备的信息时初始化驱动程序的加载

2)向内核注册特定的文件名,该文件名把应用程序使用的流I/O函数映射到流接口驱动程序的那些函数的实现。

3)通过从外围设备获得即插即用标示符,或激活一个检查子程序来发现可以处理该设备的驱动程序,为外围设备找到合适的驱动程序。

4)通过读写注册值加载跟踪驱动程序。

5)当不再需要设备时,负责卸载驱动程序。

在系统启动时初始化流驱动程序的加载。加载流驱动程序有三种方法。

第一种加载类型是在系统启动的时候进行的。当Winows CE的平台启动的时候,启动设备管理器。设备管理器从注册表的HKEY_LOCAL_MACHINE/Drivers/RootKey下面加载入口点,通常RootKey的值都被设置为Drivers/BuiltIn。然后设备管理器通过/RootKey提供的入口点开始读取HKEY_LOCAL_MACHINE /Drivers/Builtin健的内容,并加载已列出的流接口驱动程序。

第二种加载的类型是在设备管理程序自动检测外围设备设备与基于Windows CE平台的连接时进行的。PC卡是自动检测设备最常见的类型,因为在用户插入PC卡时PC卡插槽控制程序就通知Windows CE。在用户把PC卡插入插槽时,设备管理程序调用槽驱动程序(这是一个内部设备管理程序)寻找即插即用标示符。然后,设备管理程序检查HKEY_LOCAL_MACHINE/Drivers/PCMCIA健已得到与即插即用标示符所匹配的子键。如果有一个子键存在,该子键就加载键值列表里的这个驱动程序。如果没有匹配的子键,设备管理器就调用HKEY_LOCAL_MACHINE/Drivers/PCMCIA/Detect键中列表的所有侦测函数。如果有一个函数返回一个值,那么设备管理程序就加载并初始化那个流接口驱动程序。

第三种加载类型是当设备管理器程序不能够自动检测或加载某一种驱动程序的时候,一般这种情况大多数出现在串行设备上,因为Windows CE不能自动检测到串行设备。这个时候的可以使用系统提供的函数ActivateDeviceEx函数来加载驱动程序。下面介绍一下ActivateDeviceEx这个函数。

ActivateDeviceEx用于加载驱动程序,事实上Windows CE的实现通过StartOneDriver函数把一个具体的驱动程序挂接到系统中的,不过这个函数是一个纯粹的内部函数。它的实现是通过ActivateDeviceEx来实现的。在PB的%WINCEROOT%/PRIVATE/WINCEOS/

COREOS/DEVICE/LIB/目录下的文件device.c中可以找到ActivateDeviceEx函数的实现。它的代码部分如下:

HANDLE FS_ActivateDeviceEx(

LPCTSTR lpszDevKey,

LPCREGINI lpReg,

DWORD cReg,

LPVOID lpvParam

)

{

DEBUGMSG(1, (TEXT("DEVICE!ActivateDeviceEx(%s) entered/r/n"), lpszDevKey));

CELOG_ActivateDevice (lpszDevKey);

return StartOneDriver(

lpszDevKey,

MAX_LOAD_ORDER,

lpReg, cReg, lpvParam);

}

ActivateDeviceEx的函数很简单它只是负责调用StartOneDriver这个函数。这个函数将根据注册表设置来初始化不同的设备驱动。在系统刚开始运行的时候Device.exe就是调用这个函数来加载相应的驱动程序的。这个函数将引起一个特定的驱动加载过程。先介绍一下StartOneDriver的运行过程。

{

DEBUGMSG(1, (TEXT("DEVICE!ActivateDeviceEx(%s) entered/r/n"), lpszDevKey));

CELOG_ActivateDevice (lpszDevKey);

return StartOneDriver(

lpszDevKey,

MAX_LOAD_ORDER,

lpReg, cReg, lpvParam);

}

ActivateDeviceEx的函数很简单它只是负责调用StartOneDriver这个函数。这个函数将根据注册表设置来初始化不同的设备驱动。在系统刚开始运行的时候Device.exe就是调用这个函数来加载相应的驱动程序的。这个函数将引起一个特定的驱动加载过程。先介绍一下StartOneDriver的运行过程。

posted @ 2010-07-30 23:32 jiege 阅读(36) | 评论(0) | 编辑
【转】wince5.0内核的启动流程

本文简单描述一下wince5.0内核的启动流程,以mips cpu为例。msdn有一篇文章叫做Microsoft Windows CE 5.0 Board support Package,Boot Loader,and Kernel Startup Sequence非常不错,可以参考。
1. startup.首先,内核最先执行的代码位于oal当中,叫做startup,这段代码由微软留给开发者定制。当然,各个参考bsp里面都有现成的代码,开发者只需在此基础上改动。在startup()的末尾,会跳转到kernelstart函数。
2. kernelstart. 位于WINCEROOT/Private/winceos/coreos/nk/kernel/mips/startup.s 这里面是汇编代码。是所有的mips开发板都要执行的操作。所以这里面会根据不同cpu类型作判断。虽然是汇编代码,好在里面还是有不少注释,通过这些注释,可以看出它里面主要在干什么。
3. KernelRelocate. kernelstart在完成一些必要的初始化之后,会调用KernelRelocate函数,这是一个比较重要的函数,位于WINCEROOT/Private/winceos/coreos/nk/kernel/loader.c. 它会把kernel用到的数据copy的ram里面。具体的功能msdn里面有解释。 这里的ram就是在config.bib里面指定的具有ram属性的存储区域,不是ramimage. kernelRelocate以pToc为参数,那么pToc的值从何而来呢?即便你搜索完所有的文件也找不到在那里pToc被赋值。因为pToc是在makeimage阶段被romimage.exe赋值的,也就是说pToc并不是在代码中被赋值的,是由外力(romimage.exe)改动nk.bin的内容赋值的。
4.MIPSInit.  KernelRelocate处理完成之后,MIPSInit会被调用。位于WINCEROOT/Private/winceos/coreos/nk/kernel/mips/mdsched.c.这里是通用的mips的处理,其中会调用oal当中的OEMInitDebugSerial去初始化调试用的串口。
5.OEMInit.接下来就是大名鼎鼎的OEMInit了。这个函数由开发者定制。是c语言的。由上面的分析我们知道,在进入OEMInit的时候,串口已经初始化完毕,所以现在我们已经可以通过串口打印出一些调试信息了。而在此之前,我们只能通过led的方式作一些简单的显示。
6.KernelFindMemory. 位于WINCEROOT/Private/winceos/coreos/nk/kernel/loader.c
OEMInit返回之后调用该函数。这个函数主要是把ram划分为两部分:object store和应用程序可以使用的部分。object store就是用于存贮wince的ram file system的,例如开机以后我们看到的/windows目录就是位于ram file system.
7.KernelInit. 位于WINCEROOT/Private/winceos/coreos/nk/kernel/kwin32.c
这部分跟cpu无关,是kernel要完成自己的初始化。至此,kernel得初始化全部完成,可以开始线程调度。
      还有一点需要说明的时,kernel在完成初始化之后,会以IOCTL_HAL_POSTINIT为参数调用OEMIoControl,所以我们可以在这里打印出一句话表明kernel已经初始化完成。
除了kernel本身(nk.exe)之外,第一个被创建的进程是谁呢,对,就是文件系统,filesys.exe.
虽然他不是kernel本身的一部分,但是如果没有文件系统,wince也是玩不转的,注册表的初始化就是由文件系统来完成。

posted @ 2010-07-30 22:26 jiege 阅读(37) | 评论(0) | 编辑
WinCE 5.0 内核启动过程

本文简单描述一下wince5.0内核的启动流程,以mips cpu为例。msdn有一篇文章叫做Microsoft Windows CE 5.0 Board support Package,Boot Loader,and Kernel Startup Sequence非常不错,可以参考。
1. startup.首先,内核最先执行的代码位于oal当中,叫做startup,这段代码由微软留给开发者定制。当然,各个参考bsp里面都有现成的代码,开发者只需在此基础上改动。在startup()的末尾,会跳转到kernelstart函数。
2. kernelstart. 位于WINCEROOT/Private/winceos/coreos/nk/kernel/mips/startup.s 这里面是汇编代码。是所有的mips开发板都要执行的操作。所以这里面会根据不同cpu类型作判断。虽然是汇编代码,好在里面还是有不少注释,通过这些注释,可以看出它里面主要在干什么。
3. KernelRelocate. kernelstart在完成一些必要的初始化之后,会调用KernelRelocate函数,这是一个比较重要的函数,位于WINCEROOT/Private/winceos/coreos/nk/kernel/loader.c. 它会把kernel用到的数据copy的ram里面。具体的功能msdn里面有解释。 这里的ram就是在config.bib里面指定的具有ram属性的存储区域,不是ramimage. kernelRelocate以pToc为参数,那么pToc的值从何而来呢?即便你搜索完所有的文件也找不到在那里pToc被赋值。因为pToc是在makeimage阶段被romimage.exe赋值的,也就是说pToc并不是在代码中被赋值的,是由外力(romimage.exe)改动nk.bin的内容赋值的。
4.MIPSInit.  KernelRelocate处理完成之后,MIPSInit会被调用。位于WINCEROOT/Private/winceos/coreos/nk/kernel/mips/mdsched.c.这里是通用的mips的处理,其中会调用oal当中的OEMInitDebugSerial去初始化调试用的串口。
5.OEMInit.接下来就是大名鼎鼎的OEMInit了。这个函数由开发者定制。是c语言的。由上面的分析我们知道,在进入OEMInit的时候,串口已经初始化完毕,所以现在我们已经可以通过串口打印出一些调试信息了。而在此之前,我们只能通过led的方式作一些简单的显示。
6.KernelFindMemory. 位于WINCEROOT/Private/winceos/coreos/nk/kernel/loader.c
OEMInit返回之后调用该函数。这个函数主要是把ram划分为两部分:object store和应用程序可以使用的部分。object store就是用于存贮wince的ram file system的,例如开机以后我们看到的/windows目录就是位于ram file system.
7.KernelInit. 位于WINCEROOT/Private/winceos/coreos/nk/kernel/kwin32.c
这部分跟cpu无关,是kernel要完成自己的初始化。至此,kernel得初始化全部完成,可以开始线程调度。
      还有一点需要说明的时,kernel在完成初始化之后,会以IOCTL_HAL_POSTINIT为参数调用OEMIoControl,所以我们可以在这里打印出一句话表明kernel已经初始化完成。
除了kernel本身(nk.exe)之外,第一个被创建的进程是谁呢,对,就是文件系统,filesys.exe.
虽然他不是kernel本身的一部分,但是如果没有文件系统,wince也是玩不转的,注册表的初始化就是由文件系统来完成。

posted @ 2010-07-30 22:21 jiege 阅读(20) | 评论(0) | 编辑
【转】学习wince中断时将资料整理成一篇文章(一)

WINCE5.0的中断深入了解
1.总体了解流程
首先描述wince5.02440BSP)的中断流程:
流程1.创建事件aà创建线程ISTàInterruptInitialize〈系统中断号绑定线程ISTà线程IST进入等待事件a状态(挂起状态)。
流程2.外部引发中断à OEMInterruptHandler<屏蔽中断à把物理中断转换成系统中断,其他à重新使能中断。(ISR过程)>à操作系统根据系统中断号触发事件a
流程3.挂起的IST线程等待到事件a进入就绪状态,得到执行时间后开始执行中断服务代码,最后调用InterruptDone重新使能当前的中断。

你需要为你的设备驱动写好中断处理请求(ISR)和中断服务线程(IST),并牢记这些事件的顺序:
1).当一个中断发生,处理器跳转到核心的中断处理程序(exception handler );
2).这个中断处理程序禁止所有同级或低优先级的其他中断,然后为当前的IRQ调用对应的ISR;
3).ISR中会按照中断标识的形式,返回一个逻辑中断号给中断处理程序,并会置位板级设备中断;
4).中断处理程序重新使能所有的中断,而目前的中断已经在上一步中置位了,然后就触发对应的IST事件;
5).IST就绪,服务于中断设备,然后完成对中断的处理;
6).IST调用InterruptDone函数,该函数将顺序调用OAL层的OEMInterruptDone函数,它将重新使能当前的中断。
1.1物理中断和逻辑中断的对应关系如何建立
这个函数用将物理中断号来获取逻辑中断号:
KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &g_PwrButtonIrq, sizeof(UINT32), &g_PwrButtonSysIntr, sizeof(UINT32), NULL))
其中:UINT32 g_PwrButtonIrq = IRQ_EINT0;

从help里面查出,KernelIoControl函数最终是调用OEMIoControl函数。
在D:/WINCE500/PLATFORM/COMMON/SRC/COMMON/IOCTL里找到它的定义了,关键代码:
// Execute the handler
    rc = g_oalIoCtlTable.pfnHandler(
        code, pInBuffer, inSize, pOutBuffer, outSize, pOutSize
    );
SMDK2440/Src/Kernel/Oal/ioctl.c中可以找到:
const OAL_IOCTL_HANDLER g_oalIoCtlTable[] = {
#include "ioctl_tab.h"
};

在SMDK2440/Src/Inc/ioctl_tab.h文件中,找到这个表的定义。这个命令对应的函数是OALIoCtlHalRequestSysIntr。
PLATFORM/COMMON/SRC/COMMON/IOCTL/ioctl.c找到这个函数定义:
// Find if it is new or old call type
    if (inpSize > sizeof(UINT32) && pInpData[0] == -1) {
        // Second UINT32 contains flags, third and subsequents IRQs
        sysIntr = OALIntrRequestSysIntr(inpSize/sizeof(UINT32) - 2, &pInpData[2], pInpData[1]); }
else {        
        // This is legacy call, first UINT32 contains IRQ
        sysIntr = OALIntrRequestSysIntr(1, pInpData, 0);
    }

在WINCE500/PLATFORM/COMMON/SRC/COMMON/INTR/BASE/map.c找到OALIntrRequestSysIntr定义:
irq = pIrqs[0];
sysIntr = g_oalIrq2SysIntr[irq];

在同一个文件中定义:static UINT32 g_oalIrq2SysIntr[OAL_INTR_IRQ_MAXIMUM];
对这个表格赋值仅有两个地方:VOID OALIntrStaticTranslate(UINT32 sysIntr, UINT32 irq)
{
    OALMSG(OAL_FUNC&&OAL_INTR, (
        L"+OALIntrStaticTranslate(%d, %d)/r/n", sysIntr, irq
    ));
    if (irq < OAL_INTR_IRQ_MAXIMUM && sysIntr < SYSINTR_MAXIMUM) {
        g_oalSysIntr2Irq[sysIntr] = irq;
        g_oalIrq2SysIntr[irq] = sysIntr;
    }        
    OALMSG(OAL_FUNC&&OAL_INTR, (L"-OALIntrStaticTranslate/r/n"));
}

OALIntrStaticTranslate和OALIntrRequestSysIntr本身两个函数负责建立对应表。后者如果在现有的中断表中找不到已经建立的对应关系,就会分配一个未定义的Sysintr逻辑中断号给这个物理中断号。因此逻辑中断和物理中断的对应,可以说是随机的,只要保证两者是一一对应就好了,不必要硬性建立一个中断号表格(像WINCE4.2那样)。代码中只找到OALIntrStaticTranslate(SYSINTR_OHCI, IRQ_USBH);是静态对应。当中断处理程序获得了逻辑中断号,那么就会触发该中断号关联着的事件。
1.2核心部分的中断处理程序如何获得物理中断号
这个问题的目的是:如何添加一个原来系统中没有的物理中断。Physical interrupts (IRQs) are hardware lines over which devices can send interrupt signals to the microprocessor. Logical interrupts (SYSINTRs) are a mapping of the IRQ, which the OAL specifies.一般情况下将ISR与中断处理程序相关联的注册在系统启动的时候进行。在启动过程中,在OAL层kernel调用OEMInit函数。然后,OEMInit调用HookInterrupt 函数来通知中断处理程序,哪些ISR对应到某个物理中断。WINCE500/PUBLIC/COMMON/OAK/INC/nkintr.h,定义了某些逻辑中断号,声明了hookInterrupt等函数。除此之外,再没有hookInterrupt的定义。看看WINCE500/PLATFORM/SMDK2440A/src/kernel/oal/init.c里面的OEMinit函数做了些什么:
// Initialize interrupts
    if (!OALIntrInit()) {
        OALMSG(OAL_ERROR, (
            L"ERROR: OEMInit: failed to initialize interrupts/r/n"
        ));
    }

OALIntrInit函数在WINCE500/PLATFORM/COMMON/SRC/ARM/SAMSUNG/S3C2440A/Intr/intr.c文件中定义:调用OALIntrMapInit()函数初始化寄存器;最后调用:
#ifdef OAL_BSP_CALLBACKS
    // Give BSP change to initialize subordinate controller
    rc = BSPIntrInit();
#else
    rc = TRUE;
#endif

OALIntrMapInit()函数里面对两个中断表做了初始化:
for (i = 0; i < SYSINTR_MAXIMUM; i++) {
        g_oalSysIntr2Irq = OAL_INTR_IRQ_UNDEFINED;
    }
    for (i = 0; i < OAL_INTR_IRQ_MAXIMUM; i++) {
        g_oalIrq2SysIntr = SYSINTR_UNDEFINED;
    }

WINCE500/PLATFORM/COMMON/SRC/ARM/SAMSUNG/S3C2440A/Intr/sources:
TARGETNAME=oal_intr_s3c2440a
TARGETTYPE=LIBRARY
SYNCHRONIZE_DRAIN=1
NOMIPS16CODE=1
CDEFINES=$(CDEFINES) -DCEDDK_USEDDKMACRO -DOAL_BSP_CALLBACKS
----------------------------------------------------------------------------------
CDEFINES=-DSomeDef : This sets one or more preprocessor definitions. You must include the -D switch on each define you add. You can add new defines by using this syntax: "CDEFINES=$(CDEFINES) -DAnotherDef", or you can ignore existing settings with this syntax: "CDEFINES=-DOnlyDef".
----------------------------------------------------------------------------------

因此BSPIntrInit会被执行。
在WINCE500/PLATFORM/SMDK2440A/Src/Kernel/Oal/intr.c有这个函数的定义:
// Set GPG1 as EINT9
// Add static mapping for Built-In OHCI
OALIntrStaticTranslate(SYSINTR_OHCI, IRQ_USBH);

到这里,无法了解如何添加一个物理中断。在CPU接收到中断后,对中断的处理是在 OEMInterruptHandler()中,该函数的首先屏蔽该中断,最后得到实际中断IRQ所对应的sysintr的值”OEMInterruptHandler函数在WINCE500/PLATFORM/COMMON/SRC/ARM/SAMSUNG/S3C2440A/Intr/intr.c文件中,感觉到它实际上就是上面中断序列中谈到的“中断处理程序”和ISR。就是说,中断发生之后,CPU并不知道到底是哪个中断发生了,实际上WINCE中也没有建立中断矢量表,而是直接跳转到OEMInterruptHandler函数,然后在其中查看g_pIntrRegs->INTOFFSET寄存器,来查看到底发生了什么中断。
在s3c2440a_intr.h文件里面有中断号宏定义:
#define IRQ_EINT0           0           // Arbiter 0
#define IRQ_EINT1           1
#define IRQ_EINT2           2
#define IRQ_EINT3           3
......

INTOFFSET寄存器的值与这个宏定义是完全一一对应。这样,也就搞清楚了物理中断号如何获得,又如何对应到逻辑中断号,最后,触发了IST,整个中断处理就结束了。2440全部的中断源都已经被纳入了,添加一个采用某个中断源的设备驱动,只需要用kernelIOControl函数通过物理中断产生一个逻辑中断号就可以了!这样的话,只要在0x18位置有一个跳转指令就可以了(但还没有找到这条跳转指令)。
13其他中断相关函数了解
关注WINCE500/PLATFORM/COMMON/SRC/ARM/SAMSUNG/S3C2440A/Intr/intr.c中的其他函数:
OEMInterruptHandler包含了对以下中断的判断和处理:
IRQ_TIMER4,这个是系统节拍;
IRQ_TIMER2,作用未知(Profiling timer);
IRQ_EINT4_7,EINT8_23,外部中断;
任何一个中断发生后,先mask该中断(禁止中断),然后再清除中断请求:
       mask = 1 << irq;
            SETREG32(&g_pIntrRegs->INTMSK, mask);
            OUTREG32(&g_pIntrRegs->SRCPND, mask);
            OUTREG32(&g_pIntrRegs->INTPND, mask);

其它中断获取逻辑中断号:
  // First find if IRQ is claimed by chain
        sysIntr = NKCallIntChain((UCHAR)irq);
        if (sysIntr == SYSINTR_CHAIN || !NKIsSysIntrValid(sysIntr)) {
            // IRQ wasn't claimed, use static mapping
            sysIntr = OALIntrTranslateIrq(irq);
        }

关于NKCallIntChain的说明:
如果没有与ISR关联的IRQ事件,返回SYSINTR_CHAIN ;
除此之外,将返回IRQ对应的SYSINTR值。

posted @ 2010-07-30 16:23 jiege 阅读(59) | 评论(0) | 编辑
【转】学习wince中断时将资料整理成一篇文章(二)

转载:http://www.study-bbs.com/thread-29658-1-1.html
2 OAL层中断程序汇总

关于WinCE的中断处理,OAL主要是实现了ISR部分,一般IST会在设备驱动中实现。架构如图:硬件中断产生以后,会导致内核ISR的运行,然后由OAL中的ISR来处理相应的中断,最后导致相对应的IST运行完成真正的中断处理。所以在WinCE中,中断处理由ISRIST共同完成。ISR主要完成中断源的确定,屏蔽该中断并返回给内核相对应的系统中断号,ISR应该尽量短小。IST则是完成真正的中断处理,比如数据的传输和解析等。当然不是所有的中断处理都需要ISRIST,看需要,比如WinCE的系统Timer中断就只需要ISR完成。


2.1 在OAL中支持中断,需要实现以下几个中断处理函数

a. BOOL OEMInterruptEnable(DWORD sysIntr, VOID* pData, DWORD dataSize)


sysIntr:要被使能的系统中断号

 

pData:传入的数据指针,该数据由InterruptInitialize函数传入

 

dataSize:传入数据的大小

 

该函数用于使能某一个硬件中断。在设备驱动调用InterruptInitialize来初始化一个中断的时候,内核就会调用该函数来使能相应的硬件中断。

 

b. VOID OEMInterruptDisable(DWORD sysIntr)

 

sysIntr:要被屏蔽的系统中断号

 

该函数用于屏蔽一个硬件中断。如果设备驱动调用InterruptDisable函数,则该函数会被调用。

 

c. VOID OEMInterruptDone(DWORD sysIntr)

 

sysIntr:要被重新使能的系统中断号

 

该函数标志着一个中断处理过程的完成。当设备驱动调用InterruptDone函数的时候,该函数会被调用,重新使能相应的硬件中断。

 

d. ULONG OEMInterruptHandler(ULONG ra)

 

ra:指令计数,在实际应用中,没有太大意义

 

当硬件中断产生的时候,该函数就会被调用完成ISR部分的中断处理。一般会在该函数中读取系统的中断标记位,确定中断源并返回相应的系统中断号。

 

e. void OEMInterruptHandlerFIQ(void)

 

针对于ARM处理器,该函数用于处理快速中断。

 

上面5个函数完成了中断的相关处理。这里要提到两个概念:IRQSYSINTRIRQ是指物理中断或者叫硬件中断,而SYSINTR指的是系统中断,也有的地方称为虚拟中断或者逻辑中断,我个人觉得还是叫系统中断比较好。每一个IRQ会和一个系统中断SYSINTR相对应,当硬件中断产生时,ISR实际上是处理IRQ中断,然后返回相应的系统中断SYSINTR给内核,内核会根据相应的SYSINTR触发相应的IST来完成中断处理。

 

IRQSYSINTR之间的对应关系称为映射,分为静态映射和动态映射。静态映射是指在系统编译IRQ已经和SYSINTR相对应,一般通过OALIntrStaticTranslate函数来实现。而动态映射是指WinCE系统启动后,动态关联IRQSYSINTR,一般通过KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR)来实现。

 

SYSINTR的类型在nkintr.h中定义,OEMInterruptHandler函数在处理完中断以后,会返回不同类型的SYSINTR给内核,内核会根据返回值进行下一步操作,分为以下几种类型:

 

SYSINTR_NOP:表示不需要进行任何处理

 

SYSINTR_RESCHED:表示要进行一次系统调度

 

SYSINTR_CHAIN:表示不是该中断源产生,在中断链中寻找下一个中断

 

SYSINTR_RTC_ALARM:表示RTC报警产生

 

SYSINTR_TIMING:用于ILTiming测试

 

SYSINTR_PROFILE:用于系统的profile

 

SYSINTR_FIRMWARE:用于用户自定义系统中断号,所有自定义的系统中断号都应该基于该值进行累加加1,这些自定义的系统中断号用于和IRQ一一对应。

 

 

2.2 PQOAL架构下中断的实现

在以前,如果要实现OAL中的中断部分,我们需要自己建立一个IRQ表和一个SYSINTR表,然后实现前面提到的5个函数,还要实现一个InterruptInitialize的函数,该函数用于初始化中断,会在OEMInit中被调用,就完事了。自从WinCE5.0以后,微软提出了PQOAL架构,中断的实现变得“曲折”了。要实现的函数如下:

1. BOOL OALIntrInit()

该函数为中断初始化函数,会在OEMInit中被调用,用于初始化系统的中断,以及完成一些中断的静态映射。

2. BOOL OALIntrRequestIrqs(DEVICE_LOCATION *pDevLoc, UINT32 *pCount, UINT32 *pIrqs)

pDevLoc:一个DEVICE_LOCATION结构指针,包含设备信息

pCount:作为输入表示最大的IRQ数,作为输出表示实际获得的IRQ数

pIrqs:指向一个实际获得的IRQ数组

该函数用于通过设备的物理地址来得到IRQ信息,一般用于总线设备。

3. BOOL OALIntrEnableIrqs(UINT32 count, const UINT32 *pIrqs)

count:要使能多少个IRQ

pIrqs:要被使能的IRQ数组

该函数用于使能IRQ中断,该函数会被OEMInterruptEnable函数调用。

4. VOID OALIntrDisableIrqs(UINT32 count, const UINT32 *pIrqs)

count:要禁用多少个IRQ

pIrqs:要被禁用的IRQ数组

该函数用于禁用IRQ中断,该函数会被OEMInterruptDisable函数调用。

5. VOID OALIntrDoneIrqs(UINT32 count, const UINT32 *pIrqs)

count:要重新使能多少个IRQ

pIrqs:要被重新使能的IRQ数组

该函数用于重新使能IRQ中断,会被OEMInterruptDone函数调用。

除了上述5个函数以外,还要实现的一个重要函数就是OEMInterruptHandler了,这个函数前面介绍过,这里不说了。实际上在WinCE5.0以后,在/Platform/Common/Src/Common/INTR/Base目录下有个“map.c”文件,该文件实现了中断的相关接口函数,实现了中断的动态/静态映射,还完成了SYSINTR与IRQ之间的转换,我们只需要实现对硬件中断的操作和初始化就可以了。

最后还有几个函数要说一下,如下:

BSPIntrInit:被OALIntrInit函数调用

BSPIntrEnableIrq:被OALIntrEnableIrqs函数调用

BSPIntrDisableIrq:被OALIntrDisableIrqs函数调用

BSPIntrDoneIrq:被OALIntrDoneIrqs函数调用

BSPIntrRequestIrqs:被OALIntrRequestIrqs函数调用

这些函数可以被称为板级中断处理函数,总感觉这些函数有点多余,一般实现了OALIntrInit,OALIntrEnableIrqs,OALIntrDisableIrqs,OALIntrDoneIrqs和OALIntrRequestIrqs就可以了,但这是基于处理器级的实现,对于基于同一处理器的不同的板子可能中断要做一些修改,这些修改就可以在BSPIntrInit,BSPIntrEnableIrq,BSPIntrDisableIrq,BSPIntrDoneIrq和BSPIntrRequestIrqs里面完成。

在这里,OAL中的中断处理函数基本都介绍了。我想最好的理解方法就是看代码了。一般在OAL中只是做一些开关中断和清中断标记位的操作,真正的数据处理交给IST去做。但有的时候,有些特殊设备的中断会很频繁,IST来不及响应,解决办法就是在ISR中将数据保存在一块内存中,然后根据需要,每隔多少个硬件中断返回一次系统中断,从而激活IST将数据一次性读走,这里涉及一个问题就是在ISR和IST中共享数据,在config.bib中预留一块共享内存就可以了。

posted @ 2010-07-30 16:22 jiege 阅读(49) | 评论(0) | 编辑
WinCE下申请大容量物理内存

申请大容量的物理内存看起来不是难事。这里的大容量是指几十MB甚至更多的物理内存。对于C++程序员来说可能平时习惯了使用“new”操作符来实现。我也是这样。使用“new”非常简单,申请之后只需判断返回的指针是否是空即可。在其它的Windows操作系统上的确不需要在申请大容量物理内存上过多考虑。但是在Windows CE上就不同了。如果只用“new”就能搞定,那就太省事了。

  不知道Windows CE下软件开发者是否遇到过这种情况,如果使用“new”申请超过30MB的物理内存,那么返回的一定是空(NULL),甚至程序会死锁无法响应。这其实不奇怪。在《Windows CE下进程、线程和内存管理》的系列文章中我早有所言,Windows CE下每个进程占有32MB的地址空间,虽然Slot 1槽存放所有的非XIP DLL,但是我们不可能占用Slot 1槽。32MB地址空间减去必要的代码段、静态数据段、默认堆和默认栈之后,所剩的地址空间少于32MB。即使程序什么都不做也无法满足超过30MB的地址空间的申请需求。所以返回为空非常正常。好在Windows CE下运行的大多数软件不需要那么多的物理内存。

  感觉微软的技术不是支持到很远的将来,而是得过且过,只要满足目前的和不远的将来的需求就行。拿Platform Builder来说,IMGRAM64环境变量用于支持64MB物理内存。可是没有IMGRAM128或者IMGRAM256甚至IMGRAM512。可能是当时绝大多数基于Windows CE的产品都没有过64MB物理内存。现在要支持超过64MB物理内存就必须做一些修改操作。再如现在说的用“new”分配物理内存,也只是限制在32MB以内。如果想new多少就new多少,那多爽!

  “new”不行是因为地址空间不够,那我们可以采用虚拟内存分配,然后提交物理内存这种办法。理论上是这样,但是实际上还是不行。举例如下:

LPVOID g_Address1, g_Address2;

g_Address1 = VirtualAlloc(0, 32 * 1024 * 1024, MEM_RESERVE, PAGE_NOACCESS);
g_Address2 = VirtualAlloc(g_Address1, 32 * 1024 * 1024, MEM_COMMIT, PAGE_READWRITE);


  上面这段代码中第一个语句是申请32MB的虚拟地址空间,函数返回一个地址说明申请是成功的。注意这个地址一定处于0x4200 0000以上(具体参见我的专栏中《Windows CE下进程、线程和内存管理(三)》)。第二个语句是提交物理内存,容量为32MB。这个函数返回NULL,说明申请物理内存不成功。如果申请10MB、20MB的还可以。

  希望再一次破灭。最后的办法就是内存映射文件了。在Windows CE的帮助文档中只提到了内存映射文件可以用来申请虚拟地址空间。可以试一试。结果证明用内存映射文件来申请大容量物理内存是可行的。内存映射文件用于多个进程共享数据时,创建内存映射的函数的第一个参数必须设置为INVALID_HANDLE_VALUE,表示在物理内存中创建。利用这个特点我们可以申请超过32MB的物理内存。具体能够申请的大小由剩余的物理内存决定。例子如下:

#define MAXLEN (64*1024*1024)
HANDLE hFile;
hFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MAXLEN, NULL);
if(hFile == NULL)
{
///创建文件映射对象失败
return;
}
LPVOID lpAddress;
lpAddress = MapViewOfFile(hFile, FILE_MAP_WRITE|FILE_MAP_READ, 0, 0, MAXLEN);
if(lpAddress == NULL)
{
///创建文件视图失败
return;
}


  上述的函数如果都成功了,你就可以使用物理内存了。虚拟内存的首地址是lpAddress。使用完了别忘了调用函数UnmapViewOfFile(lpAddress); 和CloseHandle(hFile);

posted @ 2010-07-30 14:36 jiege 阅读(12) | 评论(0) | 编辑
关于 bootloader 之物理内存 虚拟内存 映射表。OEMAddressTable

一般在ARM架构的CPU上,物理地址都是统一编址的,寻址空间为4GB(32Bit CPU)。也就是说,针对一个ARM的处理器,它可以访问的物理空间是4GB。在WinCE中,ARM中的4GB物理地址空间将被映射为512MB的虚拟内存空间。OEMAddressTable就是一个4GB物理地址空间到WinCE Kernel中的512MB虚拟地址空间的映射表。

 

在BSP中,会定义OEMAddressTable来描述系统中可访问的物理空间及对应的虚拟地址空间,还有大小。这个表会在WinCE系统开始启动的时候传给MMU,具体到BSP中应该是在OAL中的startup.s中,OEMAddressTable的起始地址会被放到r0寄存器中,然后就跳转到KernelStart里面,KernelStart会用OEMAddressTable完成MMU得初始化。当WinCE启动以后,就只能访问虚拟地址空间了。

 

举个例子,比如我们要开发一个Flash的驱动程序,那么首先我们知道这个flash所接的片选对应的物理起始地址是多少(假如是0x60000000),大小是多少(假如是0x2000000)。如果我们要在WinCE中访问它,就必须为它定义一个虚拟地址(假如是0x80000000),并添加到OEMAddressTable中,这样,我们才能在我们的驱动里面通过这个虚拟地址访问到flash。

 

虚拟地址不是随便定义的,WinCE中有规定,必须在0x80000000---0x9FFFFFFF。实际上WinCE创建了两套虚拟地址空间,一个是0x80000000---0x9FFFFFFF,是Cache Enabled。另一个是0xA0000000---0xBFFFFFFF,是Cache Disabled。有啥区别呢:

如果我们访问的这个空间只是一段内存空间(比如SDRAM),那么就可以用Cache Enabled的空间来访问,这样存取数据的速度会比较快,因为数据被保存在Cache中。

如果我们访问的这个空间是一个外设的地址,那么我们就要使用Cached Disabled的空间来访问,这样才能使CPU与外设同步。

可能说得有点绕,我的经验就是:只要是SDRAM,可以用Cache Enabled空间访问。如果是寄存器,就用Cache Disabled空间访问。

如何定义OEMAddressTable呢,如果安装了WinCE5.0或者6.0,那么提供的参考BSP中都已经有定义了,在BSP目录下搜索“OEMAddressTable”,一看代码就明白了,这里重复一下,格式如下:

                    虚拟地址                物理地址               大小

比如:

OEMAddressTable:

dd             0x80000000          0x60000000        0x2000000

dd             0                              0                            0

上面这个表定义了一个flash的物理地址到虚拟地址的映射,物理地址是0x60000000,虚拟地址是0x80000000,大小是32MB。OEMAddressTable最后必须以0结尾,表示OEMAddressTable结束。

 

总之,说白了就是一张物理地址/虚拟地址映射表,当我们要在WinCE中要访问相关硬件的时候,查查这张表,然后通过虚拟地址就可以访问了。如果没有定义,自己添加一个物理地址到虚拟地址的映射就好了。

posted @ 2010-07-30 13:38 jiege 阅读(117) | 评论(0) | 编辑
Windows CE下操作GPIO的方法(以ARM9 S3C2410为例)

文章来源:http://www.cnblogs.com/lonemaverick/archive/2007/02/16/651481.html

GPIO是ARM芯片最基本的输入输出通道,在ADS下操作就是一个单片机工作,直接读写其寄存器。在ARM9平台上,Windows CE系统将GPIO的实地址(例如2410的GPIO的基地址为0x56000000)映射到虚拟地址空间(GPIO对应为0xB1600000),这样,通过对这段虚拟地址空间的操作,就能够完成对GPIO或者其他片内资源的控制、输入输出工作。
要操作一个平台的GPIO,在其对应BSP中按照基地址,找到虚拟地址,并且找到方便操作这个地址的数据结构就可以了,关键函数就是VirtualAlloc和VirtualCopy。并且CE的方便之处就是用户态的应用程序仍然可以使用这两个函数来访问所有这些虚拟空间,对于不太复杂的程序,甚至可以省略写驱动直接在应用程序中操作,其实在CE6之前,这些驱动也是工作在用户态的。
下面以操作Samsung S3C2410的GPIO为例,讲述这个步骤:
1.首先在BSP中的s2410.h文件,找到虚拟地址映射以及操作GPIO的寄存器结构体(这个在自己制作一些特殊设备的BSP时,会依据需要而发生更改)
//
// Registers : I/O port
//

#define IOP_BASE      0xB1600000 // 0x56000000
typedef struct  {
    unsigned int  rGPACON;  // 00
    unsigned int  rGPADAT;
    unsigned int  rPAD1[2];
   
    unsigned int  rGPBCON;  // 10
    unsigned int  rGPBDAT;
    unsigned int  rGPBUP;
    unsigned int  rPAD2;
   
    unsigned int  rGPCCON;  // 20
    unsigned int  rGPCDAT;
    unsigned int  rGPCUP;
    unsigned int  rPAD3;
   
    unsigned int  rGPDCON;  // 30
    unsigned int  rGPDDAT;
    unsigned int  rGPDUP;
    unsigned int  rPAD4;
   
    unsigned int  rGPECON;  // 40
    unsigned int  rGPEDAT;
    unsigned int  rGPEUP;
    unsigned int  rPAD5;
   
    unsigned int  rGPFCON;  // 50
    unsigned int  rGPFDAT;
    unsigned int  rGPFUP;
    unsigned int  rPAD6;
   
    unsigned int  rGPGCON;  // 60
    unsigned int  rGPGDAT;
    unsigned int  rGPGUP;
    unsigned int  rPAD7;
   
    unsigned int  rGPHCON;  // 70
    unsigned int  rGPHDAT;
    unsigned int  rGPHUP;
    unsigned int  rPAD8;
   
    unsigned int  rMISCCR;  // 80
    unsigned int  rDCKCON; 
    unsigned int  rEXTINT0;
    unsigned int  rEXTINT1; 
    unsigned int  rEXTINT2;  // 90
 unsigned int  rEINTFLT0;
 unsigned int  rEINTFLT1;
 unsigned int  rEINTFLT2;
 unsigned int  rEINTFLT3;  // A0
 unsigned int  rEINTMASK;
 unsigned int  rEINTPEND;
 unsigned int  rGSTATUS0;  // AC
 unsigned int  rGSTATUS1;  // B0
 unsigned int  rGSTATUS2;  // B4
 unsigned int  rGSTATUS3;  // B8
 unsigned int  rGSTATUS4;  // BC
 
}IOPreg; 
将这些复制备用。

2.在EVC中建立一个应用程序工程,由于VirtualCopy函数没有在头文件中定义,但是在coredll.lib里面提供了符号连接,所以我们这里直接添加一个函数定义就OK了。
#ifdef __cplusplus
extern "C"
{
#endif

BOOL VirtualCopy( LPVOID, LPVOID, DWORD, DWORD );

#ifdef __cplusplus
}
#endif
同时将步骤1里面的定义复制到这里。

3.按照驱动程序里面操作的方法在应用程序中写GPIO操作函数
(1)定义一个寄存器结构体变量
volatile IOPreg *v_pIOPRegs;
(2)给这个变量分配空间并且映射到寄存器的空间上
v_pIOPRegs = (volatile IOPreg*)VirtualAlloc(0, sizeof(IOPreg), MEM_RESERVE, PAGE_NOACCESS);
 if (v_pIOPRegs == NULL)
 {
  DEBUGMSG (1,(TEXT("v_pIOPRegs is not allocated/n/r")));
  return TRUE;
 }
 if (!VirtualCopy((PVOID)v_pIOPRegs, (PVOID)IOP_BASE, sizeof(IOPreg), PAGE_READWRITE|PAGE_NOCACHE)) {
  DEBUGMSG (1,(TEXT("v_pIOPRegs is not mapped/n/r")));
  return TRUE;
 }
 DEBUGMSG (1,(TEXT("v_pIOPRegs is mapped to %x/n/r"), v_pIOPRegs));
这3个步骤之后,对v_pIOPRegs的操作将直接和GPIO的寄存器关联
例如:设置GPB的控制寄存器为全部Output
v_pIOPRegs->rGPBCON=0x155555;
设置GPB的数据寄存器输出高电平
v_pIOPRegs->rGPBDAT=0x3FF;

更多的操作,需要查阅ARM的datasheet以及WINCE的BSP源码完成。

对于非ARM的平台,在CE下操作,也可以参考这个思路。


文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/4_webprogram/asp.net/netjs/200798/70703.html

posted @ 2010-07-30 12:36 jiege 阅读(45) | 评论(0) | 编辑
深入浅出Wince的存储

刚学wince那会,对wince下面的文件存储老是理解不了,或者说容易搞混. 最近公司在做一个wince下的终端移植项目,中间也遇到了一些存储方面的问题,我自己学wince也有一段时间了,现在对wince的几个存储的概念也可以谈论一二了.

现在市场上的基于wince的板子,基本上有下面几种存储设备, nand flash, nor flash, SDRAM,

SD卡, u 盘. SD卡和U盘不说了,跟PC机没什么驱别. 

先说说nand flash(nor flash就不说了,类似), 你可以把它理解为电脑上的硬盘, OK, 我们看一下电脑上的硬盘里放了什么?, 首先是你的操作系统文件占据了C盘的一部分空间,  C盘剩下的空间以及其它盘的空间就是你可以随便用的. 再来看看nand flash, 它一般被分为三个部分, 首先是一个叫boot loader的东东,然后是你的wince的映像文件, 最后剩下的部分的就是你可以任意使用的. Boot loader 主要做两件事,一是初始化一些硬件资源(比如cache), 二是加载wince系统运行. 你可能要说,nand flash与电脑硬盘的区别就是它多了一个boot loader. 其实这样说不完全对,电脑其实也有一个bootloader, 它的名字叫BIOS. 只不过BIOS不是放在硬盘里,而是固化在主板上的只读ROM里.

Nand flash剩下的这部分这间怎么用呢,wince第一次启动时,打开”我的设备”是看不到这部分空间的,需要你到”存储器管理器”(在控制面板里)去格式化一下,然后新建一个分区. 这之后即使你冷启动系统,它也是可见的.

有个问题我们都很想知道,wince最大可以支持多大的nand flash呢? 一般情况下,bootloader也就是几百K, wince系统映像几十M, 我们当然是希望nand flash越大越好, 这样我们能任意支配的空间就大了. 先来看一下PC上最大可以支持多大的硬盘容量. 目前市场上已出了T数量级的硬盘, 你的电脑能支持的硬盘容量是什么决定呢. 是主板,再具体点,是BIOS,如果你的主板支持48 bit LBA(寻址), 则硬盘最大可以是2的48次方.

是不是wince能支持的最大nandflash也不是wince系统决定的呢. 市场上的nand flash 芯片硬件接口无非是下面几部分,控制引脚,数据引脚夫,电源和地引脚. 然后数据口和地址是复用的. 下面是K9F1208U0B的引脚图,可以很清楚的看到上面几部分(NC表示不用)

 

 

是了,只要能和CPU在硬件上接口匹配,然后bootloader里驱动做相应改动,就可以支持市场上最大的nand flash, 目前市场上最大的nand flash容量我不是很清楚,但06年三星就已经推出了32G的nand flash, 所以现在最大的容量起码也得大过这个数. Nand flash.

好了,该说说wince下的内存了. 一般我们买一块基于wince的开发板,如果厂家说这个板子的内存是64M, 一般就是说SDRAM有64M, wince的内存在硬件上就是这个SDRAM(至于什么是SDRAM,网上有很多资料).

Wince 下的内存实际上是分为三个部分的. 先说这几部分的名字,对象存储, 系统内存,程序内存. 准确来讲,应该用下面公式说明.

 Wince内存 = 系统内存 + (对象存储 + 程序内存).

为什么要把后面两个用括号括起来呢. 是为了强调它们的关系密切. 拿64M的这个板子举例. 开发板上电, 进入控制面板—系统,上面显示内存是30M左右, 怪了,明明是64M的DRAM,怎么少了近一半?

要回答这个问题, 先来看看PC机上的情况, 假设你的电脑内存是1G, 开机,你没有运行任何程序, 打开任务管理器, 内存已经用了200多M了(不同的机器可能有点不同), 你应该已经想到是为什么了,操作系统本身运行也是要用内存的.

Wince运行也要占用内存, 这就是前面为什么少了30多M的内存. 这部分内存我把它叫系统内存, 或者说是wince映像占用的数据内存.

是不是剩下的30M左右的内存就可以完全给我们自己写的程序用了呢, 当然不是, 看一下前面的公式, 剩下的内存又被分成了两部分,对象存储和程序内存. 程序内存不用解释,就是可供我们的程序用的内存, 对象存储是wince里一个新的概念. 嵌入式wince操作系统一般是用在消费电子上面, 比如pocket pc, 手机, PDA等移动设备上. 这一类的设备一般都是有两个电源的, 一个是主电源(比如你的手机电池), 一个是后备电池(拆开手机,电路板上可以找到那个小电池). 后备电池的作用就是在主电源没电的情况下,维持操作系统的一些需要保存的数据, 比如注册表, 数据库等. 而这些要保存的数据就是放在对象存储里的,可以复制一个文件到wince下(SD卡,U盘除外),会发现对象存储占用空间变大,到这里,你应该理解对象存储了

 

好了,内存的三部分搞清楚了, 你可能想知道,这三部分的比例是怎么分的呢,是系统固定死了吗, 当然不会, 嵌入式的设备种类有上千种,嵌入式操作系统应该具有充分的灵活性满足不同的需要. 先看看系统内存怎么改.用pb打开一个你的工程, 在config.bib里的Memory域里(bib文件的格式和作用可以去网上查), 会看到类似下面的语句:

NK      80001000  01E00000  RAMIMAGE

第一个数是起始地址,第二个就是size了.

对象存储和程序内存的大小修改就比较人性化了, 打开控制面板-存储器管理器,可以看到一个滑块,左右移动它就可以改变它们的比例.  但是有一点要注意,这个设置是不会被保存的, 系统冷启动后还是会恢复默认设置的(一般是各占一半). 那怎么办呢?有办法, 如果你在配置系统时就知道这两部分内存所要占的比例,可以在config.bib里加上下面的语句

FSRAMPERCENT = 0Xxxxxxxxx

这个值可以改变默认设置.

最后一个问题,  wince最大支持多大的物理内存, 也就是SDRAM最大可以是多少. 这个是由CPU决定的, 拿三星的2410举个例子. 打开芯片手册,找到Memory controller那个章节,可以看到下面这个图