/** library to communicate with an SD card flash memory using the SPI mode (code) * @file * @author King Kévin * @copyright SPDX-License-Identifier: GPL-3.0-or-later * @date 2017 * @note peripherals used: SPI @ref flash_sdcard_spi * @warning all calls are blocking * @implements SD Specifications, Part 1, Physical Layer, Simplified Specification, Version 6.00, 10 April 10 2017 * @todo use SPI unidirectional mode, use DMA, force/wait going to idle state when initializing, filter out reserved values, check sector against size */ /* standard libraries */ #include // standard integer types #include // general utilities /* STM32 (including CM3) libraries */ #include // Cortex M3 utilities #include // real-time control clock library #include // general purpose input output library #include // SPI library #include "global.h" // global utilities #include "flash_sdcard.h" // SD card header and definitions /** @defgroup flash_sdcard_spi SPI used to communication with SD card * @{ */ #define FLASH_SDCARD_SPI 1 /**< SPI peripheral */ /** @} */ /** if the card has been initialized successfully */ static bool initialized = false; /** maximum N_AC value (in 8-clock cycles) (time between the response token R1 and data block when reading data (see section 7.5.4) * @note this is set to N_CR until we can read CSD (see section 7.2.6) */ static uint32_t n_ac = 8; /** is it a Standard Capacity SD card (true), or High Capacity SD cards (false) * @note this is indicated in the Card Capacity Status bit or OCR (set for high capacity) * @note this is important for addressing: for standard capacity cards the address is the byte number, for high capacity cards it is the 512-byte block number */ static bool sdsc = false; /** size of card in bytes */ static uint64_t sdcard_size = 0; /** size of an erase block bytes */ static uint32_t erase_size = 0; /** table for CRC-7 calculation for the command messages (see section 4.5) * @note faster than calculating the CRC and doesn't cost a lot of space * @note generated using pycrc --width=7 --poly=0x09 --reflect-in=false --reflect-out=false --xor-in=0x00 --xor-out=0x00 --generate=table */ static const uint8_t crc7_table[] = { 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 }; /** wait one SPI round (one SPI word) */ static void flash_sdcard_spi_wait(void) { spi_send(SPI(FLASH_SDCARD_SPI), 0xffff); // send not command token (i.e. starting with 1) } /** read one SPI word * @return SPI word read */ static uint16_t flash_sdcard_spi_read(void) { spi_send(SPI(FLASH_SDCARD_SPI), 0xffff); // send not command token (i.e. starting with 1) (void)SPI_DR(SPI(FLASH_SDCARD_SPI)); // clear RXNE flag (by reading previously received data (not done by spi_read or spi_xref) while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until Tx buffer is empty before clearing the (previous) RXNE flag while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_RXNE)); // wait for next data to be available return SPI_DR(SPI(FLASH_SDCARD_SPI)); // return received adat } /** test if card is present * @return if card has been detected * @note this use the SD card detection mechanism (CD/CS is high card is inserted due to the internal 50 kOhm resistor) */ static bool flash_sdcard_card_detect(void) { rcc_periph_clock_enable(RCC_SPI_NSS_PORT(FLASH_SDCARD_SPI)); // enable clock for NSS pin port peripheral for SD card CD signal gpio_set_mode(SPI_NSS_PORT(FLASH_SDCARD_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SPI_NSS_PIN(FLASH_SDCARD_SPI)); // set NSS pin as input to read CD signal gpio_clear(SPI_NSS_PORT(FLASH_SDCARD_SPI), SPI_NSS_PIN(FLASH_SDCARD_SPI)); // pull pin low to avoid false positive when card in not inserted return (0!=gpio_get(SPI_NSS_PORT(FLASH_SDCARD_SPI), SPI_NSS_PIN(FLASH_SDCARD_SPI))); // read CD signal: is card is present the internal 50 kOhm pull-up resistor will override our 1 MOhm pull-down resistor and set the signal high (see section 6.2) } /** transmit command token * @param[in] index command index * @param[in] argument command argument */ static void flash_sdcard_send_command(uint8_t index, uint32_t argument) { uint8_t command[5] = { 0x40+(index&0x3f), argument>>24, argument>>16, argument>>8, argument>>0 }; // commands are 5 bytes long, plus 1 bytes of CRC (see section 7.3.1.1) uint8_t crc7 = 0x00; // CRC-7 checksum for command message // send command for (uint8_t i=0; i>8); // save byte data[i*2+1] = (word>>0); // save byte } flash_sdcard_spi_read(); // read CRC (the CRC after the data block should clear the computed CRC) if (SPI_CRC_RXR(FLASH_SDCARD_SPI)) { // CRC is wrong token = 0xff; } else { // no error occurred token = 0; } // switch back to 8-bit SPI frames while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until the end of any transmission while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy before disabling spi_disable(SPI(FLASH_SDCARD_SPI)); // disable SPI to change format spi_disable_crc(SPI(FLASH_SDCARD_SPI)); // disable CRC since we don't use it anymore (and this allows us to clear the CRC next time we use it) spi_set_dff_8bit(SPI(FLASH_SDCARD_SPI)); // set SPI frame to 8 bits spi_enable(SPI(FLASH_SDCARD_SPI)); // enable SPI back } else { // start block token not received token = 0xff; } return token; } /** write a data block * @param[in] data data block to write * @param[in] size size of response to read (a multiple of 2) * @return data response token (0xff for other errors) */ static uint8_t flash_sdcard_write_block(uint8_t* data, size_t size) { if (size%2 || 0==size || NULL==data) { // can't (and shouldn't) read odd number of bytes return 0xff; } spi_send(SPI(FLASH_SDCARD_SPI), 0xfe); // send start block token (see section 7.3.3.2) // switch to 16-bits SPI data frame so we can use use built-in CRC-16 while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until the end of any transmission while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy before disabling spi_disable(SPI(FLASH_SDCARD_SPI)); // disable SPI to change format spi_set_dff_16bit(SPI(FLASH_SDCARD_SPI)); // set SPI frame to 16 bits SPI_CRC_PR(FLASH_SDCARD_SPI) = 0x1021; // set CRC-16-CCITT polynomial for data blocks (x^16+x^12+x^5+1) (see section 7.2.3) spi_enable_crc(SPI(FLASH_SDCARD_SPI)); // enable and clear CRC spi_enable(SPI(FLASH_SDCARD_SPI)); // enable SPI back // send block data (ideally use DMA, but switching makes it more complex and this part doesn't take too much time) for (size_t i=0; i>6)) || (!sdsc && 0==(csd[0]>>6))) { return false; } // calculate N_AC value (we use our set minimum frequency 16 MHz to calculate time) if (sdsc) { // calculate N_AC using TAAC and NSAC static const float TAAC_UNITS[] = {1E-9, 10E-9, 100E-9, 1E-6, 10E-6, 100E-6, 1E-3, 10E-3}; // (see table 5-5) static const float TAAC_VALUES[] = {10.0, 1.0, 1.2, 1.3, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 7.0, 8.0}; // (see table 5-5) double taac = TAAC_VALUES[(csd[1]>>2)&0xf]*TAAC_UNITS[csd[1]&0x7]; // time in ns n_ac=100*((taac*16E6)+(csd[2]*100))/8; // (see section 7.5.4) } else { // value is fixed to 100 ms n_ac=100E-3*16E6/8; } // calculate card size if (sdsc) { // see section 5.3.2 uint16_t c_size = (((uint16_t)csd[6]&0x03)<<10)+((uint16_t)csd[7]<<2)+(csd[8]>>6); uint8_t c_size_mutl = ((csd[9]&0x03)<<1)+((csd[10]&0x80)>>7); uint8_t read_bl_len = (csd[5]&0x0f); sdcard_size = ((c_size+1)*(1UL<<(c_size_mutl+2)))*(1UL<>7)+1)<<(((csd[12]&0x03)<<2)+(csd[13]>>6)); } else { uint8_t status[64] = {0}; // SD status (see section 4.10.2) uint16_t r2 = flash_sdcard_status(status); // get status (see table 7-4) if (r2) { // error occurred return false; } erase_size = (8192UL<<(status[10]>>4)); // calculate erase size (see table 4-44, section 4.10.2.4) } // ensure block length is 512 bytes for SDSC (should be per default) to we match SDHC/SDXC block size if (sdsc) { r1 = flash_sdcard_command_response(16, 512, NULL, 0); // set block size using CMD16 (SET_BLOCKLEN) (see table 7-3) if (r1) { // error occurred return false; } } // try to switch to high speed mode (see section 7.2.14/4.3.10) if (csd[4]&0x40) { // ensure CMD6 is supported by checking if command class 10 is set uint32_t n_ac_back = n_ac; // backup N_AC n_ac = 100E-3*16E6/8; // temporarily set timeout to 100 ms (see section 4.3.10.1) // query access mode (group function 1) to check if high speed is supported (fPP=50MHz at 3.3V, we can be faster) uint8_t fnc[64] = {0}; // function status response (see table 4-12) r1 = flash_sdcard_data_read(6, 0x00fffff1, fnc, sizeof(fnc)); // check high speed function using CMD6 (SWITCH_FUNC) to check (mode 0) access mode (function group 1) (see table 7-3/4-30) if (r1) { // error occurred return false; } if (0x1==(fnc[16]&0x0f)) { // we can to access mode function 1 (see table 4-12) r1 = flash_sdcard_data_read(6, 0x80fffff1, fnc, sizeof(fnc)); // switch to high speed function using CMD6 (SWITCH_FUNC) to switch (mode 1) access mode (function group 1) (see table 7-3/4-30) if (r1) { // error occurred return false; } if (0x1!=(fnc[16]&0x0f)) { // could not switch to high speed return false; } // we can switch clock frequency to fPP (max. 50 MHz) (see section 6.6.7) while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until the end of any transmission while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy before disabling spi_disable(SPI(FLASH_SDCARD_SPI)); // disable SPI to change clock speed spi_set_baudrate_prescaler(SPI(FLASH_SDCARD_SPI), SPI_CR1_BR_FPCLK_DIV_2); // set clock speed to 36 MHz (72/2=36 < 50 MHz) spi_enable(SPI(FLASH_SDCARD_SPI)); // enable SPI back n_ac_back /= 2; // since we go twice faster the N_AC timeout has to be halved } n_ac = n_ac_back; // restore N_AC } initialized = true; return initialized; } uint64_t flash_sdcard_size(void) { return sdcard_size; } uint32_t flash_sdcard_erase_size(void) { return erase_size; } bool flash_sdcard_read_data(uint32_t block, uint8_t* data) { if (NULL==data) { return false; } if (sdsc) { // the address for standard capacity cards must be provided in bytes if (block>UINT32_MAX/512) { // check for integer overflow return false; } else { block *= 512; // calculate byte address from block address } } return (0==flash_sdcard_data_read(17, block, data, 512)); // read single data block using CMD17 (READ_SINGLE_BLOCK) (see table 7-3) } bool flash_sdcard_write_data(uint32_t block, uint8_t* data) { if (NULL==data) { return false; } if (sdsc) { // the address for standard capacity cards must be provided in bytes if (block>UINT32_MAX/512) { // check for integer overflow return false; } else { block *= 512; // calculate byte address from block address } } uint8_t drt = flash_sdcard_data_write(24, block, data, 512); // write single data block using CMD24 (WRITE_SINGLE_BLOCK) (see table 7-3) if (0x05!=(drt&0x1f)) { // write block failed return false; } // get status to check if programming succeeded uint8_t r2[1] = {0}; // to store response token R2 (see section 7.3.2.3) uint8_t r1 = flash_sdcard_command_response(13, 0, r2, sizeof(r2)); // get SD status using CMD13 (SEND_STATUS) (see table 7-3) if (0x00!=r1) { // error occurred return false; } else if (r2[0]) { // programming error return false; } return true; // programming succeeded }