mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-19 22:35:23 +03:00
Support enabling DMA for sdcard write at runtime
This commit is contained in:
parent
75d5c64b33
commit
71335dc65d
3 changed files with 96 additions and 64 deletions
|
@ -40,11 +40,14 @@
|
||||||
// Chosen so that CMD8 will have the same CRC as CMD0:
|
// Chosen so that CMD8 will have the same CRC as CMD0:
|
||||||
#define SDCARD_IF_COND_CHECK_PATTERN 0xAB
|
#define SDCARD_IF_COND_CHECK_PATTERN 0xAB
|
||||||
|
|
||||||
|
/* Break up 512-byte SD card sectors into chunks of this size when writing without DMA to reduce the peak overhead
|
||||||
|
* per call to sdcard_poll().
|
||||||
|
*/
|
||||||
|
#define SDCARD_NON_DMA_CHUNK_SIZE 256
|
||||||
|
|
||||||
#define STATIC_ASSERT(condition, name ) \
|
#define STATIC_ASSERT(condition, name ) \
|
||||||
typedef char assert_failed_ ## name [(condition) ? 1 : -1 ]
|
typedef char assert_failed_ ## name [(condition) ? 1 : -1 ]
|
||||||
|
|
||||||
#define SDCARD_USE_DMA_FOR_TX
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
// In these states we run at the initialization 400kHz clockspeed:
|
// In these states we run at the initialization 400kHz clockspeed:
|
||||||
SDCARD_STATE_NOT_PRESENT = 0,
|
SDCARD_STATE_NOT_PRESENT = 0,
|
||||||
|
@ -62,8 +65,8 @@ typedef enum {
|
||||||
typedef struct sdcard_t {
|
typedef struct sdcard_t {
|
||||||
struct {
|
struct {
|
||||||
uint8_t *buffer;
|
uint8_t *buffer;
|
||||||
int error;
|
|
||||||
uint32_t blockIndex;
|
uint32_t blockIndex;
|
||||||
|
uint8_t chunkIndex;
|
||||||
|
|
||||||
sdcard_operationCompleteCallback_c callback;
|
sdcard_operationCompleteCallback_c callback;
|
||||||
uint32_t callbackData;
|
uint32_t callbackData;
|
||||||
|
@ -83,6 +86,13 @@ typedef struct sdcard_t {
|
||||||
|
|
||||||
static sdcard_t sdcard;
|
static sdcard_t sdcard;
|
||||||
|
|
||||||
|
#ifdef SDCARD_DMA_CHANNEL_TX
|
||||||
|
static bool useDMAForTx;
|
||||||
|
#else
|
||||||
|
// DMA channel not available so we can hard-code this to allow the non-DMA paths to be stripped by optimization
|
||||||
|
static const bool useDMAForTx = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(sdcardCSD_t) == 16, sdcard_csd_bitfields_didnt_pack_properly);
|
STATIC_ASSERT(sizeof(sdcardCSD_t) == 16, sdcard_csd_bitfields_didnt_pack_properly);
|
||||||
|
|
||||||
static void sdcard_select()
|
static void sdcard_select()
|
||||||
|
@ -299,48 +309,41 @@ static bool sdcard_sendDataBlockFinish()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the buffer of `count` bytes to the SD card.
|
* Begin sending a buffer of SDCARD_BLOCK_SIZE bytes to the SD card.
|
||||||
*
|
|
||||||
* Returns true if the write was begun (card will enter a busy state).
|
|
||||||
*/
|
*/
|
||||||
static bool sdcard_sendDataBlock(uint8_t *buffer, int count)
|
static void sdcard_sendDataBlockBegin(uint8_t *buffer)
|
||||||
{
|
{
|
||||||
spiTransferByte(SDCARD_SPI_INSTANCE, SDCARD_SINGLE_BLOCK_WRITE_START_TOKEN);
|
spiTransferByte(SDCARD_SPI_INSTANCE, SDCARD_SINGLE_BLOCK_WRITE_START_TOKEN);
|
||||||
|
|
||||||
#ifdef SDCARD_USE_DMA_FOR_TX
|
if (useDMAForTx) {
|
||||||
// Queue the transmission of the sector payload
|
// Queue the transmission of the sector payload
|
||||||
DMA_InitTypeDef DMA_InitStructure;
|
DMA_InitTypeDef DMA_InitStructure;
|
||||||
|
|
||||||
DMA_StructInit(&DMA_InitStructure);
|
DMA_StructInit(&DMA_InitStructure);
|
||||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &SDCARD_SPI_INSTANCE->DR;
|
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &SDCARD_SPI_INSTANCE->DR;
|
||||||
DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
|
DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
|
||||||
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
||||||
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
||||||
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
||||||
|
|
||||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) buffer;
|
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) buffer;
|
||||||
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
||||||
|
|
||||||
DMA_InitStructure.DMA_BufferSize = count;
|
DMA_InitStructure.DMA_BufferSize = SDCARD_BLOCK_SIZE;
|
||||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
|
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
|
||||||
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
|
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
|
||||||
|
|
||||||
DMA_DeInit(SDCARD_DMA_CHANNEL_TX);
|
DMA_DeInit(SDCARD_DMA_CHANNEL_TX);
|
||||||
DMA_Init(SDCARD_DMA_CHANNEL_TX, &DMA_InitStructure);
|
DMA_Init(SDCARD_DMA_CHANNEL_TX, &DMA_InitStructure);
|
||||||
|
|
||||||
DMA_Cmd(SDCARD_DMA_CHANNEL_TX, ENABLE);
|
DMA_Cmd(SDCARD_DMA_CHANNEL_TX, ENABLE);
|
||||||
|
|
||||||
SPI_I2S_DMACmd(SDCARD_SPI_INSTANCE, SPI_I2S_DMAReq_Tx, ENABLE);
|
SPI_I2S_DMACmd(SDCARD_SPI_INSTANCE, SPI_I2S_DMAReq_Tx, ENABLE);
|
||||||
|
} else {
|
||||||
return true;
|
// Send the first chunk now
|
||||||
#else
|
spiTransfer(SDCARD_SPI_INSTANCE, NULL, buffer, SDCARD_NON_DMA_CHUNK_SIZE);
|
||||||
// Send the sector payload now
|
}
|
||||||
spiTransfer(SDCARD_SPI_INSTANCE, NULL, buffer, count);
|
|
||||||
|
|
||||||
// And check the SD card's acknowledgement
|
|
||||||
return sdcard_sendDataBlockFinish();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sdcard_receiveCID()
|
static bool sdcard_receiveCID()
|
||||||
|
@ -425,8 +428,18 @@ static bool sdcard_checkInitDone() {
|
||||||
return status == 0x00;
|
return status == 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sdcard_init()
|
/**
|
||||||
|
* Begin the initialization process for the SD card. This must be called first before any other sdcard_ routine.
|
||||||
|
*/
|
||||||
|
void sdcard_init(bool useDMA)
|
||||||
{
|
{
|
||||||
|
#ifdef SDCARD_DMA_CHANNEL_TX
|
||||||
|
useDMAForTx = useDMA;
|
||||||
|
#else
|
||||||
|
// DMA is not available
|
||||||
|
(void) useDMA;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Max frequency is initially 400kHz
|
// Max frequency is initially 400kHz
|
||||||
spiSetDivisor(SDCARD_SPI_INSTANCE, SDCARD_SPI_INITIALIZATION_CLOCK_DIVIDER);
|
spiSetDivisor(SDCARD_SPI_INSTANCE, SDCARD_SPI_INITIALIZATION_CLOCK_DIVIDER);
|
||||||
|
|
||||||
|
@ -443,8 +456,6 @@ bool sdcard_init()
|
||||||
}
|
}
|
||||||
|
|
||||||
sdcard.state = SDCARD_STATE_RESET;
|
sdcard.state = SDCARD_STATE_RESET;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sdcard_setBlockLength(uint32_t blockLen)
|
static bool sdcard_setBlockLength(uint32_t blockLen)
|
||||||
|
@ -464,6 +475,7 @@ static bool sdcard_setBlockLength(uint32_t blockLen)
|
||||||
void sdcard_poll()
|
void sdcard_poll()
|
||||||
{
|
{
|
||||||
uint8_t initStatus;
|
uint8_t initStatus;
|
||||||
|
bool sendComplete;
|
||||||
|
|
||||||
doMore:
|
doMore:
|
||||||
switch (sdcard.state) {
|
switch (sdcard.state) {
|
||||||
|
@ -542,8 +554,10 @@ void sdcard_poll()
|
||||||
} // else keep waiting for the CID to arrive
|
} // else keep waiting for the CID to arrive
|
||||||
break;
|
break;
|
||||||
case SDCARD_STATE_SENDING_WRITE:
|
case SDCARD_STATE_SENDING_WRITE:
|
||||||
// Has the DMA write finished yet?
|
// Have we finished sending the write yet?
|
||||||
if (DMA_GetFlagStatus(SDCARD_DMA_CHANNEL_TX_COMPLETE_FLAG) == SET) {
|
sendComplete = false;
|
||||||
|
|
||||||
|
if (useDMAForTx && DMA_GetFlagStatus(SDCARD_DMA_CHANNEL_TX_COMPLETE_FLAG) == SET) {
|
||||||
DMA_ClearFlag(SDCARD_DMA_CHANNEL_TX_COMPLETE_FLAG);
|
DMA_ClearFlag(SDCARD_DMA_CHANNEL_TX_COMPLETE_FLAG);
|
||||||
|
|
||||||
DMA_Cmd(SDCARD_DMA_CHANNEL_TX, DISABLE);
|
DMA_Cmd(SDCARD_DMA_CHANNEL_TX, DISABLE);
|
||||||
|
@ -559,6 +573,19 @@ void sdcard_poll()
|
||||||
|
|
||||||
SPI_I2S_DMACmd(SDCARD_SPI_INSTANCE, SPI_I2S_DMAReq_Tx, DISABLE);
|
SPI_I2S_DMACmd(SDCARD_SPI_INSTANCE, SPI_I2S_DMAReq_Tx, DISABLE);
|
||||||
|
|
||||||
|
sendComplete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!useDMAForTx) {
|
||||||
|
// Send another chunk
|
||||||
|
spiTransfer(SDCARD_SPI_INSTANCE, NULL, sdcard.pendingOperation.buffer + SDCARD_NON_DMA_CHUNK_SIZE * sdcard.pendingOperation.chunkIndex, SDCARD_NON_DMA_CHUNK_SIZE);
|
||||||
|
|
||||||
|
sdcard.pendingOperation.chunkIndex++;
|
||||||
|
|
||||||
|
sendComplete = sdcard.pendingOperation.chunkIndex == SDCARD_BLOCK_SIZE / SDCARD_NON_DMA_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendComplete) {
|
||||||
// Finish up by sending the CRC and checking the SD-card's acceptance/rejectance
|
// Finish up by sending the CRC and checking the SD-card's acceptance/rejectance
|
||||||
if (sdcard_sendDataBlockFinish()) {
|
if (sdcard_sendDataBlockFinish()) {
|
||||||
// The SD card is now busy committing that write to the card
|
// The SD card is now busy committing that write to the card
|
||||||
|
@ -646,6 +673,9 @@ void sdcard_poll()
|
||||||
/**
|
/**
|
||||||
* Write the 512-byte block from the given buffer into the block with the given index.
|
* Write the 512-byte block from the given buffer into the block with the given index.
|
||||||
*
|
*
|
||||||
|
* If the write does not complete immediately, your callback will be called later. If the write was successful, the
|
||||||
|
* buffer pointer will be the same buffer you originally passed in, otherwise the buffer will be set to NULL.
|
||||||
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* SDCARD_OPERATION_IN_PROGRESS - Your buffer is currently being transmitted to the card and your callback will be
|
* SDCARD_OPERATION_IN_PROGRESS - Your buffer is currently being transmitted to the card and your callback will be
|
||||||
* called later to report the completion. The buffer pointer must remain valid until
|
* called later to report the completion. The buffer pointer must remain valid until
|
||||||
|
@ -656,12 +686,6 @@ void sdcard_poll()
|
||||||
*/
|
*/
|
||||||
sdcardOperationStatus_e sdcard_writeBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData)
|
sdcardOperationStatus_e sdcard_writeBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData)
|
||||||
{
|
{
|
||||||
#ifndef SDCARD_USE_DMA_FOR_TX
|
|
||||||
// When not using DMA, the writes complete before this routine returns, so we don't need the callback
|
|
||||||
(void) callback;
|
|
||||||
(void) callbackData;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sdcard.state != SDCARD_STATE_READY)
|
if (sdcard.state != SDCARD_STATE_READY)
|
||||||
return SDCARD_OPERATION_BUSY;
|
return SDCARD_OPERATION_BUSY;
|
||||||
|
|
||||||
|
@ -673,30 +697,20 @@ sdcardOperationStatus_e sdcard_writeBlock(uint32_t blockIndex, uint8_t *buffer,
|
||||||
// Card wants 8 dummy clock cycles after the command response to become ready
|
// Card wants 8 dummy clock cycles after the command response to become ready
|
||||||
spiTransferByte(SDCARD_SPI_INSTANCE, 0xFF);
|
spiTransferByte(SDCARD_SPI_INSTANCE, 0xFF);
|
||||||
|
|
||||||
if (status == 0 && sdcard_sendDataBlock(buffer, SDCARD_BLOCK_SIZE)) {
|
if (status == 0) {
|
||||||
#ifdef SDCARD_USE_DMA_FOR_TX
|
sdcard_sendDataBlockBegin(buffer);
|
||||||
|
|
||||||
sdcard.pendingOperation.buffer = buffer;
|
sdcard.pendingOperation.buffer = buffer;
|
||||||
sdcard.pendingOperation.blockIndex = blockIndex;
|
sdcard.pendingOperation.blockIndex = blockIndex;
|
||||||
sdcard.pendingOperation.callback = callback;
|
sdcard.pendingOperation.callback = callback;
|
||||||
sdcard.pendingOperation.callbackData = callbackData;
|
sdcard.pendingOperation.callbackData = callbackData;
|
||||||
|
sdcard.pendingOperation.chunkIndex = 1; // (for non-DMA transfers) we've sent chunk #0 already
|
||||||
sdcard.state = SDCARD_STATE_SENDING_WRITE;
|
sdcard.state = SDCARD_STATE_SENDING_WRITE;
|
||||||
|
|
||||||
return SDCARD_OPERATION_IN_PROGRESS;
|
return SDCARD_OPERATION_IN_PROGRESS;
|
||||||
#else
|
|
||||||
/* The data has already been received by the SD card (buffer has been transmitted), we only have to wait for the
|
|
||||||
* card to commit it. Let the caller know it can free its buffer.
|
|
||||||
*
|
|
||||||
* Leave the card selected while the write is in progress.
|
|
||||||
*/
|
|
||||||
sdcard.operationStartTime = millis();
|
|
||||||
sdcard.state = SDCARD_STATE_WRITING;
|
|
||||||
|
|
||||||
return SDCARD_OPERATION_SUCCESS;
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
sdcard_deselect();
|
sdcard_deselect();
|
||||||
|
|
||||||
// Writes shouldn't really be failing unless we gave a bad address, so reset the card
|
|
||||||
sdcard_reset();
|
sdcard_reset();
|
||||||
|
|
||||||
return SDCARD_OPERATION_FAILURE;
|
return SDCARD_OPERATION_FAILURE;
|
||||||
|
@ -706,10 +720,14 @@ sdcardOperationStatus_e sdcard_writeBlock(uint32_t blockIndex, uint8_t *buffer,
|
||||||
/**
|
/**
|
||||||
* Read the 512-byte block with the given index into the given 512-byte buffer.
|
* 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
|
* When the read completes, your callback will be called. If the read was successful, the buffer pointer will be the
|
||||||
* not be started due to the card being busy (try again later).
|
* same buffer you originally passed in, otherwise the buffer will be set to NULL.
|
||||||
*
|
*
|
||||||
* You must keep the pointer to the buffer valid until the operation completes!
|
* You must keep the pointer to the buffer valid until the operation completes!
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* true - The operation was successfully queued for later completion, your callback will be called later
|
||||||
|
* false - The operation could not be started due to the card being busy (try again later).
|
||||||
*/
|
*/
|
||||||
bool sdcard_readBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData)
|
bool sdcard_readBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData)
|
||||||
{
|
{
|
||||||
|
|
|
@ -258,7 +258,7 @@ typedef enum {
|
||||||
|
|
||||||
typedef void(*sdcard_operationCompleteCallback_c)(sdcardBlockOperation_e operation, uint32_t blockIndex, uint8_t *buffer, uint32_t callbackData);
|
typedef void(*sdcard_operationCompleteCallback_c)(sdcardBlockOperation_e operation, uint32_t blockIndex, uint8_t *buffer, uint32_t callbackData);
|
||||||
|
|
||||||
bool sdcard_init();
|
void sdcard_init(bool useDMA);
|
||||||
|
|
||||||
bool sdcard_readBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData);
|
bool sdcard_readBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData);
|
||||||
sdcardOperationStatus_e sdcard_writeBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData);
|
sdcardOperationStatus_e sdcard_writeBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData);
|
||||||
|
|
|
@ -538,7 +538,21 @@ void init(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SDCARD
|
#ifdef USE_SDCARD
|
||||||
sdcard_init();
|
bool sdcardUseDMA = false;
|
||||||
|
|
||||||
|
#ifdef SDCARD_DMA_CHANNEL_TX
|
||||||
|
|
||||||
|
#if defined(LED_STRIP) && defined(WS2811_DMA_CHANNEL)
|
||||||
|
// Ensure the SPI Tx DMA doesn't overlap with the led strip
|
||||||
|
sdcardUseDMA = !feature(FEATURE_LED_STRIP) || SDCARD_DMA_CHANNEL_TX != WS2811_DMA_CHANNEL;
|
||||||
|
#else
|
||||||
|
sdcardUseDMA = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sdcard_init(sdcardUseDMA);
|
||||||
|
|
||||||
afatfs_init();
|
afatfs_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue