Data Structures | Defines | Typedefs | Enumerations | Functions

sdcmd.h File Reference

communication with SD card More...

Go to the source code of this file.

Data Structures

struct  sd_csd_data_s
 card-specific data More...
struct  sd_cid_data_s
 card identification data More...
struct  sd_rd_mult_block_data_s
 descriptor for read multiple block More...
struct  sd_wr_mult_block_data_s
 descriptor for write multiple block More...

Defines

#define CMD(c)   (((c) & 0x3f) | 0x40)
 convert SD command code into byte to transmit
#define SD_GO_IDLE_STATE   CMD(0)
 reset SD card
#define SD_SEND_OP_COND   CMD(1)
 send host capacity support information, start card initialization
#define SD_SWITCH_FUNC   CMD(6)
 check or set switchable function
#define SD_SEND_IF_COND   CMD(8)
 send card interface condition, check whether supplied voltage is acceptable to card
#define SD_SEND_CSD   CMD(9)
 get card specific data
#define SD_SEND_CID   CMD(10)
 get card identification
#define SD_STOP_TRANSMISSION   CMD(12)
 stop a multiple block read operation
#define SD_SEND_STATUS   CMD(13)
 get card status register
#define SD_SET_BLOCKLEN   CMD(16)
 set block length
#define SD_READ_SINGLE_BLOCK   CMD(17)
 read one data block
#define SD_READ_MULTIPLE_BLOCK   CMD(18)
 read multiple data blocks until STOP_TRANSMISSION
#define SD_WRITE_BLOCK   CMD(24)
 write one data block
#define SD_WRITE_MULTIPLE_BLOCK   CMD(25)
 write multiple data blocks
#define SD_PROGRAM_CSD   CMD(27)
 program programmable bits in card specific data
#define SD_APP_CMD   CMD(55)
 next command is application-specific command
#define SD_READ_OCR   CMD(58)
 get operation condition register
#define SD_CRC_ON_OFF   CMD(59)
 switch CRC checking on/off
#define SD_A_SEND_NUM_WR_BLOCKS   CMD(22)
 get number of successfully written data blocks
#define SD_A_SEND_OP_COND   CMD(41)
 send host capacity support information, start card initialization
#define SDTOKEN_START   (0b11111110)
 token for start of data transmission
#define SDTOKEN_START_MULTIPLE   (0b11111100)
 token for start of multiple-block data transmission
#define SDTOKEN_STOP_MULTIPLE   (0b11111101)
 token for stop of multiple-block data transmission
#define CSD_STRUCTURE(csd)   ((csd).csd_structure >> 6)
#define VAL_CSD_ACCESS_TIME_ASYNC_UNIT_RAW(v)   ((v) & 0x07)
#define VAL_CSD_ACCESS_TIME_ASYNC_TIME_RAW(v)   (((v) >> 3) & 0x0f)
#define VAL_CSD_ACCESS_TIME_ASYNC_UNIT_CONV(u)
#define VAL_CSD_ACCESS_TIME_ASYNC_TIME_CONV(t)
#define VAL_CSD_ACCESS_TIME_ASYNC(v)
#define CSD_ACCESS_TIME_ASYNC(csd)   VAL_CSD_ACCESS_TIME_ASYNC((csd).taac)
#define VAL_CSD_TRAN_UNIT_RAW(v)   ((v) & 7)
#define VAL_CSD_TRAN_TIME_RAW(v)   (((v) >> 3) & 0x0f)
#define VAL_CSD_TRAN_UNIT_CONV(u)
#define VAL_CSD_TRAN_TIME_CONV(t)
#define VAL_CSD_TRAN_SPEED(v)
#define CSD_TRAN_SPEED(csd)   VAL_CSD_TRAN_SPEED((csd.tran_speed))
#define CSD_CCC(csd)
#define CSD_READ_BL_LEN(csd)   ((csd).ccc_read_bl_len[1] & 0xf)
#define CSD_READ_BL_PARTIAL(csd)   ((csd).flags1[0]>>7)
#define CSD_WRITE_BLK_MISALIGN(csd)   (((csd).flags1[0]>>6) & 0x1)
#define CSD_READ_BLK_MISALIGN(csd)   (((csd).flags1[0]>>5) & 0x1)
#define CSD_DSR_IMP(csd)   (((csd).flags1[0]>>4) & 0x1)
#define CSD1_C_SIZE(csd)
#define CSD2_C_SIZE(csd)
#define CSD_VDD_R_CURR_MIN(csd)   (((csd).flags1[2]>>3) & 0x7)
#define CSD_VDD_R_CURR_MAX(csd)   (((csd).flags1[2]) & 0x7)
#define CSD_VDD_W_CURR_MIN(csd)   (((csd).flags1[3]>>5) & 0x7)
#define CSD_VDD_W_CURR_MAX(csd)   (((csd).flags1[3]>>2) & 0x7)
#define CSD_C_SIZE_MULT(csd)
#define CSD_ERASE_BLK_EN(csd)   (((csd).flags1[4]>>6) & 0x1)
#define CSD_SECTOR_SIZE(csd)
#define CSD_WP_GRP_SIZE(csd)   ((csd).flags1[5] & 0x7f)
#define CSD_WP_GRP_ENABLE(csd)   ((csd).flags1[6]>>7)
#define CSD_R2W_FACTOR(csd)   (((csd).flags1[6]>>2) & 0x7)
#define CSD_WRITE_BL_LEN(csd)
#define CSD_WRITE_BL_PARTIAL(csd)   (((csd).flags1[7]>>5) & 0x1)
#define CSD_FILE_FORMAT_GRP(csd)   ((csd).flags1[8]>>7)
#define CSD_COPY(csd)   (((csd).flags1[8]>>6) & 0x1)
#define CSD_PERM_WRITE_PROTECT(csd)   (((csd).flags1[8]>>5) & 0x1)
#define CSD_TMP_WRITE_PROTECT(csd)   (((csd).flags1[8]>>4) & 0x1)
#define CSD_FILE_FORMAT(csd)   (((csd).flags1[8]>>2) & 0x3)
#define CSD_CRC(csd)   ((csd).flags1[9]>>1)
#define CSD1_CARD_CAPACITY_KB(csd)   ((uint32_t)(CSD1_C_SIZE(csd)+1)<<(CSD_C_SIZE_MULT(csd)+2+CSD_READ_BL_LEN(csd)-10))
#define CSD2_CARD_CAPACITY_KB(csd)   ((uint32_t)(CSD2_C_SIZE(csd)+1)<<9)
#define CSD_CARD_CAPACITY_KB(csd)
 compute card capacity
#define SECTOR_SHIFT   (9)
 shift count for conversion between sector number and byte number
#define SECTOR_SIZE   (1<<SECTOR_SHIFT)
 size of one sector in bytes
SDcard_response_1

#define SDR1_IN_IDLE_STATE   (1<<0)
#define SDR1_ERASE_RESET   (1<<1)
#define SDR1_ILLEGAL_COMMAND   (1<<2)
#define SDR1_COM_CRC_ERROR   (1<<3)
#define SDR1_ERASE_SEQUENCE_ERROR   (1<<4)
#define SDR1_ADDRESS_ERROR   (1<<5)
#define SDR1_PARAMETER_ERROR   (1<<6)

Typedefs

typedef struct sd_csd_data_s sd_csd_data_t
 card-specific data
typedef struct sd_cid_data_s sd_cid_data_t
 card identification data
typedef uint16_t sd_blen_t
 block length for SD transfers
typedef struct
sd_rd_mult_block_data_s 
sd_rd_mult_block_data_t
 descriptor for read multiple block
typedef struct
sd_wr_mult_block_data_s 
sd_wr_mult_block_data_t
 descriptor for write multiple block

Enumerations

enum  SD_State_t {
  SD_Inactive = 0, SD_Idle, SD_Ready, SD_Identify,
  SD_Standby, SD_Transfer, SD_SendData, SD_RecvData,
  SD_Program, SD_Disconnect
}

Functions

uint16_t sd_resp16_crc (uint8_t sd_response[])
 compute CRC for 136bit response block
uint8_t sd_recv_data (uint8_t *data, sd_blen_t blocklen)
 read data from SD card
void sd_send_data (const uint8_t *data, sd_blen_t blocklen, uint8_t token)
 write data to SD card
uint16_t sd_init_card (uint8_t debug)
 initialize SPI interface and SD card
uint32_t sd_block2addr (uint32_t block)
 convert block number into address used in sending R/W command to card
uint8_t sd_read_block (uint8_t *data, uint32_t address)
 read one block from SD card
uint32_t sd_write_block (const uint8_t *data, uint32_t address)
 write one block to SD card
uint8_t sd_read_multiple_block (uint8_t *const data[], uint8_t cnt, uint32_t address)
 read multiple blocks from SD card
uint8_t sd_read_multiple_block2 (sd_rd_mult_block_data_t const data[], uint8_t cnt, uint32_t address)
 read multiple blocks from SD card
uint8_t sd_write_multiple_block (const uint8_t *const data[], uint8_t cnt, uint32_t address)
 write multiple blocks to SD card
uint8_t sd_write_multiple_block2 (sd_wr_mult_block_data_t const data[], uint8_t cnt, uint32_t address)
 write multiple blocks to SD card
void sd_force_deselect (void)

Detailed Description

communication with SD card


Define Documentation

#define CSD1_C_SIZE (   csd  ) 
Value:
(((uint16_t)((csd).flags1[0] & 0x3) << 10) \
                                     | ((uint16_t)((csd).flags1[1]) << 2) \
                                     | ((uint16_t)((csd).flags1[2]) >> 6))
#define CSD2_C_SIZE (   csd  ) 
Value:
(((uint32_t)((csd).flags1[1] & 0x3f) << 16)\
                                     | ((uint16_t)((csd).flags1[2]) << 8) \
                                     | ((uint16_t)((csd).flags1[3]) ))
#define CSD_C_SIZE_MULT (   csd  ) 
Value:
((((csd).flags1[3] & 0x3) << 1) \
                                     | ((csd).flags1[4]>>7))
#define CSD_CARD_CAPACITY_KB (   csd  ) 
Value:
(CSD_STRUCTURE(csd) == 0 ? CSD1_CARD_CAPACITY_KB(csd) \
   : CSD2_CARD_CAPACITY_KB(csd))

compute card capacity

#define CSD_CCC (   csd  ) 
Value:
(((uint16_t)(csd).ccc_read_bl_len[0] << 4) \
                                     | ((uint16_t)((csd).ccc_read_bl_len[1]) >> 4))
#define CSD_SECTOR_SIZE (   csd  ) 
Value:
(((csd).flags1[4] & 0x3f) \
                                     | ((csd).flags1[5]>>7))
#define CSD_WRITE_BL_LEN (   csd  ) 
Value:
(((csd).flags1[6] & 0x3) \
                                     | ((csd).flags1[7]>>6))
#define VAL_CSD_ACCESS_TIME_ASYNC (   v  ) 
Value:
(VAL_CSD_ACCESS_TIME_ASYNC_TIME_CONV(VAL_CSD_ACCESS_TIME_ASYNC_TIME_RAW(v)) \
   * VAL_CSD_ACCESS_TIME_ASYNC_UNIT_CONV(VAL_CSD_ACCESS_TIME_UNIT_RAW(v)))
#define VAL_CSD_ACCESS_TIME_ASYNC_TIME_CONV (   t  ) 
Value:
(t == 1 ? 1.0 : t == 2 ? 1.2 : \
                                                t == 3 ? 1.3 : t == 4 ? 1.5 : \
                                                t == 5 ? 2.0 : t == 6 ? 2.5 : \
                                                t == 7 ? 3.0 : t == 8 ? 3.5 : \
                                                t == 9 ? 4.0 : t == 10 ? 4.5 : \
                                                t == 11 ? 5.0: t == 12 ? 5.5 : \
                                                t == 13 ? 6.0: t == 14 ? 7.0 : \
                                                t == 15 ? 8.0: 0)
#define VAL_CSD_TRAN_SPEED (   v  ) 
Value:
(VAL_CSD_TRAN_TIME_CONV(VAL_CSD_TRAN_TIME_RAW(v))       \
                               * VAL_CSD_TRAN_UNIT_CONV(VAL_CSD_TRAN_UNIT_RAW(v)))
#define VAL_CSD_TRAN_TIME_CONV (   t  ) 
Value:
(t == 1 ? 1.0 : t == 2 ? 1.2 :  \
                                     t == 3 ? 1.3 : t == 4 ? 1.5 :      \
                                     t == 5 ? 2.0 : t == 6 ? 2.5 :      \
                                     t == 7 ? 3.0 : t == 8 ? 3.5 :      \
                                     t == 9 ? 4.0 : t == 10 ? 4.5 :     \
                                     t == 11 ? 5.0 : t == 12 ? 5.5 :    \
                                     t == 13 ? 6.0 : t == 14 ? 7.0 :    \
                                     t == 15 ? 8.0 : 0)
#define VAL_CSD_TRAN_UNIT_CONV (   u  ) 
Value:
(u == 0 ? 100e3 : u == 1 ? 1e6 :        \
                                     u == 2 ? 10e6 : u == 3 ? 100e6 :   \
                                     0)

Typedef Documentation

typedef uint16_t sd_blen_t

block length for SD transfers

NOTE: we have no more than 2^16 bytes address space, so we will not be able to use all 32 bits defined in SD protocol anyway

descriptor for read multiple block

This structure provides the pointers to data for the sd_read_multiple_block2() function. These will transfer up to num_blocks blocks using the data pointer, when one element has been used the functions will use the next descriptor.

descriptor for write multiple block

This structure provides the pointers to data for the sd_write_multiple_block2() function. These will transfer up to num_blocks blocks using the data pointer, when one element has been used the functions will use the next descriptor.


Function Documentation

uint32_t sd_block2addr ( uint32_t  block  ) 

convert block number into address used in sending R/W command to card

Depending on card type, the address must either be a byte address (standard capacity) or a block address (high capacity)

A standard capacity card is addressed by byte number

A high capacity card is addressed by sector number

{
  if (!sd_if_cond.proto_hc)
    return block << SECTOR_SHIFT;
  else
    return block;
}

void sd_force_deselect ( void   ) 

abort any SD transfer by deselecting the card

Please, use this only for debugging stuck cards.

{
  sd_deselect ();
}

uint16_t sd_init_card ( uint8_t  dbg  ) 

initialize SPI interface and SD card

Initialize SPI interface and SD card after power-up. This function needs to be called after power to card is brought up before the SD card can be used

Parameters:
debug when !=0 print out debugging messages on stdout
Returns:
0 on success

initialize SD card currently in slot for use.

