Skip to content

Instantly share code, notes, and snippets.

@FREEWING-JP
Forked from Ralim/SDCard.cpp
Created October 19, 2017 15:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FREEWING-JP/7fedab9d32deae7a0a5f67541e42cc81 to your computer and use it in GitHub Desktop.
Save FREEWING-JP/7fedab9d32deae7a0a5f67541e42cc81 to your computer and use it in GitHub Desktop.
/*
* DualSD.cpp
*
* Created on: 1 Nov 2016
* Author: ralim
*/
#include <SDCard.hpp>
SDCard::SDCard(SPI_HandleTypeDef* hspi, uint16_t cs1Pin,
GPIO_TypeDef* cs1Port) {
_spi = hspi;
_cs1Pin = cs1Pin;
_cs1Port = cs1Port;
_sdSize = 0;
}
uint32_t SDCard::getSize() {
if (_sdSize == 0) {
//We have not read the disk size just yet :O
csd_t csd;
if (!readCSD(&csd))
return 0;
if (csd.v1.csd_ver == 0) {
uint8_t read_bl_len = csd.v1.read_bl_len;
uint16_t c_size = (csd.v1.c_size_high << 10)
| (csd.v1.c_size_mid << 2) | csd.v1.c_size_low;
uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1)
| csd.v1.c_size_mult_low;
c_size = (uint32_t) (c_size + 1) << (c_size_mult + read_bl_len - 7);
c_size /= 1024;
} else if (csd.v2.csd_ver == 1) {
uint32_t c_size = ((uint32_t) csd.v2.c_size_high << 16)
| (csd.v2.c_size_mid << 8) | csd.v2.c_size_low;
c_size *= 1024;
_sdSize = c_size;
} else {
}
}
return _sdSize;
}
/** read CID or CSR register */
uint8_t SDCard::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
if (cardCommand(cmd, 0)) {
deselectCard();
return false;
}
uint8_t temp = 0xFF;
while (temp == 0xFF) {
SPI_Recieve(&temp, 1);
}
// transfer data
SPI_Recieve(dst, 16);
SPI_Recieve(&temp, 1); //CRC1
SPI_Recieve(&temp, 1); //CRC2
deselectCard();
return true;
}
bool SDCard::readBlock(uint32_t blockaddr, uint8_t* buffer) {
if (cardCommand(CMD17, blockaddr)) {
/*
* Error
*/
deselectCard();
return false;
}
uint8_t temp = 0xFF;
while (temp == 0xFF) {
HAL_SPI_Receive(_spi, &temp, 1, 100);
}
SPI_Recieve(buffer, 512);
//eat the CRC
temp = 0xFF;
SPI_Recieve(&temp, 1);
temp = 0xFF;
SPI_Recieve(&temp, 1);
deselectCard();
return true;
}
bool SDCard::writeBlock(uint32_t blockaddr, uint8_t* buffer) {
//The cardCommand will select the card so we have to make sure we clean up
if (cardCommand(CMD24, blockaddr)) {
/*
* Error
*/
deselectCard();
return false;
}
/*
* Write the data
*/
uint8_t temp = DATA_START_BLOCK;
HAL_SPI_Transmit(_spi, &temp, 1, 100);
HAL_SPI_Transmit(_spi, buffer, 512, 100);
temp = 0xFF;
HAL_SPI_Transmit(_spi, &temp, 1, 100);
HAL_SPI_Transmit(_spi, &temp, 1, 100);
//read response
SPI_Recieve(&temp, 1);
if ((temp & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
/*
* Error
*/
deselectCard();
return false;
}
// wait for flash programming to complete
waitUntilReady();
// response is r2 so get and check two bytes for nonzero
if (cardCommand(CMD13, 0)) {
/*
* Error
*/
deselectCard();
return false;
}
SPI_Recieve(&temp, 1);
if (temp) {
/*
* Error
*/
deselectCard();
return false;
}
deselectCard();
return true;
}
void SDCard::waitUntilReady() {
uint8_t ans[1] = { 0 };
while (ans[0] != 0xFF) {
SPI_Recieve(ans, 1);
}
}
bool SDCard::initalize() {
_spi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; //slow down at first
HAL_SPI_Init(_spi); //apply the speed change
deselectCard();
//We must supply at least 74 clocks with CS high
uint8_t buffer[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
HAL_SPI_Transmit(_spi, buffer, 4, 100);
HAL_SPI_Transmit(_spi, buffer, 4, 100);
HAL_SPI_Transmit(_spi, buffer, 4, 100);
HAL_Delay(5);
selectCard();
uint8_t status;
// command to go idle in SPI mode
while ((status = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
}
// check SD version
if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) {
deselectCard();
return false; //Unsupported
} else {
// only need last byte of r7 response
HAL_SPI_Receive(_spi, buffer, 4, 100);
if (buffer[3] != 0XAA) {
return false; //failed check
}
}
// initialize card and send host supports SDHC
while ((status = cardAcmd(ACMD41, 0X40000000)) != R1_READY_STATE) {
}
// if SD2 read OCR register to check for SDHC card
if (cardCommand(CMD58, 0)) {
deselectCard();
return false;
}
//discard OCR reg
SPI_Recieve(buffer, 4);
deselectCard();
_spi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; //speed back up
HAL_SPI_Init(_spi); //apply the speed change
return true;
}
uint8_t SDCard::cardCommand(uint8_t command, uint32_t arg) {
uint8_t res = 0xFF;
/*HAL_SPI_Transmit(_spi, &res, 1, 100);
HAL_SPI_Transmit(_spi, &res, 1, 100);
HAL_SPI_Transmit(_spi, &res, 1, 100);
HAL_SPI_Transmit(_spi, &res, 1, 100);
HAL_SPI_Transmit(_spi, &res, 1, 100);
HAL_SPI_Transmit(_spi, &res, 1, 100);
HAL_SPI_Transmit(_spi, &res, 1, 100);*/
selectCard();
waitUntilReady(); //wait for card to no longer be busy
uint8_t commandSequence[] = { (uint8_t) (command | 0x40), (uint8_t) (arg
>> 24), (uint8_t) (arg >> 16), (uint8_t) (arg >> 8), (uint8_t) (arg
& 0xFF), 0xFF };
if (command == CMD0)
commandSequence[5] = 0x95;
else if (command == CMD8)
commandSequence[5] = 0x87;
HAL_SPI_Transmit(_spi, commandSequence, 6, 100);
//Data sent, now await Response
uint8_t count = 20;
while ((res & 0x80) && count) {
SPI_Recieve(&res, 1);
count--;
}
return res;
}
void SDCard::selectCard() {
HAL_GPIO_WritePin(_cs1Port, _cs1Pin, GPIO_PIN_RESET);
}
HAL_StatusTypeDef SDCard::SPI_Recieve(uint8_t* pData, uint16_t Size) {
HAL_StatusTypeDef errorcode = HAL_OK;
/* Process Locked */
__HAL_LOCK(_spi);
/* Don't overwrite in case of HAL_SPI_STATE_BUSY_RX */
if (_spi->State == HAL_SPI_STATE_READY) {
_spi->State = HAL_SPI_STATE_BUSY_TX_RX;
}
/* Set the transaction information */
_spi->ErrorCode = HAL_SPI_ERROR_NONE;
_spi->pRxBuffPtr = (uint8_t *) pData;
_spi->RxXferCount = Size;
_spi->RxXferSize = Size;
_spi->pTxBuffPtr = (uint8_t *) pData;
_spi->TxXferCount = Size;
_spi->TxXferSize = Size;
/*Init field not used in handle to zero */
_spi->RxISR = NULL;
_spi->TxISR = NULL;
/* Check if the SPI is already enabled */
if ((_spi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE) {
/* Enable SPI peripheral */
__HAL_SPI_ENABLE(_spi);
}
/* Transmit and Receive data in 8 Bit mode */
while ((_spi->RxXferCount > 0U)) {
*(__IO uint8_t *) &_spi->Instance->DR = 0xFF; //send data
while (!(__HAL_SPI_GET_FLAG(_spi, SPI_FLAG_TXE)))
;
while (!(__HAL_SPI_GET_FLAG(_spi, SPI_FLAG_RXNE)))
;
(*(uint8_t *) pData++) = _spi->Instance->DR;
_spi->RxXferCount--;
}
if (lSPI_WaitFlagStateUntilTimeout(_spi, SPI_FLAG_BSY, RESET, 100,
HAL_GetTick()) != HAL_OK) {
_spi->ErrorCode |= HAL_SPI_ERROR_FLAG;
errorcode = HAL_TIMEOUT;
}
_spi->State = HAL_SPI_STATE_READY;
__HAL_UNLOCK(_spi);
return errorcode;
}
void SDCard::deselectCard() {
HAL_GPIO_WritePin(_cs1Port, _cs1Pin, GPIO_PIN_SET);
}
/**
* @brief Handle SPI Communication Timeout.
* @param hspi: pointer to a SPI_HandleTypeDef structure that contains
* the configuration information for SPI module.
* @param Flag: SPI flag to check
* @param State: flag state to check
* @param Timeout: Timeout duration
* @param Tickstart: tick start value
* @retval HAL status
*/
HAL_StatusTypeDef SDCard::lSPI_WaitFlagStateUntilTimeout(
SPI_HandleTypeDef *hspi, uint32_t Flag, uint32_t State,
uint32_t Timeout, uint32_t Tickstart) {
while ((hspi->Instance->SR & Flag) != State) {
if (Timeout != HAL_MAX_DELAY) {
if ((Timeout == 0U) || ((HAL_GetTick() - Tickstart) >= Timeout)) {
/* Disable the SPI and reset the CRC: the CRC value should be cleared
on both master and slave sides in order to resynchronize the master
and slave for their respective CRC calculation */
/* Disable TXE, RXNE and ERR interrupts for the interrupt process */
__HAL_SPI_DISABLE_IT(hspi,
(SPI_IT_TXE | SPI_IT_RXNE | SPI_IT_ERR));
if ((hspi->Init.Mode == SPI_MODE_MASTER)
&& ((hspi->Init.Direction == SPI_DIRECTION_1LINE)
|| (hspi->Init.Direction
== SPI_DIRECTION_2LINES_RXONLY))) {
/* Disable SPI peripheral */
__HAL_SPI_DISABLE(hspi);
}
/* Reset CRC Calculation */
if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) {
SPI_RESET_CRC(hspi);
}
hspi->State = HAL_SPI_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hspi);
return HAL_TIMEOUT;
}
}
}
return HAL_OK;
}
/*
* DualSD.h
*
* Created on: 1 Nov 2016
* Author: ralim
*/
#ifndef SDCARD_HPP_
#define SDCARD_HPP_
#include "stm32f4xx.h"
#ifdef __cplusplus
// Based on the document:
//
// SD Specifications
// Part 1
// Physical Layer
// Simplified Specification
// Version 2.00
// September 25, 2006
//
// www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
//------------------------------------------------------------------------------
// SD card commands
/** GO_IDLE_STATE - init card in spi mode if CS low */
uint8_t const CMD0 = 0X00;
/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/
uint8_t const CMD8 = 0X08;
/** SEND_CSD - read the Card Specific Data (CSD register) */
uint8_t const CMD9 = 0X09;
/** SEND_CID - read the card identification information (CID register) */
uint8_t const CMD10 = 0X0A;
/** SEND_STATUS - read the card status register */
uint8_t const CMD13 = 0X0D;
/** READ_BLOCK - read a single data block from the card */
uint8_t const CMD17 = 0X11;
/** WRITE_BLOCK - write a single data block to the card */
uint8_t const CMD24 = 0X18;
/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */
uint8_t const CMD25 = 0X19;
/** ERASE_WR_BLK_START - sets the address of the first block to be erased */
uint8_t const CMD32 = 0X20;
/** ERASE_WR_BLK_END - sets the address of the last block of the continuous
range to be erased*/
uint8_t const CMD33 = 0X21;
/** ERASE - erase all previously selected blocks */
uint8_t const CMD38 = 0X26;
/** APP_CMD - escape for application specific command */
uint8_t const CMD55 = 0X37;
/** READ_OCR - read the OCR register of a card */
uint8_t const CMD58 = 0X3A;
/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be
pre-erased before writing */
uint8_t const ACMD23 = 0X17;
/** SD_SEND_OP_COMD - Sends host capacity support information and
activates the card's initialization process */
uint8_t const ACMD41 = 0X29;
//------------------------------------------------------------------------------
/** status for card in the ready state */
uint8_t const R1_READY_STATE = 0X00;
/** status for card in the idle state */
uint8_t const R1_IDLE_STATE = 0X01;
/** status bit for illegal command */
uint8_t const R1_ILLEGAL_COMMAND = 0X04;
/** start data token for read or write single block*/
uint8_t const DATA_START_BLOCK = 0XFE;
/** stop token for write multiple blocks*/
uint8_t const STOP_TRAN_TOKEN = 0XFD;
/** start data token for write multiple blocks*/
uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC;
/** mask for data response tokens after a write block operation */
uint8_t const DATA_RES_MASK = 0X1F;
/** write data accepted token */
uint8_t const DATA_RES_ACCEPTED = 0X05;
//------------------------------------------------------------------------------
typedef struct CID {
// byte 0
uint8_t mid; // Manufacturer ID
// byte 1-2
char oid[2]; // OEM/Application ID
// byte 3-7
char pnm[5]; // Product name
// byte 8
unsigned prv_m :4; // Product revision n.m
unsigned prv_n :4;
// byte 9-12
uint32_t psn; // Product serial number
// byte 13
unsigned mdt_year_high :4; // Manufacturing date
unsigned reserved :4;
// byte 14
unsigned mdt_month :4;
unsigned mdt_year_low :4;
// byte 15
unsigned always1 :1;
unsigned crc :7;
} cid_t;
//------------------------------------------------------------------------------
// CSD for version 1.00 cards
typedef struct CSDV1 {
// byte 0
unsigned reserved1 :6;
unsigned csd_ver :2;
// byte 1
uint8_t taac;
// byte 2
uint8_t nsac;
// byte 3
uint8_t tran_speed;
// byte 4
uint8_t ccc_high;
// byte 5
unsigned read_bl_len :4;
unsigned ccc_low :4;
// byte 6
unsigned c_size_high :2;
unsigned reserved2 :2;
unsigned dsr_imp :1;
unsigned read_blk_misalign :1;
unsigned write_blk_misalign :1;
unsigned read_bl_partial :1;
// byte 7
uint8_t c_size_mid;
// byte 8
unsigned vdd_r_curr_max :3;
unsigned vdd_r_curr_min :3;
unsigned c_size_low :2;
// byte 9
unsigned c_size_mult_high :2;
unsigned vdd_w_cur_max :3;
unsigned vdd_w_curr_min :3;
// byte 10
unsigned sector_size_high :6;
unsigned erase_blk_en :1;
unsigned c_size_mult_low :1;
// byte 11
unsigned wp_grp_size :7;
unsigned sector_size_low :1;
// byte 12
unsigned write_bl_len_high :2;
unsigned r2w_factor :3;
unsigned reserved3 :2;
unsigned wp_grp_enable :1;
// byte 13
unsigned reserved4 :5;
unsigned write_partial :1;
unsigned write_bl_len_low :2;
// byte 14
unsigned reserved5 :2;
unsigned file_format :2;
unsigned tmp_write_protect :1;
unsigned perm_write_protect :1;
unsigned copy :1;
unsigned file_format_grp :1;
// byte 15
unsigned always1 :1;
unsigned crc :7;
} csd1_t;
//------------------------------------------------------------------------------
// CSD for version 2.00 cards
typedef struct CSDV2 {
// byte 0
unsigned reserved1 :6;
unsigned csd_ver :2;
// byte 1
uint8_t taac;
// byte 2
uint8_t nsac;
// byte 3
uint8_t tran_speed;
// byte 4
uint8_t ccc_high;
// byte 5
unsigned read_bl_len :4;
unsigned ccc_low :4;
// byte 6
unsigned reserved2 :4;
unsigned dsr_imp :1;
unsigned read_blk_misalign :1;
unsigned write_blk_misalign :1;
unsigned read_bl_partial :1;
// byte 7
unsigned reserved3 :2;
unsigned c_size_high :6;
// byte 8
uint8_t c_size_mid;
// byte 9
uint8_t c_size_low;
// byte 10
unsigned sector_size_high :6;
unsigned erase_blk_en :1;
unsigned reserved4 :1;
// byte 11
unsigned wp_grp_size :7;
unsigned sector_size_low :1;
// byte 12
unsigned write_bl_len_high :2;
unsigned r2w_factor :3;
unsigned reserved5 :2;
unsigned wp_grp_enable :1;
// byte 13
unsigned reserved6 :5;
unsigned write_partial :1;
unsigned write_bl_len_low :2;
// byte 14
unsigned reserved7 :2;
unsigned file_format :2;
unsigned tmp_write_protect :1;
unsigned perm_write_protect :1;
unsigned copy :1;
unsigned file_format_grp :1;
// byte 15
unsigned always1 :1;
unsigned crc :7;
} csd2_t;
//------------------------------------------------------------------------------
// union of old and new style CSD register
union csd_t {
csd1_t v1;
csd2_t v2;
};
class SDCard {
public:
SDCard(SPI_HandleTypeDef* hspi, uint16_t cs1Pin, GPIO_TypeDef* cs1Port);
bool initalize(); //startup the cards
uint32_t getSize(); //get the total storage space size
bool readBlock(uint32_t blockaddr, uint8_t* buffer); //reads a single 512 byte block
bool writeBlock(uint32_t blockaddr, uint8_t* buffer); //writes a single 512 byte block
private:
void waitUntilReady(); //waits until the card is ready
uint8_t cardCommand(uint8_t command, uint32_t arg);
uint8_t cardAcmd(uint8_t cmd, uint32_t arg) {
cardCommand(CMD55, 0);
return cardCommand(cmd, arg);
}
HAL_StatusTypeDef SPI_Recieve(uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef lSPI_WaitFlagStateUntilTimeout(SPI_HandleTypeDef *hspi,
uint32_t Flag, uint32_t State, uint32_t Timeout,
uint32_t Tickstart);
uint8_t readRegister(uint8_t cmd, void* buf);
/**
* Read a cards CSD register. The CSD contains Card-Specific Data that
* provides information regarding access to the card's contents. */
uint8_t readCSD(csd_t* csd) {
return readRegister(CMD9, csd);
}
void selectCard();
void deselectCard();
SPI_HandleTypeDef* _spi;
GPIO_TypeDef *_cs1Port;
uint16_t _cs1Pin;
uint64_t _sdSize;
};
#endif
#endif /* SDCARD_HPP_ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment