打印

在21IC DIY U盘开发板上实现的SD转USB读卡器(硬件SPI方式)

[复制链接]
9489|30
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
computer00|  楼主 | 2009-4-9 00:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
单击此处下载本实验包:https://bbs.21ic.com/upfiles/img/20094/2009411142312398.rar

本程序使用AT91SAM7S64自带的SPI接口读写SD卡,SPI时钟设置为24MHz。
写入速度大约为313KB/s,读出速度大约为272KB/s。
程序中没有开启DMA传输,如果开启DMA传输,并使用两个缓冲区轮流操作的话,速度应该会有所提高。

另外圈圈想发一下牢骚的是,那个官方的SD卡协议真是TMD又长又臭啊,长一点倒算了,又没逻辑性,
写得比较混乱,该清楚的地方交代得不够清楚,不该说的地方又是一堆罗嗦,小日本的文档就是这样的?
还好,网上有一些稍微简洁的介绍,让人有一个全局的概念。另外它的命令命名也真是奇怪,居然
直接用编号来命名的,我晕,这样鬼记得这个命令是干啥用的……为了简单起见,这个程序并没有
启用CRC校验,也没有对写入出错的处理,希望有空的朋友继续完善它吧。

图为圈圈使用的32MB SD卡的实验测试结果:

相关帖子

沙发
armecos| | 2009-4-9 01:57 | 只看该作者

