字对齐
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亮度。

友好的主机可以节省代码
最大的问题在于如何用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外设。