V-USB AVR单片机USB编程

阅读更多

V-USB  AVR单片机USB编程

作者: 日期:
gashero
2009-02-22

目录

  • 1   简介
  • 2   电路图分析
  • 3   下位机程序
    • 3.1   libs-device
    • 3.2   usbdrv
      • 3.2.1   CPU核心频率
      • 3.2.2   USB标识符
      • 3.2.3   开发系统
      • 3.2.4   免费使用AVR-USB
  • 4   上位机程序
    • 4.1   opendevice
  • 5   例子
    • 5.1   examples
      • 5.1.1   custom-class
      • 5.1.2   hid-custom-rq
      • 5.1.3   hid-mouse
      • 5.1.4   hid-data
      • 5.1.5   usbtool
    • 5.2   tests

1   简介

已经可以用AVR做一些好玩的实验了,不过还是做通信实验好玩。

看了一些相关的文章,本以为又要自己买D12之类的芯片来搭建与电脑USB接口的通信。后来发现一些帖子说可以拿AVR的端口直接来做。想想也是,这样最好了。

参考文献:

  1. [u] USB1.1的AVR实现,需要12.8MHz或16.5MHz的时钟,2KB flash、128byte RAM的芯片:http://www.obdev.at/products/avrusb/index.html
  2. [u] 一套AVR系统上的操作系统OPEX : http://www.atmanecl.net/EnglishSite/OPEX.htm
  3. [u] AVRUSBBoot,对平台支持,功能强大: http://www.fischl.de/avrusbboot/
  4. [u] AVR做USB控制器的更多实例: http://www.getproject.com.cn/Article/Show/16.aspx
  5. [u] 用AVR实现的软USB: http://www.smart-io.com/second/article/avr_usb.htm
  6. [u] 对AVR-USB的初步研究: http://blog.ednchina.com/sprhawk/
  7. [u] 这个博客里面有好几篇文章在研究: http://eiart.net/blog/archive/316
  8. [u] V-USB的wiki: http://vusb.wikidot.com/

2   电路图分析

基于AVRUSB,即obdev.at网站的教程。

circuits 目录。

从USB的正极那里引入电路后,需要先串联两个1N4148二极管才给电路供电,是因为USB规范中D+/D-信号线的电压是3.3v,而经过两个二极管降压后正好是这个电压。另外一种降压的方式是在D+/D-信号线和地线之间串接3.6v电压的稳压二极管。

所有的例子中都是把D+数据线接到AVR单片机的INT0端口,这是因为INT0是AVR的最高优先级中断。你也可以在usbconfig.h 中改为其他的中断,只是确保不能有更高的中断。

如果在 usbconfig.h 中使用了 USB_SOF_HOOK 或 USB_COUNT_SOF ,必须将D-连接到中断。这样在USB帧起始脉冲时触发。

大多数例子中D+会使用一个1M的电阻接地,这是为了在使用自身供电,而没有接入USB数据线时不会误触发中断。而对于USB供电的设计,则可以忽略这个电阻的存在。至于给出的1.5K的电阻,连接在D-和电源线之间的,是为了提供齐纳二极管降压模式时的电压的。

所有例子中使用ATmega8/88/168时D+连接到PD2,因为这也是INT0,而D-连接到PD4,因为这也是定时器/计数器0的时钟输入。这样可以定期检测是否连接上USB了,如果没有连接则可以进入低功耗挂起模式。

3   下位机程序

3.1   libs-device

libs-device 目录提供了下位机的一些有用的程序片段。包含文件如下:

  1. libs-device/osccal.c :提供了测试AVR内置RC时钟的函数,查看头文件了解接口。
  2. libs-device/osctune.h :包含供 usbconfig.h 使用的代码片段。通过这些代码可以在保持USB帧同步时的RC时钟。这是连续的同步,而不只是在USB重置时的一次校准。需要注意的是这段代码仅在D-连接到中断,而不是D+,的时候才工作。

3.2   usbdrv

usbdrv 是下位机中的USB驱动程序。把所有的.c和.S文件拷贝到你的工程中,主要是 usbdrv.c 、 usbdrvasm.S 、 oddebug.c 。然后拷贝 usbconfig-prototype.h 作为 usbconfig.h 到工程中,然后修改你自己的配置。