我也发个在任意开发板上实现的任意介质(如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[] = {
    "04031104",  //4 3 9 4
    "120332003000370031003000390038003200"
    "2003R00e00d00 00H00a00t00",
    "5403R00e00d00 00H00a00t00 00e00C00o00s00 00"
    "U00S00B00 00t00e00s00t00"
};

usbs_enumeration_data usb_enum_data = {
    {
        length:                 USB_DEVICE_DESCRIPTOR_LENGTH,
        type:                   USB_DEVICE_DESCRIPTOR_TYPE,
        usb_spec_lo:            USB_DEVICE_DESCRIPTOR_USB11_LO,
        usb_spec_hi:            USB_DEVICE_DESCRIPTOR_USB11_HI,
        device_class:           0,
        device_subclass:        0,
        device_protocol:        0,
        max_packet_size:        16,
        vendor_lo:              0x71,   // Note: this is not an allocated vendor id
        vendor_hi:              0x04,
        product_lo:             0xf0,
        product_hi:             0xff,
        device_lo:              0x01,
        device_hi:              0x00,
        manufacturer_str:       0,
        product_str:            0,
        serial_number_str:      2,
        number_configurations:  1
    },
    total_number_interfaces:    1,
    total_number_endpoints:     4,
    total_number_strings:       3,
    configurations:             &usb_configuration,
    interfaces:                 &usb_interface,
    endpoints:                  usb_endpoints,
    strings:                    usb_strings
};

    ......
    
    control_endpoint->enumeration_data      = &usb_enum_data;
    usbs_start(control_endpoint);
    
    如上所示,先准备出配置、接口、端点、字符串的结构体信息,再按上面写法准备出枚举数据(内含设备信息),各字段含义请读者自行对照协议解释。在主程序里填好枚举数据结构体地址,然后调用usbs_start()函数启动USB设备驱动即可。复杂的D12驱动细节完全由ecos代劳了,只需要用户准备好枚举数据,实现ecos下的USB设备驱动就是如此地简单明了!用户完全没有必要做那些重复工作,把精力集中到有价值的事情上。
    
    ------------------------------
    | bulk only + SCSI-2协议实现 |
    ------------------------------
    完成上面步骤后,U盘还不能被正确枚举,因为bulk only中有两种类特定请求命令需要响应:1、mass storage复位;2、获取最大逻辑单元号。
    
    这可难不倒ecos的USB设备驱动。大家知道,设备请求类型分为四种:1、标准;2、类;3、厂商;4、其他。ecos里巧妙地实现了这四种类型的驱动。首先ecos提供标准请求的通用缺省处理,一般不用改动,因为所有USB设备都是应该这样处理的。当然如果用户有特殊要求,也可以替换掉ecos本身提供的缺省处理函数。对于其他三种类型,ecos自然不知道该如何处理,但它提供了通用程序框架,用户只需提供相应请求类型的处理回调函数和数据即可。
    
usbs_control_return yy_class_ctl_fn(struct usbs_control_endpoint* endp, void* data)
{
    usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
    usb_devreq*         req    = (usb_devreq*) endp->control_buffer;
    int                 length;
    int                 direction;
    int                 recipient;
    int                 i;
    unsigned char*      ch;

    length      = (req->length_hi << 8) | req->length_lo;
    direction   = req->type & USB_DEVREQ_DIRECTION_MASK;
    recipient   = req->type & USB_DEVREQ_RECIPIENT_MASK;
    
    //在DSR里不能使用printf函数,否则死机。
    
    //GET MAX LUN
    if((req->type == 0xA1) && (req->request == 0xFE)){
          endp->control_buffer[0] = 0;
        endp->buffer            = endp->control_buffer;
        endp->buffer_size       = 1;
        endp->fill_buffer_fn    = (void (*)(usbs_control_endpoint*)) 0;
        endp->complete_fn       = (usbs_control_return (*)(usbs_control_endpoint*, cyg_bool)) 0;
        result = USBS_CONTROL_RETURN_HANDLED;
    }
    //Mass Storage Reset
    if((req->type == 0x21) && (req->request == 0xFF)){
        result = USBS_CONTROL_RETURN_HANDLED;
    }
    
    return result;
}

    ......

    ep0->class_control_fn = yy_class_ctl_fn;
    ep0->class_control_data = 0;
    
    如上所示,在类请求回调函数里实现了GET MAX LUN和Mass Storage Reset两个特定类请求命令。GET MAX LUN回应0表示设备上只有一个逻辑单元。特别注意在这个回调函数里不能使用printf函数,因为此回调函数要在DSR上下文中运行,不能使用printf函数。
    
    接下来要实现SCSI-2指令集,其实也没什么难度,就是回应请求罢了,都是些按部就班的重复劳动,可以用bus hound抓一个U盘的响应过程,然后照着实现即可。具体过程最好看源码,一目了然。这里大致介绍一下程序结构:
    
    usbs_start_rx_buffer(endpoint_rx, buf[buf_flag], 512, &test_callback, (void*) &test);
    
    if(cbw->dCBWSignature!=0x43425355){
          diag_printf("CBW error! ");
          Return_CSW(0x00,FAIL,endpoint_tx);
          continue;
    }
    if(cbw->bmCBWFlags&0x80){//IN
        switch(cbw->CBWCB[0]){
            case               Read_10 : ......
            case               Inquiry : ......
            case         Read_Capacity : ......
            case  Read_Format_capacity : ......
        case         Request_Sense : ......
        case        Medium_Removal : ......
        case            Mode_Sense : ......
        default                    : ......
      }
    }
    else{//OUT
        switch(cbw->CBWCB[0]){
            case              Write_10 : ......
            case       Test_Unit_Ready : ......
            case                Verify : ......
            default                    : ......
        }
    }
    
    由上可知,先接收有效CBW命令,然后区分传输方向,根据命令选取不同处理分支,处理结束发送CSW。其中完成各个命令的处理就实现了SCSI-2指令集。下面举例说明Read_10的实现:
    
        case               Read_10 : 
                                           //pos为起始块号,len为块数
                                           pos = 0;
                                           for(i = 0; i < 4; i++){
                                                  pos <<= 8;
                                                  pos += cbw->CBWCB[2+i];
                                           }
                                           len = 0;
                                           len += cbw->CBWCB[7];
                                           len <<= 8;
                                           len += cbw->CBWCB[8];
                                           
                                           tlen = 1;
                                           for(i = 0; i < len; i++){//读出CF卡的某个扇区并发送出去,最后发送CSW。
                                                  cyg_io_bread(cf, &secbuf, &tlen, (pos + i));
                                               usbs_start_tx_buffer(endpoint_tx, (unsigned char *)(secbuf), 512, &test_callback, (void*) &test);
                                               cyg_semaphore_wait(&(test.sem));
                                           }
                                           Return_CSW(0x00,SUCCESS,endpoint_tx);
                                           //printf("Read_10 ");
                                           break;

    其他命令处理与此类似,详见源程序。
    
    注意:Inquiry命令的响应数据必须正确才能被允许格式化,有些网上代码的数据信息是不正确的,如果拿来就用可能导致无法格式化。如果没有把设备标明为可移动设备,主机就不会不断探测设备是否在位。好多不正常现象其实不是程序的错,而是数据配置错,犯这种错误实在太冤枉了。不过本程序是经过实际测试成功的,拿来就可以使用,改动也很容易,配置数据确保没有问题,请放心使用!本程序加上注释才700多行,如此短的程序就能完整实现U盘格式化、读写等功能,思路特别清晰,这说明《ecos增值包》确实是一个非常有效的快速验证平台,用它来学习也是不错的选择。《ecos增值包》用户可以得到相关源码和文档,所有人都可以免费下载演示版本以及此文档。把你的EASYARM2200和SMARTARM2200开发板改造成U盘/读卡器吧,这样又多了一个实用工具。
    
    另外,如果对读U盘的主机感兴趣,请看“《ecos增值包》之USB HOST驱动篇”,里面详细介绍了USB HOST协议栈框架,是一个通用的USB主机实现,不光U盘,HID键盘鼠标,HUB驱动等都包括了,还可以方便地增加新的协议驱动。

使用特权

评论回复
板凳
armecos| | 2009-4-9 01:57 | 只看该作者

SPI驱动程序设计

*******************
* SPI驱动程序设计 *
*******************
         2007/09/17  asdjf@163.com  www.armecos.com

    《ecos增值包》设备驱动层DEV提供了SMARTARM2200的SPI驱动,这个驱动抽象出了SPI的本质特征,其接口函数可以通用在各种带SPI的硬件平台上。此处,我们使用这个SPI驱动访问SD卡,减少了工作量,增加了移植性。下面让我们从总体上来了解这个通用SPI设备驱动的设计理念。
    
    设计目标:灵活设定工作模式(查询/中断),广泛适应各种硬件平台和SPI变种,高效率的万能SPI驱动程序。
    
    SPI(串行外围设备接口)属于一系列串行总线技术之一。它能够将处理器和一个或多个外围设备连接起来。例如:AD转换器或者RTC实时钟,只需要很少的引脚和PCB走线。这项技术最初是由Motorola提供的,现在也获得了其他供应商的支持。
    
    一个典型的SPI系统如图所示:图1
    
    在数据传输开始,主机有效其中一个片选信号然后产生时钟信号。在每个时钟周期,主机在“主出从入”线输出一比特,同时在“主入从出”线输入一比特。每个设备都连接到一个时钟线、2个数据线和它们自己的片选线。如果片选无效,那么设备将忽略任何到达的数据并使输出保持三态。反之,在每一个时钟周期输入和输出一比特。
    
    主机和设备之间利用SPI总线可以在同一时间交换大量数据。一些设备是单向的,例如:LED设备只能接收主机发来的数据,但不能反馈任何有意义的信息。此时,主机仍然会继续接收输入数据,然后抛弃。
    
    SPI一个有用的特点是没有从设备到主机的流控。如果主机试图和一个当前不存在的设备通信,如:MMC没有插在插槽上,那么也可以照常运行,只不过读到的是随机数据罢了。典型地,软件级别的CRC校验或者类似技术可以使主机察觉出此类错误。
    
    SPI操作没有完全标准化,一些设备间的差异如下:
    1、许多设备的传输单位是8bit字节,也有些设备使用16bit;
    2、片选可能是高有效或者低有效。如果混合使用不同片选有效极性的设备,那么将会使事情变得复杂;
    3、时钟支持各种速率,从128K到20MHz或者更高。某些设备需要先用低速通信,然后获得时钟信息,再选择一个更高速率用于后续传输;
    4、数据传输之间,时钟无效。无效的时钟极性可能为高,也可能为低;
    5、数据可以在上升沿或者下降沿采样;
    6、一个设备可能需要额外的延时,例如:在片选有效和第一个时钟周期之间;
    7、某些设备包括若干个复杂的传输阶段,要求片选一直有效,这样就不能在传输间隙和其他设备进行快速通信了;
    8、一些设备,如MMC卡,在传输结束,片选无效的情况下仍然需要时钟,以便完成设备内部的某些操作。
    
    主机内部的时钟和数据信号通常使用专用硬件实现。替代的方案可以是bit-banging,那是其他串行总线技术经常使用的,如I2C。片选也可以使用专用硬件实现,但通常是由GPIO代劳。
    
    一个SPI传输包括以下阶段:
    1、在线程级别上锁定总线,确保同一时间只有一个线程在访问SPI总线。
    2、有效设备片选,收发数据。可能是一次传输,也可能是一系列传输。在一系列传输期间,片选保持有效不是必须的。
    3、可选的,在片选无效后继续产生一些时钟,用于某些设备完成操作。
    4、解除总线锁定,允许其他线程在连接到这个总线上的设备上执行SPI操作。
    
    举例:
    
    #include <cyg/io/spi.h>
    #include <cyg/hal/hal_io.h>    // Defines the SPI devices
    ...
    //SPI传输开始:在线程级别上锁定总线
    cyg_spi_transaction_begin(&hal_spi_eprom);
    //基于中断模式的传输,发送4字节命令
    cyg_spi_transaction_transfer(&hal_spi_eprom, 0, 4, command, NULL, 0);
    //读回状态字节
    cyg_spi_transaction_transfer(&hal_spi_eprom, 0, 1, NULL, status, 0);
    if (!status[0]) {
        //命令响应失败,产生额外的时钟
        cyg_spi_transaction_tick(&hal_spi_eprom, 0, 1);
    } else {
        //传输数据,读回最终状态。然后无效片选
        cyg_spi_transaction_transfer(&hal_spi_eprom, 0, n, data, NULL, 0);
        cyg_spi_transaction_transfer(&hal_spi_eprom, 0, 2, NULL, status, 1);
        //检测最终状态码的代码放这里
    }
    //传输完成
    cyg_spi_transaction_end(&hal_spi_eprom);
    
    
    函数原型:
    
//SPI数据传输,支持查询/中断模式设定,在发数据同时接收数据,如果接收缓冲为NULL,则直接抛弃接收到的数据。支持一次发送多个数据。
void cyg_spi_transfer(cyg_spi_device* device, cyg_bool polled, cyg_uint32 count, const cyg_uint8* tx_data, cyg_uint8* rx_data);

//SPI时钟发送,片选无效。
void cyg_spi_tick(cyg_spi_device* device, cyg_bool polled, cyg_uint32 count);

//获得当前SPI时钟配置值
int cyg_spi_get_config(cyg_spi_device* device, cyg_uint32 key, void* buf, cyg_uint32* len);

//设置当前SPI时钟配置值,并备份旧值。
int cyg_spi_set_config(cyg_spi_device* device, cyg_uint32 key, const void* buf, cyg_uint32* len);

//SPI传输开始。
void cyg_spi_transaction_begin(cyg_spi_device* device);

//非阻塞SPI传输开始。
cyg_bool cyg_spi_transaction_begin_nb(cyg_spi_device* device);

//SPI数据传输,支持查询/中断模式设定,在发数据同时接收数据,如果接收缓冲为NULL,则直接抛弃接收到的数据。支持一次发送多个数据。支持传输完成后的片选设置。
void cyg_spi_transaction_transfer(cyg_spi_device* device, cyg_bool polled, cyg_uint32 count, const cyg_uint8* tx_data, cyg_uint8* rx_data, cyg_bool drop_cs);

//SPI时钟发送。
void cyg_spi_transaction_tick(cyg_spi_device* device, cyg_bool polled, cyg_uint32 count);

//SPI传输结束。
void cyg_spi_transaction_end(cyg_spi_device* device);

    说明:
    以上这些函数抽象了SPI传输的本质,涉及到时钟速率、设置片选、传输数据、传输过程、多线程临界控制、工作模式设定、多总线驱动等内容,完整地概括了SPI的操作内容,适应性非常强,可以用在各种硬件平台上并保持接口函数不变。
    SPI传输可以基于查询模式或者中断模式。一般在系统初始化阶段和单线程(如:redboot)里使用查询模式,多线程应用最好使用中断模式,因为这样能更有效地使用CPU,避免循环等待传输结束浪费时间。
    
    cyg_spi_device* device    设备标识,用于区分多个SPI总线设备。
    cyg_bool polled           模式选择:查询模式,中断模式
    cyg_uint32 count          传输数据数目
    const cyg_uint8* tx_data  待发送的数据缓冲区
    cyg_uint8* rx_data        接收数据缓冲区
    cyg_bool drop_cs          是否在结束时无效片选

使用特权

评论回复
地板
armecos| | 2009-4-9 02:04 | 只看该作者

SD卡驱动测试

********************
* SD卡驱动程序设计 *
********************
                 ------《ecos增值包》之SD卡驱动
    2006/09/17   asdjf@163.com  www.armecos.com

------------
SD卡驱动测试
------------

SmartARM2200平台

一张512M的SD卡,格式化成FAT32,创建两个文本文件:
a.txt
    1234567890
b.txt
    abcdefghijklmn
用winhex软件分析,FAT1位于32扇区,a.txt文件位于1980扇区,b.txt文件位于1988扇区,当然启动部分就固定在0扇区。
下面是用SD卡驱动读出的识别信息CID、CSD、0扇区数据、32扇区数据、1980扇区数据、1988扇区数据,和winhex的数据对比,完全一致。

SD卡先关电源再打开,初始化硬件,检测卡是否插入,接着发送复位命令、激活初始化处理命令、读取并解析CID、读取并解析CSD、设置SPI时钟为最大值、设置读写块长度。(其他如OCR、SCR、CRC校验等省略。)每个命令都给出了命令、参数、应答信息,同时给出编码封装后的字节序。每个命令都超时等待应答成功,有写命令的应答带有返回数据。

SD卡测试程序分析了MBR信息,准确读出了几个特定扇区的数据。接着向指定的100扇区强制写入固定数据,再读出来,结果数据一致。在完成写入命令后,还需要等待数据写入完成,写入扇区需要相对比较长的时间,这里使用忙等待模式(更快的可以使用异步模式,完成写命令后先处理其他任务,不必一直等待写数据完成)。

经测试,寄存器读、数据读写均正确完成。

test by yangyi
2007/09/17

+


        *******************************
        *        SD Card Test         *
        *******************************

Power down!
Power up!

Card insert

Request 40 00 00 00 00 95 ff
Sent command cmd = 00 arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 01
Request 41 00 00 00 00 ff ff
Sent command cmd = 01 arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 01
Request 41 00 00 00 00 ff ff
Sent command cmd = 01 arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 01
Request 41 00 00 00 00 ff ff
Sent command cmd = 01 arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 01
Request 41 00 00 00 00 ff ff
Sent command cmd = 01 arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 01
Request 41 00 00 00 00 ff ff
Sent command cmd = 01 arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
Request 4a 00 00 00 00 ff ff
Sent command cmd = 0a arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
mmc_spi_read_data(): got data and CRC 4a 36


CID data: 27 50 48 53 44 35 31 32 11 21 f0 56 01 00 68 ab
CID data: register
        : Manufacturer ID       : MID = 0x27
        : OEM/Application ID    : OID = 0x5048
        : Product name          : PNM = SD512
        : Product revision      : PRV = hwrev(0x01)  fwrev(0x01)
        : Product serial number : PSN = 0x21f05601
        : Manufacturing date    : MDT = 2006-08


Request 49 00 00 00 00 ff ff
Sent command cmd = 09 arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
mmc_spi_read_data(): got data and CRC 53 78


CSD data: 00 4f 00 32 5f 59 83 ca f6 db 7f 87 8a 40 00 2d
CID data: register
        : Data read time parameter1 : TAAC                 = 40000000ns
        : Data read time parameter2 : NSAC                 = 0
        : Max data transfer rate    : TRAN_SPEED           = 25000000Hz
        : Card command class        : CCC                  = 5f5
        : Card capacity             : C_SIZE、C_SIZE_MULT  = 994304 Blocks (485MB)
        : Max read data length      : READ_BL_LEN          = 9
        : Read block partial enable : READ_BL_PARTIAL      = 1
        : Write block misalign      : WRITE_BLK_MISALIGN   = 0
        : Read block misalign       : READ_BLK_MISALIGN    = 0
        : Write speed factor        : R2W_FACTOR           =  1
        : Max write data length     : WRITE_BL_LEN         = 9
        : Write block partial enable: WRITE_BL_PARTIAL     = 0


mmc_spi_read_disk_block(0): sending command
Request 51 00 00 00 00 ff ff
Sent command cmd = 11 arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
mmc_spi_read_disk_block(0): reading data token/data/crc
mmc_spi_read_data(): got data and CRC 96 bc


MBR dump
 0000: eb 58 90 4d  53 44 4f 53  35 2e 30 00  02 08 20 00
 0010: 02 00 00 00  00 f8 00 00  3f 00 ff 00  00 00 00 00
 0020: 00 2c 0f 00  ca 03 00 00  00 00 00 00  02 00 00 00
 0030: 01 00 06 00  00 00 00 00  00 00 00 00  00 00 00 00
 0040: 00 00 29 cb  90 87 a8 4e  4f 20 4e 41  4d 45 20 20
 0050: 20 20 46 41  54 33 32 20  20 20 33 c9  8e d1 bc f4
 0060: 7b 8e c1 8e  d9 bd 00 7c  88 4e 02 8a  56 40 b4 08
 0070: cd 13 73 05  b9 ff ff 8a  f1 66 0f b6  c6 40 66 0f
 0080: b6 d1 80 e2  3f f7 e2 86  cd c0 ed 06  41 66 0f b7
 0090: c9 66 f7 e1  66 89 46 f8  83 7e 16 00  75 38 83 7e
 00a0: 2a 00 77 32  66 8b 46 1c  66 83 c0 0c  bb 00 80 b9
 00b0: 01 00 e8 2b  00 e9 48 03  a0 fa 7d b4  7d 8b f0 ac
 00c0: 84 c0 74 17  3c ff 74 09  b4 0e bb 07  00 cd 10 eb
 00d0: ee a0 fb 7d  eb e5 a0 f9  7d eb e0 98  cd 16 cd 19
 00e0: 66 60 66 3b  46 f8 0f 82  4a 00 66 6a  00 66 50 06
 00f0: 53 66 68 10  00 01 00 80  7e 02 00 0f  85 20 00 b4
 0100: 41 bb aa 55  8a 56 40 cd  13 0f 82 1c  00 81 fb 55
 0110: aa 0f 85 14  00 f6 c1 01  0f 84 0d 00  fe 46 02 b4
 0120: 42 8a 56 40  8b f4 cd 13  b0 f9 66 58  66 58 66 58
 0130: 66 58 eb 2a  66 33 d2 66  0f b7 4e 18  66 f7 f1 fe
 0140: c2 8a ca 66  8b d0 66 c1  ea 10 f7 76  1a 86 d6 8a
 0150: 56 40 8a e8  c0 e4 06 0a  cc b8 01 02  cd 13 66 61
 0160: 0f 82 54 ff  81 c3 00 02  66 40 49 0f  85 71 ff c3
 0170: 4e 54 4c 44  52 20 20 20  20 20 20 00  00 00 00 00
 0180: 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
 0190: 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
 01a0: 00 00 00 00  00 00 00 00  00 00 00 00  0d 0a 4e 54
 01b0: 4c 44 52 20  69 73 20 6d  69 73 73 69  6e 67 ff 0d
 01c0: 0a 44 69 73  6b 20 65 72  72 6f 72 ff  0d 0a 50 72
 01d0: 65 73 73 20  61 6e 79 20  6b 65 79 20  74 6f 20 72
 01e0: 65 73 74 61  72 74 0d 0a  00 00 00 00  00 00 00 00
 01f0: 00 00 00 00  00 00 00 00  00 ac bf cc  00 00 55 aa
Read block 0 (partition table)
Signature 0x55 0xaa, should be 0x55 0xaa
Partition 0: boot ff, first sector 0d 0a 44, file system 69, last sector 73 6b 20
           : first sector (linear) 6f 72 72 65, sector count 0a 0d ff 72
Partition 1: boot 50, first sector 72 65 73, file system 73, last sector 20 61 6e
           : first sector (linear) 65 6b 20 79, sector count 6f 74 20 79
Partition 2: boot 20, first sector 72 65 73, file system 74, last sector 61 72 74
           : first sector (linear) 00 00 0a 0d, sector count 00 00 00 00
Partition 3: boot 00, first sector 00 00 00, file system 00, last sector 00 00 00
           : first sector (linear) ac 00 00 00, sector count 00 00 cc bf


mmc_spi_read_disk_block(0): sending command
Request 51 00 00 00 00 ff ff
Sent command cmd = 11 arg = 0: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
mmc_spi_read_disk_block(0): reading data token/data/crc
mmc_spi_read_data(): got data and CRC 96 bc


Sector info(0 sec : boot):
----------------------------------------------------------------

       0  eb 58 90 4d 53 44 4f 53 35 2e 30  0  2  8 20  0 
      10   2  0  0  0  0 f8  0  0 3f  0 ff  0  0  0  0  0 
      20   0 2c  f  0 ca  3  0  0  0  0  0  0  2  0  0  0 
      30   1  0  6  0  0  0  0  0  0  0  0  0  0  0  0  0 
      40   0  0 29 cb 90 87 a8 4e 4f 20 4e 41 4d 45 20 20 
      50  20 20 46 41 54 33 32 20 20 20 33 c9 8e d1 bc f4 
      60  7b 8e c1 8e d9 bd  0 7c 88 4e  2 8a 56 40 b4  8 
      70  cd 13 73  5 b9 ff ff 8a f1 66  f b6 c6 40 66  f 
      80  b6 d1 80 e2 3f f7 e2 86 cd c0 ed  6 41 66  f b7 
      90  c9 66 f7 e1 66 89 46 f8 83 7e 16  0 75 38 83 7e 
      a0  2a  0 77 32 66 8b 46 1c 66 83 c0  c bb  0 80 b9 
      b0   1  0 e8 2b  0 e9 48  3 a0 fa 7d b4 7d 8b f0 ac 
      c0  84 c0 74 17 3c ff 74  9 b4  e bb  7  0 cd 10 eb 
      d0  ee a0 fb 7d eb e5 a0 f9 7d eb e0 98 cd 16 cd 19 
      e0  66 60 66 3b 46 f8  f 82 4a  0 66 6a  0 66 50  6 
      f0  53 66 68 10  0  1  0 80 7e  2  0  f 85 20  0 b4 
     100  41 bb aa 55 8a 56 40 cd 13  f 82 1c  0 81 fb 55 
     110  aa  f 85 14  0 f6 c1  1  f 84  d  0 fe 46  2 b4 
     120  42 8a 56 40 8b f4 cd 13 b0 f9 66 58 66 58 66 58 
     130  66 58 eb 2a 66 33 d2 66  f b7 4e 18 66 f7 f1 fe 
     140  c2 8a ca 66 8b d0 66 c1 ea 10 f7 76 1a 86 d6 8a 
     150  56 40 8a e8 c0 e4  6  a cc b8  1  2 cd 13 66 61 
     160   f 82 54 ff 81 c3  0  2 66 40 49  f 85 71 ff c3 
     170  4e 54 4c 44 52 20 20 20 20 20 20  0  0  0  0  0 
     180   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     190   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1a0   0  0  0  0  0  0  0  0  0  0  0  0  d  a 4e 54 
     1b0  4c 44 52 20 69 73 20 6d 69 73 73 69 6e 67 ff  d 
     1c0   a 44 69 73 6b 20 65 72 72 6f 72 ff  d  a 50 72 
     1d0  65 73 73 20 61 6e 79 20 6b 65 79 20 74 6f 20 72 
     1e0  65 73 74 61 72 74  d  a  0  0  0  0  0  0  0  0 
     1f0   0  0  0  0  0  0  0  0  0 ac bf cc  0  0 55 aa 

----------------------------------------------------------------

mmc_spi_read_disk_block(32): sending command
Request 51 00 00 40 00 ff ff
Sent command cmd = 11 arg = 16384: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
mmc_spi_read_disk_block(32): reading data token/data/crc
mmc_spi_read_data(): got data and CRC 76 3a


Sector info(32 sec : root dir):
----------------------------------------------------------------

       0  f8 ff ff ff ff ff ff ff ff ff ff  f ff ff ff  f 
      10  ff ff ff  f  0  0  0  0  0  0  0  0  0  0  0  0 
      20   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      30   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      40   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      50   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      60   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      70   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      80   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      90   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      a0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      b0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      c0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      d0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      e0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      f0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     100   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     110   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     120   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     130   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     140   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     150   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     160   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     170   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     180   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     190   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1a0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1b0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1c0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1d0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1e0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1f0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 

----------------------------------------------------------------

mmc_spi_read_disk_block(1980): sending command
Request 51 00 0f 78 00 ff ff
Sent command cmd = 11 arg = 1013760: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
mmc_spi_read_disk_block(1980): reading data token/data/crc
mmc_spi_read_data(): got data and CRC 9a 31


Sector info(1980 sec : a.txt):
----------------------------------------------------------------

       0  31 32 33 34 35 36 37 38 39 30  0  0  0  0  0  0 
      10   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      20   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      30   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      40   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      50   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      60   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      70   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      80   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      90   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      a0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      b0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      c0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      d0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      e0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      f0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     100   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     110   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     120   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     130   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     140   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     150   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     160   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     170   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     180   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     190   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1a0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1b0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1c0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1d0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1e0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1f0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 

----------------------------------------------------------------

mmc_spi_read_disk_block(1988): sending command
Request 51 00 0f 88 00 ff ff
Sent command cmd = 11 arg = 1017856: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
mmc_spi_read_disk_block(1988): reading data token/data/crc
mmc_spi_read_data(): got data and CRC 67 a2


Sector info(1988 sec : b.txt):
----------------------------------------------------------------

       0  61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e  0  0 
      10   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      20   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      30   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      40   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      50   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      60   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      70   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      80   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      90   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      a0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      b0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      c0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      d0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      e0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
      f0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     100   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     110   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     120   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     130   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     140   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     150   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     160   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     170   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     180   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     190   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1a0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1b0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1c0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1d0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1e0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1f0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 

----------------------------------------------------------------



Write test
mmc_spi_write_disk_block(), sending command
Request 58 00 00 c8 00 ff ff
Sent command cmd = 18 arg = 51200: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
mmc_spi_write_disk_block(): sending data token/data/crc
mmc_spi_write_disk_block(): got data response token e5
mmc_spi_write_disk_block(), polling for ! busy, got response 00
mmc_spi_write_disk_block(), polling for ! busy, got response 00
mmc_spi_write_disk_block(), polling for ! busy, got response 00
mmc_spi_write_disk_block(), polling for ! busy, got response 00
mmc_spi_write_disk_block(), polling for ! busy, got response 00
mmc_spi_write_disk_block(), polling for ! busy, got response 00
mmc_spi_write_disk_block(), polling for ! busy, got response 00
mmc_spi_write_disk_block(), polling for ! busy, got response 00
mmc_spi_write_disk_block(), polling for ! busy, got response 01
mmc_spi_read_disk_block(100): sending command
Request 51 00 00 c8 00 ff ff
Sent command cmd = 11 arg = 51200: reply bytes ff ff ff ff ff ff ff
  loop 0, additional reply 00
mmc_spi_read_disk_block(100): reading data token/data/crc
mmc_spi_read_data(): got data and CRC 40 da


Sector info(100 sec : test):
----------------------------------------------------------------

       0   0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 
      10  10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 
      20  20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 
      30  30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 
      40  40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 
      50  50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 
      60  60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 
      70  70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 
      80  80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 
      90  90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f 
      a0  a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af 
      b0  b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf 
      c0  c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf 
      d0  d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df 
      e0  e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef 
      f0  f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 
     100   0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 
     110  10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 
     120  20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 
     130  30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 
     140  40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 
     150  50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 
     160  60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 
     170  70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 
     180  80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 
     190  90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f 
     1a0  a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af 
     1b0  b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf 
     1c0  c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf 
     1d0  d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df 
     1e0  e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef 
     1f0  f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 

----------------------------------------------------------------

使用特权

评论回复
5
computer00|  楼主 | 2009-4-9 02:07 | 只看该作者

哈哈~~~如果俺当初用了您这个,那么俺将无缘学习SD卡的驱动

经过这次实验的学习,俺熟练掌握了SD卡的时序,了解了SD卡的内部结构和一些寄存器,另外还记住了几个简单的命令,
并使用了一次AT91SAM7的硬件SPI功能~~~~收获良多呀~~~~

另外,俺的系统是裸奔的,并不需要移植操作系统那么麻烦~~~~~~俺以前也写过一个操作系统,每次想到要移植就很痛苦……
因为总有些底层的东西要去改,还不如裸奔,需要哪个模块就写哪个模块的代码来得快~~~~~~~~

使用特权

评论回复
6
armecos| | 2009-4-9 03:01 | 只看该作者

圈圈很棒的,比较多产,

我现在已经忘得差不多了,时序、寄存器、命令全都不记得了。ecos的抽象设计思路你可以借鉴一下,每次只要改动一点点或者不需要改动就能平滑移植到其他平台上。越到后面越快。没有平台,你就无法积累,做了后面丢了前面,现在有兴趣做,以后可能没那么大兴趣了,你现在的弱点是没有自己的平台,每个项目都是孤立的,没按照平台的标准设计(或者没意识到这个问题的重要性),以后不太容易实现技术积累。

使用特权

评论回复
7
computer00|  楼主 | 2009-4-9 09:40 | 只看该作者

这个SD没什么呀,我从FLASH换到SD卡,也就是换了低层函数而

首先,初始化部分换了,原来的初始化FLASH部分换了,换成了初始化SD。
然后再将读、写FLASH一扇区的函数换成了读、写SD卡一扇区的函数,就这么多,完事了。

这里主要就是要实现扇区、读写。

使用特权

评论回复
8
古道热肠| | 2009-4-9 10:10 | 只看该作者

呵呵,恭喜圈圈更上一层楼.

整天见eCos晃来晃去,累不累呀.弄个完整的例子放出来,对大家也许会有用,没准那天还能记得您.您那一套平台什么的,明眼人都知道,还不是从周工那儿搬过来改改,加个OS,吹得天花乱坠,实际上没人感兴趣.

使用特权

评论回复
9
古道热肠| | 2009-4-9 10:15 | 只看该作者

俺也转贴1个网友的SD卡驱动,哈哈,Bozai的

/*************************************************************/
/*                   SD/MMC操作函数库                        */
/*  环境WinAVR 20060421                                      */
/*  作者:Bozai(章其波)                                    */
/*  E-mail:sudazqb@163.com                                  */
/*  2006年11月26日                                           */
/*************************************************************/
/*          FAT diriver for  MiniMP3 Player                        */
/*                                                                 */
/* Platform   : AVRStudio4.12 sp4 + WinAVR20060421                 */
/*              optimize -0s                                       */
/* Author     : bozai(Zhang Qibo)                                  */
/* E-mail     : sudazqb@163.com                                    */
/* MSN        : zhangqibo_1985@hotmail.com                         */
/* Date       : 2006-12-26                                         */
/*******************************************************************/




#include <avr/io.h>
#include "MMC_SD.h"




//低速模式  //spi low speed
void SPI_Low(void)
{
    SPCR =   _BV(SPE)|_BV(MSTR)|_BV(SPR1)|_BV(SPR0);
    SPSR &= ~_BV(SPI2X);
}

//高速模式    //spi full speed
void SPI_High(void)
{
    SPCR =  _BV(SPE)|_BV(MSTR);
    SPSR |= _BV(SPI2X);
}

//端口初始化,模式初始化  //port initialize
void SPI_Init(void)
{
    DDR_INI();
    SPI_Low();
}

//写读一个字节            //read and write one byte
uint8 SPI_WriteByte(uint8 val)
{
    SPDR = val;
    while(!(SPSR & _BV(SPIF)));
    return SPDR;
}

/*uint8 SPI_ReadByte(void)
{
    SPDR = 0xff;
    while(!(SPSR & _BV(SPIF)));
    return SPDR;
}*/
//sd卡初始化        //sd card initialize
void MMC_SD_Init(void)
{
    SPI_Init();
    SPI_CS_Deassert();
}

//sd卡写命令        //sd send command
uint8 MMC_SD_SendCommand(uint8 cmd, uint32 arg)
{
    uint8 r1;
    uint8 retry=0;
    
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);

    SPI_CS_Assert();
    
    SPI_WriteByte(cmd | 0x40);//分别写入命令    //send command
    SPI_WriteByte(arg>>24);
    SPI_WriteByte(arg>>16);
    SPI_WriteByte(arg>>8);
    SPI_WriteByte(arg);
    SPI_WriteByte(0x95);
    
    while((r1 = SPI_WriteByte(0xff)) == 0xff)//等待响应,    //wait response
        if(retry++ > 100) break;//超时退出                    //time out error

    SPI_CS_Deassert();

    return r1;//返回状态值                    //return state
}

