Basic SDCard block read / write (minimal timeout/error handling)
This commit is contained in:
parent
3941c6c252
commit
84d3cc6175
1
Makefile
1
Makefile
|
@ -577,6 +577,7 @@ STM32F3DISCOVERY_SRC = \
|
||||||
drivers/barometer_bmp280.c \
|
drivers/barometer_bmp280.c \
|
||||||
drivers/compass_ak8975.c \
|
drivers/compass_ak8975.c \
|
||||||
drivers/sdcard.c \
|
drivers/sdcard.c \
|
||||||
|
drivers/sdcard_standard.c \
|
||||||
$(HIGHEND_SRC) \
|
$(HIGHEND_SRC) \
|
||||||
$(COMMON_SRC)
|
$(COMMON_SRC)
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,8 @@ typedef enum {
|
||||||
FEATURE_DISPLAY = 1 << 17,
|
FEATURE_DISPLAY = 1 << 17,
|
||||||
FEATURE_ONESHOT125 = 1 << 18,
|
FEATURE_ONESHOT125 = 1 << 18,
|
||||||
FEATURE_BLACKBOX = 1 << 19,
|
FEATURE_BLACKBOX = 1 << 19,
|
||||||
FEATURE_CHANNEL_FORWARDING = 1 << 20
|
FEATURE_CHANNEL_FORWARDING = 1 << 20,
|
||||||
|
FEATURE_SDCARD = 1 << 21
|
||||||
} features_e;
|
} features_e;
|
||||||
|
|
||||||
void handleOneshotFeatureChangeOnRestart(void);
|
void handleOneshotFeatureChangeOnRestart(void);
|
||||||
|
|
|
@ -69,13 +69,34 @@ void initSpi1(void)
|
||||||
GPIO_PinAFConfig(SPI1_GPIO, SPI1_NSS_PIN_SOURCE, GPIO_AF_5);
|
GPIO_PinAFConfig(SPI1_GPIO, SPI1_NSS_PIN_SOURCE, GPIO_AF_5);
|
||||||
#endif
|
#endif
|
||||||
// Init pins
|
// Init pins
|
||||||
GPIO_InitStructure.GPIO_Pin = SPI1_SCK_PIN | SPI1_MISO_PIN | SPI1_MOSI_PIN;
|
|
||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
||||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||||
|
|
||||||
|
#ifdef USE_SDCARD_SPI1
|
||||||
|
// Configure pins and pullups for SD-card use
|
||||||
|
|
||||||
|
// No pull-up needed since we drive this pin as an output
|
||||||
|
GPIO_InitStructure.GPIO_Pin = SPI1_MOSI_PIN;
|
||||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||||
GPIO_Init(SPI1_GPIO, &GPIO_InitStructure);
|
GPIO_Init(SPI1_GPIO, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
// Prevent MISO pin from floating when SDCard is deselected (high-Z) or not connected
|
||||||
|
GPIO_InitStructure.GPIO_Pin = SPI1_MISO_PIN;
|
||||||
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
||||||
|
GPIO_Init(SPI1_GPIO, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
// In clock-low mode, STM32 manual says we should enable a pulldown to match
|
||||||
|
GPIO_InitStructure.GPIO_Pin = SPI1_SCK_PIN;
|
||||||
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
|
||||||
|
GPIO_Init(SPI1_GPIO, &GPIO_InitStructure);
|
||||||
|
#else
|
||||||
|
// General-purpose pin config
|
||||||
|
GPIO_InitStructure.GPIO_Pin = SPI1_SCK_PIN | SPI1_MISO_PIN | SPI1_MOSI_PIN;
|
||||||
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||||
|
GPIO_Init(SPI1_GPIO, &GPIO_InitStructure);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SPI1_NSS_PIN
|
#ifdef SPI1_NSS_PIN
|
||||||
GPIO_InitStructure.GPIO_Pin = SPI1_NSS_PIN;
|
GPIO_InitStructure.GPIO_Pin = SPI1_NSS_PIN;
|
||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
||||||
|
@ -116,9 +137,15 @@ void initSpi1(void)
|
||||||
spi.SPI_NSS = SPI_NSS_Soft;
|
spi.SPI_NSS = SPI_NSS_Soft;
|
||||||
spi.SPI_FirstBit = SPI_FirstBit_MSB;
|
spi.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||||
spi.SPI_CRCPolynomial = 7;
|
spi.SPI_CRCPolynomial = 7;
|
||||||
|
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
|
||||||
|
|
||||||
|
#ifdef USE_SDCARD_SPI1
|
||||||
|
spi.SPI_CPOL = SPI_CPOL_Low;
|
||||||
|
spi.SPI_CPHA = SPI_CPHA_1Edge;
|
||||||
|
#else
|
||||||
spi.SPI_CPOL = SPI_CPOL_High;
|
spi.SPI_CPOL = SPI_CPOL_High;
|
||||||
spi.SPI_CPHA = SPI_CPHA_2Edge;
|
spi.SPI_CPHA = SPI_CPHA_2Edge;
|
||||||
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
|
#endif
|
||||||
|
|
||||||
#ifdef STM32F303xC
|
#ifdef STM32F303xC
|
||||||
// Configure for 8-bit reads.
|
// Configure for 8-bit reads.
|
||||||
|
@ -173,13 +200,34 @@ void initSpi2(void)
|
||||||
GPIO_PinAFConfig(SPI2_GPIO, SPI2_NSS_PIN_SOURCE, GPIO_AF_5);
|
GPIO_PinAFConfig(SPI2_GPIO, SPI2_NSS_PIN_SOURCE, GPIO_AF_5);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GPIO_InitStructure.GPIO_Pin = SPI2_SCK_PIN | SPI2_MISO_PIN | SPI2_MOSI_PIN;
|
|
||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
||||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||||
|
|
||||||
|
#ifdef USE_SDCARD_SPI2
|
||||||
|
// Configure pins and pullups for SD-card use
|
||||||
|
|
||||||
|
// No pull-up needed since we drive this pin as an output
|
||||||
|
GPIO_InitStructure.GPIO_Pin = SPI2_MOSI_PIN;
|
||||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||||
GPIO_Init(SPI2_GPIO, &GPIO_InitStructure);
|
GPIO_Init(SPI2_GPIO, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
// Prevent MISO pin from floating when SDCard is deselected (high-Z) or not connected
|
||||||
|
GPIO_InitStructure.GPIO_Pin = SPI2_MISO_PIN;
|
||||||
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
||||||
|
GPIO_Init(SPI2_GPIO, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
// In clock-low mode, STM32 manual says we should enable a pulldown to match
|
||||||
|
GPIO_InitStructure.GPIO_Pin = SPI2_SCK_PIN;
|
||||||
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
|
||||||
|
GPIO_Init(SPI2_GPIO, &GPIO_InitStructure);
|
||||||
|
#else
|
||||||
|
// General-purpose pin config
|
||||||
|
GPIO_InitStructure.GPIO_Pin = SPI2_SCK_PIN | SPI2_MISO_PIN | SPI2_MOSI_PIN;
|
||||||
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||||
|
GPIO_Init(SPI2_GPIO, &GPIO_InitStructure);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SPI2_NSS_PIN
|
#ifdef SPI2_NSS_PIN
|
||||||
GPIO_InitStructure.GPIO_Pin = SPI2_NSS_PIN;
|
GPIO_InitStructure.GPIO_Pin = SPI2_NSS_PIN;
|
||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
||||||
|
@ -219,13 +267,19 @@ void initSpi2(void)
|
||||||
spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||||
spi.SPI_Mode = SPI_Mode_Master;
|
spi.SPI_Mode = SPI_Mode_Master;
|
||||||
spi.SPI_DataSize = SPI_DataSize_8b;
|
spi.SPI_DataSize = SPI_DataSize_8b;
|
||||||
spi.SPI_CPOL = SPI_CPOL_High;
|
|
||||||
spi.SPI_CPHA = SPI_CPHA_2Edge;
|
|
||||||
spi.SPI_NSS = SPI_NSS_Soft;
|
spi.SPI_NSS = SPI_NSS_Soft;
|
||||||
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
|
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
|
||||||
spi.SPI_FirstBit = SPI_FirstBit_MSB;
|
spi.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||||
spi.SPI_CRCPolynomial = 7;
|
spi.SPI_CRCPolynomial = 7;
|
||||||
|
|
||||||
|
#ifdef USE_SDCARD_SPI2
|
||||||
|
spi.SPI_CPOL = SPI_CPOL_Low;
|
||||||
|
spi.SPI_CPHA = SPI_CPHA_1Edge;
|
||||||
|
#else
|
||||||
|
spi.SPI_CPOL = SPI_CPOL_High;
|
||||||
|
spi.SPI_CPHA = SPI_CPHA_2Edge;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef STM32F303xC
|
#ifdef STM32F303xC
|
||||||
// Configure for 8-bit reads.
|
// Configure for 8-bit reads.
|
||||||
SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF);
|
SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define SPI_0_28125MHZ_CLOCK_DIVIDER 256
|
||||||
#define SPI_0_5625MHZ_CLOCK_DIVIDER 128
|
#define SPI_0_5625MHZ_CLOCK_DIVIDER 128
|
||||||
#define SPI_18MHZ_CLOCK_DIVIDER 2
|
#define SPI_18MHZ_CLOCK_DIVIDER 2
|
||||||
#define SPI_9MHZ_CLOCK_DIVIDER 4
|
#define SPI_9MHZ_CLOCK_DIVIDER 4
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "sdcard.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -831,3 +834,527 @@ uint8_t SD_ReadByte(void)
|
||||||
/*!< Return the shifted data */
|
/*!< Return the shifted data */
|
||||||
return Data;
|
return Data;
|
||||||
}
|
}
|
||||||
|
#include "drivers/bus_spi.h"
|
||||||
|
#include "drivers/system.h"
|
||||||
|
|
||||||
|
#include "sdcard_standard.h"
|
||||||
|
|
||||||
|
#ifdef USE_SDCARD
|
||||||
|
|
||||||
|
#define SET_CS_HIGH GPIO_SetBits(SDCARD_SPI_CS_GPIO, SDCARD_SPI_CS_PIN)
|
||||||
|
#define SET_CS_LOW GPIO_ResetBits(SDCARD_SPI_CS_GPIO, SDCARD_SPI_CS_PIN)
|
||||||
|
|
||||||
|
#define DESELECT_SDCARD SET_CS_HIGH
|
||||||
|
#define SELECT_SDCARD SET_CS_LOW
|
||||||
|
|
||||||
|
#define SDCARD_INIT_NUM_DUMMY_BYTES 10
|
||||||
|
#define SDCARD_MAXIMUM_BYTE_DELAY_FOR_CMD_REPLY 8
|
||||||
|
// Chosen so that CMD8 will have the same CRC as CMD0:
|
||||||
|
#define SDCARD_IF_COND_CHECK_PATTERN 0xAB
|
||||||
|
|
||||||
|
#define STATIC_ASSERT(condition, name ) \
|
||||||
|
typedef char assert_failed_ ## name [(condition) ? 1 : -1 ]
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SDCARD_STATE_NOT_PRESENT = 0,
|
||||||
|
SDCARD_STATE_INITIALIZATION,
|
||||||
|
SDCARD_STATE_READY,
|
||||||
|
SDCARD_STATE_READING,
|
||||||
|
SDCARD_STATE_WRITING,
|
||||||
|
} sdcardState_e;
|
||||||
|
|
||||||
|
typedef struct sdcard_t {
|
||||||
|
struct {
|
||||||
|
uint8_t *buffer;
|
||||||
|
int error;
|
||||||
|
uint32_t blockIndex;
|
||||||
|
|
||||||
|
sdcard_operationCompleteCallback_c callback;
|
||||||
|
uint32_t callbackData;
|
||||||
|
} pendingOperation;
|
||||||
|
|
||||||
|
uint8_t version;
|
||||||
|
bool highCapacity;
|
||||||
|
|
||||||
|
sdcardMetadata_t metadata;
|
||||||
|
sdcardCSD_t csd;
|
||||||
|
|
||||||
|
sdcardState_e state;
|
||||||
|
} sdcard_t;
|
||||||
|
|
||||||
|
|
||||||
|
static sdcard_t sdcard;
|
||||||
|
|
||||||
|
STATIC_ASSERT(sizeof(sdcardCSD_t) == 16, sdcard_csd_bitfields_didnt_pack_properly);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SD card spec requires 8 clock cycles to be sent by us on the bus after most commands so it can finish its
|
||||||
|
* processing of that command. The easiest way for us to do this is to just wait for the bus to become idle before
|
||||||
|
* we transmit a command, sending at least 8-bits onto the bus when we do so.
|
||||||
|
*/
|
||||||
|
static bool sdcard_waitForIdle(int maxBytesToWait)
|
||||||
|
{
|
||||||
|
while (maxBytesToWait > 0) {
|
||||||
|
uint8_t b = spiTransferByte(SDCARD_SPI_INSTANCE, 0xFF);
|
||||||
|
if (b == 0xFF) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
maxBytesToWait--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for up to maxDelay 0xFF idle bytes to arrive from the card, returning the first non-idle byte found.
|
||||||
|
*
|
||||||
|
* Returns 0xFF on failure.
|
||||||
|
*/
|
||||||
|
static uint8_t sdcard_waitForNonIdleByte(int maxDelay)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < maxDelay + 1; i++) { // + 1 so we can wait for maxDelay '0xFF' bytes before reading a response byte afterwards
|
||||||
|
uint8_t response = spiTransferByte(SDCARD_SPI_INSTANCE, 0xFF);
|
||||||
|
|
||||||
|
if (response != 0xFF)
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits up to SDCARD_MAXIMUM_BYTE_DELAY_FOR_CMD_REPLY bytes for the card to become ready, send a command to the card
|
||||||
|
* with the given argument, waits up to SDCARD_MAXIMUM_BYTE_DELAY_FOR_CMD_REPLY bytes for a reply, and returns the
|
||||||
|
* first non-0xFF byte of the reply.
|
||||||
|
*
|
||||||
|
* You must select the card first with SELECT_SDCARD and deselect it afterwards with DESELECT_SDCARD.
|
||||||
|
*
|
||||||
|
* Upon failure, 0xFF is returned.
|
||||||
|
*/
|
||||||
|
static uint8_t sdcard_sendCommand(uint8_t commandCode, uint32_t commandArgument)
|
||||||
|
{
|
||||||
|
uint8_t command[6] = {
|
||||||
|
0x40 | commandCode,
|
||||||
|
commandArgument >> 24,
|
||||||
|
commandArgument >> 16,
|
||||||
|
commandArgument >> 8,
|
||||||
|
commandArgument,
|
||||||
|
0x95 /* Static CRC. This CRC is valid for CMD0 with a 0 argument, and CMD8 with 0x1AB argument, which are the only
|
||||||
|
commands that require a CRC */
|
||||||
|
};
|
||||||
|
|
||||||
|
// Go ahead and send the command even if the card isn't idle if this is the reset command
|
||||||
|
if (!sdcard_waitForIdle(SDCARD_MAXIMUM_BYTE_DELAY_FOR_CMD_REPLY) && commandCode != SDCARD_COMMAND_GO_IDLE_STATE)
|
||||||
|
return 0xFF;
|
||||||
|
|
||||||
|
spiTransfer(SDCARD_SPI_INSTANCE, NULL, command, sizeof(command));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The card can take up to SDCARD_MAXIMUM_BYTE_DELAY_FOR_CMD_REPLY bytes to send the response, in the meantime
|
||||||
|
* it'll transmit 0xFF filler bytes.
|
||||||
|
*/
|
||||||
|
return sdcard_waitForNonIdleByte(SDCARD_MAXIMUM_BYTE_DELAY_FOR_CMD_REPLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t sdcard_sendAppCommand(uint8_t commandCode, uint32_t commandArgument) {
|
||||||
|
sdcard_sendCommand(SDCARD_COMMAND_APP_CMD, 0);
|
||||||
|
|
||||||
|
return sdcard_sendCommand(commandCode, commandArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an IF_COND message to the card to check its version and validate its voltage requirements. Sets the global
|
||||||
|
* sdCardVersion with the detected version (0, 1, or 2) and returns true if the card is compatbile.
|
||||||
|
*/
|
||||||
|
static bool sdcard_validateInterfaceCondition()
|
||||||
|
{
|
||||||
|
uint8_t ifCondReply[4];
|
||||||
|
|
||||||
|
sdcard.version = 0;
|
||||||
|
|
||||||
|
SELECT_SDCARD;
|
||||||
|
|
||||||
|
uint8_t status = sdcard_sendCommand(SDCARD_COMMAND_SEND_IF_COND, (SDCARD_VOLTAGE_ACCEPTED_2_7_to_3_6 << 8) | SDCARD_IF_COND_CHECK_PATTERN);
|
||||||
|
|
||||||
|
// Don't deselect the card right away, because we'll want to read the rest of its reply if it's a V2 card
|
||||||
|
|
||||||
|
if (status == (SDCARD_R1_STATUS_BIT_ILLEGAL_COMMAND | SDCARD_R1_STATUS_BIT_IDLE)) {
|
||||||
|
// V1 cards don't support this command
|
||||||
|
sdcard.version = 1;
|
||||||
|
} else if (status == SDCARD_R1_STATUS_BIT_IDLE) {
|
||||||
|
spiTransfer(SDCARD_SPI_INSTANCE, ifCondReply, NULL, sizeof(ifCondReply));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't bother to validate the SDCard's operating voltage range since the spec requires it to accept our
|
||||||
|
* 3.3V, but do check that it echoed back our check pattern properly.
|
||||||
|
*/
|
||||||
|
if (ifCondReply[3] == SDCARD_IF_COND_CHECK_PATTERN) {
|
||||||
|
sdcard.version = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
return sdcard.version > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sdcard_readOCRRegister(uint32_t *result)
|
||||||
|
{
|
||||||
|
SELECT_SDCARD;
|
||||||
|
|
||||||
|
uint8_t status = sdcard_sendCommand(SDCARD_COMMAND_READ_OCR, 0);
|
||||||
|
|
||||||
|
uint8_t response[4];
|
||||||
|
|
||||||
|
spiTransfer(SDCARD_SPI_INSTANCE, response, NULL, sizeof(response));
|
||||||
|
|
||||||
|
if (status == 0) {
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
*result = (response[0] << 24) | (response[1] << 16) | (response[2] << 8) | response[3];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to receive a data block from the SD card.
|
||||||
|
*
|
||||||
|
* Return true on success, otherwise the card has not responded yet and you should retry later.
|
||||||
|
*/
|
||||||
|
static bool sdcard_receiveDataBlock(uint8_t *buffer, int count)
|
||||||
|
{
|
||||||
|
if (sdcard_waitForNonIdleByte(SDCARD_MAXIMUM_BYTE_DELAY_FOR_CMD_REPLY) != SDCARD_SINGLE_BLOCK_READ_START_TOKEN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
spiTransfer(SDCARD_SPI_INSTANCE, buffer, NULL, count);
|
||||||
|
|
||||||
|
// Discard trailing CRC, we don't care
|
||||||
|
spiTransferByte(SDCARD_SPI_INSTANCE, 0xFF);
|
||||||
|
spiTransferByte(SDCARD_SPI_INSTANCE, 0xFF);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the buffer of `count` bytes to the SD card.
|
||||||
|
*
|
||||||
|
* Returns true if the card accepted the write (card will enter a busy state).
|
||||||
|
*/
|
||||||
|
static bool sdcard_sendDataBlock(uint8_t *buffer, int count)
|
||||||
|
{
|
||||||
|
// Card wants 8 dummy clock cycles after the command response to become ready
|
||||||
|
spiTransferByte(SDCARD_SPI_INSTANCE, 0xFF);
|
||||||
|
|
||||||
|
spiTransferByte(SDCARD_SPI_INSTANCE, SDCARD_SINGLE_BLOCK_WRITE_START_TOKEN);
|
||||||
|
spiTransfer(SDCARD_SPI_INSTANCE, NULL, buffer, count);
|
||||||
|
|
||||||
|
// Send a dummy CRC
|
||||||
|
spiTransferByte(SDCARD_SPI_INSTANCE, 0x00);
|
||||||
|
spiTransferByte(SDCARD_SPI_INSTANCE, 0x00);
|
||||||
|
|
||||||
|
uint8_t dataResponseToken = spiTransferByte(SDCARD_SPI_INSTANCE, 0xFF);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the card accepted the write (no CRC error / no address error)
|
||||||
|
*
|
||||||
|
* The lower 5 bits are structured as follows:
|
||||||
|
* | 0 | Status | 1 |
|
||||||
|
* | 0 | x x x | 1 |
|
||||||
|
*
|
||||||
|
* Statuses:
|
||||||
|
* 010 - Data accepted
|
||||||
|
* 101 - CRC error
|
||||||
|
* 110 - Write error
|
||||||
|
*/
|
||||||
|
return (dataResponseToken & 0x1F) == 0x05;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sdcard_fetchCID()
|
||||||
|
{
|
||||||
|
uint8_t cid[16];
|
||||||
|
|
||||||
|
SELECT_SDCARD;
|
||||||
|
|
||||||
|
uint8_t status = sdcard_sendCommand(SDCARD_COMMAND_SEND_CID, 0);
|
||||||
|
|
||||||
|
if (status != 0 || !sdcard_receiveDataBlock(cid, sizeof(cid))) {
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdcard.metadata.manufacturerID = cid[0];
|
||||||
|
sdcard.metadata.oemID = (cid[1] << 8) | cid[2];
|
||||||
|
sdcard.metadata.productName[0] = cid[3];
|
||||||
|
sdcard.metadata.productName[1] = cid[4];
|
||||||
|
sdcard.metadata.productName[2] = cid[5];
|
||||||
|
sdcard.metadata.productName[3] = cid[6];
|
||||||
|
sdcard.metadata.productName[4] = cid[7];
|
||||||
|
sdcard.metadata.productRevisionMajor = cid[8] >> 4;
|
||||||
|
sdcard.metadata.productRevisionMinor = cid[8] & 0x0F;
|
||||||
|
sdcard.metadata.productSerial = (cid[9] << 24) | (cid[10] << 16) | (cid[11] << 8) | cid[12];
|
||||||
|
sdcard.metadata.productionYear = (((cid[13] & 0x0F) << 4) | (cid[14] >> 4)) + 2000;
|
||||||
|
sdcard.metadata.productionMonth = cid[14] & 0x0F;
|
||||||
|
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sdcard_fetchCSD()
|
||||||
|
{
|
||||||
|
uint32_t readBlockLen, blockCount, blockCountMult, capacityBytes;
|
||||||
|
|
||||||
|
SELECT_SDCARD;
|
||||||
|
|
||||||
|
bool success =
|
||||||
|
sdcard_sendCommand(SDCARD_COMMAND_SEND_CSD, 0) == 0
|
||||||
|
&& sdcard_receiveDataBlock((uint8_t*) &sdcard.csd, sizeof(sdcard.csd))
|
||||||
|
&& SDCARD_GET_CSD_FIELD(sdcard.csd, 1, TRAILER) == 1;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
switch (SDCARD_GET_CSD_FIELD(sdcard.csd, 1, CSD_STRUCTURE_VER)) {
|
||||||
|
case SDCARD_CSD_STRUCTURE_VERSION_1:
|
||||||
|
// Block size in bytes (doesn't have to be 512)
|
||||||
|
readBlockLen = 1 << SDCARD_GET_CSD_FIELD(sdcard.csd, 1, READ_BLOCK_LEN);
|
||||||
|
blockCountMult = 1 << (SDCARD_GET_CSD_FIELD(sdcard.csd, 1, CSIZE_MULT) + 2);
|
||||||
|
blockCount = (SDCARD_GET_CSD_FIELD(sdcard.csd, 1, CSIZE) + 1) * blockCountMult;
|
||||||
|
capacityBytes = blockCount * readBlockLen;
|
||||||
|
|
||||||
|
// Re-express that capacity (max 2GB) in our standard 512-byte block size
|
||||||
|
sdcard.metadata.numBlocks = capacityBytes / SDCARD_BLOCK_SIZE;
|
||||||
|
break;
|
||||||
|
case SDCARD_CSD_STRUCTURE_VERSION_2:
|
||||||
|
sdcard.metadata.numBlocks = (SDCARD_GET_CSD_FIELD(sdcard.csd, 2, CSIZE) + 1) * 1024;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call once SDcard has finished its initialisation phase to read ID data from the card and complete our init.
|
||||||
|
*
|
||||||
|
* Returns true on success, false on card init failure.
|
||||||
|
*/
|
||||||
|
static bool sdcard_completeInit()
|
||||||
|
{
|
||||||
|
if (sdcard.version == 2) {
|
||||||
|
// Check for high capacity card
|
||||||
|
uint32_t ocr;
|
||||||
|
|
||||||
|
if (!sdcard_readOCRRegister(&ocr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdcard.highCapacity = (ocr & (1 << 30)) != 0;
|
||||||
|
} else {
|
||||||
|
// Version 1 cards are always low-capacity
|
||||||
|
sdcard.highCapacity = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sdcard_fetchCID() || !sdcard_fetchCSD())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* The spec is a little iffy on what the default block size is for Standard Size cards (it can be changed on
|
||||||
|
* standard size cards) so let's just set it to 512 explicitly so we don't have a problem.
|
||||||
|
*/
|
||||||
|
if (!sdcard.highCapacity) {
|
||||||
|
SELECT_SDCARD;
|
||||||
|
|
||||||
|
if (sdcard_sendCommand(SDCARD_COMMAND_SET_BLOCKLEN, SDCARD_BLOCK_SIZE) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
spiSetDivisor(SDCARD_SPI_INSTANCE, 4);
|
||||||
|
|
||||||
|
sdcard.state = SDCARD_STATE_READY;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the SD Card has completed its startup sequence. Must be called with sdcard.state == SDCARD_STATE_INITIALIZATION.
|
||||||
|
*
|
||||||
|
* Changes sdcard.state to SDCARD_STATE_READY on success and returns true, returns false otherwise.
|
||||||
|
*/
|
||||||
|
static bool sdcard_checkInitDone() {
|
||||||
|
SELECT_SDCARD;
|
||||||
|
|
||||||
|
uint8_t status = sdcard_sendAppCommand(SDCARD_ACOMMAND_SEND_OP_COND, sdcard.version == 2 ? 1 << 30 /* We support high capacity cards */ : 0);
|
||||||
|
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
// When card init is complete, the idle bit in the response becomes zero.
|
||||||
|
if (status == 0x00) {
|
||||||
|
return sdcard_completeInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdcard_init()
|
||||||
|
{
|
||||||
|
// Max frequency is initially 400kHz
|
||||||
|
spiSetDivisor(SDCARD_SPI_INSTANCE, 128);
|
||||||
|
|
||||||
|
// SDCard wants 1ms minimum delay after power is applied to it
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// Transmit at least 74 dummy clock cycles with CS high so the SD card can start up
|
||||||
|
SET_CS_HIGH;
|
||||||
|
|
||||||
|
spiTransfer(SDCARD_SPI_INSTANCE, NULL, NULL, SDCARD_INIT_NUM_DUMMY_BYTES);
|
||||||
|
|
||||||
|
// Wait for that transmission to finish before we enable the SDCard, so it receives the required number of cycles
|
||||||
|
while (spiIsBusBusy(SDCARD_SPI_INSTANCE)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
SELECT_SDCARD;
|
||||||
|
|
||||||
|
uint8_t initStatus = sdcard_sendCommand(SDCARD_COMMAND_GO_IDLE_STATE, 0);
|
||||||
|
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
if (initStatus != SDCARD_R1_STATUS_BIT_IDLE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check card voltage and version
|
||||||
|
if (!sdcard_validateInterfaceCondition())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32_t ocr;
|
||||||
|
|
||||||
|
sdcard_readOCRRegister(&ocr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now the SD card will perform its startup, which can take hundreds of milliseconds. We won't wait for this to
|
||||||
|
* avoid slowing down system startup. Instead we'll periodically poll with sdcard_checkInitDone() later on.
|
||||||
|
*/
|
||||||
|
sdcard.state = SDCARD_STATE_INITIALIZATION;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call periodically for the SD card to perform in-progress transfers.
|
||||||
|
*/
|
||||||
|
void sdcard_poll()
|
||||||
|
{
|
||||||
|
switch (sdcard.state) {
|
||||||
|
case SDCARD_STATE_READING:
|
||||||
|
if (sdcard_receiveDataBlock(sdcard.pendingOperation.buffer, SDCARD_BLOCK_SIZE)) {
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
sdcard.state = SDCARD_STATE_READY;
|
||||||
|
|
||||||
|
if (sdcard.pendingOperation.callback) {
|
||||||
|
sdcard.pendingOperation.callback(
|
||||||
|
SDCARD_BLOCK_OPERATION_READ,
|
||||||
|
sdcard.pendingOperation.blockIndex,
|
||||||
|
sdcard.pendingOperation.buffer,
|
||||||
|
sdcard.pendingOperation.callbackData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDCARD_STATE_INITIALIZATION:
|
||||||
|
sdcard_checkInitDone();
|
||||||
|
break;
|
||||||
|
case SDCARD_STATE_WRITING:
|
||||||
|
if (sdcard_waitForIdle(SDCARD_MAXIMUM_BYTE_DELAY_FOR_CMD_REPLY)) {
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
sdcard.state = SDCARD_STATE_READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the 512-byte block from the given buffer into the block with the given index.
|
||||||
|
*
|
||||||
|
* Returns true if the write was successfully sent to the card, or false if the operation could
|
||||||
|
* not be started due to the card being busy (try again later), or because the write was invalid (bad address).
|
||||||
|
*
|
||||||
|
* The buffer is not copied anywhere, you must keep the pointer to the buffer valid until the operation completes!
|
||||||
|
*/
|
||||||
|
bool sdcard_writeBlock(uint32_t blockIndex, uint8_t *buffer)
|
||||||
|
{
|
||||||
|
if (sdcard.state != SDCARD_STATE_READY)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SELECT_SDCARD;
|
||||||
|
|
||||||
|
// Standard size cards use byte addressing, high capacity cards use block addressing
|
||||||
|
uint8_t status = sdcard_sendCommand(SDCARD_COMMAND_WRITE_BLOCK, sdcard.highCapacity ? blockIndex : blockIndex * SDCARD_BLOCK_SIZE);
|
||||||
|
|
||||||
|
if (status == 0 && sdcard_sendDataBlock(buffer, SDCARD_BLOCK_SIZE)) {
|
||||||
|
sdcard.state = SDCARD_STATE_WRITING;
|
||||||
|
|
||||||
|
// Leave the card selected while the write is in progress
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the 512-byte block with the given index into the given 512-byte buffer.
|
||||||
|
*
|
||||||
|
* Returns true if the operation was successfully queued for later completion, or false if the operation could
|
||||||
|
* not be started due to the card being busy (try again later).
|
||||||
|
*
|
||||||
|
* You must keep the pointer to the buffer valid until the operation completes!
|
||||||
|
*/
|
||||||
|
bool sdcard_readBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData)
|
||||||
|
{
|
||||||
|
if (sdcard.state != SDCARD_STATE_READY)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SELECT_SDCARD;
|
||||||
|
|
||||||
|
// Standard size cards use byte addressing, high capacity cards use block addressing
|
||||||
|
uint8_t status = sdcard_sendCommand(SDCARD_COMMAND_READ_SINGLE_BLOCK, sdcard.highCapacity ? blockIndex : blockIndex * SDCARD_BLOCK_SIZE);
|
||||||
|
|
||||||
|
if (status == 0) {
|
||||||
|
sdcard.pendingOperation.buffer = buffer;
|
||||||
|
sdcard.pendingOperation.blockIndex = blockIndex;
|
||||||
|
sdcard.pendingOperation.callback = callback;
|
||||||
|
sdcard.pendingOperation.callbackData = callbackData;
|
||||||
|
|
||||||
|
sdcard.state = SDCARD_STATE_READING;
|
||||||
|
// Leave the card selected for the whole transaction
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
DESELECT_SDCARD;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdcard_isReady() {
|
||||||
|
return sdcard.state == SDCARD_STATE_READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
>>>>>>> 98133e5... Basic SDCard block read / write (minimal timeout/error handling)
|
||||||
|
|
|
@ -221,3 +221,48 @@ uint16_t SD_GetStatus(void);
|
||||||
|
|
||||||
uint8_t SD_WriteByte(uint8_t byte);
|
uint8_t SD_WriteByte(uint8_t byte);
|
||||||
uint8_t SD_ReadByte(void);
|
uint8_t SD_ReadByte(void);
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct sdcard_metadata_t {
|
||||||
|
uint8_t manufacturerID;
|
||||||
|
uint16_t oemID;
|
||||||
|
|
||||||
|
char productName[5];
|
||||||
|
|
||||||
|
uint8_t productRevisionMajor;
|
||||||
|
uint8_t productRevisionMinor;
|
||||||
|
uint32_t productSerial;
|
||||||
|
|
||||||
|
uint16_t productionYear;
|
||||||
|
uint8_t productionMonth;
|
||||||
|
|
||||||
|
uint32_t numBlocks; /* Card capacity in 512-byte blocks*/
|
||||||
|
} sdcardMetadata_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SDCARD_NO_OPERATION,
|
||||||
|
SDCARD_OPERATION_IN_PROGRESS,
|
||||||
|
SDCARD_OPERATION_SUCCESS,
|
||||||
|
SDCARD_OPERATION_ERROR,
|
||||||
|
} sdcardOperationStatus_e;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SDCARD_BLOCK_OPERATION_READ,
|
||||||
|
SDCARD_BLOCK_OPERATION_WRITE,
|
||||||
|
SDCARD_BLOCK_OPERATION_ERASE,
|
||||||
|
} sdcardBlockOperation_e;
|
||||||
|
|
||||||
|
typedef void(*sdcard_operationCompleteCallback_c)(sdcardBlockOperation_e operation, uint32_t blockIndex, uint8_t *buffer, uint32_t callbackData);
|
||||||
|
|
||||||
|
bool sdcard_init();
|
||||||
|
|
||||||
|
bool sdcard_writeBlock(uint32_t blockIndex, uint8_t *buffer);
|
||||||
|
bool sdcard_readBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData);
|
||||||
|
|
||||||
|
void sdcard_poll();
|
||||||
|
bool sdcard_isReady();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "sdcard_standard.h"
|
||||||
|
|
||||||
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a bitfield from an array of bits (the bit at index 0 being the most-significant bit of the first byte in
|
||||||
|
* the buffer).
|
||||||
|
*/
|
||||||
|
uint32_t readBitfield(uint8_t *buffer, unsigned bitIndex, unsigned bitLen)
|
||||||
|
{
|
||||||
|
uint32_t result = 0;
|
||||||
|
unsigned bitInByteOffset = bitIndex % 8;
|
||||||
|
uint8_t bufferByte;
|
||||||
|
|
||||||
|
buffer += bitIndex / 8;
|
||||||
|
|
||||||
|
// Align the bitfield to be read to the top of the buffer
|
||||||
|
bufferByte = *buffer << bitInByteOffset;
|
||||||
|
|
||||||
|
while (bitLen > 0) {
|
||||||
|
unsigned bitsThisLoop = MIN(8 - bitInByteOffset, bitLen);
|
||||||
|
|
||||||
|
result = (result << bitsThisLoop) | (bufferByte >> (8 - bitsThisLoop));
|
||||||
|
|
||||||
|
buffer++;
|
||||||
|
bufferByte = *buffer;
|
||||||
|
|
||||||
|
bitLen -= bitsThisLoop;
|
||||||
|
bitInByteOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Cleanflight.
|
||||||
|
*
|
||||||
|
* Cleanflight 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.
|
||||||
|
*
|
||||||
|
* Cleanflight 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct sdcardCSD_t {
|
||||||
|
uint8_t data[16];
|
||||||
|
} sdcardCSD_t;
|
||||||
|
|
||||||
|
#define SDCARD_GET_CSD_FIELD(csd, version, fieldname) \
|
||||||
|
readBitfield(csd.data, SDCARD_CSD_V ## version ## _ ## fieldname ## _OFFSET, SDCARD_CSD_V ## version ## _ ## fieldname ## _LEN)
|
||||||
|
|
||||||
|
// For v1 and Standard Capacity cards
|
||||||
|
#define SDCARD_CSD_V1_CSD_STRUCTURE_VER_OFFSET 0
|
||||||
|
#define SDCARD_CSD_V1_CSD_STRUCTURE_VER_LEN 2
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_TAAC_OFFSET 8
|
||||||
|
#define SDCARD_CSD_V1_TAAC_LEN 8
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_NSAC_OFFSET 16
|
||||||
|
#define SDCARD_CSD_V1_NSAC_LEN 8
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_TRAN_SPEED_OFFSET 24
|
||||||
|
#define SDCARD_CSD_V1_TRAN_SPEED_LEN 8
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_CCC_OFFSET 32
|
||||||
|
#define SDCARD_CSD_V1_CCC_LEN 12
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_READ_BLOCK_LEN_OFFSET 44
|
||||||
|
#define SDCARD_CSD_V1_READ_BLOCK_LEN_LEN 4
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_READ_BLOCK_PARTIAL_ALLOWED_OFFSET 48
|
||||||
|
#define SDCARD_CSD_V1_READ_BLOCK_PARTIAL_ALLOWED_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_WRITE_BLOCK_MISALIGN_OFFSET 49
|
||||||
|
#define SDCARD_CSD_V1_WRITE_BLOCK_MISALIGN_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_READ_BLOCK_MISALIGN_OFFSET 50
|
||||||
|
#define SDCARD_CSD_V1_READ_BLOCK_MISALIGN_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_DSR_IMPLEMENTED_OFFSET 51
|
||||||
|
#define SDCARD_CSD_V1_DSR_IMPLEMENTED_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_CSIZE_OFFSET 54
|
||||||
|
#define SDCARD_CSD_V1_CSIZE_LEN 12
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_VDD_READ_CURR_MIN_OFFSET 66
|
||||||
|
#define SDCARD_CSD_V1_VDD_READ_CURR_MIN_LEN 3
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_VDD_READ_CURR_MAX_OFFSET 69
|
||||||
|
#define SDCARD_CSD_V1_VDD_READ_CURR_MAX_LEN 3
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_VDD_WRITE_CURR_MIN_OFFSET 72
|
||||||
|
#define SDCARD_CSD_V1_VDD_WRITE_CURR_MIN_LEN 3
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_VDD_WRITE_CURR_MAX_OFFSET 75
|
||||||
|
#define SDCARD_CSD_V1_VDD_WRITE_CURR_MAX_LEN 3
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_CSIZE_MULT_OFFSET 78
|
||||||
|
#define SDCARD_CSD_V1_CSIZE_MULT_LEN 3
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_ERASE_SINGLE_BLOCK_ALLOWED_OFFSET 81
|
||||||
|
#define SDCARD_CSD_V1_ERASE_SINGLE_BLOCK_ALLOWED_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_SECTOR_SIZE_OFFSET 82
|
||||||
|
#define SDCARD_CSD_V1_SECTOR_SIZE_LEN 7
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_WRITE_PROTECT_GROUP_SIZE_OFFSET 89
|
||||||
|
#define SDCARD_CSD_V1_WRITE_PROTECT_GROUP_SIZE_LEN 7
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_WRITE_PROTECT_GROUP_ENABLE_OFFSET 96
|
||||||
|
#define SDCARD_CSD_V1_WRITE_PROTECT_GROUP_ENABLE_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_R2W_FACTOR_OFFSET 99
|
||||||
|
#define SDCARD_CSD_V1_R2W_FACTOR_LEN 3
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_WRITE_BLOCK_LEN_OFFSET 102
|
||||||
|
#define SDCARD_CSD_V1_WRITE_BLOCK_LEN_LEN 4
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_WRITE_BLOCK_PARTIAL_ALLOWED_OFFSET 106
|
||||||
|
#define SDCARD_CSD_V1_WRITE_BLOCK_PARTIAL_ALLOWED_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_FILE_FORMAT_GROUP_OFFSET 112
|
||||||
|
#define SDCARD_CSD_V1_FILE_FORMAT_GROUP_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_COPY_OFFSET 113
|
||||||
|
#define SDCARD_CSD_V1_COPY_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_PERMANENT_WRITE_PROTECT_OFFSET 114
|
||||||
|
#define SDCARD_CSD_V1_PERMANENT_WRITE_PROTECT_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_TEMPORARY_WRITE_PROTECT_OFFSET 115
|
||||||
|
#define SDCARD_CSD_V1_TEMPORARY_WRITE_PROTECT_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_FILE_FORMAT_OFFSET 116
|
||||||
|
#define SDCARD_CSD_V1_FILE_FORMAT_LEN 2
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_CRC_OFFSET 120
|
||||||
|
#define SDCARD_CSD_V1_CRC_LEN 7
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V1_TRAILER_OFFSET 127
|
||||||
|
#define SDCARD_CSD_V1_TRAILER_LEN 1
|
||||||
|
|
||||||
|
// For v2 High Capacity cards
|
||||||
|
#define SDCARD_CSD_V2_CSD_STRUCTURE_VER_OFFSET 0
|
||||||
|
#define SDCARD_CSD_V2_CSD_STRUCTURE_VER_LEN 2
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_TAAC_OFFSET 8
|
||||||
|
#define SDCARD_CSD_V2_TAAC_LEN 8
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_NSAC_OFFSET 16
|
||||||
|
#define SDCARD_CSD_V2_NSAC_LEN 8
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_TRAN_SPEED_OFFSET 24
|
||||||
|
#define SDCARD_CSD_V2_TRAN_SPEED_LEN 8
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_CCC_OFFSET 32
|
||||||
|
#define SDCARD_CSD_V2_CCC_LEN 12
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_READ_BLOCK_LEN_OFFSET 44
|
||||||
|
#define SDCARD_CSD_V2_READ_BLOCK_LEN_LEN 4
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_READ_BLOCK_PARTIAL_ALLOWED_OFFSET 48
|
||||||
|
#define SDCARD_CSD_V2_READ_BLOCK_PARTIAL_ALLOWED_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_WRITE_BLOCK_MISALIGN_OFFSET 49
|
||||||
|
#define SDCARD_CSD_V2_WRITE_BLOCK_MISALIGN_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_READ_BLOCK_MISALIGN_OFFSET 50
|
||||||
|
#define SDCARD_CSD_V2_READ_BLOCK_MISALIGN_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_DSR_IMPLEMENTED_OFFSET 51
|
||||||
|
#define SDCARD_CSD_V2_DSR_IMPLEMENTED_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_CSIZE_OFFSET 58
|
||||||
|
#define SDCARD_CSD_V2_CSIZE_LEN 22
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_ERASE_SINGLE_BLOCK_ALLOWED_OFFSET 81
|
||||||
|
#define SDCARD_CSD_V2_ERASE_SINGLE_BLOCK_ALLOWED_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_SECTOR_SIZE_OFFSET 82
|
||||||
|
#define SDCARD_CSD_V2_SECTOR_SIZE_LEN 7
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_WRITE_PROTECT_GROUP_SIZE_OFFSET 89
|
||||||
|
#define SDCARD_CSD_V2_WRITE_PROTECT_GROUP_SIZE_LEN 7
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_WRITE_PROTECT_GROUP_ENABLE_OFFSET 96
|
||||||
|
#define SDCARD_CSD_V2_WRITE_PROTECT_GROUP_ENABLE_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_R2W_FACTOR_OFFSET 99
|
||||||
|
#define SDCARD_CSD_V2_R2W_FACTOR_LEN 3
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_WRITE_BLOCK_LEN_OFFSET 102
|
||||||
|
#define SDCARD_CSD_V2_WRITE_BLOCK_LEN_LEN 4
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_WRITE_BLOCK_PARTIAL_ALLOWED_OFFSET 106
|
||||||
|
#define SDCARD_CSD_V2_WRITE_BLOCK_PARTIAL_ALLOWED_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_FILE_FORMAT_GROUP_OFFSET 112
|
||||||
|
#define SDCARD_CSD_V2_FILE_FORMAT_GROUP_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_COPY_OFFSET 113
|
||||||
|
#define SDCARD_CSD_V2_COPY_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_PERMANENT_WRITE_PROTECT_OFFSET 114
|
||||||
|
#define SDCARD_CSD_V2_PERMANENT_WRITE_PROTECT_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_TEMPORARY_WRITE_PROTECT_OFFSET 115
|
||||||
|
#define SDCARD_CSD_V2_TEMPORARY_WRITE_PROTECT_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_FILE_FORMAT_OFFSET 116
|
||||||
|
#define SDCARD_CSD_V2_FILE_FORMAT_LEN 2
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_CRC_OFFSET 120
|
||||||
|
#define SDCARD_CSD_V2_CRC_LEN 7
|
||||||
|
|
||||||
|
#define SDCARD_CSD_V2_TRAILER_OFFSET 127
|
||||||
|
#define SDCARD_CSD_V2_TRAILER_LEN 1
|
||||||
|
|
||||||
|
#define SDCARD_SINGLE_BLOCK_READ_START_TOKEN 0xFE
|
||||||
|
#define SDCARD_SINGLE_BLOCK_WRITE_START_TOKEN 0xFE
|
||||||
|
|
||||||
|
#define SDCARD_BLOCK_SIZE 512
|
||||||
|
|
||||||
|
// Idle bit is set to 1 only when idle during intialization phase:
|
||||||
|
#define SDCARD_R1_STATUS_BIT_IDLE 1
|
||||||
|
#define SDCARD_R1_STATUS_BIT_ERASE_RESET 2
|
||||||
|
#define SDCARD_R1_STATUS_BIT_ILLEGAL_COMMAND 4
|
||||||
|
#define SDCARD_R1_STATUS_BIT_COM_CRC_ERROR 8
|
||||||
|
#define SDCARD_R1_STATUS_BIT_ERASE_SEQUENCE_ERROR 16
|
||||||
|
#define SDCARD_R1_STATUS_BIT_ADDRESS_ERROR 32
|
||||||
|
#define SDCARD_R1_STATUS_BIT_PARAMETER_ERROR 64
|
||||||
|
|
||||||
|
#define SDCARD_CSD_STRUCTURE_VERSION_1 0
|
||||||
|
#define SDCARD_CSD_STRUCTURE_VERSION_2 1
|
||||||
|
|
||||||
|
#define SDCARD_VOLTAGE_ACCEPTED_2_7_to_3_6 0x01
|
||||||
|
#define SDCARD_VOLTAGE_ACCEPTED_LVR 0x02
|
||||||
|
|
||||||
|
#define SDCARD_COMMAND_GO_IDLE_STATE 0
|
||||||
|
#define SDCARD_COMMAND_SEND_OP_COND 1
|
||||||
|
#define SDCARD_COMMAND_SEND_IF_COND 8
|
||||||
|
#define SDCARD_COMMAND_SEND_CSD 9
|
||||||
|
#define SDCARD_COMMAND_SEND_CID 10
|
||||||
|
#define SDCARD_COMMAND_STOP_TRANSMISSION 12
|
||||||
|
#define SDCARD_COMMAND_SET_BLOCKLEN 16
|
||||||
|
#define SDCARD_COMMAND_READ_SINGLE_BLOCK 17
|
||||||
|
#define SDCARD_COMMAND_READ_MULTIPLE_BLOCK 18
|
||||||
|
#define SDCARD_COMMAND_WRITE_BLOCK 24
|
||||||
|
#define SDCARD_COMMAND_WRITE_MULTIPLE_BLOCK 25
|
||||||
|
#define SDCARD_COMMAND_APP_CMD 55
|
||||||
|
#define SDCARD_COMMAND_READ_OCR 58
|
||||||
|
|
||||||
|
#define SDCARD_ACOMMAND_SEND_OP_COND 41
|
||||||
|
|
||||||
|
uint32_t readBitfield(uint8_t *buffer, unsigned bitIndex, unsigned bitLen);
|
|
@ -187,7 +187,7 @@ static const char * const featureNames[] = {
|
||||||
"SERVO_TILT", "SOFTSERIAL", "GPS", "FAILSAFE",
|
"SERVO_TILT", "SOFTSERIAL", "GPS", "FAILSAFE",
|
||||||
"SONAR", "TELEMETRY", "CURRENT_METER", "3D", "RX_PARALLEL_PWM",
|
"SONAR", "TELEMETRY", "CURRENT_METER", "3D", "RX_PARALLEL_PWM",
|
||||||
"RX_MSP", "RSSI_ADC", "LED_STRIP", "DISPLAY", "ONESHOT125",
|
"RX_MSP", "RSSI_ADC", "LED_STRIP", "DISPLAY", "ONESHOT125",
|
||||||
"BLACKBOX", "CHANNEL_FORWARDING", NULL
|
"BLACKBOX", "CHANNEL_FORWARDING", "SDCARD", NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
// sync this with rxFailsafeChannelMode_e
|
// sync this with rxFailsafeChannelMode_e
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include "drivers/sdcard.h"
|
#include "drivers/sdcard.h"
|
||||||
#include "drivers/sonar_hcsr04.h"
|
#include "drivers/sonar_hcsr04.h"
|
||||||
#include "drivers/gyro_sync.h"
|
#include "drivers/gyro_sync.h"
|
||||||
|
#include "drivers/sdcard.h"
|
||||||
|
|
||||||
#include "rx/rx.h"
|
#include "rx/rx.h"
|
||||||
|
|
||||||
|
@ -535,6 +536,10 @@ void init(void)
|
||||||
flashfsInit();
|
flashfsInit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SDCARD
|
||||||
|
sdcard_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef BLACKBOX
|
#ifdef BLACKBOX
|
||||||
initBlackbox();
|
initBlackbox();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -71,6 +71,17 @@
|
||||||
//#define M25P16_CS_GPIO GPIOB
|
//#define M25P16_CS_GPIO GPIOB
|
||||||
//#define M25P16_CS_PIN GPIO_Pin_12
|
//#define M25P16_CS_PIN GPIO_Pin_12
|
||||||
//#define M25P16_SPI_INSTANCE SPI2
|
//#define M25P16_SPI_INSTANCE SPI2
|
||||||
|
// SPI1
|
||||||
|
// PB5 SPI1_MOSI
|
||||||
|
// PB4 SPI1_MISO
|
||||||
|
// PB3 SPI1_SCK
|
||||||
|
// PA15 SPI1_NSS
|
||||||
|
|
||||||
|
// SPI2
|
||||||
|
// PB15 SPI2_MOSI
|
||||||
|
// PB14 SPI2_MISO
|
||||||
|
// PB13 SPI2_SCK
|
||||||
|
// PB12 SPI2_NSS
|
||||||
|
|
||||||
#define GYRO
|
#define GYRO
|
||||||
#define USE_GYRO_L3GD20
|
#define USE_GYRO_L3GD20
|
||||||
|
@ -82,6 +93,14 @@
|
||||||
|
|
||||||
#define GYRO_L3GD20_ALIGN CW270_DEG
|
#define GYRO_L3GD20_ALIGN CW270_DEG
|
||||||
|
|
||||||
|
#define USE_SDCARD
|
||||||
|
#define USE_SDCARD_SPI2
|
||||||
|
|
||||||
|
#define SDCARD_SPI_INSTANCE SPI2
|
||||||
|
#define SDCARD_SPI_CS_GPIO GPIOB
|
||||||
|
#define SDCARD_SPI_CS_PIN GPIO_Pin_12
|
||||||
|
#define SDCARD_SPI_CS_GPIO_CLK_PERIPHERAL RCC_APB2Periph_GPIOB
|
||||||
|
|
||||||
#define ACC
|
#define ACC
|
||||||
#define USE_ACC_LSM303DLHC
|
#define USE_ACC_LSM303DLHC
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue