Created
November 3, 2016 13:44
-
-
Save Ralim/4f510b895f29fe6bfef3292223afbabf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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