原创 无需Windows活动窗口进行音量控制

2008-2-22 09:03 4622 4 5 分类: MCU/ 嵌入式

引言

  我工作时喜欢有点背景音乐,会打开Windows Media Player,按下 PLAY键来播放音乐。但有电话打进来时,我希望能不用鼠标在桌面上到处找Media Player图标来调低音量或按暂停键。另外,我还有一个朋友抱怨在用PC机玩视频游戏时,游戏会占据整个屏幕,因此调整音量非常麻烦。

  为解决这一问题,本文提供了一个新方案来实现Windows操作系统下的音量控制。 该方案使用一个连接USB端口的旋钮来控制音量,转动旋钮调节音量,按下旋钮暂停播放,再按一次恢复播放。按下并旋转旋钮可以进行选曲。由于它控制整个Windows的音量,因此也控制所有正在被播放的音乐,包括你的Email告警和游戏音乐,这样即使Windows Media Player不是当前活动窗口也可以被控制。


1d92a408-414e-4126-a2fe-43f713243330.jpg


我把这一设计称为USB HID (人机接口设备) (如图1),不需驱动程序,只需将该HID兼容设备插入Windows系统就可以工作。对Windows系统来说,我的HID控制器就像一个遥控器,可以在 HID 报告中看到它 (当然不止这一个)。目前,这个有趣的控制器在我的键盘旁边有了一个固定位置



  低成本 USB

  设计需要挑选一套低成本的微控制器 + USB方案。我在网上找到几款集成了USB控制器的微控制器,但都很昂贵,而且需要我不具备的开发工具。看来我只能选择Atmel的AVR微控制器了,因为我已经有了一套Atmel的 JTAGICE-II,一个低成本的全功能 ICE (在线仿真器)。在Atmel AVR 产品线的低端,从Digi-Key得到AtTiny13的单片报价仅为$1.29,而且提供小型的8脚SOIC封装,非常适合这一应用。Atmel 提供了很好的低成本开发工具,其中包括一个叫作AVRStudio的汇编编译器,可被用来编写汇编代码。

  AtTiny13不包括 USB控制器,但没问题,Maxim的MAX3420E是一个包括了USB收发器的低成本USB控制器,其与mC的接口仅需几根SPI(串行外设接口)数据线,它将占用AtTiny五个I/O 接口中的4个,但MAX3420E自己还有 I/O,可以补偿被占用的I/O,原理图如图2:


0665cfd7-9fc0-43c6-a4e1-86031e37a51c.jpg


