应用笔记 3936

Maxim USB库


摘要 : Maxim USB库是使用ARM7™处理器并基于MAX3421E/MAX3420E提供的软件例程。本应用笔记对系统及其软件运行进行了说明,在同一ARM® C语言代码中同时实现了USB主机和外设功能。这种方法使得USB外围设备和嵌入式主机的开发和研究,具有在USB电缆的另一端带有参考设备的优势,而所有的这些都包含在同一的C语言代码中。

本应用笔记是应用笔记3997,"建立Maxim USB库"。 可下载包含Maxim演示程序的Keil™项目。

绪论

Maxim USB库是两块电路板和C语言程序的组合。利用这个系统,可以:
  • 研究、操作、测试和修改基于ARM7微控制器(µC)与MAX3420E USB外设控制器连接的USB功能设备
  • 研究、操作、测试和修改基于ARM7微控制器(µC)与MAX3421E USB主机控制器连接的USB功能主机图1显示了主机从USB存储棒中的数据检索。
  • 使用USB电缆将主机连接至外设,并同时运行主机和外设。开发USB主机或USB外设代码时,USB电缆的另一端有一个参考设计是非常有用的。这样,USB电缆的两端,主机和外设因为都是执行相同的C语言代码,从而具有可控性和定制性。
图1. MAX3421主机/ARM从任意的USB外设重新得到枚举数据,并通过串口连接至运行有终端仿真程序的PC来报告结果。
图1. MAX3421主机/ARM从任意的USB外设重新得到枚举数据,并通过串口连接至运行有终端仿真程序的PC来报告结果。

其它资料

编程指南、数据资料和Maxim网站提供的MAX3420E和MAX3421E应用笔记:

MAX3420E
MAX3421E
Maxim应用笔记AN3937,"建立Maxim USB库"

硬件

本应用C语言代码运行在两板装置上:
图2. Maxim评估板插进Keil MCB2130板。
图2. Maxim评估板插进Keil MCB2130板。

图2展示了板级设备。蓝色电路板是Keil MCB2130,其中包含一块ARM7芯片,Philips® LPC2138。这个微控制器包含2个SPI™硬件单元,连接至两块Maxim USB控制芯片。

竖立的电路板是MAX3421EEVKIT-1。一个MAX3420E外设控制器连接至ARM的一个SPI端口,并布线连接至USB的“B”连接器(J5),在图2中用“3420 P”标记(P代表外设)。MAX3421E主机/外设控制器连接至ARM的另外一个SPI端口,并布线连接至“3421P” (J2)和“3421H” (J1)连接器(H代表主机)。应用中运行的代码将MAX3421E作为主机使用,因此评估套间电路板中部的USB连接器(J2)没有使用。

主机软件使用两个串口中的一个(MCB2130电路板上的P1)将USB说明信息发送到运行终端仿真程序的PC。终端程序,如Tera终端程序,可以仿真终端(VT100),识别由程序发送的专用“转移码”序列以实现清屏和指针复位。终端程序的串口设置为38400、N、8、1,无流量控制。

图2中的米色方盒和带状电缆是Keil ULINK JTAG装载-调试器。Keil µVision®3开发环境支持该调试单元。MCB2130附带一个评估版µVision3,具有完整功能版本的Keil工具集。但评估版在代码容量上有16kB的限制。

本应用笔记的附件包含整个Keil工程的源文件,另还有.hex格式的装载模块。如有ULINK JTAG单元,可编译该代码,并可通过JTAG端口进行加载和调试。这对于快速开发USB、修改工作主机和/或外设代码使其适合开发目的是一条极好的途径。如果没有ULINK,可使用称为Flash Magic的免费程序加载、运行hex文件,这个程序可从网站www.esacademy.com获得。参考Maxim应用笔记3937可获得如何配置和使用该程序的信息。

图3. MAX3421EEVKIT-1电路板框图。带阴影的椭圆形是主要软件模块。
图3. MAX3421EEVKIT-1电路板框图。带阴影的椭圆形是主要软件模块。

程序功能

图3显示了MAX3421EEVKIT-1电路框图和本应用笔记的软件模块。MAX3420E和MAX3421E都使用SPI总线访问其寄存器组。LPC2138含有2个硬件SPI单元:
  • 一个专用SPI端口(SPI)
  • 一个通用串行接口(SSP,同步串行端口)
可对SSP编程以实现第二个SPI端口。这些端口的编程不同。例如,SSP有8个发送、接收FIFO,而SPI只有1个读缓存器。为与MAX3420E和MAX3421E兼容,两个SPI接口都编程工作在8位数据模式下。软件模块SPI_FNs.C包含两个USB控制器都使用的寄存器访问函数。