//sd卡复位        //reset sd card (software)
uint8 MMC_SD_Reset(void)
{
    uint8 i;
    uint8 retry;
    uint8 r1=0;
    retry = 0;
    SPI_Low();

    do
    {
        for(i=0;i<100;i++) SPI_WriteByte(0xff);
        r1 = MMC_SD_SendCommand(0, 0);//发idle命令    //send idle command
        retry++;
        if(retry>10) return 1;//超时退出        //time out
    } while(r1 != 0x01);    


    retry = 0;
    do
    {
        r1 = MMC_SD_SendCommand(1, 0);//发active命令    //send active command
        retry++;
        if(retry>100) return 1;//超时退出        //time out
    } while(r1);
    SPI_High();
    r1 = MMC_SD_SendCommand(59, 0);//关crc        //disable CRC

    r1 = MMC_SD_SendCommand(16, 512);//设扇区大小512    //set sector size to 512
    return 0;//正常返回        //normal return
}

//读一个扇区        //read one sector
uint8 MMC_SD_ReadSingleBlock(uint32 sector, uint8* buffer)
{
    uint8 r1;
    uint16 i;
    uint8 retry=0;

    do
    {
        r1 = MMC_SD_SendCommand(17, sector<<9);//读命令    //read command
        retry++;
        if(retry>10) return 1;//超时退出        //time out
    } while(r1 != 0x00);    


    SPI_CS_Assert();
    //等数据的开始    //wait to start recieve data
    while(SPI_WriteByte(0xff) != 0xfe);//if(retry++ > 50){SPI_CS_Deassert();return 1;}

    for(i=0; i<512; i++)//读512个数据    //read 512 bytes
    {
        *buffer++ = SPI_WriteByte(0xff);
    }

    SPI_WriteByte(0xff);//伪crc
    SPI_WriteByte(0xff);
    
    SPI_CS_Deassert();

    return 0;
}


