您的位置:首页 > 其它

控制Windows Mobile的LED 之二:控制三个LED指示灯

2010-11-05 23:24 288 查看
最近做的一个项目要求用三个GPIO同时独立的控制三个不同颜色的LED灯,且每个LED灯存在亮、灭、快闪、慢闪等四种状态。一开始想到在一个线程里面完成,但无法独立的控制三个LED灯的闪烁频率,无法避免相互影响。无奈之下,先用三个线程控制三个LED实现该功能,如果以后熟悉了线程定时器的做法,再优化。

(1)NLED设置结构体说明

struct NLED_SETTINGS_INFO
{
UINT LedNum; // @FIELD LED number, 0 is first LED
INT OffOnBlink; // @FIELD 0 == off, 1 == on, 2 == blink
LONG TotalCycleTime; // @FIELD total cycle time of a blink in microseconds
LONG OnTime; // @FIELD on time of a cycle in microseconds
LONG OffTime; // @FIELD off time of a cycle in microseconds
INT MetaCycleOn; // @FIELD number of on blink cycles
INT MetaCycleOff; // @FIELD number of off blink cycles
};

主要需要用到的几个参数是:LedNum标识不同的LED;OffOnBlink标识不同的工作状态;OnTime标识灯亮时间,OffTime标识灯灭时间,两者仅blink时有用。在很多LED的控制场合,只需要配置这个结构体之后,调用系统函数NLedSetDevice即可。例如:

void LightOPLed(int onoff)
{
NLED_SETTINGS_INFO SettingsInfo;
BOOL nled_result;

SettingsInfo.LedNum = 0; //0号LED
if(onoff)
SettingsInfo.OffOnBlink = 1; //灯亮
else
SettingsInfo.OffOnBlink = 0; //灯灭

SettingsInfo.OnTime = ~0x0;
SettingsInfo.OffTime = 0;
nled_result = NLedSetDevice( NLED_SETTINGS_INFO_ID, &SettingsInfo);

}

(2)GPIO的使用

用普通的GPIO来控制LED,置高亮,置低灭。因为在WINCE中需要对硬件进行虚拟映射之后才可以访问,一般是用MmMapIoSpace,本处是用的另外的一种方式,达到一样的效果。在NLedDriverInitialize中完成对所需硬件的映射,包括I2C,OSTimer,GPIO等。以下由于篇幅关系省略了错误判断和TRACE。

BYTE *pBaseVirtual; //虚拟映射地址变量

DWORD dwBasePhysical; //硬件基地址变量
LONG dwMMLen; //所需的内存长度
dwMMLen = ((sizeof(XLLP_I2C_T) +4*1024-1)/(4*1024)) * 4*1024 +

((sizeof(XLLP_OST_T) +4*1024-1)/(4*1024)) * 4*1024 +
((sizeof(XLLP_GPIO_T) +4*1024-1)/(4*1024)) * 4*1024;
/*以上是为了保证4K的页内存处理,所以会加4K取整*/
pBaseVirtual = (BYTE *)VirtualAlloc(NULL, dwMMLen, MEM_RESERVE, PAGE_NOACCESS); //分配虚拟内存


dwBasePhysical = MONAHANS_BASE_REG_PA_I2C; //I2C硬件地址
bRet = VirtualCopy((LPVOID)pBaseVirtual, (LPVOID)((unsigned long)dwBasePhysical >> 8), sizeof(XLLP_I2C_T), (PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL));
s_Device.m_pI2CCtrlReg = (P_XLLP_I2C_T)pBaseVirtual; //将分配的I2C映射地址赋给NLED全局变量
(BYTE *)pBaseVirtual += ((sizeof(XLLP_I2C_T) +4*1024-1)/(4*1024)) * 4*1024; //跳过刚用过的I2C内存



dwBasePhysical = MONAHANS_BASE_REG_PA_OST; //OSTimer硬件地址
bRet = VirtualCopy((LPVOID)pBaseVirtual, (LPVOID)((unsigned long)dwBasePhysical >> 8), sizeof(XLLP_OST_T), (PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL));
s_Device.m_pOSTimer = (P_XLLP_OST_T)pBaseVirtual; //将分配的OSTimer映射地址赋给NLED全局变量
(BYTE *)pBaseVirtual += ((sizeof(XLLP_OST_T) +4*1024-1)/(4*1024)) * 4*1024; //跳过刚用过的OSTimer内存