MAX3420E属于SPI单元,所以对这块芯片来说函数有一个“P” (例如Prreg和Preg)前缀,表明外设操作。MAX3421E属于SSP单元,所以其访问函数有一个“H”前缀(例如Hwreg),表明为主机操作。

外设程序

C模块3420_HIDKB.C实现一个通过使用评估电路的连接器J5连接至PC机的USB外设。MAX3420E的INT输出引脚连接至ARM7的EINT0 (外部中断0)引脚。这个中断可在由MAX3420E实现的USB外设需要服务时随时触发。

本应用列举和实现了标准的USD HID (人性化接口设备)。符合标准Windows®平台的一大优势是设备驱动内置在Windows中,从而不需要安装专用的驱动。Windows将由本应用实现的设备识别为一个标准键盘。按任意的连接至MAX3420E GP-IN引脚的四个按钮,将使“键盘”在任意打开的接收文本窗中输入文本字符串,如记事本和写字板。


注意:本应用可在任何可接收文本应用中输入文本,如电子邮件、汇编编辑器、或Word文档。在按键之前请确认有一个安全的应用,如记事本被打开,并处于有效状态。作者根据经验可以确定:在一个打开的C源文件中输入无效文本将不能进行完全编译。


主机代码

3241_Host_EVK.C文件包含主机代码,其使MAX3421E完成一定的枚举步骤,该枚举步骤与有USB设备插入连接器J4时PC所执行的枚举步骤相似。这个模块中的main()函数就像HID键盘代码的中断服务程序。EINT0中断服务程序简单如下:
                        // EINT0 Interrupt handler--MAX3420E INT pin
void INT3420 (void) __irq
{
service_irqs();         // Do the USB thing in 3420_HIDKB_INT_EVK.C module
EXTINT      = 1;        // Clear EINT0 interrupt flag (b0)
VICVectAddr = 0;        // Dummy write to indicate end of interrupt service
}
基础程序main( )包含以下无限循环子程序:

while(1)
  {
  detect_device();
  waitframes(200);      // Some devices require this
  enumerate_device();
  wait_for_disconnect();
  }
Enumerate_device()函数完成绝大部分的工作,向连接设备发送USB请求,并通过串口报告结果。任意的USB设备插入评估板的连接器J1,这个函数将向设备发送枚举请求,并通过串口报告结果(图1)。

同步主机/外设操作

因为MAX3421EEVKIT-1电路板包含一个USB外设和一个USB主机,且它们使用ARM7上单独的SPI接口,所以软件将可以很容易的编写,使主机和外设应用同时的运行。主机应用运行main()在后台与MAX3421E对话,而HID外设代码使用MAX3420E中断激活service_irqs()函数。

如果用USB电缆连接评估板的连接器J5和J1,ARM处理器可作为USB主机与外设对话,而这个外设就是其自身。图4显示了3421_Host.C的主机代码询问由3420_HIDKB.C实现外设的报告。

图4. 通过USB询问自身的代码演示
图4. 通过USB询问自身的代码演示

主机代码开发

通读enumerate_device()函数有助于理解USB主机是如何工作的,也有助于理解如何命令MAX3421E完成枚举USB设备时的各种主机操作。而开发主机代码,如果对外设插入主机的响应过程不清楚,有时将很难诊断问题。如果在全部的控制中(3420_HIDKB.C模块)拥有参考外设,将使问题很容易发现,并可改变设备的响应以测试主机纫件。

外设代码开发

主机是用于开发USB外设代码的优秀询问工具。MAX3421E和3421_Host.C模块的结合,产生一个简单但功能强大的USB信息包生成器和分析器。通过编写C语言主机代码,可以控制外设状态;MAX3421E显示外设的响应过程。

实例

假设编写外设代码以处理USB字符串。在hidkb_enum_tables.h文件中按如下方式编写生产商字符串XYZ Widget Company:
// STRING descriptor 1--Manufacturer ID
{
18,             // bLength
0x03,           // bDescriptorType = string
'X','Y','Z',' ','W','i','d','g','e','t',' ','C','o','m','p','a','n','y'
},
编译该文件,将其装入ARM的flash存储器,并运行。主机程序将报告如下生产商字符串:

图5. 不良结果,例程没有按照要求显示生产商名字,代码有问题。
图5. 不良结果,例程没有按照要求显示生产商名字,代码有问题。

