react 前端框架如何驱动企业数字化转型与创新发展
787
2022-09-19
控制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;}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~