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

      最近做的一个项目要求用三个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;
}

你可能感兴趣的:(thread,windows,struct,null,mobile,winapi)