//写一个扇区        //wirite one sector //not used in this application
uint8 MMC_SD_WriteSingleBlock(uint32 sector, uint8* buffer)
{
    uint8 r1;
    uint16 i;
    uint8 retry=0;

    do
    {
        r1 = MMC_SD_SendCommand(24, sector<<9);//写命令    //send command
        retry++;
        if(retry>10) return 1;//超时退出        //time out
    } while(r1 != 0x00);    



    SPI_CS_Assert();
    
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);

    SPI_WriteByte(0xfe);//发开始符            //send start byte
    
    for(i=0; i<512; i++)//送512字节数据        //send 512 bytes data
    {
        SPI_WriteByte(*buffer++);
    }
    
    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    
    r1 = SPI_WriteByte(0xff);
    
    if( (r1&0x1f) != 0x05)//等待是否成功    //judge if it successful
    {
        SPI_CS_Deassert();
        return r1;
    }
    //等待操作完        //wait no busy
    while(!SPI_WriteByte(0xff));//if(retry++ > 50){SPI_CS_Deassert();return 1;}

    SPI_CS_Deassert();

    return 0;
}

uint32 MMC_SD_ReadCapacity()
{
    uint8 r1;
    uint16 i;
    uint16 temp;
    uint8 buffer[16];
    uint32 Capacity;
    //uint8 retry=0;

    r1 = MMC_SD_SendCommand(9, 0);//写命令    //send command  //READ CSD
    if(r1 != 0x00)
        return r1;

    SPI_CS_Assert();
    while(SPI_WriteByte(0xff) != 0xfe);
    
    for(i=0;i<16;i++)
    {
        buffer=SPI_WriteByte(0xff);
    }    

    SPI_WriteByte(0xff);
    SPI_WriteByte(0xff);
    
    SPI_WriteByte(0xff);
    
    SPI_CS_Deassert();

/*********************************/
//    C_SIZE
    i = buffer[6]&0x03;
    i<<=8;
    i += buffer[7];
    i<<=2;
    i += ((buffer[8]&0xc0)>>6);

/**********************************/
//  C_SIZE_MULT

    r1 = buffer[9]&0x03;
    r1<<=1;
    r1 += ((buffer[10]&0x80)>>7);


/**********************************/
// BLOCKNR

    r1+=2;

    temp = 1;
    while(r1)
    {
        temp*=2;
        r1--;
    }
    
    Capacity = ((uint32)(i+1))*((uint32)temp);

/////////////////////////
// READ_BL_LEN

    i = buffer[5]&0x0f;

/*************************/
//BLOCK_LEN

    temp = 1;
    while(i)
    {
        temp*=2;
        i--;
    }
/************************/


/************** formula of the capacity ******************/
//
//  memory capacity = BLOCKNR * BLOCK_LEN
//    
//    BLOCKNR = (C_SIZE + 1)* MULT
//
//           C_SIZE_MULT+2
//    MULT = 2
//
//               READ_BL_LEN
//    BLOCK_LEN = 2
/**********************************************/

//The final result
    
    Capacity *= (uint32)temp;     
    return Capacity;        
}

