USB枚举实验
一、实验目的
1、熟悉掌握SOPC的基本流程。
2、掌握PDIUSBD12核的使用方法。
3、掌握USB枚举的基本过程。、
4、掌握PDIUSBD12芯片的基本使用方法。
二、实验设备
1、硬件:PC机,SOPC-NIOS II EDA/SOPC系统开发平台。
2、软件:QuartusII 5.1、SOPC Builder 5.1、NiosII IDE 5.1。
三、实验内容
本实验中,通过Nios II CPU,配合自定义IP核(PDIUSBD12),来初始化PDIUSBD12芯片,完成USB设备插入到主机后的基本枚举工作。系统中出了需要PDIUSBD12核外,还需要一个输入型PIO核,来负责检测USB的中断请求。
四、实验原理
PDIUSBD12(以下简称D12)是一款带并行总线的USB接口器件,通常与微控制器进行通信,另外它还支持本地的DMA传输。D12完全符合USB1.1版的规范,还符合大多数器件的分类规格:成像类、海量存储器件、通信器件等设备以及人机接口设备。另外它还集成了许多特性,包括SoftConnect TM、GoodLink TM、可编程时钟输出、低频晶振和终止寄存器集合、所有这些特性都为系统显著节约了成本,同时使USB功能在外设上的应用变得容易。D12芯片内部功能框图如图12-1所示,它内部包含了一个模拟收发器、电压调整器、PLL、位时钟恢复、Philips串行接口引擎(SIE)、SoftConnect TM、GoodLink TM、存储器管理单元(MMU)、并行和DMA接口等。
涂12-1 D12芯片内部功能框图
图12-1 D12芯片内部功能框图
D12的端点适用于不同类型的设备,端点可以通过“Set Mode”命令配置为四种不同的模式,分别为模式0(Non-ISO模式:非同步传输)、模式1(ISO-OUT模式:同步输出模式)、模式2(ISO-IN模式:同步输入模式)和模式3(ISO-IO模式:同步输入/输出传输模式)。
模式0(非同步模式)
模式1(同步输出模式)
模式2(同步输入模式)
模式3(同步输入/输出模式)
主端点(端点2)在有些方面是比较特别的,它是进行吞吐大数据的主要特点。同样地,它执行主机的特性以减轻传输大数据的任务:
1)缓冲。允许USB与本地CPU之间的并行读写操作,这样就增加了数据的吞吐量。
缓冲区切换是自动处理的,这样就实现了透明的缓冲区操作。
2)支持DMA操作。可以和对其它端点的正常I/O操作交叉进行。
3)DMA操作中的自动指针处理。在跨过缓冲区边界时不需要本地CPU干预。
4)可配置为同步传输或非同步(批量和中断)传输。
配置PIDUSBD12芯片时需要用到的引脚有:
D7~D0:双向数据总线
nCS:片选
SUSPEND:器件处于挂起状态
nINT:中断请求
nRD、nWR:读、写信号
A0:地址位。A0=1选择命令指令,A0=0选择数据。
命令总汇
下面就对上表中的各个命令作详细讲解。
●设置地址使能。【命令:D0H,处理:写1个字节】
该命令用于设置USB分配的地址和使能功能。
地址:写入的值即为地址
使能:置1使能该功能
●设置端点使能。【命令:D8H,处理:写1个字节】
普通/同步端点:置1表示普通/同步端点使能
●设置模式。【命令:F3H,处理:写2个字节】
设置模式命令后跟2个写入的数据,第一个字节包含配置字节信息,第二个字节是时钟分频因素字节。
无LAZYCLOCK:1表示CLKOUT不会切换到LazyClock;0表示CLKOUT在SUSPEND引脚变高后切换到LazyClock。LazyClock的频率是30KHz左右。已编程
值将不会被总线复位所改变。
时钟运行:1表示内部时钟和PLL即使在挂起状态下仍然运行;0表示只要不需要时,
晶振和PLL就停止运行。为了满足严格的挂起电流要求,该位需要设置为0。
已编程值将不会被总线复位所改变。
中断模式:1表示报告左右的错误和“NAKing”并产生一个中断;0表示只有OK被报告。已编程值将不会被总线复位所改变。
SoftConnect:1表示如果VBUS可用,上行数据上拉电阻被连接;0表示不连接。已编程值将不会被总线复位所改变。
端点配置:00-模式0;01-模式1;10-模式2;11-模式3。
时钟分频系数:该值用来表示CLKOUT的时钟分频系数。用N表示分频系数,那么输出的频率就是48MHz/(N+1)。复位置为11。这产生4MHz的输出频率,然
后可由用户自行调节。当N为0时,得到48MHz;当N取最大11时,
得到4MHz。D12的设计确保了在改变频率时不会出现干扰。已编程值
将不会被总线复位所改变。
SET_TO_ONE:该位需要在任何DMA读或写操作之前置为1。该位在上电复位时为0,复位后,可将其一直设为1。
仅有SOF中断模式:将该位置1后,仅当帧时钟的起始(SOF)时刻引起中断的产生,而不管引脚中断模式的设置状态(设置DMA位5)。
设置DMA。【命令:FBH,处理:读/写1个字节】
DMA突发串:00-单周期DMA;01-突发串(4周期)DMA;10-突发串(8周期)
DMA;11-突发串(16周期)DMA。
DMA使能:向该位写1会通过激活DMREQ启动DMA操作。在激活DMAREQ之前需
要装满(DMA读操作)或清空(DMA写操作)主端点缓冲区。在单
周期DMA模式中,DMAREQ在突发串数目耗尽后无效。然后下一个
突发串时重新激活。这个过程一直持续道nEOT和nDMACK以及nRD
或nWR一起被激活,此时将该位置0并终止DMA操作。DMA操作也
可通过向该位写入0来终止。
DMA方向:1表示从外部共享存储器到D12(DMA写操作);0表示从D12到外部
共享存储器(DMA读操作)。
自动重装:当该位设为1,DMA操作会自动重新重装。
中断引脚模式:0表示正常的中断引脚模式,中断寄存器所有位的逻辑或产生中
断;1表示出了正常中断外,在SOF起始位时也产生中断。
端点索引4中断使能:1表示只要端点缓冲区包含一个有效的信息包就产生中断。
通常在DMA操作关闭,以减少不必要的CPU响应。
端点索引5中断使能:1表示只要端点缓冲区有效(见缓冲区生效命令)就产
生中断。通常在DMA操作关闭,以减少不必要的CPU响应。
●读中断寄存器。【命令:F4H,处理:读2个字节】
总线复位:在总线复位后将产生一个中断将该位置1。总线复位通过nRESET脚的硬件复位基本相同,有一点除外,就是总线复位产生一个中断并且器
件在默认地址0处使能。
挂起改变:当D12没有收到3个SOF时,将会进入挂起状态并将挂起改变位置1。
任何挂起或唤醒状态的改变都将该位置1并产生中断。
DMA EOT:该位表示DMA操作已结束。
●选择端点。【命令:00~05H,处理:可选读1个字节】
满/空:1表示缓冲区已满;0表示缓冲区为空。
停止:1表示选择的端点处于停止状态。
●读端点状态。【命令:80~85H,处理:读1个字节】
●读最后处理状态寄存器。【命令:40~45H,处理:读1个字节】
数据接收/发送成功:1表示数据已经成功地接收或发送。
SETUP信息包:1表示最后成功接收的信息包有一个SETUP标志(对IN缓冲区进
行读总为0)。
数据0/1包:1表示最后成功接收/发送包含有一个DATA1 PID。
前一状态未读:1表示在前一状态读出之前发生了第二次事件。
●读缓冲区。【命令:F0H,处理:读多个字节(最大130)】
读缓冲区命令后,返回一系列从选择端点数据缓冲区读出的数据。每读一个字节,内部缓冲区指针自动加1。读缓冲区命令不会将缓冲区指针复位到缓冲区起始端,这意味着可被其它的命令所中断(选择端点命令除外)。
缓冲区数据结构如下:
字节1:保留,可为任意值
字节2:数据字节的数目(长度)
字节3:数据字节1
字节4:数据字节2
……
头两个字节DMA读操作中可跳过。因此第一个读出的字节是数据字节1,第二个就是数据字节2……。D12可通过USB信息包的EOP终止来决定包的最后一个字节。
●写缓冲区。【命令:F0H,处理:写多个字节(最大130)】
写缓冲区命令后跟一系列需要写入端点缓冲区的数据。数据的结构必须与前面描述的读缓冲区命令一样。第一个字节(保留)总为0。在DMA写操作中,头两个字节会被绕过,因此第一个写入的字节就是数据1,第二个写入的字节就是数据2……。在非同步传输中,数据被发送到主机之前缓冲区必须被完全填满并且切换到下一个缓冲区。例外的情况是,当前的缓冲区内容简要被发送到主机时,由有效的nEOT指示DMA传输的
结束。
●清缓冲区。【命令:F2H,处理:无】
当一个信息包完全接收之后,内部端点缓冲区满标志置位,所有后续的包将被返回的NAK拒绝。当CPU已读取数据,它应当通过清缓冲区命令来释放缓冲区。当缓冲区清空之后,新的信息包就可被接受了。
●使缓冲区有效。【命令:FAH,处理:无】
当CPU已将数据写入IN缓冲区,它应当通过使缓冲区有效命令设置缓冲区满标志,这表示缓冲区内的数据有效并可在接收到下一个IN标志时送入主机。
●设置端点状态。【命令:40~45H,处理:写1个字节】
当一个停止控制的端点接收到SETUP标志时自动解除停止,而不管信息包的内容如何。如果端点应该停在停止状态,CPU可以重新停止它。
当一个停止的端点解除了停止(设置端点命令或接收到一个SETUP标志),它同时被重新初始化。将缓冲区刷新,如果是OUT缓冲区就等待一个DATA0 PID,如果是IN 缓冲区就写入一个DATA0 PID。即使在解除停止时,将设置端点状态写为0,也将初始化端点。
停止:1表示端点处于停止状态。
●应答SETUP。【命令:F1H,处理:无】
一个SETUP信息包的到达将IN缓冲区刷新并禁止对IN和OUT端点的两条命令:缓冲区有效和清零缓冲区命令。CPU要通过应答SETUP命令重新使能这些命令。这确保了最后的SETUP保留在缓冲区内,并且在CPU看到SETUP包并应答之前,不会有任何包发回主机。CPU必须将应答SETUP命令发送到IN和OUT端点。
●发送恢复。【命令:F6H,处理:无】
发送一个上行数据流恢复信号10ms。该命令通常用于器件处于挂起状态时。恢复命令后不跟读出或写入的数据。
●读当前帧数目。【命令:F5H,处理:读1或2字节】
该命令后跟1到2个读出的字节并返回最后成功接收的SOF帧数目。帧数目为返回的低位字节。
五、实验步骤
1、将光盘中提供的自定义IP核文件夹(myIP文件夹)中的所有文件夹拷贝到“Nios安
装目录\kits\nios2_51\components”下。
2、使用QuartusII建立一个工程文件和顶层文件,方法与实验一相同。
3、使用SOPC Builder建立一个简单的NIOSII硬件系统。
1)启动SOPC Builder。方法与实验一相同。
2)指定目标FPGA及时钟。方法与实验一相同。
3)添加NIOSII内核及其它外设。本实验添加的IP核如下:
A、添加NIOS II、SRAM、JTAG-UART、Avalon总线的IP核,并重新命名。
B、添加D12核。单击选中System Contents列表中的myIP类中的D12,然后点击
底部的【Add】按钮,直接在弹出的窗口中点击【Finish】按钮,添加该IP核,
并将其重命名为D12。
C、加入一个1位的输入型PIO做为USB的INT端口,并重命名。
至此,本实验所有的外设均添加完成,完成后,工作区如图12-2所示。
图12-2 本实验中创建的系统
4)指定基地址和中断优先级。
5)设置Nios II复位和异常地址。
6)编译生成Nios II系统。
4、在QuartusII中编译这个简单的NIOS II硬件系统并生成其配置文件。
1)在QuartusII加入Nios II系统符号到顶层文件。
2)为顶层文件加入相应的其它电路,在本实验中为了防止SRAM和FLASH数据冲突,将FLASH的CS信号设定为高电平,即加入一个VCC信号给FLASH的CS信号。
3)为QuartusII内的各端口加入输入/输出引脚。并将输入/输出的引脚进行重新命名。
添加完成后如图12-3所示。
图12-3 本实验的顶层文件
4)设置编译过程的各种参数,如没有使用的引脚信号的定义等。
5)编译顶层文件,找出措误的原因并更正,直到编译正确。
6)根据后面的附表为输入/输出信号分配FPGA的管脚。
7)管脚分配正确后,再次编译以使公配的管脚生效。
2、在NiosII IDE中建立用户C/C++工程,编写用户程序,用户可参考示例程序。
3、编写完用户后,编译用户程序,将提示的程序错误改正,直到编译正确。
4、用电缆连接开发平台与电脑,打开电源,先通用过下载电缆下载本实验生成的SOF 文件至FPGA,在Nios II IDE中设置硬件连接,运行用户程序。观察运行结果。
六、实验结果
程序运行后,将USB线方头插入实验箱上的USB接口,扁头插入PC的USB接口,此时系统会弹出发现新硬件的对话框,安装光盘中提供的USB驱动,到最后出现图12-4所示对话框时,便表明驱动已经完全安装,枚举完全正确。当枚举正确后,实验箱上D12边上的Link指示灯会亮起。验证完成后,请退出Nios II IDE软件,关闭Quartus II软件,关闭实验箱电源,拔出USB下载电缆。
图12-4 枚举正确后的对话框
试验附录程序如下:
usb.h文件
#ifndef __usb_h__
#define __usb_h__
#include "system.h"
#define USB_HOST 0X06
#define USB_DEVICE 0x02
#define USB_DISABLE 0X00
#define RESET_ALL 0X05
#define CHECK_EXIST 0X06
#define SET_USB_ID 0X12
#define SET_USB_MODE 0X15
#define GET_STATUS 0X22
#define UNLOCK_USB 0X23
#define RD_USB_DATA 0X28
#define WR_USB_DATA5 0X2A
#define WR_USB_DATA7 0X2B
#define GET_IC_VER 0X01
#define ENTER_SLEEP 0X03
#define CHK_SUSPEND 0X0B
#define RD_USB_DATA0 0X27
#define RET_SUCCESS 0X51
#define RET_ABORT 0X5B
#define INT_EP2_OUT 0x02
#define INT_EP2_IN 0x0a
//host
#define DISK_READ 0X54
#define DISK_RD_GO 0X55
#define DISK_READY 0X59
#define DISK_INIT 0X51
//status
#define USB_INT_CONNECT 0x15
#define USB_INT_DISCONNECT 0X16 #define USB_INT_SUCCESS 0X14
#define USB_INT_DISK_READ 0X1D
//usb
#define PIO_USB_DB *(volatile unsigned long int *)USB_DB_BASE
#define PIO_USB_WR *(volatile unsigned long int *)USB_WR_BASE
#define PIO_USB_RD *(volatile unsigned long int *)USB_RD_BASE
#define PIO_USB_A0 *(volatile unsigned long int *)USB_A0_BASE
#define PIO_USB_INT *(volatile unsigned long int *)USB_INT_BASE
#define PIO_USB_DB_DIR *(volatile unsigned long int *)(USB_DB_BASE+4)
#define VID 0X0FFE
#define PID 0X1000
/*--------------------------------------------------------------------------------------
* Struct
*-------------------------------------------------------------------------------------*/ typedef struct{
char receive_buffer[200];
int send_ok_flag;
int receive_ok_flag;
}USB_T;
/*--------------------------------------------------------------------------------------
* Extern
*-------------------------------------------------------------------------------------*/ extern USB_T usb;
extern int initialize_usb(void);
extern int set_usb_mode(unsigned char);
extern int send_string_to_usb(char *str,int str_len);
extern void write_command_to_usb(unsigned char command);
extern void write_data_to_usb(unsigned char data);
#endif //__usb_h__
usb.c文件
/*--------------------------------------------------------------------------------------
* Include
*-------------------------------------------------------------------------------------*/
#include "../inc/usb.h"
#include "altera_avalon_pio_regs.h"
#include "sys/alt_irq.h"
#include
#include
/*--------------------------------------------------------------------------------------
* Function
*-------------------------------------------------------------------------------------*/
void write_command_to_usb(unsigned char command);
void write_data_to_usb(unsigned char data);
unsigned char read_data_from_usb(void);
void delay(void);
/*--------------------------------------------------------------------------------------
* Variable
*-------------------------------------------------------------------------------------*/
USB_T usb;
/*
* === FUNCTION ====================================================================== * Name: irq_usb
* Description:
*
====================================================================== ===============
*/
void irq_usb(void)
{
unsigned int i;
unsigned char interrupt_status,data_len;
// static int times=0;
write_command_to_usb(GET_STA TUS);
interrupt_status=read_data_from_usb();
switch(interrupt_status){
//Device
case INT_EP2_OUT:
write_command_to_usb(RD_USB_DATA);
data_len=read_data_from_usb();
for(i=0;i usb.receive_buffer[i]='\0'; usb.receive_ok_flag=1; break; case INT_EP2_IN: write_command_to_usb(UNLOCK_USB); usb.send_ok_flag=1; break; default :break; } IOWR_ALTERA_A V ALON_PIO_EDGE_CAP(USB_NINT_BASE,0x00); } /* * === FUNCTION ====================================================================== * Name: send_string_to_usb * Description: * ====================================================================== =============== */ int send_string_to_usb(char *str,int str_len) { int i; write_command_to_usb(WR_USB_DATA7); write_data_to_usb(str_len); for(i=0;i return 0; } /* * === FUNCTION ====================================================================== * Name: initialize_usb * Description: * ====================================================================== =============== int initialize_usb(void) { PIO_USB_RD=1; PIO_USB_WR=1; PIO_USB_A0=1; usb.receive_ok_flag=0; // enable the io interrupt IOWR_ALTERA_A V ALON_PIO_IRQ_MASK(USB_NINT_BASE,1); IOWR_ALTERA_A V ALON_PIO_EDGE_CAP(USB_NINT_BASE,0); alt_irq_register(USB_NINT_IRQ,NULL,irq_usb); set_usb_mode(USB_DEVICE); return 0; } /* * === FUNCTION ====================================================================== * Name: set_usb_mode * Description: * ====================================================================== =============== */ int set_usb_mode(unsigned char type) { write_command_to_usb(SET_USB_MODE); write_data_to_usb(type); read_data_from_usb(); if((read_data_from_usb())==0x51)return 0; else return -1; } /* * === FUNCTION ====================================================================== * Name: write_command_to_usb * Description: ====================================================================== =============== */ void write_command_to_usb(unsigned char command) { //A0 PIO_USB_A0=1; //DB DIR output PIO_USB_DB_DIR=0xff; PIO_USB_DB=command; PIO_USB_WR=0; PIO_USB_WR=1; } /* * === FUNCTION ====================================================================== * Name: uart_send_byte * Description: * ====================================================================== =============== */ void delay(void) { int i; for(i=0;i<1000;i++); } /* * === FUNCTION ====================================================================== * Name: write_data_to_usb * Description: * ====================================================================== =============== */ void write_data_to_usb(unsigned char data) { //A0 PIO_USB_A0=0; //DB DIR output PIO_USB_DB_DIR=0xff; PIO_USB_DB=data; usleep(20); PIO_USB_WR=0; delay(); usleep(20); PIO_USB_WR=1; } /* * === FUNCTION ====================================================================== * Name: read_data_from_usb * Description: * ====================================================================== =============== */ unsigned char read_data_from_usb(void) { unsigned char data=0; //A0 PIO_USB_A0=0; //DB DIR output PIO_USB_DB_DIR=0; PIO_USB_RD=0; delay(); data=PIO_USB_DB; PIO_USB_RD=1; return data; } system.h文件 /* system.h * * Machine generated for a CPU named "cpu" as defined in: * d:\My_FPGA\USB_meiji\software\hello_world_1_syslib\..\..\USB1.ptf * * Generated: 2011-11-12 14:20:23.875 * */ #ifndef __SYSTEM_H_ #define __SYSTEM_H_ /* * system configuration * */ #define ALT_SYSTEM_NAME "USB1" #define ALT_CPU_NAME "cpu" #define ALT_CPU_ARCHITECTURE "altera_nios2" #define ALT_DEVICE_FAMIL Y "CYCLONEII" #define ALT_STDIN "/dev/jtag_uart" #define ALT_STDIN_TYPE "altera_avalon_jtag_uart" #define ALT_STDIN_BASE 0x00001858 #define ALT_STDIN_DEV jtag_uart #define ALT_STDIN_PRESENT #define ALT_STDOUT "/dev/jtag_uart" #define ALT_STDOUT_TYPE "altera_avalon_jtag_uart" #define ALT_STDOUT_BASE 0x00001858 #define ALT_STDOUT_DEV jtag_uart #define ALT_STDOUT_PRESENT #define ALT_STDERR "/dev/jtag_uart" #define ALT_STDERR_TYPE "altera_avalon_jtag_uart" #define ALT_STDERR_BASE 0x00001858 #define ALT_STDERR_DEV jtag_uart #define ALT_STDERR_PRESENT #define ALT_CPU_FREQ 100000000 #define ALT_IRQ_BASE NULL #define ALT_LEGACY_INTERRUPT_API_PRESENT /* * processor configuration