{
  uint8_t i;
  uint8_t r;
  uint8_t chk;
  uint8_t save_debug = debug;
  debug = dbg;
  spi_init (get_div_for_speed((9<<3)|0)); /* set SPIfreq <= 400kHz */
  /* SD spec says to wait at least 74 clock cycles after power-up */
  debug_msg("SD_INIT");
  for (i=0; i<10; i++) spi_write (0xff);
  debug_msg(".");
  spi_select (); /* no wait for ready */
  for (i=0; i<10; i++) spi_write (0xff);
  sd_deselect ();
  debug_msg(".\r\n");
  i = 0;
  do {
    _delay_ms(5);
    debug_byte (i++);
    r = sd_go_idle ();
  } while (r & 0b11110000);
  do {
    /* the card needs some time after power-up */
    sd_if_cond.vhs = 0b0001;
    sd_if_cond.check = chk = 0b10101010;
    r = sd_get_if_cond (&sd_if_cond.version, &sd_if_cond.vhs, &sd_if_cond.check);
  } while (r & 0b00000010);
  if (r & 0b00000100) {
    /* SEND_IF_COND not supported, version 1.x */
    sd_if_cond.proto_version = 1;
  } else {
    sd_if_cond.proto_version = 2;
    dprintf_P (PSTR("IFC.ver=%02X .vhs=%02X .check=%02X\r\n"),
               sd_if_cond.version, sd_if_cond.vhs, sd_if_cond.check);
  }

  /* read Operation Condition Register */
  do {
    r = sd_get_ocr (&sd_ocr);
    dprintf_P (PSTR("OCR.volt=%02X .ready=%02X\r\n"),
               (uint16_t)((sd_ocr & 0xFF0000) >> 16),
               (uint16_t)(sd_ocr >> 30));

  } while (0 && !(sd_ocr >> 31));

  /* initiate startup, set HC mode if v2.x */
  do {
    r = sd_set_op_cond (sd_if_cond.proto_version >= 2);
  } while ((r & (1<<0)) && (r & (1<<2)) == 0);

  /* enable CRC checking, disabled by default in SPI mode */
  r = sd_crc_on_off (1);

  r = sd_get_csd (&sd_csd);
  
  if (sd_if_cond.proto_version >= 2) {
    for (i = 0; i<16; i++) {
      r = sd_get_ocr (&sd_ocr);
      dprintf_P (PSTR("OCR.volt=%02X .ready=%02X\r\n"),
                 (uint16_t)((sd_ocr & 0xFF0000) >> 16),
                 (uint16_t)(sd_ocr >> 30));
      if (sd_ocr & ((uint32_t)1<<31)) break;
      _delay_ms(1);
    }

    debug_msg ("SD2.0 ");
    if (sd_ocr & ((uint32_t)1<<30)) {
      sd_if_cond.proto_hc = 1;
      debug_msg ("HC");
    } else {
      sd_if_cond.proto_hc = 0;
      debug_msg ("SC");
    }
  } else {
    sd_if_cond.proto_hc = 0;
    debug_msg ("SD1.x");
  }

  if (CSD_STRUCTURE(sd_csd) == 0 || CSD_STRUCTURE(sd_csd) == 1) {
    uint32_t capacity = CSD_CARD_CAPACITY_KB(sd_csd);
    uint8_t spi_div;

    /* convert and round to MB */
    capacity >>= 9;
    capacity += capacity & 1;
    capacity >>= 1;

    dprintf_P(PSTR(" %5ld MB"), capacity);
    dprintf_P(PSTR(" (csd=%1d "), CSD_STRUCTURE(sd_csd));
    if (CSD_STRUCTURE(sd_csd) == 0) {
      dprintf_P(PSTR("size=%X mult=%X blen=%X"),
                CSD1_C_SIZE(sd_csd), CSD_C_SIZE_MULT(sd_csd),
                CSD_READ_BL_LEN(sd_csd)
                );
    } else {
      dprintf_P(PSTR("size=%ld"),
                CSD2_C_SIZE(sd_csd)
                );
    }
    dprintf_P(PSTR(")\r\n"));

    dprintf_P(PSTR("Access time: %dE%dns + %d SPI clock cycles"),
              (int)VAL_CSD_ACCESS_TIME_ASYNC_TIME_CONV(VAL_CSD_ACCESS_TIME_ASYNC_TIME_RAW(sd_csd.taac))*10,
              VAL_CSD_ACCESS_TIME_ASYNC_UNIT_RAW(sd_csd.taac)-1,
              sd_csd.nsac);

    /* switch to max. allowed SPI speed */
    spi_div = get_div_for_speed(sd_csd.tran_speed);

    spi_write (0xff);
    spi_set_speed (spi_div);
    spi_write (0xff);
    spi_select ();
    spi_write (0xff);
    spi_write (0xff);
    spi_deselect ();
  }
  debug_msg ("\r\n");

  sd_init_cmd_profiles (&sd_csd);

  /* read and display identification from card */
  r = sd_get_cid (&sd_cid);
  if (r == 0) {
    for (i = 0; i < sizeof(sd_cid.pnm); i++)
      if (!isprint (sd_cid.pnm[i])) sd_cid.pnm[i] = ' ';
    dprintf_P (PSTR("%02x %.2s %.5s %c.%c %8lx %d/%02d\r\n"),
               sd_cid.mid, sd_cid.oid, sd_cid.pnm,
               int2hex (sd_cid.prv >> 4), int2hex (sd_cid.prv & 0xf),
               sd_cid.psn,
               sd_cid.mdt[1] & 0xf, ((sd_cid.mdt[1] >> 4)|(sd_cid.mdt[0] << 4)) & 0xff);
  }

  debug = save_debug;
  return r;
}

uint8_t sd_read_block ( uint8_t *  data,
uint32_t  address 
)

read one block from SD card

Parameters:
data pointer to buffer for read data
address card address to read data from, use sd_block2addr()

read one block from SD card

