zoukankan      html  css  js  c++  java
  • 【连载】【FPGA黑金开发板】NIOSII那些事儿USB设备模式(十九)

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

     P6152176

    简介

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

    clip_image002

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

    硬件开发

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

    clip_image004

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

    clip_image006

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

    clip_image008

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

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

    clip_image010

          下面是中断引脚部分,由于我们是低电平中断,而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的时序图,如下图所示

    clip_image014

         我们就根据上面的时序图编写驱动部分,在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:  avic633@gmail.com  
     * =================================================================
     */
    //-----------------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,直接安装就可以了。

    clip_image016

    上位机编程

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

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

    clip_image018

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

    clip_image020

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

    clip_image022

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

  • 相关阅读:
    14 用DFT计算线性卷积
    13 DFT变换的性质
    12 有限长序列的分类
    前端常见跨域解决方案
    JS基础-构造函数、原型、原型链、继承
    浏览器中的 JavaScript 执行机制
    再也不怕面试官问我 Promise 相关问题了
    前端面试---手写代码常考题
    从输入 URL 到页面展示,这中间发生了什么?
    学习笔记-CSS-圣杯布局和双飞翼布局
  • 原文地址:https://www.cnblogs.com/kingst/p/1760827.html
Copyright © 2011-2022 走看看