stmhal: Add dma support for sdcard.
This started out using IgorLektorovEpam work in PR #1389 and reworked it.
This commit is contained in:
parent
b677f03407
commit
6edffd0df5
151
stmhal/sdcard.c
151
stmhal/sdcard.c
|
@ -24,8 +24,6 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO make it work with DMA
|
|
||||||
|
|
||||||
#include STM32_HAL_H
|
#include STM32_HAL_H
|
||||||
|
|
||||||
#include "py/nlr.h"
|
#include "py/nlr.h"
|
||||||
|
@ -34,6 +32,8 @@
|
||||||
#include "pin.h"
|
#include "pin.h"
|
||||||
#include "genhdr/pins.h"
|
#include "genhdr/pins.h"
|
||||||
#include "bufhelper.h"
|
#include "bufhelper.h"
|
||||||
|
#include "dma.h"
|
||||||
|
#include "irq.h"
|
||||||
|
|
||||||
#if MICROPY_HW_HAS_SDCARD
|
#if MICROPY_HW_HAS_SDCARD
|
||||||
|
|
||||||
|
@ -64,8 +64,12 @@
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TODO: I think that as an optimization, we can allocate these dynamically
|
||||||
|
// if an sd card is detected. This will save approx 260 bytes of RAM
|
||||||
|
// when no sdcard was being used.
|
||||||
static SD_HandleTypeDef sd_handle;
|
static SD_HandleTypeDef sd_handle;
|
||||||
|
static DMA_HandleTypeDef sd_rx_dma, sd_tx_dma;
|
||||||
|
static DMA_InitTypeDef sd_rx_dma_init, sd_tx_dma_init;
|
||||||
|
|
||||||
void sdcard_init(void) {
|
void sdcard_init(void) {
|
||||||
GPIO_InitTypeDef GPIO_Init_Structure;
|
GPIO_InitTypeDef GPIO_Init_Structure;
|
||||||
|
@ -98,13 +102,45 @@ void HAL_SD_MspInit(SD_HandleTypeDef *hsd) {
|
||||||
// enable SDIO clock
|
// enable SDIO clock
|
||||||
__SDIO_CLK_ENABLE();
|
__SDIO_CLK_ENABLE();
|
||||||
|
|
||||||
// GPIO have already been initialised by sdcard_init
|
// NVIC configuration for SDIO interrupts
|
||||||
|
HAL_NVIC_SetPriority(SDIO_IRQn, IRQ_PRI_SDIO, IRQ_SUBPRI_SDIO);
|
||||||
|
HAL_NVIC_EnableIRQ(SDIO_IRQn);
|
||||||
|
|
||||||
// interrupts are not used at the moment
|
// TODO: Since SDIO is fundamentally half-duplex, we really only need to
|
||||||
// they are needed only for DMA transfer (I think...)
|
// tie up one DMA channel. However, the HAL DMA API doesn't
|
||||||
|
// seem to provide a convenient way to change the direction. I believe that
|
||||||
|
// its as simple as changing the CR register and the Init.Direction field
|
||||||
|
// and make DMA_SetConfig public.
|
||||||
|
|
||||||
|
// Configure DMA Rx parameters
|
||||||
|
sd_rx_dma_init.PeriphInc = DMA_PINC_DISABLE;
|
||||||
|
sd_rx_dma_init.MemInc = DMA_MINC_ENABLE;
|
||||||
|
sd_rx_dma_init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||||
|
sd_rx_dma_init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||||
|
sd_rx_dma_init.Mode = DMA_PFCTRL;
|
||||||
|
sd_rx_dma_init.Priority = DMA_PRIORITY_VERY_HIGH;
|
||||||
|
sd_rx_dma_init.FIFOMode = DMA_FIFOMODE_ENABLE;
|
||||||
|
sd_rx_dma_init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||||
|
sd_rx_dma_init.MemBurst = DMA_MBURST_INC4;
|
||||||
|
sd_rx_dma_init.PeriphBurst = DMA_PBURST_INC4;
|
||||||
|
|
||||||
|
// Configure DMA Tx parameters
|
||||||
|
sd_tx_dma_init.PeriphInc = DMA_PINC_DISABLE;
|
||||||
|
sd_tx_dma_init.MemInc = DMA_MINC_ENABLE;
|
||||||
|
sd_tx_dma_init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||||
|
sd_tx_dma_init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||||
|
sd_tx_dma_init.Mode = DMA_PFCTRL;
|
||||||
|
sd_tx_dma_init.Priority = DMA_PRIORITY_VERY_HIGH;
|
||||||
|
sd_tx_dma_init.FIFOMode = DMA_FIFOMODE_ENABLE;
|
||||||
|
sd_tx_dma_init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||||
|
sd_tx_dma_init.MemBurst = DMA_MBURST_INC4;
|
||||||
|
sd_tx_dma_init.PeriphBurst = DMA_PBURST_INC4;
|
||||||
|
|
||||||
|
// GPIO have already been initialised by sdcard_init
|
||||||
}
|
}
|
||||||
|
|
||||||
void HAL_SD_MspDeInit(SD_HandleTypeDef *hsd) {
|
void HAL_SD_MspDeInit(SD_HandleTypeDef *hsd) {
|
||||||
|
HAL_NVIC_DisableIRQ(SDIO_IRQn);
|
||||||
__SDIO_CLK_DISABLE();
|
__SDIO_CLK_DISABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +204,10 @@ uint64_t sdcard_get_capacity_in_bytes(void) {
|
||||||
return cardinfo.CardCapacity;
|
return cardinfo.CardCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SDIO_IRQHandler(void) {
|
||||||
|
HAL_SD_IRQHandler(&sd_handle);
|
||||||
|
}
|
||||||
|
|
||||||
mp_uint_t sdcard_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
|
mp_uint_t sdcard_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
|
||||||
// check that dest pointer is aligned on a 4-byte boundary
|
// check that dest pointer is aligned on a 4-byte boundary
|
||||||
if (((uint32_t)dest & 3) != 0) {
|
if (((uint32_t)dest & 3) != 0) {
|
||||||
|
@ -179,12 +219,23 @@ mp_uint_t sdcard_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blo
|
||||||
return SD_ERROR;
|
return SD_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We must disable IRQs because the SDIO peripheral has a small FIFO
|
HAL_SD_ErrorTypedef err = SD_OK;
|
||||||
// buffer and we can't let it fill up in the middle of a read.
|
|
||||||
// This will not be needed when SD uses DMA for transfer.
|
if (query_irq() == IRQ_STATE_ENABLED) {
|
||||||
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
dma_init(&sd_rx_dma, DMA_STREAM_SDIO_RX, &sd_rx_dma_init, DMA_CHANNEL_SDIO_RX, DMA_PERIPH_TO_MEMORY, &sd_handle);
|
||||||
HAL_SD_ErrorTypedef err = HAL_SD_ReadBlocks_BlockNumber(&sd_handle, (uint32_t*)dest, block_num, SDCARD_BLOCK_SIZE, num_blocks);
|
sd_handle.hdmarx = &sd_rx_dma;
|
||||||
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
|
||||||
|
err = HAL_SD_ReadBlocks_BlockNumber_DMA(&sd_handle, (uint32_t*)dest, block_num, SDCARD_BLOCK_SIZE, num_blocks);
|
||||||
|
if (err == SD_OK) {
|
||||||
|
// wait for DMA transfer to finish, with a large timeout
|
||||||
|
err = HAL_SD_CheckReadOperation(&sd_handle, 100000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_deinit(sd_handle.hdmarx);
|
||||||
|
sd_handle.hdmarx = NULL;
|
||||||
|
} else {
|
||||||
|
err = HAL_SD_ReadBlocks_BlockNumber(&sd_handle, (uint32_t*)dest, block_num, SDCARD_BLOCK_SIZE, num_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -200,70 +251,26 @@ mp_uint_t sdcard_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t n
|
||||||
return SD_ERROR;
|
return SD_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We must disable IRQs because the SDIO peripheral has a small FIFO
|
HAL_SD_ErrorTypedef err = SD_OK;
|
||||||
// buffer and we can't let it drain to empty in the middle of a write.
|
|
||||||
// This will not be needed when SD uses DMA for transfer.
|
if (query_irq() == IRQ_STATE_ENABLED) {
|
||||||
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
dma_init(&sd_tx_dma, DMA_STREAM_SDIO_TX, &sd_tx_dma_init, DMA_CHANNEL_SDIO_TX, DMA_MEMORY_TO_PERIPH, &sd_handle);
|
||||||
HAL_SD_ErrorTypedef err = HAL_SD_WriteBlocks_BlockNumber(&sd_handle, (uint32_t*)src, block_num, SDCARD_BLOCK_SIZE, num_blocks);
|
sd_handle.hdmatx = &sd_tx_dma;
|
||||||
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
|
||||||
|
err = HAL_SD_WriteBlocks_BlockNumber_DMA(&sd_handle, (uint32_t*)src, block_num, SDCARD_BLOCK_SIZE, num_blocks);
|
||||||
|
if (err == SD_OK) {
|
||||||
|
// wait for DMA transfer to finish, with a large timeout
|
||||||
|
err = HAL_SD_CheckWriteOperation(&sd_handle, 100000000);
|
||||||
|
}
|
||||||
|
dma_deinit(sd_handle.hdmatx);
|
||||||
|
sd_handle.hdmatx = NULL;
|
||||||
|
} else {
|
||||||
|
err = HAL_SD_WriteBlocks_BlockNumber(&sd_handle, (uint32_t*)src, block_num, SDCARD_BLOCK_SIZE, num_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
DMA not implemented
|
|
||||||
bool sdcard_read_blocks_dma(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
|
|
||||||
// check that dest pointer is aligned on a 4-byte boundary
|
|
||||||
if (((uint32_t)dest & 3) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that SD card is initialised
|
|
||||||
if (sd_handle.Instance == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do the read
|
|
||||||
if (HAL_SD_ReadBlocks_BlockNumber_DMA(&sd_handle, (uint32_t*)dest, block_num, SDCARD_BLOCK_SIZE) != SD_OK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for DMA transfer to finish, with a large timeout
|
|
||||||
if (HAL_SD_CheckReadOperation(&sd_handle, 100000000) != SD_OK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sdcard_write_blocks_dma(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
|
|
||||||
// check that src pointer is aligned on a 4-byte boundary
|
|
||||||
if (((uint32_t)src & 3) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that SD card is initialised
|
|
||||||
if (sd_handle.Instance == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SD_Error status;
|
|
||||||
|
|
||||||
status = HAL_SD_WriteBlocks_BlockNumber_DMA(&sd_handle, (uint32_t*)src, block_num, SDCARD_BLOCK_SIZE, num_blocks);
|
|
||||||
if (status != SD_OK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for DMA transfer to finish, with a large timeout
|
|
||||||
status = HAL_SD_CheckWriteOperation(&sd_handle, 100000000);
|
|
||||||
if (status != SD_OK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
// Micro Python bindings
|
// Micro Python bindings
|
||||||
//
|
//
|
||||||
|
|
Loading…
Reference in New Issue