Parameters:
data buffer to put data into
address SD card address to read data from

{
  uint8_t r;

  sd_select ();
  sd_send_cmd_arg32 (SD_READ_SINGLE_BLOCK, address);
  r = sd_recv_response1 (&sd_cmd_profiles[sd_read_cmd_profile]);
  if (r == 0) {
    sd_recv_data (data, sd_current_blocklen);
  }
  sd_deselect ();

  if (r != 0) {
    /* error in sending command */
    sd_r1_print_P (PSTR("SDread"), r);
  }
  return r;
}

uint8_t sd_read_multiple_block ( uint8_t *const   data[],
uint8_t  cnt,
uint32_t  address 
)

read multiple blocks from SD card

Read some blocks from SD card. This function allows to put the read data into discontigous buffers.

Parameters:
data[] array of pointers to data buffers
cnt number of blocks to read
address card address of first block to read, use sd_block2addr()

{
  uint8_t r;
  uint8_t save_debug = debug;
  debug = 1;
  sd_select ();
  sd_send_cmd_arg32 (SD_READ_MULTIPLE_BLOCK, address);
  r = sd_recv_response1 (&sd_cmd_profiles[sd_read_cmd_profile]);
  if (r == 0) {
    for (; cnt; cnt--) {
      r = sd_recv_data (*data++, sd_current_blocklen);
      if ((r & 0b00011111) != 0b00000101) break;
    }
    r = sd_stop_trans ();
  }
  sd_deselect ();
  debug = save_debug;
  return r;
}

uint8_t sd_read_multiple_block2 ( sd_rd_mult_block_data_t const   data[],
uint8_t  cnt,
uint32_t  address 
)

read multiple blocks from SD card

Read some blocks from SD card. This function allows to put the read data into discontigous buffers.

Parameters:
data[] array of pointers to data buffers
cnt number of blocks to read
address card address of first block to read, use sd_block2addr()

{
  uint8_t r;
  uint8_t save_debug = debug;

  debug = 1;
  sd_select ();
  sd_send_cmd_arg32 (SD_READ_MULTIPLE_BLOCK, address);
  r = sd_recv_response1 (&sd_cmd_profiles[sd_read_cmd_profile]);
  if (r == 0) {
    uint8_t * ptr = data->buffer;
    uint8_t num_blocks = data->num_blocks;
    
    for (; cnt; cnt--) {
      if (num_blocks == 0) break;

      r = sd_recv_data (ptr, sd_current_blocklen);
      if ((r & 0b00011111) != 0b00000101) break;
      if (--num_blocks == 0) {
        data++;
        ptr = data->buffer;
        num_blocks = data->num_blocks;
      } else {
        ptr += sd_current_blocklen;
      }
    }
    r = sd_stop_trans ();
  }
  sd_deselect ();
  debug = save_debug;
  return r;
}

uint8_t sd_recv_data ( uint8_t *  data,
sd_blen_t  blocklen 
)

read data from SD card

Parameters:
data pointer to buffer for data
blocklen number of bytes to receive

{
  uint16_t crc = 0x0000;
  uint16_t crc_read;
  sd_blen_t i;
  uint8_t t;

  do {
    t = spi_transfer (0xff, 0, 1);
  } while (t == 0xff); /* wait for start block token */

  for (i=blocklen; i; i--) {
    uint8_t b = spi_transfer (0xff, 0, 1);
    *data++ = b;
    crc = _crc_xmodem_update (crc, b);
  }
  crc_read = spi_transfer (0xff, 0, 1); crc_read <<= 8;
  crc_read |= spi_transfer (0xff, 0, 1);

  return crc_read == crc;
}

uint16_t sd_resp16_crc ( uint8_t  sd_response[]  ) 

compute CRC for 136bit response block

CRC: XMODEM

void sd_send_data ( const uint8_t *  data,
sd_blen_t  blocklen,
uint8_t  token 
)

write data to SD card

Parameters:
data pointer to buffer with data
blocklen number of bytes to send
token token value to start transmission

{
  uint16_t crc = 0x0000;
  sd_blen_t i;

  spi_write (token);
  for (i = blocklen; i; i--) {
    uint8_t b = *data++;
    spi_write (b);
    crc = _crc_xmodem_update (crc, b);
  }
  if (data) {
    /* only send CRC when we actually did send some data */
    spi_write (crc >> 8);
    spi_write (crc & 0xff);
  }
}