使用特权

评论回复
10
computer00|  楼主 | 2009-4-9 10:24 | 只看该作者

为了加快速度,俺用的块连续读、写命令~~~~~~~

使用特权

评论回复
11
armecos| | 2009-4-9 11:58 | 只看该作者

对比一下ecos的代码,就知道差距了,

ecos抽象出一个设备驱动层,对上层操作是统一的设备文件接口,在这种框架下,上层软件不用动,下面的驱动也有规范可循。命名也特别讲究,不用费脑子起名字,还能保证不重复,好记。在可移植性、可扩展性、可生产性、组件化方面圈圈没有任何考虑,还不具备平台特性。

用平台的好处可大了,你可以在瞬间完成一个大项目,每次只要做一点点工作,功能只受限于你的想象力,可以把精力集中在创新上,避免重复劳动。

完整的平台代码我自己都没看,太庞大了,其实没必要,思路比代码重要,谁有功夫看那一堆一堆的乏味的code。99.999%的代码抄的Linux里的,但是知道怎么抄更重要,有现成的我肯定不会自己做。

使用特权

评论回复
12
computer00|  楼主 | 2009-4-9 12:30 | 只看该作者

呵呵,俺的出发点刚好跟您的相反,俺是出于学习,而非应

就像练武功一样,只有经过千锤百炼,才能应付自如。另外,俺认为这样的嵌入式系统就应该要使用裸奔的技术。
只要我没做过的,就想亲自去尝试一下,别人已经做好的,俺会觉得索然无味。

