Data Structures | Defines | Typedefs | Enumerations | Functions

sdcmd.c File Reference

communication with SD card - implementation More...

#include "config.h"
#include <sdcmd.h>
#include <spi.h>
#include <util/crc16.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include "crc7.h"

Data Structures

struct  sd_cmd_profile_s
 timeouts for different SD command types More...

Defines

#define spi_sdread()   spi_read(0xff)
#define dprintf_P(fmt,...)
#define T(v)
#define T1(v)   T(v|0), T(v|1), T(v|2), T(v|3), T(v|4), T(v|5), T(v|6), T(v|7)

Typedefs

typedef struct sd_cmd_profile_s sd_cmd_profile_t
 timeouts for different SD command types

Enumerations

enum  sd_cmd_profile_list {
  sd_startup_cmd_profile, sd_read_cmd_profile, sd_read_data_profile, sd_write_cmd_profile,
  sd_write_data_profile, SD_CMD_NUM_PROFILES
}

Functions

 ISR (TIMER3_COMPA_vect)
 ISR (TIMER3_OVF_vect)
uint8_t sd_go_idle (void)
 put SD card into IDLE state
uint8_t sd_get_if_cond (uint8_t *version, uint8_t *vhs, uint8_t *check)
 get SD card interface condition
uint8_t sd_get_cid (sd_cid_data_t *cid)
 read card identification data from SD card
uint16_t sd_get_status (void)
 read card status register
uint8_t sd_set_blocklen (sd_blen_t blocklen)
 set SD block length
uint8_t sd_read_block (uint8_t *data, uint32_t address)
 read one data block from SD card
uint8_t sd_read_multiple_block (uint8_t *const data[], uint8_t cnt, uint32_t address)
 read multiple data 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 data blocks from SD card
uint32_t sd_write_block (const uint8_t *data, uint32_t address)
 write one data block to SD card
uint8_t sd_write_multiple_block (const uint8_t *const data[], uint8_t cnt, uint32_t address)
 write multiple data 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 data blocks to SD card
uint8_t sd_program_csd (sd_csd_data_t *csd)
 program card specific data register
uint8_t sd_get_ocr (uint32_t *ocr)
 read operation condition register from card
uint32_t sd_block2addr (uint32_t block)
 convert block number into address used in sending R/W command to card
void sd_force_deselect ()
uint16_t sd_init_card (uint8_t dbg)
 initialize SPI interface and SD card

Detailed Description

communication with SD card - implementation


Define Documentation

#define dprintf_P (   fmt,
  ... 
)
Value:
do {            \
    if (debug) printf_P (fmt, ## __VA_ARGS__);  \
  } while (0)
#define T (   v  ) 
Value:
(VAL_CSD_TRAN_SPEED(v) ?                                        \
              (uint8_t)(((float)F_CPU+VAL_CSD_TRAN_SPEED(v)-1)/VAL_CSD_TRAN_SPEED(v)) : \
              0xff)

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 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 data block from SD card

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 data blocks from SD card

read multiple blocks from SD card

{
  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 data blocks from SD card

read multiple blocks from SD card

{
  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;
}

uint32_t sd_write_block ( const uint8_t *  data,
uint32_t  address 
)

write one data block to SD card

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 data blocks to SD card

write multiple blocks to SD card

{
  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 data blocks to SD card

write multiple blocks to SD card

{
  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;
}