/* * rm_write_proc.c * * Created on: Apr 11, 2022 * Author: carlk */ #include #include // #include "main.h" void show_STA(uint32_t STA); /* Example of write procedure using DMA Send CMD24 (WRITE_BLOCK) as follows: a) Program the SDMMC data length register (SDMMC data timer register should be already programmed before the card identification process) b) Program DMA channel (refer to DMA configuration for SDMMC controller) c) Program the SDMMC argument register with the address location of the card from where data is to be transferred d) Program the SDMMC command register: CmdIndex with 24(WRITE_BLOCK); WaitResp with ‘1’ (SDMMC card host waits for a response); CPSMEN with ‘1’ (SDMMC card host enabled to send a command). Other fields are at their reset value. e) Wait for SDMMC_STA[6] = CMDREND interrupt, then Program the SDMMC data control register: DTEN with ‘1’ (SDMMC card host enabled to send data); DTDIR with ‘0’ (from controller to card); DTMODE with ‘0’ (block data transfer); DMAEN with ‘1’ (DMA enabled); DBLOCKSIZE with 0x9 (512 bytes). Other fields are don’t care. f) Wait for SDMMC_STA[10] = DBCKEND, (DBCKEND is set in case of no errors) */ HAL_StatusTypeDef RM_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks) { SDMMC_TypeDef *SDMMCx = hsd1.Instance; HAL_StatusTypeDef sd_state = HAL_OK; // Send CMD24 (WRITE_BLOCK) as follows: // a) Program the SDMMC data length register (SDMMC data timer register should be // already programmed before the card identification process) // /* Set the SDMMC Data TimeOut value */ // SDMMCx->DTIMER = SDMMC_DATATIMEOUT; /* Set the SDMMC DataLength value */ SDMMCx->DLEN = 128; // WORDS // b) Program DMA channel (refer to DMA configuration for SDMMC controller) /* DMA configuration for SDMMC controller a) Enable DMA2 controller and clear any pending interrupts b) Program the DMA2_Channel4 (or DMA2_Channel5) source address register with the memory location base address and DMA2_Channel4 (or DMA2_Channel5) destination address register with the SDMMC_FIFO register address c) Program DMA2_Channel4 (or DMA2_Channel5) control register (memory increment, not peripheral increment, peripheral and source width is word size) d) Enable DMA2_Channel4 (or DMA2_Channel5) */ DMA_HandleTypeDef *hdma = hsd1.hdmatx; // a) Enable DMA2 controller and clear any pending interrupts /* Enable the Peripheral */ __HAL_DMA_ENABLE(hdma); /** * @note Do not Clear Channel 4 global interrupt flag when the channel in ON. Instead clear specific flags transfer complete, half transfer & transfer error flag with LL_DMA_ClearFlag_TC4, LL_DMA_ClearFlag_HT4, LL_DMA_ClearFlag_TE4. bug id 2.4.1/2.5.1 in Product Errata Sheet. */ /* Clear all flags */ LL_DMA_ClearFlag_TC4(hdma->DmaBaseAddress); LL_DMA_ClearFlag_HT4(hdma->DmaBaseAddress); LL_DMA_ClearFlag_TE4(hdma->DmaBaseAddress); __HAL_DMA_DISABLE(hdma); LL_DMA_ClearFlag_GI4(hdma->DmaBaseAddress); // b) Program the DMA2_Channel4 (or DMA2_Channel5) source address register with // the memory location base address and DMA2_Channel4 (or DMA2_Channel5) // destination address register with the SDMMC_FIFO register address /* DMA2 */ // hdma->ChannelIndex = (((uint32_t) hdma->Instance - (uint32_t) DMA2_Channel1) // / ((uint32_t) DMA2_Channel2 - (uint32_t) DMA2_Channel1)) << 2U; // hdma->DmaBaseAddress = DMA2; /* Configure DMA Channel destination address */ hdma->Instance->CPAR = (uint32_t)&SDMMCx->FIFO; /* Configure DMA Channel source address */ hdma->Instance->CMAR = (uint32_t)pData; // c) Program DMA2_Channel4 (or DMA2_Channel5) control register (memory // increment, not peripheral increment, peripheral and source width is word size) // Bit 0 EN: channel enable // Bit 1 TCIE: transfer complete interrupt enable: 1: enabled // Bit 2 HTIE: half transfer interrupt enable: 0: disabled // Bit 3 TEIE: transfer error interrupt enable: 1: enabled // Bit 4 DIR: data transfer direction: 1: read from memory // Bit 5 CIRC: circular mode: 0: disabled // Bit 6 PINC: peripheral increment mode: 0: disabled // Bit 7 MINC: memory increment mode: 1: enabled // Bits 9:8 PSIZE[1:0]: peripheral size: 10: 32 bits // Bits 11:10 MSIZE[1:0]: memory size: 10: 32 bits // Bits 13:12 PL[1:0]: priority level: 11: very high // Bit 14 MEM2MEM: memory-to-memory mode: 0: disabled // // 3 2 1 0 // // 10987654321098765432109876543210 // hdma->Instance->CCR = 0b00000000000000000011101010011010; uint32_t tmpreg = hdma->Instance->CCR; // Clear bits tmpreg &= ~(DMA_CCR_MSIZE | DMA_CCR_PSIZE | DMA_CCR_MINC | DMA_CCR_PINC); // memory increment, tmpreg |= DMA_MINC_ENABLE; // not peripheral increment, // peripheral tmpreg |= DMA_PDATAALIGN_WORD; // and source width is word size tmpreg |= DMA_MDATAALIGN_WORD; hdma->Instance->CCR = tmpreg; // // d) Enable DMA2_Channel4 (or DMA2_Channel5) // hdma->Instance->CCR |= DMA_CCR_EN; __HAL_DMA_ENABLE(hdma); // c) Program the SDMMC argument register with the address location of the card from // where data is to be transferred hsd1.Instance->ARG = WriteAddr; // d) Program the SDMMC command register: CmdIndex with 24(WRITE_BLOCK); // WaitResp with ‘1’ (SDMMC card host waits for a response); CPSMEN with ‘1’ // (SDMMC card host enabled to send a command). Other fields are at their reset // value. // Bit 11 SDIOSuspend: SD I/O suspend command // Bit 10 CPSMEN: Command path state machine (CPSM) Enable bit // Bit 9 WAITPEND: CPSM Waits for ends of data transfer (CmdPend internal signal). // Bit 8 WAITINT: CPSM waits for interrupt request // Bits 7:6 WAITRESP: Wait for response bits // They are used to configure whether the CPSM is to wait for a response, and if yes, which // kind of response. // 00: No response, expect CMDSENT flag // 01: Short response, expect CMDREND or CCRCFAIL flag // 10: No response, expect CMDSENT flag // 11: Long response, expect CMDREND or CCRCFAIL flag // Bits 5:0 CMDINDEX: Command index // 3 2 1 0 // 10987654321098765432109876543210 // 0b00000000000000000000010001011000 tmpreg = SDMMC_CMD_WRITE_SINGLE_BLOCK | SDMMC_RESPONSE_SHORT | SDMMC_CPSM_ENABLE; hsd1.Instance->CMD = (hsd1.Instance->CMD & ~(SDMMC_CMD_CMDINDEX | SDMMC_CMD_WAITRESP | SDMMC_CMD_CPSMEN)) | tmpreg; // e) Wait for SDMMC_STA[6] = CMDREND interrupt, then Program the SDMMC data // control register: DTEN with ‘1’ (SDMMC card host enabled to send data); DTDIR // with ‘0’ (from controller to card); DTMODE with ‘0’ (block data transfer); DMAEN // with ‘1’ (DMA enabled); DBLOCKSIZE with 0x9 (512 bytes). Other fields are don’t // care. uint32_t errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_WRITE_SINGLE_BLOCK, SDMMC_CMDTIMEOUT); if (errorstate != HAL_SD_ERROR_NONE) { /* Clear all the static flags */ __HAL_SD_CLEAR_FLAG(&hsd1, SDMMC_STATIC_FLAGS); hsd1.ErrorCode |= errorstate; hsd1.State = HAL_SD_STATE_READY; hsd1.Context = SD_CONTEXT_NONE; printf("SD Error: 0x%lx\n", errorstate); // stm32l4xx_ll_sdmmc.h return HAL_ERROR; } tmpreg = SDMMCx->DCTRL; // DTEN with ‘1’ (SDMMC card host enabled to send data); tmpreg |= SDMMC_DCTRL_DTEN; // DTDIR with ‘0’ (from controller to card); tmpreg &= ~(SDMMC_DCTRL_DTDIR); // DTMODE with ‘0’ (block data transfer); tmpreg &= ~(SDMMC_DCTRL_DTMODE); // DMAEN with ‘1’ (DMA enabled); tmpreg |= SDMMC_DCTRL_DMAEN; // DBLOCKSIZE with 0x9 (512 bytes). // #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) tmpreg &= ~(SDMMC_DCTRL_DBLOCKSIZE_Msk); tmpreg |= 0x9 << SDMMC_DCTRL_DBLOCKSIZE_Pos; SDMMCx->DCTRL = tmpreg; // f) Wait for SDMMC_STA[10] = DBCKEND, (DBCKEND is set in case of no errors) while (!(SDMMCx->STA & SDMMC_FLAG_DBCKEND)) { const char* uint_binary_str(unsigned int number); printf("\tSTA : 0b%s\n", uint_binary_str(hsd1.Instance->STA)); show_STA(SDMMCx->STA); HAL_Delay(1000); } return sd_state; }