使用特权

评论回复
13
xwj| | 2009-4-9 16:07 | 只看该作者

呵呵,自己DIY的最大好处就是学习

可以学得通通透透。

当然,第一次的话有个现成代码参考下可以少走歪路,显著提高速度,但千万不要不求甚解哦;-)

使用特权

评论回复
14
mindshare| | 2009-4-10 11:49 | 只看该作者

不错不错~!

To OO:
   不错不错~! 只有自己摸索出来的东西才有乐趣,也才能更深入的了解没一个细节.

To ARMecos:
   为什么一定要用ecos的代码.我是指一定~! 每个人的情况不同,很反对这种不动脑子,就对别人说3道4的人

使用特权

评论回复
15
computer00|  楼主 | 2009-4-11 10:27 | 只看该作者

修改了一点小错误,重新上传了。

使用特权

评论回复
16
armecos| | 2009-4-12 01:20 | 只看该作者

其实,使用平台和你学习并不矛盾,

    现在的软件复杂度上升,比如:工程量达到10000人年,10000个人做1年才能完成,你一个人需要做10000年,问题是,你能活10000年嘛,所以,全都自己做不现实。
    
    解决这个问题的方法就是组件对象模型COM。比如你组装一辆汽车,不用管发动机是4缸、8缸还是12缸的,只要对外接口(安装孔位置、尺寸大小)一致,你就能直接替换,改变发动机不会影响你的其他部分,一样有DIY的乐趣。就象有些人DIY“山寨直升机”,他们的航空发动机不可能自己造,但是仍然可以体验DIY飞机的乐趣。
    
    我感觉做软硬件根本目的是实现业务流程,而不是DIY本身。比如:做读卡器就是要实现读写的功能,这个功能有价值。因此了解思路比实现细节更重要,使用别人现成的成果即可以体现合作精神,又不会感觉索然无味,因为这其中最有趣味的是实现读卡器功能,了解工作原理,而不是乏味的coding,故 用别人的现成组件又有何妨呢!