U1 是USB控制器,通过SPI接口配置寄存器。U2连接USB接头上的 5V VBUS 信号,将其降压到3.3V给MAX3420E 和AtTiny13供电。旋钮编码器和LED接MAX3420E的通用输入输出引脚。J2用来给AtTiny调试和装载程序。电阻R4 用来隔离系统复位信号与可编程/装载复位信号。当装载器或ICE使用J2时,上拉电阻R3用来关断MAX3420E 的SPI接口。 

  为了节省一个引脚,AtTiny13采用半双工模式与MAX3420E 的SPI接口中的一根双向数据线通信(既做 MOSI又作MISO)。电阻R7 用来避免读取MAX3420E寄存器数据时造成冲突。在第8个SCLK 上升沿,MAX3420E SPI接口采样命令字节第8位,随后开始在数据线上输出第1位数据。由于采用bit-bang 的SPI接口无法在数据第8位将SCLK拉高,然后在同一指令内立即放弃对数据线的控制,所以有一段时间 AtTiny13和MAX3420E都在驱动数据线 。电阻R7用来在此期间限制电流,限制到大约3mA。


  SPI通信

  AtTiny13并没有一个硬件的SPI单元,但幸运的是MAX3420E的SPI接口非常简单,可以任何速度运行。这意味着AtTiny13可通过软件利用bit-bang方式的I/O模拟SPI接口。MAX3420E可支持3线, 4线或5线的SPI接口。不能减少的信号是SCLK (串行时钟), SS# (从器件选择),以及一个双向数据引脚MOSI/MISO (主出从入/主入从出)。我用了AtTiny13的第4个输入引脚连接MAX3420E 的INT 引脚,以节省代码空间,降低SPI总线流量。


  关于HID

  USB HID的规格书定义了一系列传感器、按钮、灯光等用户希望连接到应用程序的外设。 “Universal Serial Bus HID Usage Tables”(www.usb.org)详细介绍了数百种建议的“应用”。如果你希望建立一个弹球仿真程序的控制器,你可以在 “Game Controls”那一页(在HID中每一类设备都有一“页”)看到挡板的“Usage ID”是0x2A。当你建立自己的HID报告格式时,可以将报告字节中的一位对应该页代码,这样,该弹球仿真程序就可以自动搜寻该HID设备,在器件报告描述符中找到该页代码,如果匹配 ,会将该位对应成一个弹球挡板。

  这并不意味着每一个弹球仿真程序会检查所有应用。HID是一个双向协议:HID设备和 Windows应用必须使用HID规范中指定的代码。

  HID文档很粗略,是一个典型的复杂规范。你仅需要弄懂1%就可以让你的设备工作,但问题在于这1%在哪里,如何实现HID在规范中没有很好的体现,但确实需要了解清楚(a)你需要规范的那一部分, (b) Windows支持什么。你无法在HID规范中找到(b)的答案,你需要检查Microsoft的文档 。例如,当我试图插入一个USB音量控制器来启动Windows Media Player时,通过查找HID规范,我找到一系列具有“启动应用程序”(AL)功能的应用代码,其中一个应用ID是“A/V Capture/Playback”。我想这个应该可以启动Media Player,但通过发送该代码给Windows居然没有反应,记住:HID定义的只是可能的应用。实际上,在HID规范中列出的所有能启动的应用程序中,Windows XP唯一支持的是启动“邮件阅读”。

  HID兼容外设通过发送报告与Windows通信,HID设计的主要任务是用一个报告格式将你的控制内容发给OS。本设计需要实现以下功能,每个都在HID应用列表中有一个入口:

  * 音量增加
  *




音量降低
  * 暂停
  * 播放
  * 下一曲
  * 上一曲

  表1是我的HID报告描述符,我用www.usb.org下载的HID_tool程序生成,不用试图去弄明白实际HEX值,让工具来帮你的忙。

  当主机发送一个Get_Descriptor-Report 请求时,AtTiny13软件将该数据发给主机,报告描述符定义了该单字节报告的格式,如表2所示。从“Usage(VOL+)”这行开始,字节中始于LSB(USB总是低位在先)的每一位都有描述。控制仅需6位,所以后两位补0 [“Input(Constant)” 行]。

  一旦Windows识别出该设备,OS开始周期性的发送 IN请求给 Endpoint 3-IN,期待表2中定义的一个字节的响应。软件监视旋钮的变化,有动作时(如音量增加)将报告字节的相应位置。若没有变化,软件返回一个全0的字节。MAX3420E可以使这样的USB更新非常容易实现。 SPI主机 (AtTiny13)将报告字节写入一个叫做EP3INFIFO(Endpoint 3 IN FIFO)的寄存器,然后向EP3INBC (Endpoint 3-IN byte count)寄存器写入1,意味着下次响应USB IN请求时发送一个字节。发送结束后,MAX3420E产生一个中断请求,表示下一个字节可以被装入EP3INFIFO了。程序如表3所示:


08837126-42d6-40a0-b834-1d2d78d5b3fd.jpg


非常简单,但你可以注意到代码包括很多页。剩下的代码用来循环检测旋钮变化,并闪烁LED。其余是一些USB外设所必须的辅助代码。辅助代码管理设备枚举,暂停和恢复,以及USB复位。


eaba0ba1-4913-4d8c-bc53-c38b53c543f8.jpg


优化代码

  AtTiny13只有512个字节的程序存储空间。我担心不能满足一个完整的USB应用程序所需,因为程序需要做如下工作:

  1. 将设备枚举为HID类。包括解释主机的各种 “Get_Descriptor”请求,找出对应数据,装载Endpoint Zero FIFO,将数据发往主机。
  2. 检查随时都有可能发生的USB总线复位。
  3. 检查USB总线挂起和恢复。
  4. 周期性的读取旋钮位置和按键信息。
  5. 当主机通过EP3-IN (用来发送HID控制数据)接收一个IN包后,装载数据进行下一次IN传输。
  6. 闪烁 “活动状态”LED。我通过3个途径来压缩代码,首先,我忽略了可选的USB字符串描述符。这些文本字符串用来说明设备,如供应商和设备类型。这些字符串仅能提供一些信息,对于USB外设工作不起任何作用。其次,我并没有使用可选的USB遥控唤醒功能。尽管实现并不困难(主要由MAX3420E来实现),但是很消耗代码空间。最后,我把HID报告描述符放进E2PROM而不是程序存储器 (flash)。AtTiny13有64字节的E2PROM,数据放进E2PROM可以节省程序存储器 (flash)。


  加载代码

  project zip文件包括两个加载模块, “max3420e_code.hex”和 “max3420e_code.eep”,如果你使用我提供的原理图,可以用6针的 J2连接在线编程器AVRISP2 (Digi-Key ATAVRISP2-ND)加载代码。如果你想修改代码,也可以用J2连接AVR系列的在线仿真器ATJTAGICE2-ND。无论用哪种方式加载,都别忘了要加载 .hex文件 (程序代码)和.eep文件 (E2PROM数据)。


  两个汇编细节

  编写描述符表数据时,AVR的架构和汇编器有两点需要特别注意。


  字对齐

  AVR程序存储空间为16位宽,每个. DB (定义字节)中字节数为偶数。为程序易读,最好每行写一个.DB及说明。下面的代码段说明汇编器自动为每个字节补一个字节“0”来实现字对齐:

  DX:
  0001e6 0012 .DB 0x12
  0001e7 0001 .DB 0x01
  0001e8 0000 .DB 0x00
  0001e9 0002 .DB 0x02

  一个方法是在每个.DB中将两个单字节描述符分为一组,如下所示:

  DY:
  0001e6 0112 .DB 0x12,0x01
  0001e7 0200 .DB 0x00,0x02

  以上仅适用于16位宽的程序存储器,用来存储HID报告描述符的8位宽E2PROM则不存在这一问题。

  指向一个字节

  第二点有关如何建立一个指向USB描述符数据的字节指针,AVR有单个的索引寄存器X, Y 和Z,指向代码存储器字节,字节寻址通过将16位闪存地址乘2实现。然后偶字节LSB置0,奇字节置1,如将Z寄存器指向描述符的第一个字节 (地址为DD),则代码如下所示:

  ldi

 ZH,HIGH(DD*2)
  ldi  ZL,LOW(DD*2) 
  lpm desclen,Z

  上面的例子将DD表中的第一个字节装入变量‘desclen’ (描述符长度)。HID描述符的长度位于表中的第8个字节,所以第二个例子介绍如何将ZL装入此地址:

  ldi ZL,LOW((HD*2)+7) ; length is 8th byte of HID descriptor (offset 7)


  闪烁的蓝色LED

  我喜欢用一个闪烁的LED来表明一切正常。我本来希望每秒闪烁一次,但编程过程中改成了用一个蓝色LED缓慢改变亮度,以产生一个心跳脉冲的效果。AtTiny13的定时器/计数器做了大部分工作。我将其设为快速PWM模式,此时 8位定时器从0连续计到0xFF,再回到0,然后我用计数器的比较寄存器OCC0B来回切换 LED状态。通过逐步改变比较门限 ,产生的PWM输出可以连续改变LED亮度。


a5587c28-864c-4119-a466-bf6f5c2d918e.jpg


