【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)

声明:本文为原创作品,版权归本博文作者所有,如需转载,请注明出处http://www.cnblogs.com/kingst/ 

 【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第1张图片

简介

      这一节,我们来讲讲黑金开发板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 卡)。

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第2张图片

      由于芯片内部集成了USB通讯协议的基本固件,因此,免去了我们自己编写USB通讯协议的麻烦了。不仅如此,它还集成了文件系统的管理固件,那么,我们不就可以直接读取U盘中的内容了?事实就是这样的,真的方便了很多哦。下面我们就来看看这款芯片到底有多好用吧。

硬件开发

      首先,我们看看这部分电路,如下图所示,我们采用的是8位总线模式,电路结构非常简单,与FPGA相连的一共有12根线,其中8根数据线,1根中断线,3根控制线。

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第3张图片

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

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第4张图片

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

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第5张图片

都设置好以后,自动分配地址,中断,接下来就可以编译了。

      编译好以后,回到Quartus界面,整理好以后,如下图所示,要记得USB_DB数据线是双向的,因此,一定要用分配bidir双向引脚,而不要用output。

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第6张图片

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

clip_image012

      都设置好以后,我们就可以分配引脚了,TCL脚本有关USB部分的代码如下

#------------------------USB---------------------------------------#
set_location_assignment	PIN_117	-to	USB_DB[0]
set_location_assignment	PIN_118	-to	USB_DB[1]
set_location_assignment	PIN_127	-to	USB_DB[2]
set_location_assignment	PIN_128	-to	USB_DB[3]
set_location_assignment	PIN_133	-to	USB_DB[4]
set_location_assignment	PIN_134	-to	USB_DB[5]
set_location_assignment	PIN_135	-to	USB_DB[6]
set_location_assignment	PIN_137	-to	USB_DB[7]

set_location_assignment	PIN_113	-to	USB_A0
set_location_assignment	PIN_115	-to	USB_WR
set_location_assignment	PIN_116	-to	USB_nINT
set_location_assignment	PIN_114	-to	USB_RD

分配好引脚以后,大家就可以编译了。

软件开发

      USB分主机模式和设备模式,这两种模式硬件部分是相同的,只是在软件编程方面有些不同。这一节,我们来讲设备模式,也就是开发板通过USB接口与主机(电脑)相连,实现开发板与电脑的数据通信。

      我们首先打开NIOS II 9.0 IDE软件,还是老过程,首先编译一遍,Ctril+b。编译成功以后,我们在system.h中会看到USB部分的代码,如下表所示

#define USB_DB_NAME "/dev/USB_DB"
#define USB_DB_TYPE "altera_avalon_pio"
#define USB_DB_BASE 0x00001840
......
#define USB_NINT_NAME "/dev/USB_nINT"
#define USB_NINT_TYPE "altera_avalon_pio"
#define USB_NINT_BASE 0x00001850
......
#define USB_WR_NAME "/dev/USB_WR"
#define USB_WR_TYPE "altera_avalon_pio"
#define USB_WR_BASE 0x00001860
......
#define USB_RD_NAME "/dev/USB_RD"
#define USB_RD_TYPE "altera_avalon_pio"
#define USB_RD_BASE 0x00001870
......	
#define USB_A0_NAME "/dev/USB_A0"
#define USB_A0_TYPE "altera_avalon_pio"
#define USB_A0_BASE 0x00001880
......

      下面,我们来添加USB部分的代码。

      首先,我们建立一个在inc下面建立一个usb.h文件,内容如下

#ifndef __usb_h__
#define __usb_h__
//-----------------Include files-------------------------//
#include "system.h"
//----------------- CH375 DEFINE-------------------------//
//下面部分是USB寄存器地址,这部分定义可以看CH376的芯片手册
#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
//-----------------bus define----------------------------//
/*下面是USB的接口部分定义,这次我没有像以往那样定义结构体,是为了让大家感受一下各种形式的编程。大家要注意PIO_USB_DB_DIR的定义,通过以前的讲解,不知道大家是否理解,它是USB数据线的方向控制寄存器的定义,知道为什么要+4么,大家自己考虑吧,不明白就看看附录中的有关PIO问题解析部分内容吧*/
#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

typedef struct{
	char receive_buffer[200];
    int send_ok_flag;
	int receive_ok_flag;
}USB_T;
//-----------------Extern function------------------------//

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__

      接下来,我们看看CH376的时序图,如下图所示

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第7张图片

     我们就根据上面的时序图编写驱动部分,在driver中建立一个usb.c文件,内容如下表所示

/*
 * ==================================================================
 *       Filename:  usb.c
 *   Description:  
 *        Version:  1.0.0
 *        Created:  2010.4.16
 *       Revision:  none
 *       Compiler:  Nios II 9.0 IDE
 *         Author:  马瑞 (AVIC)
 *			Email:  [email protected]  
 * =================================================================
 */
//-----------------Include files-------------------------//
#include "../inc/usb.h"
#include "altera_avalon_pio_regs.h"
#include "sys/alt_irq.h"
#include <unistd.h>
#include <stdio.h>
//-----------------Function Prototype--------------------//
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------------------------------//

/* 
 * ===  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_STATUS);

	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<data_len;i++)
                            usb.receive_buffer[i]=read_data_from_usb();
			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_AVALON_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<str_len;i++)write_data_to_usb(str[i]);

	return 0;
}
/* 
 * ===  FUNCTION  ===================================================
 *          Name:  initialize_usb
 *  Description:  初始化USB
 * =================================================================
 */
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_AVALON_PIO_IRQ_MASK(USB_NINT_BASE,1);
	IOWR_ALTERA_AVALON_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:  usb_usb_mode
 *  Description:  设置USB模式
 * =================================================================
 */
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:  delay
 *  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;
}

      编写好驱动以后,我们需要编写主函数测试代码

#include <stdio.h>
#include <unistd.h>
#include "../inc/usb.h"

int main()
{
    unsigned char tmp[] = "Hello USB!\n";

    initialize_usb();

    while(1){ 
        if(usb.receive_ok_flag){
            printf("%s\n",usb.receive_buffer);
            usb.receive_ok_flag = 0;
        }

        send_string_to_usb(tmp,sizeof(tmp));
        usleep(100000);
    }
    return 0;
}

      程序都写完了,但工作还没有结束,如果要想调试,我们首先还需要在你的电脑上安装CH376的驱动。

     首先,去南京沁恒的网站下载驱动,下载地址是:http://www.wch.cn/download/list.asp?id=66,CH376的驱动跟CH372,CH375是一样的。

双击CH372DRV.EXE,开始安装驱动,如下图所示,点击INSTALL,直接安装就可以了。

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第8张图片

上位机编程

      为了调试,我们还需要上位机的软件来配合,就像串口调试精灵的一个东西。这部分工作属于上位机部分的内容了。我在这里简单介绍一下吧。

南京沁恒网站提供了上位机需要的静态库函数和头文件,下载地址是:http://www.wch.cn/download/list.asp?id=28,我们可以利用他们构建自己的上位机。我使用的是NI公司的Labwindows/CVI 8.1,当然大家也可以使用VC等软件开发。

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第9张图片

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

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第10张图片

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

【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九)_第11张图片

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

你可能感兴趣的:(【连载】【FPGA黑金开发板】NIOSII那些事儿--USB设备模式(十九))