使用特权

评论回复
17
computer00|  楼主 | 2009-4-12 23:27 | 只看该作者

这个原理俺早理解了,照你这么说,俺根本不用做这个。

可是实际制作过程中往往会遇到很多让你意想不到的问题,通过不断地解决问题,这样才能让自己提高。
如果只想不做,或者只说不做,永远都没有意义。

使用特权

评论回复
18
armecos| | 2009-4-13 17:39 | 只看该作者

我是说在平台基础上做效果更好,没有否认DIY的好处,

在平台基础上学习才能有积累,没有平台就没有技术积累。

使用特权

评论回复
19
computer00|  楼主 | 2009-4-13 18:36 | 只看该作者

谁说的?平台也不过是代码的累积而已,特殊的裸奔系统而

裸奔代码照样可以积累。只是一些模块而已,把这些模块搭起来就行了。

如果每次都弄个平台,系统资源消耗了不说,每次调试还累得要死,因为每修改一下进入调试状态都要等很久的加载时间啊,这等于在浪费生命。

再说了,如果要上平台,也当然是用俺自己开发的平台了……至少自己对里面的结构熟。

使用特权

评论回复
20
jiaojinxing| | 2009-5-3 18:46 | 只看该作者

支持圈圈

平台还是用好,
效率有两方面,程序执行效率与程序开发效率,
模块化可以增强组件的重用,加快开发效率,
但为了做到跨平台,抽象性有时要牺牲执行效率
频繁地更换平台也不是一件好事
建议考虑rt-thread这个操作系统,它包括了内核、文件系统、网络协议栈、GUI、输入法、网页服务器、FTP服务器、Shell等
更重要它是基于GPL的,以后可能用BSD授权,不像ecos卖个天价,其实到底还不是人家的东西

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

246

主题

14693

帖子

210

粉丝