声明:本文转载于http://www.cnblogs.com/kingst,版权归黑金动力社区(http://www.heijin.org)所有。 
简介
这一节,我们来讲讲黑金开发板USB部分的内容。黑金开发板上使用的USB芯片是南京沁恒公司的CH376,它支持USB 设备(DEVICE)方式和USB(HOST) 主机方式,并且内置了USB 通讯协议的基本固件,内置了处理Mass-Storage海量存储设备的专用通讯协议的固件,内置了SD 卡的通讯接口固件,内置了FAT16和FAT32 以及FAT12 文件系统的管理固件,支持常用的USB 存储设备(包括U 盘/USB 硬盘/USB 闪存盘/USB 读卡器)和SD 卡(包括标准容量SD 卡和高容量HC-SD 卡以及协议兼容的MMC 卡和TF 卡)。

由于芯片内部集成了USB通讯协议的基本固件,因此,免去了我们自己编写USB通讯协议的麻烦了。不仅如此,它还集成了文件系统的管理固件,那么,我们不就可以直接读取U盘中的内容了?事实就是这样的,真的方便了很多哦。下面我们就来看看这款芯片到底有多好用吧。
硬件开发
首先,我们看看这部分电路,如下图所示,我们采用的是8位总线模式,电路结构非常简单,与FPGA相连的一共有12根线,其中8根数据线,1根中断线,3根控制线。

下面,我们就在软核中添加USB部分的模块,其实都是通过PIO模块控制的。添加后如下图所示,其中USB_DB为8位输出PIO;USB_WR,USB_RD,USB_A0都是1位输出PIO。

而USB_nINT为输入PIO,而且电平中断,如下图设置,

都设置好以后,自动分配地址,中断,接下来就可以编译了。
编译好以后,回到Quartus界面,整理好以后,如下图所示,要记得USB_DB数据线是双向的,因此,一定要用分配bidir双向引脚,而不要用output。

下面是中断引脚部分,由于我们是低电平中断,而NIOS电平中断只支持高电平中断,所以我们需要加一个非门,如下图示所示