出现了什么问题? 原来,USB规定文本字符串用Unicode表示,即每个字符用两个字节表示。回顾外设的源代码,更改如下:
// STRING descriptor 1--Manufacturer ID
        {
        35                      // bLength
        0x03,                   // bDescriptorType = string
        'X',0,'Y',0,'Z',0,' ',0,'W',0,'i',0,'d',0,'g',0,'e',0,'t',0,
        ' ',0,'C',0,'o',0,'m',0,'p',0,'a',0,'n',0,'y' // Unicode!
        },
编译、运行,应该可以看到:

图6. 正常的代码例程,使用Unicode格式,可看到结果。
图6. 正常的代码例程,使用Unicode格式,可看到结果。

该结果有所改善,但并不完美。Company中的“y”到哪去了? 仔细计数该字符串有35个字符,并将其作为第一个字节输入以显示字符串长度。设计没有注意的是:长度字节也是字符串,必须包含前两个字节。代码最终的修改如下:
// STRING descriptor 1--Manufacturer ID
        {
        37               // bLength
        0x03,                   // bDescriptorType = string
        'X',0,'Y',0,'Z',0,' ',0,'W',0,'i',0,'d',0,'g',0,'e',0,'t',0,
        ' ',0,'C',0,'o',0,'m',0,'p',0,'a',0,'n',0,'y' // Unicode!
        },
这是结果应为如下所示:

图7. 最佳的代码实例。代码现在可实现设计想要实现的功能。
图7. 最佳的代码实例。代码现在可实现设计想要实现的功能。

通过测试外设代码和使用主机控制器及编码运行/分析,由于报告的结果是由MAX3420E发送至MAX3421E的实际USB报文,从而可以看到与PC机响应完全相同的结果。最好的是,这些都可在一个可控的基础上实现,因而不必担心PC机的如何响应而使代码开发出现错误。

代码说明

图8. Keil工程的结构
图8. Keil工程的结构

图8显示了三个主要模块,并展开显示了它们的文件依靠。没有展开的模块是Keli开发环境所需要的内部处理文件。

SPI_FNs.C

这个模块包含由其它两个模块调用的公用函数。

void init_PLL(void)

此函数设定LPC2138的PLL和时钟除法器。附着于LPC2138 (FOSC)的晶体为12.000MHz。根据公式CCLK = FOSC × M,设定CPU频率为48MHz,其中M = 4。LPC2138使用内部CCO (电流控制振荡器)倍频FOSC以获得更高的内部频率。FCCO = CCLK × 2 × P。FCCO必须处于156MHz和320MHz之间。对于P = 2,FCCO = 48MHz × 2 × 2 = 192MHz。


注意:Keil文件startup.s也包含设置PLL参数的启动代码。init_PLL()函数将有效的取代这些设置。


重要警告:修改这些设定会有一定的风险。错误的设定能迫使LPC2138超出其规定的限度,从而使Keil调试器停止作用。如果这种情况发生,补救方法是使用Flash Magic公用软件擦除LPC2138的flash存储器(使用第二个串口,连接至Keil电路板的P2),修改代码中的PLL设定,然后再试验。

init_IO

这个函数初始化LPC2138的I/O引脚和SPI单元(SPI和SSP):
  • SPI由CCLK/8时钟控制,允许最大值。SCLK为48MHz/8 = 6MHz。
  • SSP由CCLK/(2 × VBDIV)时钟控制。VBDIV设置为1,使MAX3421E的SCK信号为48MHz/2 = 24MHz。MAX3421E的SCK输入最高可运行至26MHz。
关于SPI接口
SPI接口使用如下信号:
  • MOSI 主机输出数据,从机输入数据
  • MISO 主机输入数据,从机输出数据
  • SCLK 串口时钟,由LPC2138提供
  • SS# 从机选择,由LPC2138驱动
LPC2138的SPI硬件控制前三个信号,但SS#使用一个GP-OUT引脚(P0.7)直接设置和清除。

关于SSP接口
虽然SSP有一个硬件SS#引脚,但使用GP-OUT引脚作SS# (P0.20)更简单。

Hwreg
Hwritebytes

这些函数使用LPC2138的SSP硬件向MAX3421E写入寄存器和FIFO数据。H前缀表示主机操作。Hwreg写入一个信号寄存器值;Hwritebites向MAX3421E的FIFO写入多个字节。

Hrreg
Hreadbytes

这些函数使用LPC2138的SSP硬件从MAX3421E中读出寄存器和FIFO数据。

