声明:本文转载于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盘等相关内容。谢谢大家!
阅读(1431) | 评论(0) | 转发(0) |