SPI方式驱动SD卡的方法

SD卡的驱动主要有初始化、读、写、擦除等。

1、 初始化 步骤:

(1)      延时至少74clock

(2)      发送CMD0,需要返回0x01,进入Idle状态

(3)      循环发送CMD55+ACMD41,直到返回0x00,进入Ready状态。

如果是MMC,此步应发送CMD1

2、 步骤:

(1)      发送CMD17(单块)或CMD18(多块)读命令,返回0x00

(2)      接收数据开始令牌0xfe(或0xfc + 正式数据512Bytes + CRC 校验2Bytes

默认正式传输的数据长度是512Bytes,可用CMD16设置。

3、 步骤:

(1)      发送CMD24(单块)或CMD25(多块)写命令,返回0x00

(2)      发送数据开始令牌0xfe(或0xfc + 正式数据512Bytes + CRC校验2Bytes

4、 擦除 步骤:

(1)      发送CMD32,跟一个参数来指定首个要擦出的扇区号(SD手册上说是块号)

(2)      发送CMD33,,指定最后的扇区号

(3)      发送CMD38,擦除指定区间的扇区

3步顺序不能颠倒。

还要注意发送CMD命令时,后面要跟一个字节的CRC校验数据,总之要保证每次发送的数据包长度符合协议要求,命令、数据符合时序要求。

=======================================================================================================================

=======================================================================================================================

 

SD卡在现在的日常生活与工作中使用非常广泛,时下已经成为最为通用的数据存储卡。在诸如MP3、数码相机等设备上也都采用SD卡作为其存储设备。SD卡之所以得到如此广泛的使用,是因为它价格低廉、存储容量大、使用方便、通用性与安全性强等优点。既然它有着这么多优点,那么如果将它加入到单片机应用开发系统中来,将使系统变得更加出色。这就要求对SD卡的硬件与读写时序进行研究。对于SD卡的硬件结构,在官方的文档上有很详细的介绍,如SD卡内的存储器结构、存储单元组织方式等内容。要实现对它的读写,最核心的是它的时序,笔者在经过了实际的测试后,使用51单片机成功实现了对SD卡的扇区读写,并对其读写速度进行了评估。下面先来讲解SD卡的读写时序。

 

        SD卡SPI模式下与单片机的连接图:

    SD卡支持两种总线方式:SD方式与SPI方式。其中SD方式采用6线制,使用CLK、CMD、DAT0~DAT3进行数据通信。而SPI方式采用4线制,使用CS、CLK、DataIn、DataOut进行数据通信。SD方式时的数据传输速度与SPI方式要快,采用单片机对SD卡进行读写时一般都采用SPI模式。采用不同的初始化方式可以使SD卡工作于SD方式或SPI方式。这里只对其SPI方式进行介绍。
(2)       SPI方式驱动SD卡的方法
     SD卡的SPI通信接口使其可以通过SPI通道进行数据读写。从应用的角度来看,采用SPI接口的好处在于,很多单片机内部自带SPI控制器,不光给开发上带来方便,同时也见降低了开发成本。然而,它也有不好的地方,如失去了SD卡的性能优势,要解决这一问题,就要用SD方式,因为它提供更大的总线数据带宽。SPI接口的选用是在上电初始时向其写入第一个命令时进行的。以下介绍SD卡的驱动方法,只实现简单的扇区读写。
1)  命令与数据传输
SD卡自身有完备的命令系统,以实现各项操作。
 

          写命令的例程:
//-----------------------------------------------------------------------------------------------
  向SD卡中写入命令,并返回回应的第二个字节
//-----------------------------------------------------------------------------------------------
unsigned char Write_Command_SD(unsigned char *CMD)
{
   unsigned char tmp;
   unsigned char retry=0;
   unsigned char i;
 
   //禁止SD卡片选
   SPI_CS=1;
   //发送8个时钟信号
   Write_Byte_SD(0xFF);
   //使能SD卡片选
   SPI_CS=0;
 
   //向SD卡发送6字节命令
   for (i=0;i<0x06;i++)
   {
      Write_Byte_SD(*CMD++);
   }
  
   //获得16位的回应
   Read_Byte_SD(); //read the first byte,ignore it.
   do
   {  //读取后8位
      tmp = Read_Byte_SD();
      retry++;
   }
   while((tmp==0xff)&&(retry<100));
   return(tmp);
}
 
2)  初始化
SD卡的初始化是非常重要的,只有进行了正确的初始化,才能进行后面的各项操作。在初始化过程中,SPI的时钟不能太快,否则会造初始化失败。在初始化成功后,应尽量提高SPI的速率。在刚开始要先发送至少74个时钟信号,这是必须的。在很多读者的实验中,很多是因为疏忽了这一点,而使初始化不成功。随后就是写入两个命令CMD0与CMD1,使SD卡进入SPI模式
           初始化例程:
//--------------------------------------------------------------------------
    初始化SD卡到SPI模式
//--------------------------------------------------------------------------
unsigned char SD_Init()
   unsigned char retry,temp;
   unsigned char i;
   unsigned char CMD[] = {0x40,0x00,0x00,0x00,0x00,0x95};
   SD_Port_Init(); //初始化驱动端口
  
   Init_Flag=1; //将初始化标志置1
 
   for (i=0;i<0x0f;i++)
   {
      Write_Byte_SD(0xff); //发送至少74个时钟信号
   }
   
   //向SD卡发送CMD0
   retry=0;
   do
   { //为了能够成功写入CMD0,在这里写200次
     temp=Write_Command_SD(CMD);
     retry++;
     if(retry==200)
     { //超过200次
       return(INIT_CMD0_ERROR);//CMD0 Error!
     }
   }
   while(temp!=1);  //回应01h,停止写入
  
   //发送CMD1到SD卡
   CMD[0] = 0x41; //CMD1
   CMD[5] = 0xFF;
   retry=0;
   do
   { //为了能成功写入CMD1,写100次
     temp=Write_Command_SD(CMD);
     retry++;
     if(retry==100)
     { //超过100次
       return(INIT_CMD1_ERROR);//CMD1 Error!
     }
   }
   while(temp!=0);//回应00h停止写入
  
   Init_Flag=0; //初始化完毕,初始化标志清零
  
   SPI_CS=1;  //片选无效
   return(0); //初始化成功
}
  • 3
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基本的SPI驱动程序,可用于SD卡的读/写: ```c #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <stddef.h> #include "driver/spi_master.h" #include "driver/gpio.h" // SPI总线的引脚定义 #define PIN_NUM_MISO 25 // MasterIn SlaveOut #define PIN_NUM_MOSI 23 // MasterOut SlaveIn #define PIN_NUM_CLK 19 // 时钟 #define PIN_NUM_CS 22 // 片选 // SD卡命令定义 #define CMD_GO_IDLE_STATE 0x00 #define CMD_SEND_OP_COND 0x01 #define CMD_SEND_IF_COND 0x08 #define CMD_SEND_CSD 0x09 #define CMD_SEND_CID 0x0A #define CMD_STOP_TRANSMISSION 0x0C #define CMD_SEND_STATUS 0x0D #define CMD_SET_BLOCKLEN 0x10 #define CMD_READ_SINGLE_BLOCK 0x11 #define CMD_WRITE_BLOCK 0x18 #define CMD_APP_CMD 0x37 #define CMD_READ_OCR 0x3A // SD卡响应标志定义 #define R1_IDLE_STATE 0x01 #define R1_ILLEGAL_COMMAND 0x04 #define R1_CRC_ERROR 0x08 #define R1_ERASE_SEQUENCE_ERROR 0x10 #define R1_ADDRESS_ERROR 0x20 #define R1_PARAMETER_ERROR 0x40 // SD卡状态标志定义 #define STATUS_READY 0x00 #define STATUS_IDLE 0x01 #define STATUS_ERASE_RESET 0x02 #define STATUS_ILLEGAL_COMMAND 0x04 #define STATUS_CRC_ERROR 0x08 #define STATUS_ERASE_SEQUENCE_ERROR 0x10 #define STATUS_ADDRESS_ERROR 0x20 #define STATUS_PARAMETER_ERROR 0x40 #define STATUS_APP_CMD 0x80 // 定义SPI总线句柄 static spi_device_handle_t spi; // 初始化SPI总线 static void spi_init(void) { esp_err_t ret; // 配置SPI总线参数 spi_bus_config_t buscfg = { .miso_io_num = PIN_NUM_MISO, .mosi_io_num = PIN_NUM_MOSI, .sclk_io_num = PIN_NUM_CLK, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = 4096, }; // 初始化SPI总线 ret = spi_bus_initialize(VSPI_HOST, &buscfg, 1); ESP_ERROR_CHECK(ret); // 配置SPI设备参数 spi_device_interface_config_t devcfg = { .mode = 0, // SPI模式0 .clock_speed_hz = 400000, // 时钟频率400kHz .spics_io_num = PIN_NUM_CS, // 片选引脚 .queue_size = 1, }; // 添加SPI设备 ret = spi_bus_add_device(VSPI_HOST, &devcfg, &spi); ESP_ERROR_CHECK(ret); } // 发送SD卡命令 static uint8_t sdcard_cmd(uint8_t cmd, uint32_t arg) { uint8_t buf[6]; memset(buf, 0, sizeof(buf)); // 构造命令 buf[0] = cmd | 0x40; buf[1] = (uint8_t)(arg >> 24); buf[2] = (uint8_t)(arg >> 16); buf[3] = (uint8_t)(arg >> 8); buf[4] = (uint8_t)arg; buf[5] = 0x95; // 发送命令 spi_transaction_t t = { .length = 48, .tx_buffer = buf, .flags = SPI_TRANS_USE_TXDATA, }; spi_device_transmit(spi, &t); // 接收响应 uint8_t response; t.length = 8; t.rx_buffer = &response; spi_device_transmit(spi, &t); return response; } // 初始化SD卡 static bool sdcard_init(void) { uint8_t response; uint32_t i; bool success = false; // 发送CMD0命令,使SD卡进入空闲状态 for (i = 0; i < 10; ++i) { response = sdcard_cmd(CMD_GO_IDLE_STATE, 0); if (response == R1_IDLE_STATE) { success = true; break; } } if (!success) { return false; } // 发送CMD8命令,检测SD卡是否支持高容量 response = sdcard_cmd(CMD_SEND_IF_COND, 0x1AA); if (response != R1_IDLE_STATE) { return false; } // 发送ACMD41命令,激活SD卡 for (i = 0; i < 10; ++i) { response = sdcard_cmd(CMD_APP_CMD, 0); if (response != R1_IDLE_STATE) { continue; } response = sdcard_cmd(CMD_SEND_OP_COND, 0x40000000); if (response == R1_IDLE_STATE) { success = true; break; } } if (!success) { return false; } // 发送CMD58命令,读取SD卡OCR response = sdcard_cmd(CMD_READ_OCR, 0); if (response != R1_IDLE_STATE) { return false; } // SD卡初始化成功 return true; } // 读取指定扇区的数据 static bool sdcard_read_block(uint32_t sector, uint8_t *buffer) { uint8_t response; uint32_t i; // 发送CMD17命令,读取单个扇区 response = sdcard_cmd(CMD_READ_SINGLE_BLOCK, sector << 9); if (response != R1_IDLE_STATE) { return false; } // 接收数据 spi_transaction_t t = { .rxlength = 512*8, .rx_buffer = buffer, }; spi_device_transmit(spi, &t); // 读取响应 for (i = 0; i < 8; ++i) { spi_device_transmit(spi, &t); } // 检查响应 if ((response = sdcard_cmd(CMD_SEND_STATUS, 0)) != R1_IDLE_STATE) { return false; } if ((response = sdcard_cmd(CMD_STOP_TRANSMISSION, 0)) != R1_IDLE_STATE) { return false; } // 读取成功 return true; } // 写入数据到指定扇区 static bool sdcard_write_block(uint32_t sector, const uint8_t *buffer) { uint8_t response; uint32_t i; // 发送CMD24命令,写入单个扇区 response = sdcard_cmd(CMD_WRITE_BLOCK, sector << 9); if (response != R1_IDLE_STATE) { return false; } // 发送数据 spi_transaction_t t = { .length = 512*8, .tx_buffer = buffer, }; spi_device_transmit(spi, &t); // 发送CRC uint8_t crc[2] = { 0xFF, 0xFF }; t.length = 16; t.tx_buffer = crc; spi_device_transmit(spi, &t); // 读取响应 for (i = 0; i < 8; ++i) { spi_device_transmit(spi, &t); } // 检查响应 if ((response = sdcard_cmd(CMD_SEND_STATUS, 0)) != R1_IDLE_STATE) { return false; } // 写入成功 return true; } ``` 这只是一个基本的SPI驱动程序,需要根据实际情况进行修改和完善。具体的SD卡操作流程和命令可以参考SD卡规范。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值