Maxim USB库是使用ARM7™处理器并基于MAX3421E/MAX3420E提供的软件例程。本应用笔记对系统及其软件运行进行了说明,在同一ARM® C语言代码中同时实现了USB主机和外设功能。这种方法使得USB外围设备和嵌入式主机的开发和研究,具有在USB电缆的另一端带有参考设备的优势,而所有的这些都包含在同一的C语言代码中。
Maxim USB库是两块电路板和C语言程序的组合。利用这个系统,可以:
图1. MAX3421主机/ARM从任意的USB外设重新得到枚举数据,并通过串口连接至运行有终端仿真程序的PC来报告结果。
本应用C语言代码运行在两板装置上:
图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电路框图和本应用笔记的软件模块。MAX3420E和MAX3421E都使用SPI总线访问其寄存器组。LPC2138含有2个硬件SPI单元:
可对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询问自身的代码演示
通读 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. 不良结果,例程没有按照要求显示生产商名字,代码有问题。
出现了什么问题? 原来,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格式,可看到结果。
该结果有所改善,但并不完美。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. 最佳的代码实例。代码现在可实现设计想要实现的功能。
通过测试外设代码和使用主机控制器及编码运行/分析,由于报告的结果是由MAX3420E发送至MAX3421E的实际USB报文,从而可以看到与PC机响应完全相同的结果。最好的是,这些都可在一个可控的基础上实现,因而不必担心PC机的如何响应而使代码开发出现错误。
图8. Keil工程的结构
图8显示了三个主要模块,并展开显示了它们的文件依靠。没有展开的模块是Keli开发环境所需要的内部处理文件。
这个模块包含由其它两个模块调用的公用函数。
此函数设定LPC2138的PLL和时钟除法器。附着于LPC2138 (F OSC )的晶体为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设定,然后再试验。---
这个函数初始化LPC2138的I/O引脚和SPI单元(SPI和SSP):
关于SPI接口
SPI接口使用如下信号:* MOSI 主机输出数据,从机输入数据
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接口:
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这个函数更新连接至MAX3421E通用输出引脚GPO[6:0]的7段信息显示,函数保留GPO[7]的设置,GPO[7]控制USB “A”连接器(评估板的J1)的VBUS开关。
这个模块实现MAX3421E USB主机,该主机访问USB设备并通过MCB22310的串口报告枚举数据。作为参考,附录A给出了主机枚举由3420_HIDKB.C实现的内置设备的LeCroy (CATC)总线过程。
这个模块中有三种函数类型:
下面的说明将按此顺序。
MAX3421E包含上电复位,因此这个操作并不是必须的。尽管如此,程序开发阶段使用该字段,不需要重复上电即可初始化调试过程,是复位功能的很好方式。启动该函数使器件清零,不会带有以前的调试信息。
复位MAX3421E使片上振荡器停止工作。解除复位后,这个函数在返回之前将等待OSCOKIRQ位(振荡器正常中断请求)变为有效。
这个函数在3420_HIDKB.C模块中。
这个函数建立ARM向量中断如下:
该函数在3420_HIDKB.C模块中。它进行HID键盘仿真。初始化ARM中断的代码仅需将该地址置为一个中断向量。
用于USB主机测量时间的一个简单方法是计算1ms帧标志的个数。当微控制器设定寄存器位SOFKANAB = 1时,MAX3421E自动产生这些由全速模式的SOF数据包或低速模式的“保持激活”脉冲组成的帧标志,随后FRAMIRQ位每毫秒触发一次。
USB 规定要求确定的长延迟时间,例如复位一个设备后给它一个“复位恢复时间”。调用waitframes函数将是实现相对长的延迟时间的一个简便方法。
这个函数在检测到一个USB设备插入USB-A连接器J1后返回。
该函数在检测到一个插入USB-A连接器的设备断开后返回。
所有发送USB数据包的函数都调用这个函数。图9显示了IN事务处理的总线过程和实现它的C程序。
图9. 启动USB总线动作,由MAX3421E调用C程序函数
A.成功(ACK)
B.设备占用(NAK)
C.非正常握手(STALL)
D.设备故障,如超时、串音或总线错误
这个函数处理NAK重试,连续重新启动数据传输直到:
如果设备无反应,函数重试传输,直至受到RETRY_LIMIT限制。
这个函数以无数据模式执行控制-写操作( _ND = no data )。这个函数仅在主机Set_Address请求时调用。它可以实现Sent_Packet的两个调用:第一个用于SETUP数据包,第二用于IN-Handshake数据包。
这个函数尽可能多地调用Send_Packet (指定的IN),以重新获得USB的数据记录。MAX3420E和MAX3421E终端FIFOS为64字节长,如此长的字符描述,举例来说,需要多个IN请求数据包组成一个大数组(XfrData[2000])。
这个函数处理3态CONTROL-Read USB事务,调用三个函数:
这个函数执行附录A所示的10个数据传输。因为它调用以前的函数来实现低级USB操作,所以通过研究这个函数可很容易理解枚举步骤。
这个函数检查4位HRSL值(图9中的第3项)以打印输出主机的传输结果。如果通过数据为0,不打印输出。这个函数返回HRSL值,因此调用这个函数可以很容易地检测错误,例如:
if(print_error(HR)) return;
这个模块使用MAX3420E实现一个USB外设,符合标准HID类,可以象标准键盘输入一样自动输入文本。入口点是service_irqs函数,该函数在响应LPC2138的EINT1引脚时作为中断服务程序调用。ENT1引脚连接至MAX3420E的INT输出引脚。
这个函数以与Reset_Host相同的方式复位MAX3420E。
这个函数完成三件事:
这个函数直接处理总线复位和设备断开。这个函数也调用do_SETUPC处理枚举和do_IN3处理键盘输入。
注意 :为精简代码,本应用不处理USB的挂起-恢复任务。实现该功能的例子可参考Maxim应用笔记3690,"MAX3420E的USB杖举程序(及其他)"。AN3690中的枚举代码作为3420_HIDKB.C的基础。---
这个函数通过检查8字节SETUP数据包中的各个字节处理设备枚举,然后调用函数以处理特定的请求。代码仅处理USB标准请求类,因为本应用程序中没有实现USB分类或客户请求。
这个函数根据HIDKB_enum_tables.h中的Message[]字符数组发出按键信息。每字符三个字节的格式定义在与HID Report Descriptor相同的文件中,如图10所示。
图10. Report Descriptor详述了一个键盘按键的3字节数据格式
插入J1的USB设备可能是十亿个左右可用设备的任意一个,因此不必说,将有各种各样的响应满足主机的枚举请求。任意一个通过兼容性测试的设备(有USB标识显示证明)都将在枚举时显示无误。本程序中的故障检测由每次主机事务处理后对HRSL值的检查完成。因为许多故障条件难以模拟,所以故障检查没有进行广泛的测试。
全部0条评论
快来发表一下你的评论吧 !