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 |
communication with SD card - implementation
#define dprintf_P | ( | fmt, | ||
... | ||||
) |
do { \ if (debug) printf_P (fmt, ## __VA_ARGS__); \ } while (0)
#define T | ( | v | ) |
(VAL_CSD_TRAN_SPEED(v) ? \
(uint8_t)(((float)F_CPU+VAL_CSD_TRAN_SPEED(v)-1)/VAL_CSD_TRAN_SPEED(v)) : \
0xff)
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
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
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; }