友好的主机可以节省代码

  最大的问题在于如何用512个字节的程序存储空间满足一个全功能USB设备的需求。一开始我就想到代码空间会是一个问题,因此我一直考虑对主机做一些假设来节省代码。这是每个编程人员都应该考虑的哲学问题。秘密在于假设主机越友好,就越能节省代码。

  这里有一个例子,在枚举期间,我们回送的接口描述符报告我们有一个接口 (=0),和一个备用选项(=0)。现在假设主机发出一个Set_Interface请求。我们需要检查主机是否仅是在要求我们的接口(0)和备用设置(0)吗? 友好的主机不会设置一个我们在枚举期间没有定义的特性。实际上,如果仅有一个接口和备用设置,主机甚至都不应该发送Set_Interface请求,因为没有其它可选的设置。因此只要我们假设主机是友好的,就可以不必验证Set_Interface请求是否IF=0, AS="0"。对于PC机的主流操作系统,这是一个安全的假设。

  我开始的时候通过这种假设来节省代码,但后来通过将一些USB数据写入E2PROM,我又复原了这些检查,而且实际上还有一些剩余的代码空间。这一应用使用了512字节代码空间的90%,你可以自己再添加一些代码。


  结论

  USB已成为了PC机上替代串口的标准接口,本文说明尽管USB比串口要复杂,但USB 也不一定总需要复杂的代码和昂贵的处理器。 大部分代码都是USB模版文件,可以在不同项目中复用。USB的优势很明显,如电缆供电,自动握手和错误检查,以及与操作系统的接口(类似HID钩子函数)。如果你还想使用现有的微控制器和开发工具,你仍可以使用基于SPI的USB控制器MAX3420E来开发你的USB外设。

文章评论1条评论)

登录后参与讨论

用户210741 2008-3-16 21:09

挺好,我最近玩了三天红外线和PC上位机程序 咱们的功能差不多,我的是控制系统音量 控制千千静听(利用它的全局快捷键) 当然它还可以模拟按键 调用其它程序“shell”
相关推荐阅读
用户71098 2014-11-18 09:41
智能家居不应被手机化绑架
现在很多产品都围绕着手机开发,手机集成的功能也越来越多,甚至可以实现移动办公。以智能家居来说,几乎所有的产品都围绕着手机终端开发,小到一只灯泡、大到电视、冰箱、洗衣机…只要摆在家里的电器,都可以通...
用户71098 2014-11-18 09:40
智能家居不应被手机化绑架
现在很多产品都围绕着手机开发,手机集成的功能也越来越多,甚至可以实现移动办公。以智能家居来说,几乎所有的产品都围绕着手机终端开发,小到一只灯泡、大到电视、冰箱、洗衣机…只要摆在家里的电器,都可以通...
用户71098 2014-11-01 10:03
浅谈智能家居
最早期,小商家们打着智能家居的幌子,推出了遥控开关、定时开关,以低廉的价格去打入市场。这时,所谓的智能家居,就是狭义上的传统开关智能化,与真正意义的智能家居相差甚远。但凭着商家搞得噱头,当时这类开...
用户71098 2013-09-09 16:09
GSM继电器的PCB回来了
前几天发出打样的PCB回来了,先上几个图               ...
用户71098 2013-09-09 16:08
GSM继电器板基本焊接好了
PCB到手几天了,今天上午把元件基本焊接好,除了一些端子没有,回头把没有的原件买回来喊上就可以了,指示灯位置也跟外壳吻合,太好了,简单测试了下,GSM模块跟电脑连接测试,打电话和发信息都没问题了,...
用户71098 2013-08-29 15:13
想用SIM900A做个小东西
        早几年前,折腾过TC35I,后来因为别的事情就搁置了,现在想再搞起来,但TC35I的连接座手焊有一定难度,所以就不用它了,网上搜索一番,发现SIM900A还挺好,邮票口的封装,手焊...
我要评论
1
4
关闭 站长推荐上一条 /2 下一条