API的细节信息在 usbdrv.h 中,请小心阅读所有部分。配置选项文档在 usbconfig-prototype.h 中。

3.2.1   CPU核心频率

AVRUSB支持的时钟频率有12MHz、12.8MHz、15MHz、16MHz、16.5MHz、20MHz。其他的不支持。除非使用默认的12MHz,否则需要修改 usbdrv.h 中的配置。

  1. 12MHz :能够支持的最低频率。
  2. 15MHz :类似于12MHz,但是包含了一些NOP。允许生成更小的代码。
  3. 16MHz :用于支持Arduino和其他已经制造好的板子。因为16MHz无法整除为低速USB所需的1.5MHz时钟,所以代码中包含了一些狡猾的技巧来支持。
  4. 12.8MHz 和 16.5MHz :AVR的内部晶振频率。
  5. 20MHz :跟16MHz的处理方式一样。

3.2.2   USB标识符

每个USB设备拥有一个制造商(vendor)和一个产品(product)标识符,即VID和PID。VID需要向usb.org申请,每个1500美元,有VID后,就可以随便分配PID了。

因为1500美元对很多公司和爱好者(hobbyist)来说依然很贵。所以我们搞到一些免费的VID/PID对,查看文件 USB-License.txt 了解更多。

3.2.3   开发系统

本驱动开发面向gcc 3,而在gcc4下仍然可以很好的工作,只是代码尺寸会变大。我们还是推荐gcc套件,因为自由。本驱动也移植到了IAR的编译器,只不过一直使用gcc开发,所以对gcc的优化也是最好的。

3.2.4   免费使用AVR-USB

使用了GPL2和GPL3协议。

4   上位机程序

libs-host

libs-host/opendevice.c :这个模块包含的函数用于寻找和打开给定ID、名字、序列号的设备。它基于libusb/libusb-win32,并且返回libusb设备句柄。查看头文件了解用法。

libs-host/hiddata.c :包含的函数用于在HID功能报告上进行数据传输。基于libusb和Windows本地函数。在Windows上无需驱动DLL。

libs-host/hidsdi.h :这个头文件在MinGW的Windows DDK中缺失了。在你发现"include file not found"错误时用它。

4.1   opendevice

函数声明:

int usbGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen)

这个函数获取字符串的设备描述符,返回ISO latin1编码的字符串。index是描述符序号。获取的字符串存入buf,最大缓存长度输入buflen,函数返回实际字符串长度,包括结尾的\0,负数表示错误。出错后通过 usb_strerror() 获取错误信息。

int usbOpenDevice(usb_dev_handle **device, int vendorID, char *vendorNamePattern, int productID, char*productNamePattern, char *serialNumberPattern, FILE *printMatchingDevicesFp, FILE *warningsFp)

遍历所有USB设备并搜索。首先通过vendorID和productID来匹配,0表示任意数字。如果这两个ID匹配成功了,就开始匹配名字,可以基于带vendor的名字 vendorNamePattern ,或带product的名字 productNamePattern ,或带序列号的 serialNamePattern 。设备的匹配要求所有非空模式的匹配成功。如果某个字段不存在就填入一个NULL。模式用的是Unix shell方式的, '*' 表示0个或多个字符,'?'是一个字符,方括号用以从序列中选取一个字符(破折号可以用于指定范围),如果列表开始与"^"则是反义匹配。

其他参数warningsFp如果不为NULL则用于显示打印警告信息。printMatchingDevicesFp用以打印匹配成功的设备,而不打开设备。

如果打开了设备,结果USB设备存入 *device 中。返回0成功,失败返回错误码。如下:

  1. USBOPEN_SUCCESS=0 :无错误
  2. USBOPEN_ERR_ACCESS=1 :权限不够
  3. USBOPEN_ERR_IO=2 :I/O错误
  4. USBOPEN_ERR_NOTFOUND=3 :没找到设备

