1
0
Fork 0
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:
Nicholas Sherlock 2015-11-23 18:47:48 +13:00 committed by borisbstyle
parent 75d5c64b33
commit 71335dc65d
3 changed files with 96 additions and 64 deletions

View file

@ -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)
{ {

View file

@ -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);

View file

@ -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