SSP接口
SSP硬件需要译码,从而在发送和接收通道上需要数据FIFO。在一个SPI操作中,每8位数据移总是伴随着8位数据移。LPC2138使用如下步骤访问MAX3421E的SPI接口:
  1. 触发SS# (低)
  2. 发送由登记号和方向位构成的命令字节
  3. 发送/接收一个或多个数据字节
  4. 释放SS# (高)
SPI写操作简单明了—每个数据都会移出,输入FIFO内的数据可以被忽略。但读操作更加复杂,因为读FIFO至少包含一个坏字节,即步骤2中自动由时钟控制记入的。由于以前的多字节写操作也可以使输入数据失时效。输入FIFO不能由硬件禁止或清除。因此,读SPI数据的代码首先要手动清除FIFO,即从SSPDR中读出字节,直至表示“读FIFO不为空”的标志位被释放。

Pwreg
PwregAS
Pwritebytes

这些函数使用LPC2138的SPI硬件向MAX3420E写入寄存器和FIFO数据。第二个函数与第一个函数一样写入寄存器值,但其还设置SPI命令字节中的ACKSTAT位。这是停止USB控制转移的捷径。细节请参考应用笔记3598,"MAX3420E编程指南"

Prreg
PrregAS
Preadbytes

这些函数使用LPC2138的SPI硬件从MAX3420E中读出寄存器和FIFO数据。第二个函数与第一个函数一样读出寄存器值,但还设置SPI命令中的ACKSTAT位。

readout

readout这个函数更新连接至MAX3421E通用输出引脚GPO[6:0]的7段信息显示,函数保留GPO[7]的设置,GPO[7]控制USB “A”连接器(评估板的J1)的VBUS开关。

3421_Host.C

这个模块实现MAX3421E USB主机,该主机访问USB设备并通过MCB22310的串口报告枚举数据。作为参考,附录A给出了主机枚举由3420_HIDKB.C实现的内置设备的LeCroy (CATC)总线过程。

这个模块中有三种函数类型:
  1. 初始化函数
  2. 公用函数
  3. main()中调用的高级函数
下面的说明将按此顺序。

Reset_Host

MAX3421E包含上电复位,因此这个操作并不是必须的。尽管如此,程序开发阶段使用该字段,不需要重复上电即可初始化调试过程,是复位功能的很好方式。启动该函数使器件清零,不会带有以前的调试信息。

复位MAX3421E使片上振荡器停止工作。解除复位后,这个函数在返回之前将等待OSCOKIRQ位(振荡器正常中断请求)变为有效。

initialize_3420

这个函数在3420_HIDKB.C模块中。

initialize_ARM_Interrupts

这个函数建立ARM向量中断如下:
  1. EINT0连接至MAX3420E的INT引脚,使用优先级0 (最高)。
  2. Timer0用于功能发光体闪烁,使用优先级1。
  3. EINT2连接至MAX3421E的INT引脚,指定优先级为2,在这个程序中没有使用到该中断。当然,这个中断是为了方便才提供的。
  4. Timer1用于检测发送/停止按钮(在3420_HIDKB.C中使用),使用优先级3。

service_irqs

该函数在3420_HIDKB.C模块中。它进行HID键盘仿真。初始化ARM中断的代码仅需将该地址置为一个中断向量。

waitframes

用于USB主机测量时间的一个简单方法是计算1ms帧标志的个数。当微控制器设定寄存器位SOFKANAB = 1时,MAX3421E自动产生这些由全速模式的SOF数据包或低速模式的“保持激活”脉冲组成的帧标志,随后FRAMIRQ位每毫秒触发一次。

USB 规定要求确定的长延迟时间,例如复位一个设备后给它一个“复位恢复时间”。调用waitframes函数将是实现相对长的延迟时间的一个简便方法。

detect_device

这个函数在检测到一个USB设备插入USB-A连接器J1后返回。

wait_for_disconnect

该函数在检测到一个插入USB-A连接器的设备断开后返回。

Send_Packet

所有发送USB数据包的函数都调用这个函数。图9显示了IN事务处理的总线过程和实现它的C程序。

图9. 启动USB总线动作,由MAX3421E调用C程序函数
图9. 启动USB总线动作,由MAX3421E调用C程序函数

  1. 微控制器在FNADDR寄存器中设定函数地址。因为在微控制器重新载入FNADDR寄存器之前,装载值一直保持不变,所以每个数据包不需重复这个步骤。
  2. 微控制器通过写入HXFR寄存器开始传输数据,指明USB令牌和终端。然后等待HXFRDNIRQ (主机传输完成)触发IRQ,发送信号完成。
  3. 微控制器读HRSL (主机计算结果)寄存器来决定事务处理的结果。16个计算结果代码表示下列结果:
    A.成功(ACK)
    B.设备占用(NAK)
    C.非正常握手(STALL)
    D.设备故障,如超时、串音或总线错误
  4. 微控制器读计数寄存器字节,然后卸载RCVFIFO并将数据存入一个字节数组中。


