声明:本文转载于http://www.cnblogs.com/kingst,版权归黑金动力社区(http://www.heijin.org)所有。
上一节,我们讲了USB的设备模式,可以实现计算机与黑金开发板的数据通信。这一节,我们以上一节为基础,研究一下如何利用CH376芯片来实现U盘的读写。
大家都知道,不管是U盘,SD卡,还是我们经常用到的电脑硬盘,都存在文件的建立,存取,修改等操作。从系统角度来说,负责管理这些工作的我们称之为文件管理系统,简称文件系统。经常用电脑的人对这个概念一定很熟悉,比如我们在装系统的时候,不可避免的会遇到选择何种文件系统(除非你用GHOST,呵呵),FAT32还是NTFS等等,这个就是文件系统。文件系统是对文件存储器空间进行组织和分配,负责文件的存储并对存入的文件进行保护和检索的系统,具体地说,他负责为用户建立文件、存入、读出、修改、转储文件爱你,控制文件的存取,当用户不再使用时撤销文件等。
在U盘中,同样存在文件系统。大家熟知的,当我们想格式化U盘的时候,我们也需要选择文件系统类型,如下图所示
因此,如果要想通过U盘来实现开发板与电脑之间的数据交换,那么,在开发板上也也应该在FAT规范下通过文件的形式存取U盘中的数据。不过,CH376已经把这个工作为我们做好了。
CH376在芯片内部集成了FAT文件系统,支持 FAT32、FAT16和FAT12,符合WINDOWS的文件系统格式。这种无微不至的“关怀”实在令我们感动啊,因为这样我们就不需要在自己动手编写了文件系统了,为我们减少了很多的工作量,剩下的时间看看世界杯还是很不错哦,呵呵。
CH376对U盘文件的读写方式分为两种:扇区模式和字节模式。
扇区模式下,以扇区(每扇区通常是512字节)为基本单位对U盘文件进行读写,所以读写速度略快,但是通常情况下需要额外的文件数据缓冲区,额外的文件数据缓冲区必须是扇区长度512 的整数倍,所以适用于RAM多、数据量大、频繁读写数据的单片机系统。扇区读写的子程序主要有扇区读CH376SecRead 和扇区写CH376SecWrite。
字节模式下,以字节为基本单位对U盘文件进行读写,少则 1 字节,多则 65535字节,读写速度略慢,但是不需要额外的文件数据缓冲区,使用方便,适用于 RAM 少(从几字节到几十 K 都可以)、数据量小或者数据零碎、不经常读写数据的单片机系统。但是,因为闪存只能进行有限次擦写,如果频繁地向U盘写入零碎的数据,可能会缩短U 盘中闪存的使用寿命。
下面,我们就通过我们黑金开发板来实践一下,看如何使用CH376来实现U盘的读取的。
在南京沁恒的官方网站上,为我们提供了CH376相关的例程,不过都是基于单片机的,我们需要移植到NIOS II下实现。
首先,我们需要自己来写USB的底层驱动,这个跟上一节的USB驱动稍有不同。我们在driver下建立hal.c文件,如下所示
002 | * ===================================================================================== |
006 | * Description: ch376底层驱动 |
011 | * Compiler: Nios II 9.0 IDE |
014 | * Email: avic633@gmail.com |
016 | * ===================================================================================== |
018 | //-----------------Include files-------------------------// |
019 | #include "../inc/hal.h" |
020 | #include "altera_avalon_pio_regs.h" |
021 | #include "sys/alt_irq.h" |
024 | #include "../inc/ch376inc.h" |
027 | //-----------------Function Prototype--------------------// |
028 | void xWriteCH376Cmd(unsigned char command); |
029 | void xWriteCH376Data(unsigned char data); |
030 | unsigned char xReadCH376Data(void); |
031 | unsigned int mInitCH376Host(void); |
033 | int set_usb_mode(unsigned char type); |
034 | unsigned char Query376Interrupt(void); |
035 | void irq_usb(void * context,unsigned int id); |
036 | void mDelaymS(int ms); |
037 | //-----------------Variable------------------------------// |
041 | * === FUNCTION ====================================================================== |
043 | * Description: 设置工作模式 |
044 | * ===================================================================================== |
046 | int set_usb_mode(unsigned char type) |
049 | xWriteCH376Cmd(SET_USB_MODE); |
050 | xWriteCH376Data(type); |
053 | if((xReadCH376Data()) == CMD_RET_SUCCESS) |
059 | * === FUNCTION ====================================================================== |
060 | * Name: xWriteCH376Cmd |
062 | * ===================================================================================== |
064 | void xWriteCH376Cmd(unsigned char command) |
078 | * === FUNCTION ====================================================================== |
081 | * ===================================================================================== |
089 | * === FUNCTION ====================================================================== |
090 | * Name: xWriteCH376Data |
092 | * ===================================================================================== |
094 | void xWriteCH376Data(unsigned char data) |
110 | * === FUNCTION ====================================================================== |
111 | * Name: xReadCH376Data |
113 | * ===================================================================================== |
116 | unsigned char xReadCH376Data(void) |
118 | unsigned char data=0; |
133 | * === FUNCTION ====================================================================== |
134 | * Name: mInitCH376Host |
136 | * ===================================================================================== |
138 | unsigned int mInitCH376Host(void) |
143 | usb.receive_ok_flag=0; |
145 | //enable the io interrupt |
147 | IOWR_ALTERA_AVALON_PIO_IRQ_MASK(USB_NINT_BASE,0); |
148 | IOWR_ALTERA_AVALON_PIO_EDGE_CAP(USB_NINT_BASE,0); |
150 | set_usb_mode(USB_HOST); |
152 | return USB_INT_SUCCESS; |
155 | * === FUNCTION ====================================================================== |
156 | * Name: Query376Interrupt |
157 | * Description: 读取中断引脚 |
158 | * ===================================================================================== |
160 | unsigned char Query376Interrupt( void ) |
162 | return(PIO_USB_NINT?TRUE:FALSE); |
165 | * === FUNCTION ====================================================================== |
168 | * ===================================================================================== |
接下来,我们需要在inc下建立hal.h文件
02 | * ===================================================================================== |
06 | * Description: ch376底层驱动 |
11 | * Compiler: Nios II 9.0 IDE |
14 | * Email: avic633@gmail.com |
16 | * ===================================================================================== |
21 | //-----------------Include files-------------------------// |
24 | //------------------Data struct--------------------------// |
26 | //----------------- CH375 DEFINE-------------------------// |
30 | #define USB_DEVICE 0x02 |
31 | #define USB_DISABLE 0X00 |
34 | #define CHECK_EXIST 0X06 |
35 | #define SET_USB_ID 0X12 |
36 | #define SET_USB_MODE 0X15 |
37 | #define GET_STATUS 0X22 |
38 | #define UNLOCK_USB 0X23 |
39 | #define RD_USB_DATA 0X28 |
40 | #define WR_USB_DATA5 0X2A |
41 | #define WR_USB_DATA7 0X2B |
42 | #define GET_IC_VER 0X01 |
43 | #define ENTER_SLEEP 0X03 |
44 | #define CHK_SUSPEND 0X0B |
45 | #define RD_USB_DATA0 0X27 |
47 | #define RET_SUCCESS 0X51 |
50 | #define INT_EP2_OUT 0x02 |
51 | #define INT_EP2_IN 0x0a |
55 | #define DISK_RD_GO 0X55 |
57 | #define DISK_READY 0X59 |
62 | #define USB_INT_CONNECT 0x15 |
64 | //----------------------------USB HOST---------------------------// |
65 | #define CMD_RET_SUCCESS 0x51 /* 命令操作成功 */ |
66 | #define CMD_RET_ABORT 0x5F /* 命令操作失败 */ |
68 | //------------------------bus define----------------------------// |
70 | #define PIO_USB_DB *(volatile unsigned long int *)USB_DB_BASE |
71 | #define PIO_USB_WR *(volatile unsigned long int *)USB_WR_BASE |
72 | #define PIO_USB_RD *(volatile unsigned long int *)USB_RD_BASE |
73 | #define PIO_USB_A0 *(volatile unsigned long int *)USB_A0_BASE |
74 | #define PIO_USB_NINT *(volatile unsigned long int *)USB_NINT_BASE |
76 | #define PIO_USB_DB_DIR *(volatile unsigned long int *)(USB_DB_BASE+4) |
82 | char receive_buffer[200]; |
87 | //-----------------Extern function------------------------// |
90 | extern void xWriteCH376Cmd(unsigned char command); |
91 | extern void xWriteCH376Data(unsigned char data); |
92 | extern unsigned char xReadCH376Data(void); |
93 | extern unsigned int mInitCH376Host(void); |
94 | extern unsigned char Query376Interrupt(void); |
95 | extern void mDelaymS(int ms); |
上面属于底层驱动部分,如果想实现U盘的读取,还需要处理文件系统子程序file_sys.c,官方已有提供,我们将其加入到drvier下。将头文件file_sys.h放到inc下。
将上述内容都设置好以后,我们需要编写一个测试程序。官方提供了很多的例程,大家可以讲我们编写的底层驱动替换下自带的就可以 。
下面程序演示字节读写,文件枚举,用于将U盘中的/C51/CH376HFT.C文件中的前200个字符显示出来,如果找不到原文件CH376HFT.C,那么该程序将显示C51子目录下所有以CH376开头的文件名,如果找不到C51子目录,那么该程序将显示根目录下的所有文件名。
002 | * ===================================================================================== |
011 | * Compiler: Nios II 9.0 IDE |
014 | * Email: avic633@gmail.com |
016 | * ===================================================================================== |
018 | #include "../inc/ch376inc.h" |
019 | #include "../inc/hal.h" |
020 | #include "../inc/file_sys.h" |
033 | s = mInitCH376Host( ); /* 初始化CH376 */ |
036 | printf( "Wait Udisk/SD\n" ); |
038 | while (CH376DiskConnect()!=USB_INT_SUCCESS ){ |
039 | usleep( 1000*100 ); /* 没必要频繁查询 */ |
040 | printf("USB FAILURE\n"); |
043 | for ( i = 0; i < 100; i ++ ) { /* 最长等待时间,100*50mS */ |
045 | printf( "Ready ?\n" ); |
046 | s = CH376DiskMount( ); /* 初始化磁盘并测试磁盘是否就绪 */ |
047 | if ( s == USB_INT_SUCCESS ) break; /* 准备好 */ |
048 | else if ( s == ERR_DISK_DISCON ) break; /* 检测到断开,重新检测并计时 */ |
049 | if ( CH376GetDiskStatus( ) >= DEF_DISK_MOUNTED && i >= 5 ) break; /* 有的U盘总是返回未准备好,不过可以忽略,只要其建立连接MOUNTED且尝试5*50mS */ |
051 | if ( s == ERR_DISK_DISCON ) { /* 检测到断开,重新检测并计时 */ |
052 | printf( "Device gone\n" ); |
055 | if ( CH376GetDiskStatus( ) < DEF_DISK_MOUNTED ) { /* 未知USB设备,例如USB键盘、打印机等 */ |
056 | printf( "Unknown device\n" ); |
057 | goto UnknownUsbDevice; |
059 | i = CH376ReadBlock( buf ); /* 如果需要,可以读取数据块CH376_CMD_DATA.DiskMountInq,返回长度 */ |
060 | if ( i == sizeof( INQUIRY_DATA ) ) { /* U盘的厂商和产品信息 */ |
062 | printf( "UdiskInfo: %s\n", ((P_INQUIRY_DATA)buf) -> VendorIdStr ); |
067 | strcpy( buf, "\\C51\\CH376HFT.C" ); /* 源文件名,多级目录下的文件名和路径名必须复制到RAM中再处理,而根目录或者当前目录下的文件名可以在RAM或者ROM中 */ |
068 | printf("buf:%s\n",buf); |
069 | s = CH376FileOpenPath( buf ); /* 打开文件,该文件在C51子目录下 */ |
070 | if ( s == ERR_MISS_DIR || s == ERR_MISS_FILE ) { /* 没有找到目录或者没有找到文件 */ |
071 | /* 列出文件,完整枚举可以参考EXAM13全盘枚举 */ |
072 | if ( s == ERR_MISS_DIR ) strcpy( buf, "\\*" ); /* C51子目录不存在则列出根目录下的文件 */ |
073 | else strcpy( buf, "\\C51\\CH376*" ); /* CH376HFT.C文件不存在则列出\C51子目录下的以CH376开头的文件 */ |
074 | printf( "List file %s\n", buf ); |
075 | s = CH376FileOpenPath( buf ); /* 枚举多级目录下的文件或者目录,输入缓冲区必须在RAM中 */ |
076 | while ( s == USB_INT_DISK_READ ) { /* 枚举到匹配的文件 */ |
077 | CH376ReadBlock( buf ); /* 读取枚举到的文件的FAT_DIR_INFO结构,返回长度总是sizeof( FAT_DIR_INFO ) */ |
078 | pDir = (P_FAT_DIR_INFO)buf; /* 当前文件目录信息 */ |
079 | if ( pDir -> DIR_Name[0] != '.' ) { /* 不是本级或者上级目录名则继续,否则必须丢弃不处理 */ |
080 | if ( pDir -> DIR_Name[0] == 0x05 ) pDir -> DIR_Name[0] = 0xE5; /* 特殊字符替换 */ |
081 | pDir -> DIR_Attr = 0; /* 强制文件名字符串结束以便打印输出 */ |
082 | printf( "*** EnumName: %s\n", pDir -> DIR_Name ); /* 打印名称,原始8+3格式,未整理成含小数点分隔符 */ |
084 | xWriteCH376Cmd( CMD0H_FILE_ENUM_GO ); /* 继续枚举文件和目录 */ |
086 | s = Wait376Interrupt( ); |
089 | else { /* 找到文件或者出错 */ |
090 | TotalCount = 200; /* 准备读取总长度 */ |
091 | printf( "从文件中读出的前%d个字符是:\n",(UINT16)TotalCount ); |
092 | while ( TotalCount ) { /* 如果文件比较大,一次读不完,可以再调用CH376ByteRead继续读取,文件指针自动向后移动 */ |
093 | if ( TotalCount > sizeof(buf) ) i = sizeof(buf); /* 剩余数据较多,限制单次读写的长度不能超过缓冲区大小 */ |
094 | else i = TotalCount; /* 最后剩余的字节数 */ |
096 | s = CH376ByteRead( buf, i, &RealCount ); /* 以字节为单位读取数据块,单次读写的长度不能超过缓冲区大小,第二次调用时接着刚才的向后读 */ |
098 | TotalCount -= (UINT8)RealCount; /* 计数,减去当前实际已经读出的字符数 */ |
100 | for ( s=0; s!=RealCount; s++ ) printf( "%C", buf[s] ); /* 显示读出的字符 */ |
102 | if ( RealCount < i ) { /* 实际读出的字符数少于要求读出的字符数,说明已经到文件的结尾 */ |
104 | printf( "文件已经结束\n" ); |
109 | s = CH376FileClose( FALSE ); /* 关闭文件 */ |
114 | printf( "Take out\n" ); |
115 | while ( CH376DiskConnect( ) == USB_INT_SUCCESS ) { /* 检查U盘是否连接,等待U盘拔出 */ |
好了,这节内容就到此结束了。有关FAT文件系统部分内容,建议大家自己搜索相关资料,对它进一步了解,对程序的理解很有好处。谢谢大家!
阅读(1458) | 评论(0) | 转发(0) |