都设置好以后,我们就可以分配引脚了,TCL脚本有关USB部分的代码如下
| 01 | #------------------------USB---------------------------------------# |
| 02 | set_location_assignment PIN_117 -to USB_DB[0] |
| 03 | set_location_assignment PIN_118 -to USB_DB[1] |
| 04 | set_location_assignment PIN_127 -to USB_DB[2] |
| 05 | set_location_assignment PIN_128 -to USB_DB[3] |
| 06 | set_location_assignment PIN_133 -to USB_DB[4] |
| 07 | set_location_assignment PIN_134 -to USB_DB[5] |
| 08 | set_location_assignment PIN_135 -to USB_DB[6] |
| 09 | set_location_assignment PIN_137 -to USB_DB[7] |
| 11 | set_location_assignment PIN_113 -to USB_A0 |
| 12 | set_location_assignment PIN_115 -to USB_WR |
| 13 | set_location_assignment PIN_116 -to USB_nINT |
| 14 | set_location_assignment PIN_114 -to USB_RD |
分配好引脚以后,大家就可以编译了。
软件开发
USB分主机模式和设备模式,这两种模式硬件部分是相同的,只是在软件编程方面有些不同。这一节,我们来讲设备模式,也就是开发板通过USB接口与主机(电脑)相连,实现开发板与电脑的数据通信。
我们首先打开NIOS II 9.0 IDE软件,还是老过程,首先编译一遍,Ctril+b。编译成功以后,我们在system.h中会看到USB部分的代码,如下表所示
| 01 | #define USB_DB_NAME "/dev/USB_DB" |
| 02 | #define USB_DB_TYPE "altera_avalon_pio" |
| 03 | #define USB_DB_BASE 0x00001840 |
| 05 | #define USB_NINT_NAME "/dev/USB_nINT" |
| 06 | #define USB_NINT_TYPE "altera_avalon_pio" |
| 07 | #define USB_NINT_BASE 0x00001850 |
| 09 | #define USB_WR_NAME "/dev/USB_WR" |
| 10 | #define USB_WR_TYPE "altera_avalon_pio" |
| 11 | #define USB_WR_BASE 0x00001860 |
| 13 | #define USB_RD_NAME "/dev/USB_RD" |
| 14 | #define USB_RD_TYPE "altera_avalon_pio" |
| 15 | #define USB_RD_BASE 0x00001870 |
| 17 | #define USB_A0_NAME "/dev/USB_A0" |
| 18 | #define USB_A0_TYPE "altera_avalon_pio" |
| 19 | #define USB_A0_BASE 0x00001880 |
下面,我们来添加USB部分的代码。
首先,我们建立一个在inc下面建立一个usb.h文件,内容如下
| 03 | //-----------------Include files-------------------------// |
| 05 | //----------------- CH375 DEFINE-------------------------// |
| 06 | //下面部分是USB寄存器地址,这部分定义可以看CH376的芯片手册 |
| 08 | #define USB_DEVICE 0x02 |
| 09 | #define USB_DISABLE 0X00 |
| 12 | #define CHECK_EXIST 0X06 |
| 13 | #define SET_USB_ID 0X12 |
| 14 | #define SET_USB_MODE 0X15 |
| 15 | #define GET_STATUS 0X22 |
| 16 | #define UNLOCK_USB 0X23 |
| 17 | #define RD_USB_DATA 0X28 |
| 18 | #define WR_USB_DATA5 0X2A |
| 19 | #define WR_USB_DATA7 0X2B |
| 20 | #define GET_IC_VER 0X01 |
| 21 | #define ENTER_SLEEP 0X03 |
| 22 | #define CHK_SUSPEND 0X0B |
| 23 | #define RD_USB_DATA0 0X27 |
| 25 | #define RET_SUCCESS 0X51 |
| 28 | #define INT_EP2_OUT 0x02 |
| 29 | #define INT_EP2_IN 0x0a |
| 33 | #define DISK_RD_GO 0X55 |
| 34 | #define DISK_READY 0X59 |
| 37 | #define USB_INT_CONNECT 0x15 |
| 38 | #define USB_INT_DISCONNECT 0X16 |
| 39 | #define USB_INT_SUCCESS 0X14 |
| 40 | #define USB_INT_DISK_READ 0X1D |
| 41 | //-----------------bus define----------------------------// |
| 42 | /*下面是USB的接口部分定义,这次我没有像以往那样定义结构体,是为了让大家感受一下各种形式的编程。大家要注意PIO_USB_DB_DIR的定义,通过以前的讲解,不知道大家是否理解,它是USB数据线的方向控制寄存器的定义,知道为什么要+4么,大家自己考虑吧,不明白就看看附录中的有关PIO问题解析部分内容吧*/ |
| 43 | #define PIO_USB_DB *(volatile unsigned long int *)USB_DB_BASE |
| 44 | #define PIO_USB_WR *(volatile unsigned long int *)USB_WR_BASE |
| 45 | #define PIO_USB_RD *(volatile unsigned long int *)USB_RD_BASE |
| 46 | #define PIO_USB_A0 *(volatile unsigned long int *)USB_A0_BASE |
| 47 | #define PIO_USB_INT *(volatile unsigned long int *)USB_INT_BASE |
| 48 | #define PIO_USB_DB_DIR *(volatile unsigned long int *)(USB_DB_BASE+4) |
| 54 | char receive_buffer[200]; |
| 58 | //-----------------Extern function------------------------// |
| 61 | extern int initialize_usb(void); |
| 62 | extern int set_usb_mode(unsigned char); |
| 63 | extern int send_string_to_usb(char *str,int str_len); |
| 64 | extern void write_command_to_usb(unsigned char command); |
| 65 | extern void write_data_to_usb(unsigned char data); |
接下来,我们看看CH376的时序图,如下图所示