dwBasePhysical = MONAHANS_BASE_REG_PA_GPIO; //GPIO硬件地址
bRet = VirtualCopy((LPVOID)pBaseVirtual, (LPVOID)((unsigned long)dwBasePhysical >> 8), sizeof(XLLP_GPIO_T), (PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL)); //将分配的GPIO映射地址赋给NLED全局变量
s_Device.m_pGPIOReg = (P_XLLP_GPIO_T)pBaseVirtual; //将分配的GPIO映射地址赋给NLED全局变量

(3)对LED的亮和灭的控制

首先要在NLedDriverSetDevice添加对不同LED的控制,为此需添加三个LED的宏定义。由于三个LED类似,为此这里仅专门对红色LED的控制列举。对闪烁的控制需要建立一个线程和事件来控制,在下一节说明。如下:

BOOL WINAPI NLedDriverSetDevice(INT nInfoId, PVOID pInput )

{

.................

struct NLED_SETTINGS_INFO *p = ( struct NLED_SETTINGS_INFO* )pInput;

.................

switch(p->LedNum)
{

case RED_LED:
if(p->OffOnBlink == 1) //如果是全亮
{
ResetEvent(nled_Redset_event); //LED状态改变,取消闪烁事件
p->OffTime = 0; //灭时间为0
LedRGBSet(XLLP_GPIO2_2_RED_LED,p->OnTime,p->OffTime); //设置GPIO
}
else if(p->OffOnBlink == 0) //如果是全灭
{
ResetEvent(nled_Redset_event); //LED状态改变,取消闪烁事件
p->OnTime = 0; //亮时间为0
LedRGBSet(XLLP_GPIO2_2_RED_LED,p->OnTime,p->OffTime); //设置GPIO
}
else //如果是闪烁
{
RED.OffTime = p->OffTime;
RED.OnTime = p->OnTime; //获得亮灭的时间,并赋给全局变量,以便线程中调用
SetEvent( nled_Redset_event); //置闪烁事件有效
}
break;

}

}

其中,GPIO的控制函数如下:

void LedRGBSet(UINT8 pinNum,DWORD onTime, DWORD offTime) //zhangcheng 20101101
{

XllpGpioSetDirection(s_Device.m_pGPIOReg, pinNum, XLLP_GPIO_DIRECTION_OUT);
if((onTime !=0)&&(offTime == 0))
XllpGpioSetOutputLevel(s_Device.m_pGPIOReg, pinNum, XLLP_HI);
else if((onTime == 0)&&(offTime != 0))
XllpGpioSetOutputLevel(s_Device.m_pGPIOReg, pinNum, XLLP_LO);
}

(4)LED闪烁线程的处理

定义三个LED的事件句柄和准备传入线程的全局变量

HANDLE nled_Redset_event,nled_Greenset_event,nled_Blueset_event;

struct NLED_SETTINGS_INFO RED,GREEN,BLUE;

在NLedDriverInitialize中要创建时间跟线程:

nled_Redset_event = CreateEvent(NULL, TRUE, FALSE,NULL); //最后一个参数不需事件名字
nled_Greenset_event = CreateEvent(NULL, TRUE, FALSE,NULL); //初始化是FALSE,必须是设置灯闪烁才为SETEVENT
nled_Blueset_event = CreateEvent(NULL, TRUE, FALSE,NULL); //手动清信号,这个很重要,否则线程循环会阻塞
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)nled_Redset_thread, 0, 0, NULL);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)nled_Greenset_thread, 0, 0, NULL);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)nled_Blueset_thread, 0, 0, NULL);

以红色LED的线程说明:

DWORD WINAPI nled_Redset_thread()
{
DWORD dwWaitTime = INFINITE;
while(1)
{
int status;
status = WaitForSingleObject(nled_Redset_event, dwWaitTime); //线程阻塞,必须事件有效才会往后执行
if (status == WAIT_FAILED){ //如果事件是线程等待函数执行完后清掉了,第二次就阻塞在这里了,所以要用不自动清的事件
RETAILMSG(1,(TEXT("nled_Redset_thread: wait failed!/r/n")));
}
XllpGpioSetDirection(s_Device.m_pGPIOReg, XLLP_GPIO2_2_RED_LED, XLLP_GPIO_DIRECTION_OUT);
XllpGpioSetOutputLevel(s_Device.m_pGPIOReg, XLLP_GPIO2_2_RED_LED, XLLP_HI);
Sleep(RED.OnTime); //亮OnTime时间
XllpGpioSetOutputLevel(s_Device.m_pGPIOReg, XLLP_GPIO2_2_RED_LED, XLLP_LO);
Sleep(RED.OffTime); //灭OffTime时间,形成闪烁
}
return TRUE;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: