diff --git a/stmhal/sdcard.c b/stmhal/sdcard.c index 3a5822b484..7ee371ae8d 100644 --- a/stmhal/sdcard.c +++ b/stmhal/sdcard.c @@ -24,8 +24,6 @@ * THE SOFTWARE. */ -// TODO make it work with DMA - #include STM32_HAL_H #include "py/nlr.h" @@ -34,6 +32,8 @@ #include "pin.h" #include "genhdr/pins.h" #include "bufhelper.h" +#include "dma.h" +#include "irq.h" #if MICROPY_HW_HAS_SDCARD @@ -64,8 +64,12 @@ #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 DMA_HandleTypeDef sd_rx_dma, sd_tx_dma; +static DMA_InitTypeDef sd_rx_dma_init, sd_tx_dma_init; void sdcard_init(void) { GPIO_InitTypeDef GPIO_Init_Structure; @@ -98,13 +102,45 @@ void HAL_SD_MspInit(SD_HandleTypeDef *hsd) { // enable SDIO clock __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 - // they are needed only for DMA transfer (I think...) + // TODO: Since SDIO is fundamentally half-duplex, we really only need to + // 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) { + HAL_NVIC_DisableIRQ(SDIO_IRQn); __SDIO_CLK_DISABLE(); } @@ -168,6 +204,10 @@ uint64_t sdcard_get_capacity_in_bytes(void) { 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) { // check that dest pointer is aligned on a 4-byte boundary 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; } - // We must disable IRQs because the SDIO peripheral has a small FIFO - // 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. - mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); - HAL_SD_ErrorTypedef err = HAL_SD_ReadBlocks_BlockNumber(&sd_handle, (uint32_t*)dest, block_num, SDCARD_BLOCK_SIZE, num_blocks); - MICROPY_END_ATOMIC_SECTION(atomic_state); + HAL_SD_ErrorTypedef err = SD_OK; + + if (query_irq() == IRQ_STATE_ENABLED) { + dma_init(&sd_rx_dma, DMA_STREAM_SDIO_RX, &sd_rx_dma_init, DMA_CHANNEL_SDIO_RX, DMA_PERIPH_TO_MEMORY, &sd_handle); + sd_handle.hdmarx = &sd_rx_dma; + + 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; } @@ -200,70 +251,26 @@ mp_uint_t sdcard_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t n return SD_ERROR; } - // We must disable IRQs because the SDIO peripheral has a small FIFO - // 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. - mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); - HAL_SD_ErrorTypedef err = HAL_SD_WriteBlocks_BlockNumber(&sd_handle, (uint32_t*)src, block_num, SDCARD_BLOCK_SIZE, num_blocks); - MICROPY_END_ATOMIC_SECTION(atomic_state); + HAL_SD_ErrorTypedef err = SD_OK; + + if (query_irq() == IRQ_STATE_ENABLED) { + dma_init(&sd_tx_dma, DMA_STREAM_SDIO_TX, &sd_tx_dma_init, DMA_CHANNEL_SDIO_TX, DMA_MEMORY_TO_PERIPH, &sd_handle); + sd_handle.hdmatx = &sd_tx_dma; + + 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; } -#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 //