我们就根据上面的时序图编写驱动部分,在driver中建立一个usb.c文件,内容如下表所示
| 002 | * ================================================================== |
| 008 | * Compiler: Nios II 9.0 IDE |
| 010 | * Email: avic633@gmail.com |
| 011 | * ================================================================= |
| 013 | //-----------------Include files-------------------------// |
| 014 | #include "../inc/usb.h" |
| 015 | #include "altera_avalon_pio_regs.h" |
| 016 | #include "sys/alt_irq.h" |
| 019 | //-----------------Function Prototype--------------------// |
| 020 | void write_command_to_usb(unsigned char command); |
| 021 | void write_data_to_usb(unsigned char data); |
| 022 | unsigned char read_data_from_usb(void); |
| 024 | //-----------------Variable------------------------------// |
| 027 | //-----------------Function------------------------------// |
| 030 | * === FUNCTION =================================================== |
| 033 | * ================================================================= |
| 038 | unsigned char interrupt_status,data_len; |
| 039 | // static int times=0; |
| 041 | write_command_to_usb(GET_STATUS); |
| 043 | interrupt_status=read_data_from_usb(); |
| 045 | switch(interrupt_status){ |
| 049 | write_command_to_usb(RD_USB_DATA); |
| 050 | data_len=read_data_from_usb(); |
| 053 | usb.receive_buffer[i]=read_data_from_usb(); |
| 054 | usb.receive_buffer[i]='\0'; |
| 056 | usb.receive_ok_flag=1; |
| 061 | write_command_to_usb(UNLOCK_USB); |
| 069 | IOWR_ALTERA_AVALON_PIO_EDGE_CAP(USB_NINT_BASE,0x00); |
| 073 | * === FUNCTION =================================================== |
| 074 | * Name: send_string_to_usb |
| 076 | * ================================================================= |
| 078 | int send_string_to_usb(char *str,int str_len) |
| 082 | write_command_to_usb(WR_USB_DATA7); |
| 083 | write_data_to_usb(str_len); |
| 090 | * === FUNCTION =================================================== |
| 091 | * Name: initialize_usb |
| 092 | * Description: 初始化USB |
| 093 | * ================================================================= |
| 095 | int initialize_usb(void) |
| 100 | usb.receive_ok_flag=0; |
| 102 | // enable the io interrupt |
| 103 | IOWR_ALTERA_AVALON_PIO_IRQ_MASK(USB_NINT_BASE,1); |
| 104 | IOWR_ALTERA_AVALON_PIO_EDGE_CAP(USB_NINT_BASE,0); |
| 106 | alt_irq_register(USB_NINT_IRQ,NULL,irq_usb); |
| 108 | set_usb_mode(USB_DEVICE); |
| 113 | * === FUNCTION =================================================== |
| 115 | * Description: 设置USB模式 |
| 116 | * ================================================================= |
| 118 | int set_usb_mode(unsigned char type) |
| 120 | write_command_to_usb(SET_USB_MODE); |
| 121 | write_data_to_usb(type); |
| 122 | read_data_from_usb(); |
| 124 | if((read_data_from_usb())==0x51)return 0; |
| 129 | * === FUNCTION =================================================== |
| 130 | * Name: write_command_to_usb |
| 132 | * ================================================================= |
| 134 | void write_command_to_usb(unsigned char command) |
| 148 | * === FUNCTION =================================================== |
| 151 | * ================================================================= |
| 159 | * === FUNCTION =================================================== |
| 160 | * Name: write_data_to_usb |
| 162 | * ================================================================= |
| 164 | void write_data_to_usb(unsigned char data) |
| 181 | * === FUNCTION =================================================== |
| 182 | * Name: read_data_from_usb |
| 184 | * ================================================================= |
| 187 | unsigned char read_data_from_usb(void) |
| 189 | unsigned char data=0; |
编写好驱动以后,我们需要编写主函数测试代码
| 03 | #include "../inc/usb.h" |
| 07 | unsigned char tmp[] = "Hello USB!\n"; |
| 12 | if(usb.receive_ok_flag){ |
| 13 | printf("%s\n",usb.receive_buffer); |
| 14 | usb.receive_ok_flag = 0; |
| 17 | send_string_to_usb(tmp,sizeof(tmp)); |
程序都写完了,但工作还没有结束,如果要想调试,我们首先还需要在你的电脑上安装CH376的驱动。
首先,去南京沁恒的网站下载驱动,下载地址是:,CH376的驱动跟CH372,CH375是一样的。
双击CH372DRV.EXE,开始安装驱动,如下图所示,点击INSTALL,直接安装就可以了。

上位机编程
为了调试,我们还需要上位机的软件来配合,就像串口调试精灵的一个东西。这部分工作属于上位机部分的内容了。我在这里简单介绍一下吧。
南京沁恒网站提供了上位机需要的静态库函数和头文件,下载地址是:,我们可以利用他们构建自己的上位机。我使用的是NI公司的Labwindows/CVI 8.1,当然大家也可以使用VC等软件开发。

我感觉这个软件还是蛮好用的,大家可以研究一下。写好的上位机面板如下图所示,

我们可以利用它进行简单的发送和接收,软件还不够完善。下面简单介绍一下使用方法,首先需要将FPGA运行起来,然后点击上位机的打开按钮。如果是接收的话,点击Reveive,每点一次接收一次。如果发送的话,将你发送的数据写到上面的输入框中,点击Send,每点一次发送一次。如下图示

好了,到这里,有关USB的设备模式的内容就讲完了。下一节,我们将讲解有关USB主模式的内容,也就是如何读取U盘等相关内容。谢谢大家!
阅读(1614) | 评论(0) | 转发(0) |