uint32_t sd_write_block ( const uint8_t *  data,
uint32_t  address 
)

write one block to SD card

Parameters:
data pointer to buffer for data to write
address card address to write data to, use sd_block2addr()

write one block to SD card

Parameters:
data buffer with data to write
address SD card address to write data to

{
  uint8_t r;
  uint8_t resp_token;
  uint16_t r2;

  do {
    sd_select ();
    sd_send_cmd_arg32 (SD_WRITE_BLOCK, address);
    r = sd_recv_response1 (&sd_cmd_profiles[sd_write_cmd_profile]);
    if (r == 0) {
      sd_send_data (data, sd_current_blocklen, SDTOKEN_START);
      resp_token = sd_recv_data_response (&sd_cmd_profiles[sd_write_data_profile]);
      if ((resp_token & 0x1F) == 0x05) /* data was accepted */ {
        sd_busy_wait ();
      } else {
        printf_P (PSTR("SDwrite: response %02x "), resp_token);
      }
    }
    sd_deselect ();
  } while (r == 0 && (resp_token & 0x1F) == 0x0B); /* repeat on CRC error */

  if (r == 0) {
    /* check status */
    sd_select ();
    sd_send_cmd_0 (SD_SEND_STATUS);
    r2 = sd_recv_response2 (&sd_cmd_profiles[sd_write_cmd_profile]);
    sd_deselect ();
    return r2;
  } else {
    /* error in sending command */
    sd_r1_print_P (PSTR("SDwrite"), r);
  }
  
  return ((uint32_t)r) << 16;
}

uint8_t sd_write_multiple_block ( const uint8_t *const   data[],
uint8_t  cnt,
uint32_t  address 
)

write multiple blocks to SD card

Write some blocks to SD card. This function allows to provide the data to write in discontigous buffers.

Parameters:
data[] array of pointers to data buffers
cnt number of blocks to write
address card address of first block to write, use sd_block2addr()

{
  uint8_t r;
  sd_select ();
  sd_send_cmd_arg32 (SD_READ_MULTIPLE_BLOCK, address);
  r = sd_recv_response1 (&sd_cmd_profiles[sd_write_cmd_profile]);
  if (r == 0) {
    for (; cnt; cnt--, data) {
      sd_send_data (*data++, sd_current_blocklen, SDTOKEN_START_MULTIPLE);
      r = sd_recv_data_response (&sd_cmd_profiles[sd_write_data_profile]);
      if (r) break;
      sd_busy_wait ();
    }
    sd_send_data (0, 0, SDTOKEN_STOP_MULTIPLE);
  }
  sd_deselect ();
  return r;
}

uint8_t sd_write_multiple_block2 ( sd_wr_mult_block_data_t const   data[],
uint8_t  cnt,
uint32_t  address 
)

write multiple blocks to SD card

Write some blocks to SD card. This function allows to provide the data to write in discontigous buffers.

Parameters:
data[] array of pointers to data buffers
cnt number of blocks to write
address card address of first block to write, use sd_block2addr()

{
  uint8_t r;
  sd_select ();
  sd_send_cmd_arg32 (SD_READ_MULTIPLE_BLOCK, address);
  r = sd_recv_response1 (&sd_cmd_profiles[sd_write_cmd_profile]);
  if (r == 0) {
    const uint8_t * ptr = data->buffer;
    uint8_t num_blocks = data->num_blocks;
    
    for (; cnt; cnt--) {
      if (num_blocks == 0) break;

      sd_send_data (ptr, sd_current_blocklen, SDTOKEN_START_MULTIPLE);
      r = sd_recv_data_response (&sd_cmd_profiles[sd_write_data_profile]);
      if (r) break;
      if (--num_blocks == 0) {
        data++;
        ptr = data->buffer;
        num_blocks = data->num_blocks;
      } else {
        ptr += sd_current_blocklen;
      }
      sd_busy_wait ();
    }
    sd_send_data (0, 0, SDTOKEN_STOP_MULTIPLE);
  }
  sd_deselect ();
  return r;
}