这个函数处理NAK重试,连续重新启动数据传输直到:
  1. 收到ACK握手,或
  2. 超过常数NAK_LIMIT设定的预设限制
如果设备无反应,函数重试传输,直至受到RETRY_LIMIT限制。

CTL_Write_ND

这个函数以无数据模式执行控制-写操作(_ND = no data)。这个函数仅在主机Set_Address请求时调用。它可以实现Sent_Packet的两个调用:第一个用于SETUP数据包,第二用于IN-Handshake数据包。

IN_Transfer

这个函数尽可能多地调用Send_Packet (指定的IN),以重新获得USB的数据记录。MAX3420E和MAX3421E终端FIFOS为64字节长,如此长的字符描述,举例来说,需要多个IN请求数据包组成一个大数组(XfrData[2000])。

CTL_Read

这个函数处理3态CONTROL-Read USB事务,调用三个函数:
  1. 调用Send_Packet发送SETUP数据包
  2. 调用IN_Transfer使数据进入XfrDaTa[]中
  3. 调用Send_Packet发送OUT-Handshake数据包

enumerate_device

这个函数执行附录A所示的10个数据传输。因为它调用以前的函数来实现低级USB操作,所以通过研究这个函数可很容易理解枚举步骤。

print_error

这个函数检查4位HRSL值(图9中的第3项)以打印输出主机的传输结果。如果通过数据为0,不打印输出。这个函数返回HRSL值,因此调用这个函数可以很容易地检测错误,例如:
if(print_error(HR)) return;

3420_HIDKB.C

这个模块使用MAX3420E实现一个USB外设,符合标准HID类,可以象标准键盘输入一样自动输入文本。入口点是service_irqs函数,该函数在响应LPC2138的EINT1引脚时作为中断服务程序调用。ENT1引脚连接至MAX3420E的INT输出引脚。

Reset_Peripheral

这个函数以与Reset_Host相同的方式复位MAX3420E。

initialize_3420

这个函数完成三件事:
  1. 配置MAX3420E SPI接口以实现全双工操作(分离MOSI和MISO引脚)
  2. 配置MAX3420E INT引脚为上升沿有效
  3. 通过应用程序使能MAX3420E中断

service_irqs

这个函数直接处理总线复位和设备断开。这个函数也调用do_SETUPC处理枚举和do_IN3处理键盘输入。


注意:为精简代码,本应用不处理USB的挂起-恢复任务。实现该功能的例子可参考Maxim应用笔记3690,"MAX3420E的USB杖举程序(及其他)"。AN3690中的枚举代码作为3420_HIDKB.C的基础。

do_SETUP

这个函数通过检查8字节SETUP数据包中的各个字节处理设备枚举,然后调用函数以处理特定的请求。代码仅处理USB标准请求类,因为本应用程序中没有实现USB分类或客户请求。

do_IN3

这个函数根据HIDKB_enum_tables.h中的Message[]字符数组发出按键信息。每字符三个字节的格式定义在与HID Report Descriptor相同的文件中,如图10所示。

图10. Report Descriptor详述了一个键盘按键的3字节数据格式
图10. Report Descriptor详述了一个键盘按键的3字节数据格式

最终考虑和免责声明

插入J1的USB设备可能是十亿个左右可用设备的任意一个,因此不必说,将有各种各样的响应满足主机的枚举请求。任意一个通过兼容性测试的设备(有USB标识显示证明)都将在枚举时显示无误。本程序中的故障检测由每次主机事务处理后对HRSL值的检查完成。因为许多故障条件难以模拟,所以故障检查没有进行广泛的测试。

附录A

3240E_HIDKB.C枚举的USB总线跟踪过程

附录A
更详细的图
(PDF, 234kB)
下一步
EE-Mail 订阅EE-Mail,接收关于您感兴趣的新文档的自动通知。
© , Maxim Integrated Products, Inc.
The content on this webpage is protected by copyright laws of the United States and of foreign countries. For requests to copy this content, contact us.
APP 3936:
应用笔记 3936,AN3936, AN 3936, APP3936, Appnote3936, Appnote 3936