WINCE6.0 简单LED驱动程序的编写
扫描二维码
随时随地手机看文章
把它写成了实验报告的样子! 在此BLOG中的有些函数是针对特定的BSP, 比如:地址的映射函数, 在每一个BSP中它的地址映射函数都不同, 但是都是地址映射(废话当然是地址映射了)。其实里面有很多的东西都还可以添加:比如添加读取LED信息状态, 优化地址映射(不必把全部的GPIO都映射, 可以自定义一个小的结构体来实现)。
一. 实验目的
a) 掌握流驱动的结构
b) 掌握一般流驱动的编写方法
二. 实验设备
a) S3C6410 开发板一台
b) PC 机一台, VS2005, CE6.0 环境
三. 实验内容
a) 进行简单 LED 驱动的编写。
四. 实验原理
a) 硬件原理图
i.
ii.
iii. 从原理图可以知道如果我们要点亮 LEDx , 只需要 GPMx 输出高电平, 如果要熄灭 LEDx , 只需要 GPMx 输出低电平即可。
b) 软件原理设计
i. 由于在 WINCE 中使用都是虚拟的地址, 所以需要将实际的物理地址转换为虚拟地址来使用。 在系统提供的 BSP 中提供了一个函数来实现从物理地址到虚拟地址的映射。
1. void *DrvLib_MapIoSpace (UINT32 PhysicalAddress , UINT32 NumberOfBytes , BOOL CacheEnable )
2. 把物理地址转换为虚拟地址。(其实它就是在地址映射表中查找相应的物理地址,然后返回对应的虚拟地址加上它的偏移。)
3. UINT32 PhysicalAddress :要实现映射的物理地址的起始地址。
4. UINT32 NumberOfBytes : 要映射物理地址的长度。
5. BOOL CacheEnable :该物理地址是否使用了CACHE.( 具体参照地址映射表) 。
ii. LED 上下文结构体的定义
1. 定义了一个 LED 驱动的上下文, 用来保存 LED 驱动的信息。 但是只是简单的 LED 驱动, 没有包含多的数据。
typedef struct
{
volatile S3C6410_GPIO_REG *pGPIOReg ;
}LED_PUBLIC_CONTEXT , *PLED_PUBLIC_CONTEXT ;
2 . S3C6410_GPIO_REG 是BSP 预先定义的一个GPIO 使用的数据结构。
iii. GPIO 寄存器的使用。 GPIO 寄存器的地址都映射到了虚拟的地址上。 BSP 提供了一个结构体方便 GPIO 寄存器的使用。
1. typedef struct
{ ……………..
UINT32 GPMCON; // 820
UINT32 GPMDAT; // 824
UINT32 GPMPUD; // 828
………………..
} S3C6410_GPIO_REG, *PS3C6410_GPIO_REG;
2. 在使用这个结构全的时候把 GPIO 的虚拟地址的基地址映射到此结构的开始即可以操作此结构体的数据来操作实际的寄存器。
3. 进行GPIO 物理地址到虚拟地址的映射: pLedContext ->pGPIOReg = (volatile S3C6410_GPIO_REG *)DrvLib_MapIoSpace (S3C6410_BASE_REG_PA_GPIO , sizeof (S3C6410_GPIO_REG ), FALSE ) ;
iv. 实现 LED 灯状态的操作。
1. 获得了 GPIO 的虚拟地址就可以像实际的物理地址那样实现寄存器的操作。
2.
a) // 使能上拉
b) pLedContext ->pGPIOReg ->GPMPUD |= 0x0ff;
c) // 设置为输出
d) pLedContext ->pGPIOReg ->GPMCON = 0x111111;
e) // 关闭所有的LED
f) LED_ALL_OFF (pLedContext ->pGPIOReg ->GPMDAT );
3. 为了方便进行操作,定义了一组宏。
// 打开或关闭LED0
#define LED0_ON (x ) (x |= 0x00000001)
#define LED0_OFF (x ) (x &= 0xfffffffe)
// 打开或关闭LED1
#define LED1_ON (x ) (x |= 0x00000002)
#define LED1_OFF (x ) (x &= 0xfffffffd)
// 打开或关闭LED2
#define LED2_ON (x ) (x |= 0x00000004)
#define LED2_OFF (x ) (x &= 0xfffffffb)
// 打开或关闭LED3
#define LED3_ON (x ) (x |= 0x00000008)
#define LED3_OFF (x ) (x &= 0xfffffff7)
// 打开或关闭所有的LED
#define LED_ALL_ON (x ) (x |= 0x0000000f)
#define LED_ALL_OFF (x ) (x &= 0xfffffff0)
五. 实验步骤
a) Xxx_Init 函数的原型:
i. DWORD XXX_Init( LPCTSTR pContext , DWORD dwBusContext );
ii. pContext: Pointer to a string containing the registry path to the active key for the stream interface driver.
iii. lpvBusContext: Potentially process-mapped pointer passed as the fourth parameter to ActivateDeviceEx . If this driver was loaded through legacy mechanisms, then dwBusContext is zero. This pointer, if used, has only been mapped again as it passes through the protected server library (PSL). The XXX _Init function is responsible for performing all protection checking
iv. 返回值 : Returns a handle to the device context created if successful. Returns zero if not successful. This handle is passed to the XXX_Open (Device Manager) , XXX_PowerDown (Device Manager) , XXX_PowerUp (Device Manager) , and XXX_Deinit (Device Manager) functions
v. 注意:当调用 设备管理程序当调用 ActivateDeviceEx 函数的时候会间接调用到此函数, ActivateDeviceEx 的作用就是加载设备
vi. LED_Init 函数的编写。 LED_Init 最主要的功能就是进行硬件的初使化。
vii. LED_Init 函数的实现:
DWORD Led_Init (LPCTSTR pContext )
{
volatile PLED_PUBLIC_CONTEXT pLedContext ;
RETAILMSG (DEBUG_LED ,(TEXT ("Led_Init Function!/n" )));
// 申请LED 的CONTEXT
pLedContext = (PLED_PUBLIC_CONTEXT )LocalAlloc (LPTR , sizeof (pContext ));
if (!pLedContext )
{
RETAILMSG (DEBUG_LED , (TEXT ("Can't alloc memory for led context!/n" )));
return NULL ;
}
// 得到GPIO 寄存器的地址
pLedContext ->pGPIOReg = (volatile S3C6410_GPIO_REG *)DrvLib_MapIoSpace (S3C6410_BASE_REG_PA_GPIO , sizeof (S3C6410_GPIO_REG ), FALSE );
if (pLedContext ->pGPIOReg == NULL )
{
RETAILMSG (DEBUG_LED , (TEXT ("LED for pGPIORges: DrvLib_MapIoSpace failed!/n" )));
LocalFree (pLedContext );
return NULL ;
}
// 使能上拉
pLedContext ->pGPIOReg ->GPMPUD |= 0x0ff;
// 设置为输出
pLedContext ->pGPIOReg ->GPMCON = 0x111111;
// 关闭所有的LED
LED_ALL_OFF (pLedContext ->pGPIOReg ->GPMDAT );
RETAILMSG (DEBUG_LED , (TEXT ("Led0 on! %d!/n" ), (pLedContext ->pGPIOReg ->GPMDAT )));
return pLedContext ;
` }
a) Xxx_Write 函数原型:
DWORD XXX_Write(DWORD
hOpenContext
, LPCVOID
pBuffer
, DWORD
Count
);
i. hOpenContext : Handle to the open context of the device. The call to the XXX_Open (Device Manager) function returns this identifier 。
ii. pBuffer : Pointer to the buffer that contains the data to write.
iii. Count: Number of bytes to write from the pBuffer buffer into the device.
iv. 返回值: The number of bytes written indicates success. A value of –1 indicates failure.
v. LED_Write 函数的作用只是进行对 LED 状态的写入。
自定义结构体:
typedef struct
{
unsigned char cLedNum ;
unsigned char fLedStatue ;
}LED_DATA , *PLED_DATA ;
cLedNum :LED 的标号。0 对应第一个LED, 1 对应第2 个LED … 4 表示全部熄灭(因为它只有4 个LED )
fLegStatue :将要实现的操作。 0 熄灭, 1 点亮。
vi. LED_Write 函数的实现。
DWORD Led_Write (DWORD hOpenContext ,
DWORD pBuffer ,
DWORD Count )
{
PLED_DATA pLedData = (PLED_DATA )pBuffer ;
RETAILMSG (DEBUG_LED , (TEXT ("Current hOpenContext %d /n" )), hOpenContext );
RETAILMSG (DEBUG_LED , (TEXT ("Corrent GPMDATA %d!/n" ), (((PLED_PUBLIC_CONTEXT )hOpenContext )->pGPIOReg ->GPMDAT )));
switch (pLedData ->cLedNum )
{
case 0:
if (pLedData ->fLedStatue )
{
LED0_ON (((PLED_PUBLIC_CONTEXT )hOpenContext )->pGPIOReg ->GPMDAT );
RETAILMSG (DEBUG_LED , (TEXT ("Led0 on! %d/n!" ), ((PLED_PUBLIC_CONTEXT )hOpenContext )->pGPIOReg ->GPMDAT ));
}
else
{
LED0_OFF (((PLED_PUBLIC_CONTEXT )hOpenContext )->pGPIOReg ->GPMDAT );
RETAILMSG (DEBUG_LED , (TEXT ("Led0 off!/n!" )));
}
break ;
case 1:
。。。。。。
break ;
case 2:
。。。。。。
break ;
case 3:
。。。。。。
case 4:
if (pLedData ->fLedStatue )
{ LED_ALL_ON (((PLED_PUBLIC_CONTEXT )hOpenContext )->pGPIOReg ->GPMDAT );
}
else
{ LED_ALL_OFF (((PLED_PUBLIC_CONTEXT )hOpenContext )->pGPIOReg ->GPMDAT );
}
default :
break ;
}
return (Count );
}
b) 其它流接口函数的编写:其它的流接口函数并没有实现函数过程, 它们一般就直接返回。在Led_Deinit 函数中进行了申请内存的释放,也调用了一个放弃映射的函数DrvLib_UnmapIoSpace , 其实这个函数在6.0 之中没有作用, 它是直接返回结果的函数。
c) LED 的配置:
设备管理器要使用LED 流驱动的接口,就必须将LED 提供的接口函数导出。在Led.def 文件中添加如下代码:
LIBRARY LED
EXPORTS Led_Init
Led_Deinit
Led_Open
Led_Close
Led_Read
Led_Write
Led_Seek
Led_PowerDown
Led_PowerUp
Led_IOControl
导出了LED 的接口函数,要把Led.dll 添加到镜像文件中去还要修改platform.bib 文件。
在 MODULES 节中添加代码如图:
最后修改注册表 platform.reg :
HKEY_LOCAL_MACHINE/Drives/BuildIn 下添加注册表项 LED( 任意名字都可 )
注册表项中的内容:
六. 实验结果及分析
a) 按照上述步骤进行 LED 流驱动的编写, 再编写一个简单的应用程序即可进行 LED 灯的控制。