add library to read SD card flash memery: card identification implemented
This commit is contained in:
parent
87b788b19e
commit
30c72a78a8
|
@ -0,0 +1,225 @@
|
||||||
|
/* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/** library to communicate with an SD card flash memory using the SPI mode (code)
|
||||||
|
* @file flash_sdcard.c
|
||||||
|
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* standard libraries */
|
||||||
|
#include <stdint.h> // standard integer types
|
||||||
|
#include <stdlib.h> // general utilities
|
||||||
|
|
||||||
|
/* STM32 (including CM3) libraries */
|
||||||
|
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||||
|
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||||
|
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||||
|
#include <libopencm3/stm32/spi.h> // 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 */
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** 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)
|
||||||
|
while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until Tx buffer is empty before clearing the (previous) RXNE flag
|
||||||
|
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_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<LENGTH(command); i++) {
|
||||||
|
spi_send(SPI(FLASH_SDCARD_SPI), command[i]); // send data
|
||||||
|
crc7 = (crc7_table[((crc7<<1)^command[i])])&0x7f; // update checksum
|
||||||
|
}
|
||||||
|
spi_send(SPI(FLASH_SDCARD_SPI), (crc7<<1)+0x01); // send CRC value (see section 7.3.1.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** transmit command token and receive response token
|
||||||
|
* @param[in] index command index
|
||||||
|
* @param[in] argument command argument
|
||||||
|
* @param[out] response response data to read (if no error occurred)
|
||||||
|
* @param[in] size size of response to read
|
||||||
|
* @return response token R1 of 0xff if error occurred or card is not present
|
||||||
|
*/
|
||||||
|
static bool flash_sdcard_command_response(uint8_t index, uint32_t argument, uint8_t* response, size_t size)
|
||||||
|
{
|
||||||
|
// send command token
|
||||||
|
while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy
|
||||||
|
gpio_clear(SPI_NSS_PORT(FLASH_SDCARD_SPI), SPI_NSS_PIN(FLASH_SDCARD_SPI)); // set CS low to select slave and start SPI mode (see section 7.2)
|
||||||
|
flash_sdcard_spi_wait(); // wait for N_CS before writing command (see section 7.5.1.1)
|
||||||
|
flash_sdcard_send_command(index, argument); // send command token
|
||||||
|
|
||||||
|
// verify response token R1
|
||||||
|
flash_sdcard_spi_wait(); // wait for N_CR before reading response (see section 7.5.1.1)
|
||||||
|
uint8_t r1 = flash_sdcard_spi_read(); // get response (see section 7.3.2.1)
|
||||||
|
if (0x00==(r1&0xfe) && 0!=size && NULL!=response) { // we have to read a response
|
||||||
|
for (size_t i=0; i<size; i++) {
|
||||||
|
response[i] = flash_sdcard_spi_read(); // get byte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// end communication
|
||||||
|
flash_sdcard_spi_wait(); // wait for N_EC before closing communication (see section 7.5.1.1)
|
||||||
|
while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until Tx buffer is empty
|
||||||
|
while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy (= transmission completed)
|
||||||
|
gpio_set(SPI_NSS_PORT(FLASH_SDCARD_SPI), SPI_NSS_PIN(FLASH_SDCARD_SPI)); // set CS high to unselect card
|
||||||
|
flash_sdcard_spi_wait(); // wait for N_DS before allowing any further communication (see section 7.5.1.1)
|
||||||
|
while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until Tx buffer is empty
|
||||||
|
while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy (= transmission completed)
|
||||||
|
return r1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool flash_sdcard_setup(void)
|
||||||
|
{
|
||||||
|
// check if card is present
|
||||||
|
if (!flash_sdcard_card_detect()) {
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure SPI peripheral
|
||||||
|
rcc_periph_clock_enable(RCC_SPI_SCK_PORT(FLASH_SDCARD_SPI)); // enable clock for GPIO peripheral for clock signal
|
||||||
|
gpio_set_mode(SPI_SCK_PORT(FLASH_SDCARD_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_SCK_PIN(FLASH_SDCARD_SPI)); // set SCK as output (clock speed will be negotiated later)
|
||||||
|
rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(FLASH_SDCARD_SPI)); // enable clock for GPIO peripheral for MOSI signal
|
||||||
|
gpio_set_mode(SPI_MOSI_PORT(FLASH_SDCARD_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI_PIN(FLASH_SDCARD_SPI)); // set MOSI as output
|
||||||
|
rcc_periph_clock_enable(RCC_SPI_MISO_PORT(FLASH_SDCARD_SPI)); // enable clock for GPIO peripheral for MISO signal
|
||||||
|
gpio_set_mode(SPI_MISO_PORT(FLASH_SDCARD_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SPI_MISO_PIN(FLASH_SDCARD_SPI)); // set MISO as input
|
||||||
|
gpio_set(SPI_MISO_PORT(FLASH_SDCARD_SPI), SPI_MISO_PIN(FLASH_SDCARD_SPI)); // pull pin high to detect when the card is not answering (or not present) since responses always start with MSb 0
|
||||||
|
rcc_periph_clock_enable(RCC_SPI_NSS_PORT(FLASH_SDCARD_SPI)); // enable clock for GPIO peripheral for NSS (CS) signal
|
||||||
|
gpio_set_mode(SPI_NSS_PORT(FLASH_SDCARD_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, SPI_NSS_PIN(FLASH_SDCARD_SPI)); // set NSS (CS) as output
|
||||||
|
rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function
|
||||||
|
rcc_periph_clock_enable(RCC_SPI(FLASH_SDCARD_SPI)); // enable clock for SPI peripheral
|
||||||
|
spi_reset(SPI(FLASH_SDCARD_SPI)); // clear SPI values to default
|
||||||
|
spi_init_master(SPI(FLASH_SDCARD_SPI), SPI_CR1_BAUDRATE_FPCLK_DIV_256, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); // initialise SPI as master, divide clock by 256 (72E6/256=281 kHz) since maximum SD card clock frequency (fOD, see section 7.8/6.6.6) during initial card-identification mode is 400 kHz (maximum SPI PCLK clock is 72 Mhz, depending on which SPI is used), set clock polarity to idle low (not that important), set clock phase to do bit change on falling edge (from SD card spec, polarity depends on clock phase), use 8 bits frames (as per spec), use MSb first
|
||||||
|
spi_set_full_duplex_mode(SPI(FLASH_SDCARD_SPI)); // ensure we are in full duplex mode
|
||||||
|
spi_enable_software_slave_management(SPI(FLASH_SDCARD_SPI)); // control NSS (CS) manually
|
||||||
|
spi_set_nss_high(SPI(FLASH_SDCARD_SPI)); // set NSS high (internally) so we can output
|
||||||
|
spi_enable_ss_output(SPI(FLASH_SDCARD_SPI)); // disable NSS output since we control CS manually
|
||||||
|
gpio_set(SPI_NSS_PORT(FLASH_SDCARD_SPI), SPI_NSS_PIN(FLASH_SDCARD_SPI)); // set CS high to unselect card
|
||||||
|
// sadly we can't use the interrupts as events to sleep (WFE) since sleep disables also communication (e.g. going to sleep until Rx buffer is not empty prevents transmission)
|
||||||
|
spi_enable(SPI(FLASH_SDCARD_SPI)); // enable SPI
|
||||||
|
|
||||||
|
// start card-identification (see section 7.2.1/4.2)
|
||||||
|
uint8_t r1 = 0;
|
||||||
|
// send CMD0 (GO_IDLE_START) to start the card identification (see section 7.2.1)
|
||||||
|
r1 = flash_sdcard_command_response(0, 0, NULL, 0); // (see table 7-3)
|
||||||
|
if (0x01!=r1) { // error occurred, not in idle state
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// send CMD8 (SEND_IF_COND) to inform about voltage (1: 2.7-3.6V, aa: recommended check pattern) (see section 7.2.1)
|
||||||
|
uint8_t r7[4] = {0}; // to store response toke R7 (see section 7.3.2.6)
|
||||||
|
r1 = flash_sdcard_command_response(8, 0x000001aa, r7, sizeof(r7)); // (see table 7-3)
|
||||||
|
if (0x01==r1) { // command supported, in idle state
|
||||||
|
if (!(r7[2]&0x1)) { // 2.7-3.6V not supported (see table 5-1)
|
||||||
|
return false;
|
||||||
|
} else if (0xaa!=r7[3]) { // recommended pattern not returned (see section 4.3.13)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (0x05!=r1) { // illegal command (cards < physical spec v2.0 don't support CMD8) (see section 7.2.1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// send CMD58 (READ_OCR) to read Operation Conditions Register (see section 7.2.1)
|
||||||
|
uint8_t r3[4] = {0}; // to store response token R3 (see section 7.3.2.4)
|
||||||
|
r1 = flash_sdcard_command_response(58, 0, r3, sizeof(r3)); // (see table 7-3)
|
||||||
|
if (0x01!=r1) { // error occurred, not in idle state
|
||||||
|
return false;
|
||||||
|
} else if (!(r3[1]&0x30)) { // 3.3V not supported (see table 5-1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
// send CMD55 (APP_CMD) to issue following application command (see table 7-4)
|
||||||
|
r1 = flash_sdcard_command_response(55, 0, NULL, 0); // (see table 7-3)
|
||||||
|
if (0x01!=r1) { // error occurred, not in idle state
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// send ACMD41 (SD_SEND_OP_COND) with Host Capacity Support (0b: SDSC Only Host, 1b: SDHC or SDXC Supported) (see section 7.2.1)
|
||||||
|
r1 = flash_sdcard_command_response(41, 0x40000000, NULL, 0); // (see table 7-4)
|
||||||
|
if (r1&0xfe) { // error occurred
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (0x00!=r1); // wait until card is ready (see section 7.2.1)
|
||||||
|
// send CMD58 (READ_OCR) to read Card Capacity Status (CCS) (see section 7.2.1)
|
||||||
|
r1 = flash_sdcard_command_response(58, 0, r3, sizeof(r3)); // (see table 7-3)
|
||||||
|
if (r1&0xfe) { // error occurred
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// CCS is bit 30 in OCR, but we don't really care about the result (see table 5-1)
|
||||||
|
// now the card identification is complete and we should be in data-transfer mode (see figure 7-1)
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/** library to communicate with an SD card flash memory using the SPI mode (API)
|
||||||
|
* @file flash_sdcard.h
|
||||||
|
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||||
|
* @date 2017
|
||||||
|
* @note peripherals used: SPI @ref flash_sdcard_spi
|
||||||
|
* @warning all calls are blocking
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** setup communication with SD card
|
||||||
|
* @return if card has been initialized correctly
|
||||||
|
*/
|
||||||
|
bool flash_sdcard_setup(void);
|
Loading…
Reference in New Issue