Obdev的免费USB ID,查看 USB-IDs-for-free.txt 了解详细。

  1. USB_VID_OBDEV_SHARED=5824 :bodev的共享产品ID
  2. USB_PID_OBDEV_SHARED_CUSTOM=1500 :共享PID,用以自定义类设备
  3. USB_PID_OBDEV_SHARED_HID=1503 :共享PID,用以除了鼠标和键盘的HID
  4. USB_PID_OBDEV_SHARED_CDCACM=1505 :共享PID,CDC猫设备
  5. USB_PID_OBDEV_SHARED_MIDI=1508 :共享PID,MIDI类设备

5   例子

5.1   examples

examples 目录,包含一些教学目的的例子。如下是一些介绍:

  1. custom-class :一个自定义类型设备,主机端使用libusb。演示了主机端和设备的连续小数量数据通信。没有演示大批量数据的传输,也没有设备持续返回数据(使用 usbFunctionWrite() 和 usbFunctionRead() )。查看hid-data了解这两个函数。
  2. hid-custom-rq :这个例子实现的功能同 custom-class ,但是声明了设备类型为HID。这会在Windows上产生一个"给出驱动CD"的对话框。设备可以像上面的例子一样被控制(Windows上必须安装filter版本的libusb-win32)。与上面例子的不同在于演示如何定义一个HID类型设备。
  3. hid-mouse :这个例子实现了鼠标。不需要主机端驱动,因为现代操作系统都内置了。它演示了真实的HID类型设备的实现和断点中断的使用。
  4. hid-data :这个例子演示了HID类型设备被误用(misuse),在HID功能报告中传输任意数据。这个技术也很有用,在于无需驱动( hid-custom-rq 尚且需要libusb-win32),而主机端程序无需安装。例子也同时演示了如何使用 usbFunctionWrite() 和usbFunctionRead() 。
  5. usbtool :这是个通用目的的USB设备开发和调试工具。你可以在开发和测试阶段一直开着它。也同时演示了libusb API的使用。

更多相关信息参考特定目录的README文件。

这里例子只是演示了基本功能,更多例子参考 http://www.obdev.at/avrusb/projects.html 。大部分其中的功能在我们的wiki中http://www.obdev.at/goto.php?t=avrusb-wiki .

顺便提及:

  1. 使用RC振荡器做系统时钟:AVR-USB模块使用12.8MHz和16.5MHz的时钟都要确保时钟误差不超过1%。这也允许使用RC振荡器。AVR内置的RC振荡器在出厂时精度是10%,因此必须使用外部参考来校准。EasyLogger的例子展示了如何使用。
  2. 动态生成描述符:有时候你希望使用跳线或其他条件指定USB设备类型。AVR-USB有非常灵活的接口可以提供USB描述符。查看AVR-Doper了解运行时如何提供描述符。
  3. 虚拟串口:一些人希望用虚拟串口与其他设备通信。我们强烈不建议使用这种方法,因为它已经被USB规范禁用。如果你仍然需要这种路由,查看AVR-CDC。
  4. 实现挂起模式:AVR-USB没有实现挂起模式。这意味着设备无法在主机睡眠时进入省电状态。设备firmware可以自由实现挂起模式。查看USB2LPT的例子。

上面的项目在如下页列表中: http://www.obdev.at/avrusb/prjall.html

5.1.1   custom-class

这个例子演示了通过USB接口控制一个LED灯。

本例演示了设备与主机之前的小数据量通信,同样演示了如何在主机端使用libusb或libusb-win32构建驱动。这里没有演示usbFunctionWrite() 和 usbFunctionRead() 的使用。查看 hid-data 的例子了解他们如何使用。

使用需求:目标硬件,有如电路图目录中的制作即可。AVR的开发环境。主机端开发环境,Unix上需要C编译器和libusb,Windows上需要Driver Development Kit。而MinGW已经附带了一个免费版本的DDK了。

进入firmware目录修改你自己需要的配置,包括CPU时钟、目标设备、熔丝值、ISP编程器。修改 config.h 定义你连接到D+和D-的端口。输入 make hex 构建 main.hex ,然后 make flash 下载firmware到设备。不要忘记 make fuse 对熔丝编程。如果你使用带有bootloader的板子,遵循bootloader的下载方式。

注意 make hex 会把驱动从顶级目录拷贝到firmware目录,如果你使用其他的构建系统,需要手动拷贝过来。

