我也发个在任意开发板上实现的任意介质(如CF/SD)转USB读卡器
硬件SPI方式,very easy!SD卡驱动不用愁。
**************************************** * 在EASYARM2200和SMARTARM2200上实现U盘 * **************************************** 2008/02/01 asdjf@163.com www.armecos.com 一些《ecos增值包》用户来信要求提供EASYARM2200和SMARTARM2200开发板上的U盘源码,尽管这是一项非常成熟的技术,但网络上几乎没有一份详细完整的文档,大部分范例代码基于查询方式,而且存在比较多的问题,为了方便《ecos增值包》用户深入学习USB技术,我们提供ecos平台上实现的U盘完整源码和详细文档。这份资料不同于网上下载的零散资料,而是专门针对这两款开发板量身打造的,所以使用者可以无障碍地使用它在很短时间内掌握U盘开发技术。同时由于此产品基于ecos平台,用户修改代码和增加功能也易如反掌。 简单说,制作U盘需要实现三个方面的内容:1、USB设备驱动;2、存储介质驱动;3、MASS STORAGE大容量存储协议(bulk only + SCSI-2指令集)。至于FAT文件系统,由主机负责管理,U盘设备只需正确响应相应的SCSI-2指令即可。如下图所示: USB设备 存储介质 主机 ----------- ------------------- | |----RAM | | USB | U盘 |----ROM | 管理FAT文件系统 |<------>| 或 |----NAND FLASH | | | 读卡器 |----CF卡 ------------------ | |----SD卡 ----------- <----------------> 大容量存储协议 bulk only + SCSI-2 ecos本身提供了USB设备驱动和多种大容量存储介质驱动,大大减轻了我们的编程负担,我们既可以在RAM上虚拟可读写的U盘,在ROM上实现只读的假U盘,又可以在NAND FLASH上实现真正的U盘,当然,还可以在CF卡和SD卡上实现读卡器功能。 ---------------------- | 大容量存储介质驱动 | ---------------------- ecos将所有设备抽象为设备文件,我们只要打开相关存储设备,然后读写就可以了,就是这么简单!没有必要再去折腾什么CF/SD卡驱动、ATA、SPI等底层细节,所有不同存储设备的编程界面都是一致的,大大节省了开发时间,当然,如果你对这些驱动感兴趣,《ecos增值包》的其他相关章节有详细文档和源代码说明,这里我们就直接使用设备驱动。 首先,打开存储设备文件: cyg_io_lookup("/dev/hda/", &cf); //打开CF卡设备驱动,设备文件名为/dev/hda/,句柄保存在cf变量里。 如果需要读扇区,就调用块读(bread)函数: cyg_io_bread(cf, buf, &len, pos); cf保存已打开设备文件句柄,buf为读出数据缓冲区,len指示读出扇区数(注意不是字节数),pos指示起始位置(注意以扇区为单位)。 如果需要写扇区,就调用块写(bwrite)函数: cyg_io_bwrite(cf, buf, &len, pos); cf保存已打开设备文件句柄,buf为写入数据缓冲区,len指示写入扇区数(注意不是字节数),pos指示起始位置(注意以扇区为单位)。 怎么样,很简单吧!无论什么存储设备,都是“打开---读---写”这三步,就可以读写相应扇区了,根本不用去理会底层的细枝末节,而且接口高度抽象统一,更换一种存储介质仅需要改个设备文件名即可,其余部分一个字也不用动。这也是为什么我们能在很短时间内完成多种存储介质U盘设计的原因,毕竟只需要改个名字,再多存储介质也不怕,依此类推即可。 除了操作扇区,SCSI-2指令集还需要设备提供存储器容量(总的扇区数),CHS参数等信息。ecos的设备驱动也已经提供好了相关函数ide_ident()。 对于CF卡,设备识别ECH寄存器提供了相关信息: typedef struct ide_identify_data_t_ { cyg_uint16 general_conf; // 00 : general configuration cyg_uint16 num_cylinders; // 01 : number of cylinders (default CHS trans) cyg_uint16 reserved0; // 02 : reserved cyg_uint16 num_heads; // 03 : number of heads (default CHS trans) cyg_uint16 num_ub_per_track; // 04 : number of unformatted bytes per track cyg_uint16 num_ub_per_sector; // 05 : number of unformatted bytes per sector cyg_uint16 num_sectors; // 06 : number of sectors per track (default CHS trans) cyg_uint16 num_card_sectors[2]; // 07-08 : number of sectors per card cyg_uint16 reserved1; // 09 : reserved cyg_uint16 serial[10]; // 10-19 : serial number (string) cyg_uint16 buffer_type; // 20 : buffer type (dual ported) cyg_uint16 buffer_size; // 21 : buffer size in 512 increments cyg_uint16 num_ECC_bytes; // 22 : number of ECC bytes passed on R/W Long cmds cyg_uint16 firmware_rev[4]; // 23-26 : firmware revision (string) cyg_uint16 model_num[20]; // 27-46 : model number (string) cyg_uint16 rw_mult_support; // 47 : max number of sectors on R/W multiple cmds cyg_uint16 reserved2; // 48 : reserved cyg_uint16 capabilities; // 49 : LBA, DMA, IORDY support indicator cyg_uint16 reserved3; // 50 : reserved cyg_uint16 pio_xferc_timing; // 51 : PIO data transfer cycle timing mode cyg_uint16 dma_xferc_timing; // 52 : single word DMA data transfer cycle timing mode cyg_uint16 cur_field_validity; // 53 : words 54-58 validity (0 == not valid) cyg_uint16 cur_cylinders; // 54 : number of current cylinders cyg_uint16 cur_heads; // 55 : number of current heads cyg_uint16 cur_spt; // 56 : number of current sectors per track cyg_uint16 cur_capacity[2]; // 57-58 : current capacity in sectors cyg_uint16 mult_sectors; // 59 : multiple sector setting cyg_uint16 lba_total_sectors[2]; // 60-61 : total sectors in LBA mode cyg_uint16 sw_dma; // 62 : single word DMA support cyg_uint16 mw_dma; // 63 : multi word DMA support cyg_uint16 apio_modes; // 64 : advanced PIO transfer mode supported cyg_uint16 min_dma_timing; // 65 : minimum multiword DMA transfer cycle cyg_uint16 rec_dma_timing; // 66 : recommended multiword DMA cycle cyg_uint16 min_pio_timing; // 67 : min PIO transfer time without flow control cyg_uint16 min_pio_iordy_timing; // 68 : min PIO transfer time with IORDY flow control // cyg_uint16 reserved4[187]; // 69-255: reserved } ide_identify_data_t;
其中cylinders_num, heads_num, sectors_num是我们需要的CHS参数,lba_sectors_num是总容量。只要调用ide_ident()函数,就可以在传回的这个结构体变量里获得相关信息。 SD卡与此类似,从CSD和盘信息区里获得相关参数,详见源代码,此处不再赘述。 综上,我们实现了任意扇区读写,获得了CHS参数和总容量,OK!做U盘读卡器的信息量已经足够了。下面完成USB设备驱动和SCSI-2协议。 --------------- | USB设备驱动 | --------------- 此处说的USB设备驱动特指D12 driver,注意EASYARM2200和SMARTARM2200上都要使用D12 PACK小板,《ecos增值包》里已经提供了D12的USB设备驱动,用户需要做的只是提供枚举数据,很简单吧! ecos下的D12驱动写得特别专业,不得不佩服ecos社区的专家,他们一直站在最前沿,不断跟踪技术发展,不象我们半路出家,很多事情也许根本就没有意识到,写出的程序,通用性和扩展性跟ecos社区的专家没法比。一般我们用的D12程序都是基于前后台,中断加死循环那种模式的,而ecos充分发挥了OS的特性,一下子提供三种运行模式:1、ISR+DSR模式;2、线程模式;3、轮询POLL模式。ISR+DSR模式能大大减少中断延迟,提高性能指标,我们原来在中断ISR内做所有事情的模式肯定不如在DSR中做大部分工作的中断性能好。前后台采用信号量方式进行同步不会白白浪费CPU时间片。依靠应用程序提供缓冲区而不是采用全局变量缓冲区能大大提高灵活性避免内存拷贝。应用程序不直接调用驱动,而是采用申请功能,完成函数唤醒的方式使用USB设备驱动,思路清晰,能实现流水线作业,减少空等时间。总之,ecos的D12驱动非常有特色,值得学习借鉴,具体使用方法在相关文档里有详细介绍,见《ecos增值包》之USB章节。 D12驱动有一些需要特别注意的问题:1、时序问题,因为ARM芯片速度可能比D12读写速度快,所以有必要在某些位置加延时,否则可能读写错数据,某些网上下载的程序使用比较慢的CPU如51,不会遇到此问题;2、D12的主端点采用双缓冲区64*2字节,注意要等待一段时间等双缓冲区填满后再访问,否则可能出现多读64字节或少读64字节的现象。有些网上下载的程序没有采用中断方式,不会遇到此问题;3、结构体定义要加“__attribute__((packed))”属性,否则得到的结构体不是紧凑的,可能为字节对齐加入了多余字节。 下面让我们用实际程序来解释一下如何写ecos下的USB设备驱动。 ecos采用一种非常灵活的方式组织枚举数据,一个枚举数据结构体包含了接口、端点、字符串的总数量,设备、配置、接口、端点、字符串的结构体信息。一个USB设备只有一个设备描述符,可能有多个配置描述符,所以,把接口、端点、字符串描述符存放在数组里,用指针指示,同时分别记录接口、端点、字符串描述符的个数。配置描述符也存放在数组里,不过不用记录个数,由应用程序负责解释。 usb_configuration_descriptor usb_configuration = { length: USB_CONFIGURATION_DESCRIPTOR_LENGTH, type: USB_CONFIGURATION_DESCRIPTOR_TYPE, total_length_lo: 0x2E, total_length_hi: 0, number_interfaces: 1, configuration_id: 1, // id 0 is special according to the spec configuration_str: 0, attributes: 0x60, max_power: 0x32 };
usb_interface_descriptor usb_interface = { length: USB_INTERFACE_DESCRIPTOR_LENGTH, type: USB_INTERFACE_DESCRIPTOR_TYPE, interface_id: 0, alternate_setting: 0, number_endpoints: 4, interface_class: 0x08, interface_subclass: 0x06, interface_protocol: 0x50, interface_str: 0 };
usb_endpoint_descriptor usb_endpoints[USBTEST_MAX_ENDPOINTS] = { { 0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x0a }, { 0x07, 0x05, 0x01, 0x03, 0x10, 0x00, 0x0a }, { 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00 }, { 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x0a }, }; const unsigned char* usb_strings[] = { " |
|