【VC++开源代码栏目提醒】:本文主要为网学会员提供“AVR035 高效AVR C 编程 - 其它资料”,希望对需要AVR035 高效AVR C 编程 - 其它资料网友有所帮助,学习一下!
AVRUSB 技术探讨作者:邵子扬、王育强、吕益光摘要 本文介绍了一种独特的 AVR 单片机和计算机进行 USB 通信的方法:AVRUSB。
介绍了AVRUSB 技术的基本原理、特点、应用,同时还详细的介绍了 AVRUSB 系统的单片机软件开发和计算机软件开发的方法。
关键字: AVRUSB,LibUSB,LibUSB-Win32正文1 简介1.1 AVRUSB 是什么 AVRUSB 技术是利用高性能的 8 位 RISC 架构的 AVR 单片机,使用单片机的 IO 口来模拟USB 的通信端口,由软件来实现 USB 通信协议,将普通的 AVR 单片机模拟成一个 USB 低速设备,从而实现 AVR 单片机与计算机之间的通信和控制。
AVRUSB 技术的基本原理就是利用 AVR 单片机的普通 IO 端口来模拟 USB 的硬件端口进行通信。
因为低速 USB 设备的速度是 1.5M 位/秒,而 AVR 单片机是单指令周期的,在单片机使用 12MHz 的时钟频率时,正好是 1.5MHz 的 8 倍。
也就是说,单片机每 8 条指令就精确完成一个数据位的采集。
采用这种方法时,对单片机的时序要求非常严格,所以软件的核心部分
代码完全由汇编语言实现。
1.2 AVRUSB 的历史 AVRUSB 技术最早的
文档可见于 AVR 的官方应用笔记《AVR309 Software UniversalSerial Bus USB》(软件 USB)一文中。
在这篇应用笔记里,详细的介绍了如何使用 AVR单片机的普通 IO 口来实现 USB 通信,同时介绍了计算机的驱动程序以及计算机上用户程序的编程方法,并提供了全部源码。
但是 AVR309 中介绍的单片机程序完全是使用汇编语言编写的,不利于将程序移植到其他应用环境中,也不利于程序的维护(毕竟使用汇编语言的人相对比较少,使用 C 语言编程是大部分人的选择),这使得其应用受到了很多限制。
后来,OBJECTIVE 公司推出了 AVRUSB。
它以汇编语言实现 USB 通信的底层接口,用 C语言实现用户层的程序接口。
用户接口被简化为很简单的几个函数(在最简化的情况下,只需要三个函数,一个初始化函数、一个轮询函数和一个数据处理函数)和一个配置文件,用户可以完全不会使用汇编语言编程。
同时它还提供了一个稳定而成熟的用户程序框架,用户可以在此框架的基础上,通过修改和扩展接口函数的功能来实现各种 USB 通信功能和控制功能,实现各种带 USB 接口的应用系统。
并且,AVRUSB 支持目前最流行的 AVR GCC 编译器和IAR C 编译器,因此具有很强的实用性。
1.3 AVRUSB 的特点1.3.1 低成本 传统的单片机与计算机进行 USB 通信,需要使用专用的接口芯片进行 USB 协议转换,如CP2101、FT232、CH342、PDIUSBD12、SL811 等。
象 CP2101、FT232 这样的芯片使用起来虽然简单,但是功能比较单一;而 PDIUSBD12、SL811 功能较强,但是使用复杂。
并且这些专用芯片的价格都相对较高,增加了系统的成本。
而 AVRUSB 简单易用,成本低廉,只需要一个普通的低成本 AVR 单片机以及很少的几个外部元件,就可以组成一个 USB 系统。
AVRUSB 的
代码为 AVR GCC 编译器做了高度优化,同时也完全兼容于更专业的 IAR C 编译器。
程序编译后在最小情况下还不到 2KB,因此绝大部分的 AVR 单片机都可以使用 AVRUSB(只要支持外部中断 INT0,Flash 容量不小于 2KB 就可以实现 AVRUSB 的功能)。
这样在很多低成本的小容量 AVR 单片机上也可以使用 AVRUSB,如 ATtiny2313、ATmega45、ATmega48等,因此 AVRUSB 技术具有很高的实用价值。
1.3.2 资源丰富,容易开发 AVRUSB 提供了一个完整而又简单易用、成熟稳定的应用程序框架。
这个框架包括了底层(单片机部分)和上层(PC 部分),单片机可以使用 gcc(或者 IAR)编程;PC 上则可以使用各种通用编程软件,如 Windows 下使用
VC、VB、
Delphi、C Builder、BDS2006、GCC,Linux 下使用 GCC 等等。
用户可以在这个框架基础上添加和扩展各种功能,快速开发出适合于各种需求的单片机控制系统,而且 AVRUSB 支持 Windows、Linux、MacOS 等多种操作系统,具有很好的跨平台特性。
1.4 AVRUSB 的应用 AVR 单片机低成本、高性能的特性,使得 AVRUSB 非常适合于应用到 USB 加密狗、USB接口的系统控制、低速 USB 数据采集等,这样构成的具有 USB 通信功能的单片机系统比很多使用专用芯片的系统简单、成本低。
目前,AVRUSB 已经成功应用到了很多产品上,比较有名的有:USBaspUSB 接口的 AVR编程器、AVRCDCUSB 转 RS232 串口、USB Bootlader(USB 接口的 Bootloader 软件)等。
在 http://www.obdev.at/products/avrusb/projects.html 中,还专门列举了很多使用AVRUSB 的
开源项目,这些项目提供了完整的单片机程序和
计算机程序的
代码和原理图。
我们在开发自己的 AVRUSB 应用时,可以参考这些资源,在这些
开源项目的基础上进行修改,快速开发出适合于自己需求的应用来。
本文作者也成功的将 AVRUSB 用到公司的一个项目中,很好的实现了一个 USB 接口的开关矩阵控制。
1.5 AVRUSB 的限制 因为 AVRUSB 使用普通 IO 口模拟来 USB 通信的过程,由软件实现了硬件完成的功能。
而USB 通信的速率是比较高的。
因此,在进行 USB
通信时单片机的 CPU 占用率比较高的(大于90)。
另外,因为受到单片机的处理能力限制,所以通信的数据处理能力不是很强,最大数据处理速度约为 20k/s,因此 AVRUSB 不适合用于大数据量通信的应用场合。
2 硬件结构 构成一个 AVRUSB 系统的硬件结构非常简单,只需要一个普通的 AVR 单片机(大部分型号都可以),再加上少量的外部元件(晶体、几个电阻以及可选的稳压二极管等),就组成了一个基本的 AVRUSB
系统,如图 1。
U1 23 2 PC0/ADC0 RXD/PD0 24 3 PC1/ADC1 TXD/PD1 25 4 PC2/ADC2 INT0/PD2 26 5 PC3/ADC3 INT1/PD3 VUSB 27 6 PC4/ADC4/SDA XCK/T0/PD4 28 11 PC5/ADC5/SCL T1/PD5USB R4 1 12 PC6/RST AIN0/PD6 1 152 13
VBUS AIN1/PD7 2 D- R2 68 14 D- PB0/ICP1 3 D R3 68 15 VCC D PB1/OC1A 4 16 7 GND PB2/OC1B/SS VCC 17 20 PB3/MOSI/OC2 AVCC 18 21 D1 D2 PB4/MISO ARef 19 C1 PB5/SCK 9 8 PB6/X1/TOSC1 GND 10 22 PB7/X2/TOSC2 GND 12M ATMEGA8 C2 C3 图 1. AVRUSB 的基本硬件接口 图中的单片机以 ATmega8 为例。
数据线 D-上的上拉电阻 R4 用来通知计算机这是一个低速 USB 设备(这是在 USB 规范中定义的,更多内容请参考 AVR309 以及 USB 的官方
文档)。
D12MHz 晶体和两个 22p 的电容 C2 和 C3 组成单片机运行所必须的时钟。
和 D- 数据线可使用单片机的任意 IO 端口,但是必须使用相同的 IO 端口。
在这里 D 连接到 PB1,D- 连接到PB0。
此外数据线 D还需要连接到 INT0 上,这是为了在不同的 AVR 单片机中使用 AVRUSB 时有更好的适应性和兼容性,无需修改底层核心部分程序的
代码。
如果 D-连接到端口 D 上(就是和 INT0 同一端口中),同时 D只连接到 INT0,还可以节省出一个端口来。
电阻 R2、R3 起到限流和保护作用,防止在意外情况下损坏计算机的 USB 端口或单片机的端口。
单片机所需的电源 Vcc 可由 USB 的 5V 输出电源直接提供,或者由 USB 的 5V 电源转换得到(如 LDO、稳压二极管等),或者通过电池等其他外部电源来供电。
D和 D-上的 3.6V 稳压二极管 D1 和 D2 起到限制数据线上的电平的作用。
因为在 USB 规 而范中规定数据线 D和 D-上的电平范围是 3.0V 至 3.6V, AVR 单片机的输出电平是 Vcc。
如果单片机的 Vcc 是 5V,在没有 D1 和 D2 的情况下将造成电平不匹配,会造成在很多计算机中无法正确识别出 USB 设备。
如果用户系统的 Vcc 在 3.0V 至 3.6V 之间,就可以省略这两个稳压二极管。
从这里也可以看出用户系统的 Vcc 必须高于 3V。
上面硬件就组成了一个最小的 AVRUSB 系统,它能够和计算机进行 USB 通信。
在上面最小系统的基础上,如果添加一个红外传感器,就可以接收发送红外信号,就是一个 USB 红外控制器;如果添加一个 MAX202,就是一个带缓冲的 USB lt-gt RS232 串口转换器;如果加入ADC 转换功能,就是 USB 的数据采集器;如果加入 ADC 和电源控制,就能够实现一个简单实用的 USB 充电器……各种功能的 AVRUSB 系统都是在这个最小系统的基础上,添加不同功能的外围模块或接口来实现的。
3 单片机程序的开发 要使用 AVRUSB,就需要在单片机中开发合适的
软件,实现特定的功能。
下面将具体介绍开发需要使用的工具软件和开发的步骤。
3.1 开发环境 AVRUSB 可使用 AVR GCC 编译器或 IAR C 编译器。
因为 AVRUSB 特别为 AVR GCC 编译器做了优化,并且 AVR GCC 还是
免费软件,使用非常广泛,是目前 AVR 单片机主要的开发工具软件之一,所以我们下面以 AVR GCC 为例来介绍。
在 Windows 操作系统下开发时,使用 WinAVR 中带有的 AVR GCC 编译器;
代码编辑、仿真和调试使用了 Atmel 公司的 AVR Studio,这个 IDE 的好处是不需要用手工修改 makefile配置文件,减少了初学者的使用难度。
如果希望使用其他 IDE 或编辑软件作为开发工具也可以,下面的使用步骤也是类似的。
如果是在 Linux 操作系统下开发,可以使用 Linux 版本的AVR GCC,以及其他工具软件进行编辑和调试。
3.2 建立项目文件 要将 AVRUSB 加入到自己的程序中,首先需要在 AVR Studio 中建立一个新的项目(Project),然后将 AVRUSB 所需要的文件复制到项目文件的文件夹中(当然也可以不用复制文件,只添加已经存在的 AVRUSB 的文件路径到项目中也可以,但是将项目的所有文件放在一个文件夹下更容易对整个项目进行维护)。
一般来说,凡是使用 AVRUSB 的项目,在项目文件夹下都会单独存放一个 USBDRV 的文件夹,里面存放着所有与 AVRUSB 相关的文件。
AVRUSB 包含了多个文件,但是我们只需要添加以下几个文件到项目中: usbconfig.h 用户配置文件 iarcompat.h 为兼容 IAR 编译器而定义的宏 usbdrv.h usb 驱动接口文件的头文件 usbdrv.c usb 驱动接口文件 usbdrvasm.asm 为兼容 IAR 编译器而使用的底层接口函数文件的别名 usbdrvasm.S 汇编语言编写的底层接口函数 oddebug.h 调试用函数的头文件(不使用调试功能时可以不添加) oddebug.c 包含调试用的函数(不使用调试功能时可以不添加) 注意到上面的文件除了 usbconfig.h 外, 在 都在 USBDRV 文件夹中。
USBDRV 文件夹中一般都有一个 usbconfig-prototype.h 文件,这个文件是用户配置文件 usbconfig.h 的原始模板,我们需要将这个文件复制到项目文件夹中并将它改名为 usbconfig.h。
复制后,还需要再添加 USBDRV 文件夹的路径到项目的包含路径中,这样在编译时才可以正确找到上面的文件,如图二(因为 USBDRV 文件夹在项目文件夹下,所以显示出的是相对路径)。
如果不使用 AVR Studio,也可以使用其他的 IDE 软件或编辑软件,但是可能会需要用户自行手工修改项目配置文件 makefile,具体做法这里就不做介绍了。
图 2. 添加 USBDRV 路径到项目中3.3 参数配置 在编译项目之前,除了需要对项目本身的参数进行配置外(如 AVR 单片机的型号、系统时钟频率、
代码优化等级等),还需要对 AVRUSB 的参数进行配置,这样才能产生出正确的
代码。
AVRUSB 中包含的参数虽然看起来很多,其实配置起来很容易。
在用户配置文件usbconfig.h 中存放了所有与 USB 相关的配置参数,配置参数都是以宏定义的方式给出,配置参数的过程就是修改相关的宏定义。
只要修改这个文件中的参数就可以实现不同的功能,其它的文件不用修改。
为了获得尽可能高的效率,AVRUSB 中使用了大量的宏定义和条件编译,用以获得最小的
代码大小和最快的运行速度。
表 配置文件 usbconfig.h 中的参数非常多, 1 列出了主要需要修改的参数。
一般的情况下只要修改这几个参数就可以了,其它的参数可以根据用户的实际需求做适当的修改。
表 1:usbconfig.h 中的主要参数USB_CFG_IOPORTNAME 定义 USB 数据线使用的端口。
只要是通用的 IO 都可以,没 有特殊的要求。
USB_CFG_DMINUS_BIT USB 数据线 D-使用的引脚。
USB_CFG_DPLUS_BIT USB 数据线 D使用的引脚。
因为 D要求同时连接到 INT0 上,所以一般情况下需要使用 3 个 IO 口。
如果 D使用的引 脚就是 INT0,那么可以少使用一个 IO 端口。
USB_CFG_VENDOR_ID 设备生产商的 ID 号USB_CFG_DEVICE_ID 设备的产品 ID 号。
这两个参数就是 Windows 识别 USB 设备 的主要参数。
需要注意的是,这两个参数都是低字节在前, 高字节在后。
USB_CFG_DEVICE_VERSION 设备的版本号次版本号在前,主版本号在后。
在 Windows 的设备管理中可以看到这个版本号USB_CFG_VENDOR_NAME 设备生产商的名称,它在 Windows 的设备管理中可以看到。
这里一般写入的是网址。
USB_CFG_VENDOR_NAME_LEN 设备生产商名称的长度。
USB_CFG_DEVICE_NAME 设备的名称,它在 Windows 的设备管理中可以看到。
设备 名称和生产商的名称都是以字符的方式定义的,它们目前 不支持中文。
USB_CFG_DEVICE_NAME_LEN 设备名称的长度。
3.4 主要接口函数 配置好参数,就可以开始编写用户程序了。
用户程序和计算机之间的 USB 通信是通过AVRUSB 提供的接口函数完成的,接口函数有 6 个,下面将分别介绍。
3.4.1 初始化函数 在使用 AVRUSB 前,需要进行必要的初始化,这通过调用初始化函数 usbInit完成。
一般是在程序其他部分初始化完成后再调用函数 usbInit,最后再调用 sei函数允许中断。
void main … usbInit sei … while1 //主循环 … 3.4.2 USB 事件处理函数 在用户程序的主循环中需要定期调用 USB 事件处理函数 usbPoll。
USB 事件处理函数usbPoll在没有 USB 事件需要处理时将直接返回,否则将调用内部函数进行相应的事件处理,最后再将数据通过传递到后面介绍的用户接口函数中。
通常的用法是: while1 //主循环 usbPoll //USB 事件处理 …… //其他用户事件 一次 USB 通信的超时时间是 50ms。
所以在编程时注意其他事件不要占用太长的时间,使得 usbPoll函数不能及时执行。
3.4.3 用户事件接口函数 在用户程序中需要编写 USB 用户事件接口函数,完成 USB 通信。
AVRUSB 将用户接口简化为以下 3 个函数,这三个函数需要用户进行编程处理,它们将完成 USB 通信的数据处理。
usbFunctionWrite 主机向单片机写入数据 usbFnctionRead 主机从单片机中读取数据 usbFunctionSetup 一般功能设置 函数 usbFunctionSetup 负责传递 USB 请求,参数存放在一个 8 字节的数组中uchardata8,其的含义是: uchar requestType //0 请求类型 uchar request //1 请求的内容 unsigned value //2 3 参数 unsigned index //4 5 序号 unsigned length //6 7 长度 在一般情况下,除了 data0和 data1用于存放 USB 事件的请求参数外,data2至data7都可以传递用户参数。
这样在数据量非常小的时候,通过 usbFunctionSetup 就可以完成全部参数传递了,不需要使用函数 usbFnctionRead 和 usbFunctionWrite。
这时可以在usbconfig.h 中将宏 USB_CFG_IMPLEMENT_FN_READ 和 USB_CFG_IMPLEMENT_FN_WRITE 设置为0,禁止使用函数 usbFnctionRead 和 usbFunctionWrite,这样可以节省出不少
代码空间。
在传递的数据比较多时,就需要使用 usbFunctionWrite 和 usbFnctionRead 函数了。
这两个函数具有相同的参数uchar data uchar len,len 表示参数的数量,data 指向数据缓冲区。
在编程时,一般是先在 usbFunctionSetup 函数中根据计算机发送过来的请求类型和参数来设置一个全局标志,然后在 usbFunctionWrite 和 usbFnctionRead 函数中根据相应全局标志进行不同的功能处理。
3.4.4 数据校验 为了保证 USB 数据通信的可靠,避免传输中出现误码,还应当对数据进行校验。
USB 通信使用了 CRC 校验来保证通信的可靠性,数据校验的方法是调用 usbCrc16函数,函数的使用方法是: crc usbCrc16uchar unsignedusbAppBuf 1 usbRxLen - 3 USB 数据缓冲区中存放的校验结果在 uchar unsignedusbAppBuf 1usbRxLen - 3 和 uchar unsignedusbAppBuf 1usbRxLen - 2 两个字节中。
将计算结果和缓冲区的数据进行比较就可以知道通信缓冲区中的数据是否正确了。
需要注意的是 usbCrc16 函数并不是在用户程序中调用的,而是在 usbdrv.c 文件中的 usbPo.