一个基本的 iButton 接口






4.48/5 (24投票s)
关于如何连接 Dallas Semiconductor 的 iButton 和 1-Wire 网络的示例。
目录
- 引言
- 什么是 iButton
- 在这里如何使用
- 系统要求
- Button 接口类
- 驱动程序初始化
- 会话启动和终止
- 获取 1-Wire 设备列表
- 启动工作线程
- 暂停和恢复工作线程
- 在应用程序中处理通知消息
- 设备选择
- NVRAM 读/写函数
- 设备监控期间的 NVRAM I/O
- 工作线程
- CDialog 通知处理程序
- 结论
引言
首先,希望本文内容不会冒犯任何人!本文旨在演示一种有趣的技术在一种更]有趣(并且在大多数州都是合法)的行业中的应用。在很多方面,这个行业与其他任何商业形式(想想安然)一样腐败,也]不比其他行业更腐败。由于它受到政府和特殊利益集团的密切]监控,因此它可能比大多数行业]更]负责任。
在成人娱乐行业,跟踪现金交易是一个]可疑的过程。我的客户部分]解决了这个问题,]使用了]一家]名为“iButton”的]有趣的小]设备,由 Dallas Semiconductor 制造(另一部分涉及]分布在]整个]俱乐部]的]定制硬件,]通过]以太网]使用]专有]通信协议)。使用]这种]设备,]计算机]系统]可以]跟踪]舞蹈、]存入]账单]接受器]的]钱、]授权]门]访问]等。该]系统]安装]在]全国各地的]舞厅,]在]许多]情况下,]使]俱乐部的]收入]翻倍,]因为]现在]可以]准确]地]跟踪]舞蹈]收入]。]要]查看]您]附近]的]系统]演示……
如]上]所述,]现金]交易]监控]很]复杂。]支持]两种]现金]交易]模型——“]借记]账户”]和“]贷记]账户”。]以下]图]示]了]这]两种]模型]之间]的]区别。][根据]俱乐部]的要求,]使用]其中]一种]或]另一种]或]两种]方法。
贷记账户
借记账户
在这两种]情况下,]这种]方法]仅]在]存在]激活]包厢、]窗口]或其他]启动]了]表演者]和]客户]之间]定时]交互]的]设备]的]方法]时]才]起作用。]在]表演者]和]客户]之间]直接]接触]的]情况]下],]使用]了]带有]激活]灯]的]包厢]。]这]仍然]需要]检票员],]他们]是]在]俱乐部]里]四处]走动]的]人],]确保]没有]表演者]在]没有]激活]的]情况下]与]客户]在]包厢]里]。]大多数]俱乐部]还有]广泛]的]视频]监控],]这些]监控]被]持续]监控]和]录像]。]有趣]的是],]VHS]磁带]仍]在使用],]而不是]花哨]的]数字]录像机]。]据]解释],]原因]是],]警察]只会]拿]走]磁带]作为]证据],]而]如果]您]有]数字]录像机],]他们]会]拿]走]整个]设备]。
该]系统的]其他]功能]包括]能够]根据]表演者](]由]iButton]标识)]和]激活]点](]包厢]、]包厢]类型]、]淋浴]等)]收取]不同]的]金额]。]这]都]是]因为]表演者]iButton]的]唯一]ID]才]可能]。
什么是 iButton?
iButton]就像]雪花]。]没有]两]个]是]相同]的]。]在]制造]时],]每个]设备]的]只读]内存](]ROM])]中]都]写入]了]一个]唯一]的]8]字节](]64]位])]代码]。]除了]ROM]之外],]iButtons]还有]几种]类型],]其中]许多]包括]非易失性]随机]访问]内存](]NVRAM])、]定时]器]功能]和]实时]时钟]。]有趣]的]设备]包括]解码]环]、]Java]解释器]和]温度]传感器]。]设备]使用]“1-wire”]网络]寻址],]这是]Dallas] Semiconductor]为]支持]iButton]接口]而]设计]的]网络]协议]。
有关]iButtons]的]更多]信息]和]下载]iButton] SDK],]请]访问]此处:] www.ibutton.com
在]本文]中],]我]将]说明
- 读取 ROM 代码
- 读取和写入 NVRAM 页面
- 通过工作线程进行 1-Wire 轮询
- 自定义对话框通知
- iButton 去抖
此代码]适用于]所有]支持]NVRAM]的]iButton],]例如]DS1994]。
在这里如何使用?
在我]客户]使用的]软件]中],]使用]带]NVRAM]的]iButton]作为]复制]保护]机制]和]许可证]续订]服务]。]使用的]加密]算法]基于]公共]域]的]“Blowfish”]算法]。]主题]的]CP]文章]在此处]。]验证]加密]的]代码]本身]就]是]加密]的],]并且]由于]它]是用]专有]脚本]语言](]哦],]有机会]推广]AAL])]编写]的],]因此]很难]确定]验证]测试]所]在]的确切]位置],]因为]这]实际上]是]用于]整个]应用程序]的]通用]代码]。(]来]吧],]Bill],]试试]看)。
iButton 代码加密
首先],]iButton]代码]的]加密]版本]本身]会]写入]NVRAM]。]这]可以]防止]能力]中等]的]人]将]授权]从]一个]iButton]复制]到]另一]个]。]由于]iButtons]是]唯一]的],]一个]按钮]的]解密]iButton]代码]将]不]等于]另一]个]按钮]的]代码]。
NIC 地址加密
其次],]计算机]的]NIC]地址](]不是]IP]地址])]被]加密]并]存储]在]iButton]中]。]这]可以]防止]软件]在]未]注册]操作]的]计算机]上]运行]。]除了]防止]非法]副本],]这]也]确保]了]对]运行]软件]的]计算机]系统]有]一些]控制]。]几乎]所有]情况]下],]都会]向]客户]销售]一个]交钥匙]系统]。
许可证时间加密
软件]通常]会]向]俱乐部]授予]一定]的]操作]时数]的]许可证]。]这]确保]了]持续]的]收入]来源],]并且]在]某些]客户]未]及时]付款]的]情况下],]它]确保]了]系统]在]一定]时间]后]会]关闭]。]是的],]在这个]行业],]人们]喜欢]保留]他们]的]现金],]并且]需要]使用]诸如]此类]方法]来]确保]人们]为]他们]的]系统]付款]。]许可]的]操作]时数]被]加密]并]存储]在]iButton]中]。]在]软件]操作]期间],]这个]值]会]频繁]地]递减]。]使用]这种]方法]代替]iButton]的]计时器]功能]是为了]确保]时间]只]在]软件]实际]运行]时]才]递减]。
系统要求
除了]识别]在]俱乐部]发生]的]交易]之外],]iButton]还]用于]在]现金]结算]和]收入]报表](]以及]其他]地方])]的]列表框]和]组合框]中]快速]选择]表演者]。]为了]支持]这种]功能],]存在]几个]挑战],]其中]一些]与]iButtons]无关]。
轮询
1-wire]网络]是]一个]轮询]网络]。]它]不会]在]设备]被]添加到]网络]或]从]网络]中]移除]时]通知]系统]。]因此],]使用]一个]线程]来]监控]网络]上]的]设备]。]存在]两个]并发症]。]一个]是],]1-wire]驱动程序]消耗]大量]CPU]资源]。]在]1.6Ghz] P4]上]以]100ms]为]间隔]轮询]一个]包含]两个]设备]的]网络]会导致]CPU]利用率]接近]50%]。]这]对于]持续]操作]是]不可接受]的],]因此]需要]一种]仅]在]需要]时]才]激活]轮询]的]机制]。
其次],]因为]人]实际]上]使用]一个]简单]的]“]读取器”](]见]图])]将]iButton]连接]到]网络],]iButton]可以在]短](]小于]500ms])]时间]内]出现]和]消失]数]次]。]其中]一个]结果]是],]网络]上]设备]的]顺序]可能]会]发生]变化]。]这]在]该]系统]的]初始]实现]中]导致]了]问题],]因为]我]期望]许可证]iButton](]它]物理]上]安装]在]连接]到]并行]端口]的]支架]中]——]顺便]说]一句],]支架]有]自己]的]唯一]地址])]总是]出现在]设备]列表]中]的]第一个]。]唉],]这]不]一定]是]这样]。]为了]补偿]这个]问题],]实现]了一个]简单]的]去抖]系统]。]这个]机制]在]iButton]添加到]网络]时]提供]即时]通知],]但]会]延迟]500ms]后]才]从]网络]中]移除]“]iButton]移除”]通知]。]这]可以]防止]向]用户]界面]发出]多个]“]新]按钮”]通知]。
通知
下一个]问题]是如何]将]事件]通知]到]包含]列表框]或]组合框]的]对话框]。]在]本文]提供的]示例]中],]使用]了]一个]自定义]消息]通知]方案],]并且]必须]重写]CDialog]的]PreTranslateMessage]方法]。]在]实际]使用]的]系统中],]消息]被]放入]内部]队列],]在]应用程序]的]OnIdle()]函数]期间]进行]处理]。]由于]当]模态]对话框]存在]时],]此]函数]不]处于]活动]状态],]对话框]实现]了一个]定时器],]该]定时器]会]发出]消息]并]激活]相同]的]OnIdle]处理]。]对于]有]兴趣]的]人]来说],]这种]实现]方式]的]原因是]它]还]服务]于]其他]目的],]例如]启用]处理]来自]俱乐部]网络]的]其他]传入]消息]。]由于]Access]用作]数据库],]所有]数据库]事务]必须]在]应用程序]线程]中]发生]。]好了]。]这就是]技术]原因]。
冲突
轮询]例程]工作]线程]与]任何]应用程序]线程] I/O]之间的]冲突]使用]信号量]解决]。
Button 接口类
这个]类]是一个]框架],]用于]添加]iButton]系列]支持]的]额外]功能]。]它]当前]读取]ROM]代码],]读取]和]写入]NVRAM]页面],]并]提供]一种]用于]通知]网络]上]设备]更改]的]轮询]机制]。]类]定义]是
class ButtonInterface { private: static UINT ButtonInterface::MonitorThreadStartup(void* v); public: ButtonInterface(void); virtual ~ButtonInterface(void); bool StartSession(void); void EndSession(void); void StartMonitor(int msSleep=100, int debounceTime=500); void EndMonitor(void) {endMonitor=true;}; void PauseMonitor(void) {pauseMonitor=true;}; void ResumeMonitor(void) {pauseMonitor=false;}; void BeginIO(void); void EndIO(void); bool SelectDevice(CString romCode); bool SelectDeviceStrong(CString romCode); bool ReadPage(int page, unsigned char* data, int len); bool WritePage(int page, unsigned char* data, int len); bool IsMonitorActive(void) {return monitorActive;}; UINT GetNotificationAdded(void) {return notificationAdded;}; UINT GetNotificationRemoved(void) {return notificationRemoved;}; int GetButtonList(CString* codes); protected: void Monitor(void); void PostNotification(UINT msg, CString code); struct ButtonInfo { ButtonInfo(void) { touched=false; newButton=true; } LARGE_INTEGER currentSampleTime; bool touched; bool newButton; }; protected: // button code to info mapping std::map<CString, ButtonInfo> buttonInfo; // notification message data std::map<CString, CString*> msgData; HINSTANCE hInst; // library instance long hSess; // session instance bool endMonitor; // end monitor thread flag bool pauseMonitor; // pause monitor thread. bool monitorActive; // worker thread is running int sleepTime; // sample rate in ms int debounceTime; // debounce time, in ms CRITICAL_SECTION cs; // thread blocking BYTE stateBuffer[15360]; // internal for TMX interface UINT notificationAdded; // button added notification message UINT notificationRemoved; // button removed notification message LARGE_INTEGER freq; // counts per second private: // function pointers to TMX DLL short (far pascal* TMReadDefaultPort) (short far* portNum, short far* portType); long (far pascal* TMExtendedStartSession) (short portNum, short portType, void far* enhSessOpt); short (far pascal* TMSetup)(long hSess); short (far pascal* TMEndSession)(long hSess); short (far pascal* TMFirst)(long hSess, void far* stateBuff); short (far pascal* TMNext)(long hSess, void far* stateBuff); short (far pascal* TMRom) (long hSess, void far* stateBuff, short far* ROM); short (far pascal* TMAccess) (long hSess, void far* stateBuff); short (far pascal* TMStrongAccess) (long hSess, void far* stateBuff); short (far pascal* TMWritePacket) (long hSess, void far* stateBuff, short page, unsigned char far* data, short len); short (far pascal* TMReadPacket) (long hSess, void far* stateBuff, short page, unsigned char far* data, short len); short (far pascal* TMTouchByte)(long hSess, short byte); short (far pascal* TMBlockStream) (long hSess, unsigned char far* data, short len); };
驱动程序初始化
可以通过]包含]应用程序]中的]LIB]来]初始化]驱动程序],]或者]通过]在]运行时]加载]DLL]并]以]编程]方式]确定]接口]点]来]初始化]驱动程序]。]我]选择了]后者],]因为它]能够]在]不]重新编译](]由于]依赖]的]LIB])]的]情况下]更新]驱动程序]。]这]简化]了]俱乐部]的]新]iButton]驱动程序]软件]更新],]在]许多]情况下],]他们]可以]自己]完成]。]以下]代码]说明]了]加载]DLL]并]获取]该]ButtonInterface]对象]的]实现]所需]函数]的]入口点]。
ButtonInterface::ButtonInterface(void) : hSess(0), endMonitor(false), pauseMonitor(false), monitorActive(false) { InitializeCriticalSection(&cs); notificationAdded=RegisterWindowMessage( "iButtonNotification_ButtonAdded"); notificationRemoved=RegisterWindowMessage( "iButtonNotification_ButtonRemoved"); QueryPerformanceFrequency(&freq); hInst = LoadLibrary("IBFS32.DLL"); if (hInst != NULL) { TMExtendedStartSession=(long (far pascal *) (short,short,void far *)) GetProcAddress(hInst, "TMExtendedStartSession"); TMReadDefaultPort=(short (far pascal *) (short far*, short far*)) GetProcAddress(hInst, "TMReadDefaultPort"); TMSetup=(short (far pascal *)(long)) GetProcAddress(hInst, "TMSetup"); TMEndSession=(short (far pascal *)(long)) GetProcAddress(hInst, "TMEndSession"); TMFirst=(short (far pascal *)(long, void far*)) GetProcAddress(hInst, "TMFirst"); TMNext=(short (far pascal *)(long, void far*)) GetProcAddress(hInst, "TMNext"); TMRom=(short (far pascal *)(long, void far*, short far*)) GetProcAddress(hInst, "TMRom"); TMAccess=(short (far pascal *)(long, void far*)) GetProcAddress(hInst, "TMAccess"); TMStrongAccess=(short (far pascal *)(long, void far*)) GetProcAddress(hInst, "TMStrongAccess"); TMTouchByte=(short (far pascal *)(long, short)) GetProcAddress(hInst, "TMTouchByte"); TMBlockStream=(short (far pascal *) (long, unsigned char far*, short)) GetProcAddress(hInst, "TMBlockStream"); // nvram devices only TMWritePacket=(short (far pascal *) (long, void far*, short, unsigned char far*, short)) GetProcAddress(hInst, "TMWritePacket"); TMReadPacket=(short (far pascal *) (long, void far*, short, unsigned char far*, short)) GetProcAddress(hInst, "TMReadPacket"); } } ButtonInterface::~ButtonInterface(void) { endMonitor=true; while (monitorActive) {}; FreeLibrary(hInst); DeleteCriticalSection(&cs); }
会话启动和终止
所有]与]1-wire]网络的]接口]都]必须]在]会话]内]执行]。]会话]机制]在]会话]处于]活动]状态]时]阻止]其他]应用程序]访问]1-wire]网络]。]Dallas] Semiconductor]建议]您]将]会话]保持]打开]时间]尽可能]短],]以免]其他]应用程序]被]阻止]访问]网络]。]由于]这是一个]专有]的]交钥匙]系统],]因此]这]不是]问题],]并且]会话]会]在]ButtonInterface]对象的]整个]生命周期]内]保持]。]以下]代码]说明]了]启动]和]终止]会话],]这]也]会自动]终止]工作]线程]。
bool ButtonInterface::StartSession(void) { if (hInst != NULL) { short portNum; short portType; TMReadDefaultPort(&portNum, &portType); hSess=TMExtendedStartSession(portNum, portType, NULL); if (hSess != 0) { // must be called before any non-session functions // can be called TMSetup(hSess); } } return hSess != 0; } void ButtonInterface::EndSession(void) { if (hSess > 0) { endMonitor=true; while (monitorActive) {}; TMEndSession(hSess); hSess=NULL; } }
获取 1-Wire 设备列表
随时]可以通过]调用]GetButtonList]方法]在]应用程序]线程]中]获取]所有]1-wire]设备]的]列表]。]请]注意],]此]方法]不会]影响]ButtonInterface]对象]内部]用于]确定]1-wire]设备]的]添加]和]删除]的]标志]。]以下]代码]说明]了]如何]获取]1-wire]网络]上]的]设备]。]该]对象]目前]最多]处理]32]个]设备]。]每个]设备]的]ROM]代码]都]作为]CString]数组]返回],]而不是]十六进制]值]。]请]注意],]ROM]代码]的]读取]顺序]与]iButton]上]显示]的]顺序]相反]。]这]纠正]了]驱动程序]返回]ROM]代码]时]是]反序]的事实]。
int ButtonInterface::GetButtonList(CString* codes) { short ret=TMFirst(hSess, stateBuffer); int n=0; while ( (ret==1) && (n<32) ) { // if MSB of ROM[0] != 0, then write, else read short ROM[8]={0, 0, 0, 0, 0, 0, 0, 0}; TMRom(hSess, stateBuffer, ROM); // convert the ROM code into a string char s[3]="\0\0"; CString romCode=""; for (int i=7; i>=0; i--) { sprintf(s, "%02X", ROM[i]); romCode+=s; } codes[n]=romCode; ++n; ret=TMNext(hSess, stateBuffer); } return n; }
这段]代码]通过]简单]的]“First”]和]“Next”]迭代]来]获取]网络]上的]设备]。]每个]字节]都]被]转换]为]字节]十六进制]值]的]ASCII]表示]。
启动工作线程
轮询]工作]线程]以]默认]的]100ms]轮询]间隔]和]500ms]去抖]电路]启动]。]终止]会]设置]一个]标志],]导致]线程]在]下次]启动]轮询]时]退出]。]由于]使用]了]相当]不精确]的]Sleep]函数],]这些]计时]值]仅]是]近似]的]。
... void StartMonitor(int msSleep=100, int debounceTime=500); void EndMonitor(void) {endMonitor=true;}; ...
void ButtonInterface::StartMonitor(int msSleep, int debounce) { if (hSess > 0) { sleepTime=msSleep; debounceTime=debounce; AfxBeginThread(MonitorThreadStartup, this); } }
UINT ButtonInterface::MonitorThreadStartup(void* v) { ButtonInterface* btn=(ButtonInterface*)v; btn->Monitor(); return 0; }
工作]线程]使用]与]应用程序]线程]网络]标识]相同]的方法]。]此外],]它]会]设置]和]清除]各种]标志]来]标识]新]设备]、]从]网络]中]移除]的]设备],]并]处理]设备]去抖]。
暂停和恢复工作线程
当]暂停]时],]工作]线程]会]在]轮询]间隔]时]唤醒],]并且]只]检查]线程]是否]需要]终止]。]暂停]时]不]执行]轮询]。]当]您]只想]出于]性能]或其他]原因]停止]轮询],]而不]必]经历]销毁]和]重新]创建]对象](]这将]导致]重新]加载]DLL])]时],]这]非常有用]。
... void PauseMonitor(void) {pauseMonitor=true;}; void ResumeMonitor(void) {pauseMonitor=false;}; ...
在应用程序中处理通知消息
ButtonInterface]对象]建立]自己]独特]的]“]按钮]添加”]和]“]按钮]移除”]通知]。]这些]可以]通过]在]类]头]文件]中]定义]的]两个]方法]来]确定
... UINT GetNotificationAdded(void) {return notificationAdded;}; UINT GetNotificationRemoved(void) {return notificationRemoved;}; ...
设备选择
在]读取]和]写入]NVRAM]之前],]必须]在]1-wire]上]选择]特定]的]设备]。]为了]确保]正确]选择],]必须]使用]所需]设备]的]ROM]代码]调用]TMAccessStrong]驱动程序]函数]。]如果]使用]TMAccess],]您]将]不]保证]所需]的]设备]实际]上]在]网络]上](]这]已经]通过]经验]验证])。]ButtonInterface]类]提供]了]选择]设备]的]两种]方法]。]“]较弱”]的]SelectDevice]方法]可以在]使用]SelectDeviceStrong]方法]验证]设备]已]存在]于]网络]之后]使用]。]有关]更多]信息],]请]阅读]iButton] SDK]中]的]TMAccess]和]TMAccessStrong]文档]。
bool ButtonInterface::SelectDevice(CString romCode) { short ROM[9]; for (int i=7; i>=0; i--) { CString hex=romCode.Mid(i*2, 2); sscanf(hex, "%02X", &ROM[7-i]); } TMRom(hSess, stateBuffer, ROM); int n=TMAccess(hSess, stateBuffer); return n==1; }
bool ButtonInterface::SelectDeviceStrong(CString romCode) { short ROM[9]; for (int i=7; i>=0; i--) { CString hex=romCode.Mid(i*2, 2); sscanf(hex, "%02X", &ROM[7-i]); } TMRom(hSess, stateBuffer, ROM); int n=TMStrongAccess(hSess, stateBuffer); return n==1; }
NVRAM 读/写函数
NVRAM]页面]读/写]函数]与]硬件]实现]紧密]相关],]每个]页面]最多]读取]和]写入]32]个]字节]。]此]实现]不支持]智能]的]跨]页面]边界]检测]。]虽然]驱动程序]提供]了]TMReadPacket]和]TMWritePacket]函数],]但]这些]函数]仅]支持]NVRAM]设备],]例如]DS1993]。]当然],]这些]信息]没有]在]TMReadPacket]函数]中]提到],]只有]TMWritePacket]函数]提到]了],]这]导致]我]花了]大约]4]个小时]才]弄]清楚]为什么]我]无法]读取]或]写入]DS1994]设备]。
ReadPage]方法]通过]1-wire]网络]发出]一个]命令],]请求]指定]页面],]然后]读取]所需]字节]数],]最多]为]页面]大小],]即]32]个]字节]。
bool ButtonInterface::ReadPage(int page, unsigned char* data, int len) { if (len > 32) return false; TMAccess(hSess, stateBuffer); TMTouchByte(hSess, 0xF0); TMTouchByte(hSess, (short)((page*32)&0xFF)); TMTouchByte(hSess, (short)((page*32)>>8));
类似]地],]WritePage]将]最多]一个]页面]大小](]32]个]字节])]发送]到]指定]页面]
bool ButtonInterface::WritePage(int page, unsigned char* data, int len) { if (len > 32) return false; // write to scratchpad TMAccess(hSess, stateBuffer); TMTouchByte(hSess, 0x0F); TMTouchByte(hSess, (short)((page*32)&0xFF)); TMTouchByte(hSess, (short)((page*32)>>8)); TMBlockStream(hSess, data, (short)len); // get target address and ending offset/data status byte TMAccess(hSess, stateBuffer); TMTouchByte(hSess, 0xAA); unsigned char auth[3]; for (int i=0; i<3; i++) { auth[i]=(unsigned char)TMTouchByte(hSess, 0xFF); } // copy scratchpad to memory TMAccess(hSess, stateBuffer); TMTouchByte(hSess, 0x55); for (int i=0; i<3; i++) { TMTouchByte(hSess, auth[i]); } return true; }
请]注意],]当]一个]字节]被]放置]到]网络]上]时],]1-wire]网络]总是]返回]一个]字节]。]在]读取]字节]的]情况下],]将]0xFF]放置]到]网络]上]。]在]写入]字节]的]情况]下],]将]非]0xFF]的]字节]放置]到]网络]上]。]因此],]真正的]二进制]数据]无法]存储]在]NVRAM]中]。]另请]注意],]在]写入]过程]中],]目标]的]起始]和]结束]地址]在]数据]传输]后]从]网络]中]读取],]并且]必须]写]回]到]网络]才能]实际]提交]数据]到]内存]。]这]可以]用于]确保]数据]完整性]。
设备监控期间的 NVRAM I/O
为了]避免]与]设备]监控]线程]发生]冲突],]应用程序]必须]使用]以下]方法]启动]和]终止]I/O]会话
void ButtonInterface::BeginIO(void) { EnterCriticalSection(&cs); }
void ButtonInterface::EndIO(void) { LeaveCriticalSection(&cs); }
当]不]使用]监控]线程]时],]则]不需要]这样做]。
工作线程
这个]线程]相当]不]言自明]。]去抖]功能]是最]有趣]的],]它]涉及到]计时]一个]设备]在]列表中]保持]“]未]触碰”]多]久]。]一旦]设备]保持]“]未]触碰”]达到]指定]时间],]就会]将]一个]通知]消息]发布]到]应用程序]线程],]并且]设备]会]从]列表中]移除]。]这]也]会]终止]对]移除]设备]的]测试],]因此],]如果]多个]设备]同时]被]移除],]通知]将]会]被]轮询]速率]延迟]。
void ButtonInterface::Monitor(void) { CString* codes=new CString[32]; monitorActive=true; while (!endMonitor) { Sleep(sleepTime); if (pauseMonitor) { continue; } EnterCriticalSection(&cs); // untouch all buttons in list std::map<CString, ButtonInfo>::iterator iter=buttonInfo.begin(); while (iter != buttonInfo.end()) { (*iter).second.touched=false; ++iter; } int numButtons=GetButtonList(codes); // touch all found devices for (int i=0; i<numButtons; i++) { CString romCode=codes[i]; iter=buttonInfo.find(romCode); if (iter != buttonInfo.end()) { (*iter).second.touched=true; QueryPerformanceCounter(&(*iter).second.currentSampleTime); } else { buttonInfo[romCode]=ButtonInfo(); } } // Debounce all activity // 1. If a button is new, then issue an add notification immediately // 2. If a button hasn't been "touched" for the debounce time, // then issue a remove notification LARGE_INTEGER sampleTime; QueryPerformanceCounter(&sampleTime); iter=buttonInfo.begin(); while (iter != buttonInfo.end()) { CString code=(*iter).first; ButtonInfo& bi=(*iter).second; if (bi.newButton) { PostNotification(notificationAdded, (*iter).first); bi.newButton=false; } else if (bi.touched==false) { if (sampleTime.QuadPart-bi.currentSampleTime.QuadPart > freq.QuadPart*debounceTime/1000) { PostNotification(notificationRemoved, (*iter).first); buttonInfo.erase(iter); break; } } ++iter; } LeaveCriticalSection(&cs); } endMonitor=false; delete[] codes; monitorActive=false; }
CDialog 通知处理程序
以下]代码]说明]了]如何在]CDialog]派生]类]中]处理]监视器]线程]通知]。]在此]示例]中],]按钮]代码]要么]被]添加到]列表]控件]列表]中],]要么]被]移除]。]请]注意],]ROM]代码]*]CString]由]消息]处理]程序]释放]。
BOOL CButtonTestDlg::PreTranslateMessage(MSG* msg) { BOOL ret; if (msg->message==btn->GetNotificationAdded()) { CString* code=(CString*)msg->lParam; buttonList.ButtonPresent(code, true); delete code; ret=true; } else if (msg->message==btn->GetNotificationRemoved()) { CString* code=(CString*)msg->lParam; buttonList.ButtonPresent(code, false); delete code; ret=true; } else { ret=CDialog::PreTranslateMessage(msg); } return ret; }
结论
总而言之],]iButton]是]跟踪]货币]交易]的]绝佳]解决方案]。]在]我]客户]系统]的]特定]案例]中],]所有]资金]都]维护]在]监控]俱乐部]交易]的]服务器]中]。]在]其他]应用程序]中],]资金]可以直接]维护]在]iButton] NVRAM]中],]为]授权]请求]和]资金]贷记/借记]提供]自主]支持]。]还有]许多]其他]有趣]的]应用]!