进入firmware目录,修改Makefile到你的配置,包括CPU时钟、目标设备、熔丝值和ISP编程器。修改 usbconfig.h 可以修改你所使用的D+与D-的端口定义。而本实验需要用一个输出端口接LED,然后修改 main.c 中的对应控制端口。

构建主机端程序:确保机器上有libusb或libusb-win32。如果是Unix确保libusb-config在搜索路径中。在Windows上编辑缺省的Makefile.windows 设置搜索路径。然后就去构建。

使用方式:程序有三种参数, status 用于查询LED状态, on 打开LED, off 关闭LED。

效果还不错,只是我一直没发用pyusb进行控制。

5.1.2   hid-custom-rq

类似于custom-class例子,区别在于设备表现为USB HID类。例子还展示了如何发送其他请求给HID兼容的实现。这样就可以提供驱动CD对话框在Windows上,且依旧可以用libusb-win32控制。

需要安装过滤器版本的libusb-win32,来得到完整的功能。设备驱动版本只存取已经注册了inf文件的设备。过滤器版本存取所有设备。

实际的测试中控制LED灯没有成功。也是走控制消息的。

5.1.3   hid-mouse

这个项目实现了USB鼠标设备,为了演示功能避免复杂性,只是让鼠标指针按照预先计算的绕圈而已。

没有实现 USBRQ_HID_SET_REPORT 和 report-ID。可以参考hid-data的例子。也没有实现挂起模式的功能。

Makefile的几个参数都一样的。

5.1.4   hid-data

这个例子展示了与HID类型设备交换数据。因为操作系统自带了HID驱动,所以无需另外安装驱动。这个例子只能传送固定大小的数据块,最高可以到HID功能报告驱动限制的大小。这个技术非常适合于Windows无驱动的程序。主机端应用无需安装。例子同时演示了 usbFunctionWrite() 和 usbFunctionRead() 函数。

在主机端确保你拥有libusb(Unix)或DDK(Windows)。对Windows我们强烈推荐MinGW,因为它自带了DDK了。然后进入commandline目录,运行 make ,或windows下运行 make -f Makefile.windows 。

设备实现了对EEPROM上128byte数据的访问。你可以发送128字节的数据到设备,或者读取出来。读写的例子:

hidtool write 0x01,0x02,0x03,0x04,...
hidtool read

5.1.5   usbtool

通用目的的USB命令行控制工具。当你基于USB实现一些协议的时候,必须写两个程序,分别在通信的两端。对于USB这意味着要写firmware和设备驱动。usbtool可以节省编写主机端设备驱动的时间。可以发送和接收控制请求,也可以通过交互和批量端点模式收发数据。

不仅如此usbtool也是使用libusb的好例子。

命令语法:

$ usbtool [options] 

可选命令:

  1. list :打印所有设备的列表,-v、-V、-p、-P可以用于过滤列表

  2. control in|out  :发送控制请求,请求参数如下:

    1. type :请求类型,可以是standard、class、vendor、reserved。取决于设备应答请求的方式:standard请求通过驱动响应,class请求由class实现(例如HID、CDC),而vendor请求是自定义代码
    2. recipient :设备上的请求容器,可以是device、interface、endpoint、other。对standard和class请求,应该用recipient,对vendor请求,选择你自己的代码
    3. request :8bit数字值,标志请求
    4. value :16bit数字值,附加数值
    5. index :另外一个16bit数字值,传递给设备的

    使用 -v、-V、-p、-P可以只输出特定设备,使用-d、-D发送数据到一个输出请求。-n、-O、-b检测接收到数据以后如何处理。

  3. interrupt in|out :使用out交互模式发送和接收数据,-in端点。使用-v、-V、-p、-P指定输出设备。-d、-D指定输出端点。-n、-O、-b指定从IN端点接收到数据以后干什么。-e设置端点编号,-c选择配置,-i认领指定接口

  4. bulk in|out :同交互输入输出,只不过是对批量端点。

选项:

@waiting

5.2   tests

一些工具,用于计算驱动程序的大小。

你可能感兴趣的:(avr,usb,v-usb)