一,前言:因为友善尚未公布mini2440开发板上的测试程序,所以我自己就写了一个。还好不是太难。
(WINCE6.0+mini2440)
二,首先来分析一下LEDDriver。
2.1,入口函数:(Sources文件里指明了DLLENTRY=DllEntry)
BOOL WINAPI
DllEntry(HANDLEhinstDLL,
DWORD dwReason,
LPVOID /* lpvReserved */)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
DEBUGREGISTER((HINSTANCE)hinstDLL);
return TRUE;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
#ifdef UNDER_CE
case DLL_PROCESS_EXITING:
break;
case DLL_SYSTEM_STARTED:
break;
#endif
}
return TRUE;
}
这里要对dwReason作一些说明,一个程序要调用Dll里的函数,首先要先把DLL文件映射到进程的地址空间。要把一个DLL文件映射到进程的地址空间,有两种方法:静态链接和动态链接的LoadLibrary。当一个DLL文件被映射到进程的地址空间时,传递的dwReason参数为DLL_PROCESS_ATTACH。这种调用只会发生在第一次映射时。如果同一个进程后来为已经映射进来的DLL再次调用LoadLibrary,操作系统只会增加DLL的使用次数,它不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数。但如果是不同进程用LoadLibrary同一个DLL时,则每个进程的第一次映射都会调用DllMain函数,并传递DLL_PROCESS_ATTACH。(驱动一般会在这个case下或XXX_Init()里作些初始化的工作)。
相反地,当进程结束或者使用FreeLibrary解除DLL的映射时,系统再次调用DLL的DllMain(),此时传递的dwReason就是DLL_PROCESS_DETACH。
当进程创建一个线程时,系统调用DLL的DllMain(),此时传递的dwReason是DLL_THREAD_ATTACH。
当线程结束时,系统调用DLL的DllMain(),此时传递的dwReason是DLL_THREAD_DETACH。
2.2:
DWORD LED_Init(DWORD dwContext)
{
RETAILMSG(1,(TEXT("LED_Init----\r\n")));
// 1. Virtual Alloc
Virtual_Alloc();
LEDGpioInit();
mInitialized = TRUE;
return TRUE;
}
先看一看
void Virtual_Alloc()
{
// GPIO Virtual alloc
s2440IOP = (volatile IOPreg *) VirtualAlloc(0,sizeof(IOPreg),MEM_RESERVE, PAGE_NOACCESS);
if(s2440IOP == NULL) {
RETAILMSG(1,(TEXT("For s2440IOP: VirtualAlloc faiLED!\r\n")));
}
else {
if(!VirtualCopy((PVOID)s2440IOP,(PVOID)(IOP_BASE),sizeof(IOPreg),PAGE_READWRITE | PAGE_NOCACHE )) {
RETAILMSG(1,(TEXT("For s2440IOP: VirtualCopy faiLED!\r\n")));
}
}
}
先是用VirtualAlloc()来预留一块内存,如果成功则用VirtualCopy()映射IOP_BASE到刚申请的一块内存里。
2.3:
BOOL LEDGpioInit()
{
RETAILMSG(1,(TEXT("LED_Gpio_Setting----\r\n")));
s2440IOP->rGPBCON= (s2440IOP->rGPBCON&~(3 << 10)) | (1<< 10);// GPB5 == OUTPUT.
s2440IOP->rGPBCON= (s2440IOP->rGPBCON&~(3 << 12)) | (1<< 12);// GPB6 == OUTPUT.
s2440IOP->rGPBCON= (s2440IOP->rGPBCON&~(3 << 14)) | (1<< 14);// GPB7 == OUTPUT.
s2440IOP->rGPBCON= (s2440IOP->rGPBCON&~(3 << 16)) | (1<< 16);// GPB8 == OUTPUT.
return TRUE;
}
配置跟LED阴级连接的四个IO管脚为输出方向。这里有意思的是,wince对寄存器的访问方式,s2440IOP是一个指向IOPreg结构体的指针,可以看到该结构体的成员排列顺序和datasheet里的寄存器顺序里一样的,并且IOP_BASE的值是0xB1600000(寄存器rGPACON首地址0x56000000映射到内核中的虚拟地址),所以s2440IOP->rGPBCON就是对应寄存器的值了。这跟linux的(首地址+位移)的访问方式有点不一样。
2.4:
BOOL LED_IOControl(DWORD hOpenContext,
DWORD dwCode,
PBYTE pBufIn,
DWORD dwLenIn,
PBYTE pBufOut,
DWORD dwLenOut,
PDWORD pdwActualOut)
{
LEDGpioInit();
switch(dwCode)
{
case IO_CTL_LED_1_ON:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT&~(0x1<<5);
break;
case IO_CTL_LED_2_ON:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT&~(0x1<<6);
break;
case IO_CTL_LED_3_ON:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT&~(0x1<<7);
break;
case IO_CTL_LED_4_ON:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT&~(0x1<<8);
break;
case IO_CTL_LED_ALL_ON:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT&~(0xF<<5);
break;
case IO_CTL_LED_1_OFF:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT|(0x1<<5);
break;
case IO_CTL_LED_2_OFF:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT|(0x1<<6);
break;
case IO_CTL_LED_3_OFF:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT|(0x1<<7);
break;
case IO_CTL_LED_4_OFF:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT|(0x1<<8);
break;
case IO_CTL_LED_ALL_OFF:
s2440IOP->rGPBDAT=s2440IOP->rGPBDAT|(0xF<<5);
break;
default:
break;
}
RETAILMSG(1,(TEXT("LED:Ioctl code = 0x%x\r\n"), dwCode));
return TRUE;
}
这个就是控制LED亮与灭的接口了,其实就是把相应管脚置低或置高。
三,测试程序分别用了win32 API函数和MFC两种方式来实现。见后面的附件。
Win32 API方式:采用了Boling D.的编程风格,这种方式可以自己掌控窗口的创建,窗口过程,对话框的创建,对话框过程。
MFC:相对来说就简单多了,只要动动鼠标,VS就会帮你生成很多代码。------------------------------------------
本文乃原创!
转载请注明出处:http://sparklecliz.cublog.cn/
------------------------------------------
文件:
ledtest.rar
大小:
57KB
下载:
文件:
LedControl.rar
大小:
160KB
下载: