Skip to content

Instantly share code, notes, and snippets.

@tschiemer
Created October 15, 2020 09:12
Show Gist options
  • Save tschiemer/cd443ef1b4bcfde33b37d3293b4d5490 to your computer and use it in GitHub Desktop.
Save tschiemer/cd443ef1b4bcfde33b37d3293b4d5490 to your computer and use it in GitHub Desktop.
nucleo stm32l476 SDIO note
#include "mbed.h"
#include "stm32l4xx_hal_rcc.h"
#include "stm32l4xx_hal_rcc_ex.h"
#include "stm32l4xx_hal_gpio.h"
#include "stm32l4xx_hal_gpio_ex.h"
#include "stm32l4xx_ll_sdmmc.h"
// #include "stm32l4xx_hal_sdmmc.h"
#include "stm32l4xx_hal_sd.h"
using namespace std::chrono;
Timer t;
#define ADDRESS 0
#define BLOCK_N 1
uint8_t blocks[512 * BLOCK_N] = "Hello World!\n";
SD_HandleTypeDef hsd;
HAL_SD_CardCIDTypeDef pCID;
HAL_SD_CardCSDTypeDef pCSD;
HAL_SD_CardInfoTypeDef pCardInfo;
#define check_error( op, msg ) if ( (op) != HAL_OK) { \
printf("HAL ERROR: %s\n", msg); \
while(1); \
}
// (#)Initialize the SDMMC low level resources by implementing the HAL_SD_MspInit() API:
void HAL_SD_MspInit(SD_HandleTypeDef *hsd)
{
// printf("MspInit\n");
// wait_us(1000);
// (##) Call the function HAL_RCCEx_PeriphCLKConfig with RCC_PERIPHCLK_SDMMC1 for
// PeriphClockSelection and select SDMMC1 clock source (MSI, main PLL or PLLSAI1)
// #if defined(RCC_HSI48_SUPPORT)
// #define RCC_SDMMC1CLKSOURCE_HSI48 0x00000000U /*!< HSI48 clock selected as SDMMC1 clock */
// #else
// #define RCC_SDMMC1CLKSOURCE_NONE 0x00000000U /*!< No clock selected as SDMMC1 clock */
// #endif /* RCC_HSI48_SUPPORT */
// #define RCC_SDMMC1CLKSOURCE_PLLSAI1 RCC_CCIPR_CLK48SEL_0 /*!< PLLSAI1 "Q" clock selected as SDMMC1 clock */
// #define RCC_SDMMC1CLKSOURCE_PLL RCC_CCIPR_CLK48SEL_1 /*!< PLL "Q" clock selected as SDMMC1 clock */
// #define RCC_SDMMC1CLKSOURCE_MSI RCC_CCIPR_CLK48SEL /*!< MSI clock selected as SDMMC1 clock */
// #if defined(RCC_CCIPR2_SDMMCSEL)
// #define RCC_SDMMC1CLKSOURCE_PLLP RCC_CCIPR2_SDMMCSEL /*!< PLL "P" clock selected as SDMMC1 kernel clock */
// #endif /* RCC_CCIPR2_SDMMCSEL */
RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit;
HAL_RCCEx_GetPeriphCLKConfig(&RCC_PeriphClkInit);
RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SDMMC1;
RCC_PeriphClkInit.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_MSI;
// RCC_PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSI;
// RCC_PeriphClkInit.PLLSAI1.PLLSAI1M = 1;
// RCC_PeriphClkInit.PLLSAI1.PLLSAI1N = 12;
// RCC_PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
// RCC_PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV4;
// RCC_PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;
// RCC_PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_48M2CLK;
// printf("MspInit DONE\n");
// pClkInit.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_MSI;
check_error(HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit), "init: clk");
// (##) Enable the SDMMC interface clock using __HAL_RCC_SDMMC1_CLK_ENABLE();
__HAL_RCC_SDMMC1_CLK_ENABLE();
// (##) SDMMC pins configuration for SD card
// (+++) Enable the clock for the SDMMC GPIOs using the functions __HAL_RCC_GPIOx_CLK_ENABLE();
// (+++) Configure these SDMMC pins as alternate function pull-up using HAL_GPIO_Init()
// and according to your pin assignment;
GPIO_InitTypeDef PORT;
__HAL_RCC_GPIOC_CLK_ENABLE(); // PC8-12
__HAL_RCC_GPIOD_CLK_ENABLE(); // PD2
PORT.Mode = GPIO_MODE_AF_PP; // Alternative function mode
PORT.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //GPIO_Speed_40MHz; // High speed // GPIO_SPEED_FREQ_HIGH
// PORT.OType = GPIO_OType_PP; // Output push-pull
PORT.Pull = GPIO_PULLUP;//GPIO_NOPULL;//GPIO_PULLUP; // Pull-up
PORT.Alternate = GPIO_AF12_SDMMC1;
PORT.Pin = GPIO_PIN_8;
HAL_GPIO_Init(GPIOC, &PORT);
PORT.Pin = GPIO_PIN_9;
HAL_GPIO_Init(GPIOC, &PORT);
PORT.Pin = GPIO_PIN_10;
HAL_GPIO_Init(GPIOC, &PORT);
PORT.Pin = GPIO_PIN_11;
HAL_GPIO_Init(GPIOC, &PORT);
PORT.Pin = GPIO_PIN_12;
// PORT.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &PORT);
PORT.Pin = GPIO_PIN_2;
HAL_GPIO_Init(GPIOD, &PORT);
// (##) On STM32L4Rx/STM32L4Sxx devices, no DMA configuration is need, an internal DMA for SDMMC Peripheral is used.
// (##) On other devices, perform DMA configuration if you need to use DMA process (HAL_SD_ReadBlocks_DMA()
// and HAL_SD_WriteBlocks_DMA() APIs).
// (+++) Enable the DMAx interface clock using __HAL_RCC_DMAx_CLK_ENABLE();
// (+++) Configure the DMA using the function HAL_DMA_Init() with predeclared and filled.
// (##) NVIC configuration if you need to use interrupt process when using DMA transfer.
// (+++) Configure the SDMMC and DMA interrupt priorities using functions
// HAL_NVIC_SetPriority(); DMA priority is superior to SDMMC's priority
// (+++) Enable the NVIC DMA and SDMMC IRQs using function HAL_NVIC_EnableIRQ()
// (+++) SDMMC interrupts are managed using the macros __HAL_SD_ENABLE_IT()
// and __HAL_SD_DISABLE_IT() inside the communication process.
// (+++) SDMMC interrupts pending bits are managed using the macros __HAL_SD_GET_IT()
// and __HAL_SD_CLEAR_IT()
// (##) NVIC configuration if you need to use interrupt process (HAL_SD_ReadBlocks_IT()
// and HAL_SD_WriteBlocks_IT() APIs).
// (+++) Configure the SDMMC interrupt priorities using function HAL_NVIC_SetPriority();
// (+++) Enable the NVIC SDMMC IRQs using function HAL_NVIC_EnableIRQ()
// (+++) SDMMC interrupts are managed using the macros __HAL_SD_ENABLE_IT()
// and __HAL_SD_DISABLE_IT() inside the communication process.
// (+++) SDMMC interrupts pending bits are managed using the macros __HAL_SD_GET_IT()
// and __HAL_SD_CLEAR_IT()
// (#) At this stage, you can perform SD read/write/erase operations after SD card initialization
// printf("MspInit DONE\n");
}
/**
* @brief De-Initialize SD MSP.
* @param hsd: Pointer to SD handle
* @retval None
*/
void HAL_SD_MspDeInit(SD_HandleTypeDef *hsd)
{
/* Disable NVIC for SDIO interrupts */
//HAL_NVIC_DisableIRQ(SDMMC1_IRQn);
__HAL_RCC_SDMMC1_CLK_DISABLE();
}
HAL_StatusTypeDef sd_init(){
printf("sd_init(): start\n");
hsd.Instance = SDMMC1;
// uint32_t ClockEdge; /*!< Specifies the clock transition on which the bit capture is made.
// This parameter can be a value of @ref SDMMC_LL_Clock_Edge */
hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
#if !defined(STM32L4R5xx) && !defined(STM32L4R7xx) && !defined(STM32L4R9xx) && !defined(STM32L4S5xx) && !defined(STM32L4S7xx) && !defined(STM32L4S9xx)
// uint32_t ClockBypass; /*!< Specifies whether the SDMMC Clock divider bypass is
// enabled or disabled.
// This parameter can be a value of @ref SDMMC_LL_Clock_Bypass */
hsd.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
#endif /* !STM32L4R5xx && !STM32L4R7xx && !STM32L4R9xx && !STM32L4S5xx && !STM32L4S7xx && !STM32L4S9xx */
// uint32_t ClockPowerSave; /*!< Specifies whether SDMMC Clock output is enabled or
// disabled when the bus is idle.
// This parameter can be a value of @ref SDMMC_LL_Clock_Power_Save */
hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
// uint32_t BusWide; /*!< Specifies the SDMMC bus width.
// This parameter can be a value of @ref SDMMC_LL_Bus_Wide */
hsd.Init.BusWide = SDMMC_BUS_WIDE_1B;
// uint32_t HardwareFlowControl; /*!< Specifies whether the SDMMC hardware flow control is enabled or disabled.
// This parameter can be a value of @ref SDMMC_LL_Hardware_Flow_Control */
hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
// uint32_t ClockDiv; /*!< Specifies the clock frequency of the SDMMC controller.
// This parameter can be a value between Min_Data = 0 and Max_Data = 1023 */
hsd.Init.ClockDiv = 0;
#if defined(STM32L4R5xx) || defined(STM32L4R7xx) || defined(STM32L4R9xx) || defined(STM32L4S5xx) || defined(STM32L4S7xx) || defined(STM32L4S9xx)
// uint32_t Transceiver; /*!< Specifies whether external Transceiver is enabled or disabled.
// This parameter can be a value of @ref SDMMC_LL_Transceiver */
hsd.Init.Transceiver = SDMMC_TRANSCEIVER_DISABLE;
#endif /* STM32L4R5xx || STM32L4R7xx || STM32L4R9xx || STM32L4S5xx || STM32L4S7xx || STM32L4S9xx */
// SDMMC1->DTIMER = 0;
// SDMMC1->DLEN = 0;
// SDMMC1->DCTRL = 0;
HAL_StatusTypeDef state = HAL_SD_Init(&hsd);
// printf("init = %d\n", state);
// check_error(state, "Init")
if (state == HAL_OK){
check_error(HAL_SD_ConfigWideBusOperation(&hsd, SDMMC_BUS_WIDE_4B), "Wide");
// check_error(HAL_SD_ConfigSpeedBusOperation(&hsd, SDMMC_SPEED_MODE_HIGH), "Speed");
}
printf("sd_init(): exit\n");
return state;
}
void sd_deinit(){
check_error(HAL_SD_DeInit(&hsd), "DeInit");
}
void sd_stat(){
// HAL_StatusTypeDef HAL_SD_SendSDStatus (&hsd, uint32_t *pSDstatus);
HAL_SD_CardStateTypeDef state = HAL_SD_GetCardState (&hsd);
printf("sd card state = %d\n", state);
// #define HAL_SD_CARD_READY 0x00000001U /*!< Card state is ready */
// #define HAL_SD_CARD_IDENTIFICATION 0x00000002U /*!< Card is in identification state */
// #define HAL_SD_CARD_STANDBY 0x00000003U /*!< Card is in standby state */
// #define HAL_SD_CARD_TRANSFER 0x00000004U /*!< Card is in transfer state */
// #define HAL_SD_CARD_SENDING 0x00000005U /*!< Card is sending an operation */
// #define HAL_SD_CARD_RECEIVING 0x00000006U /*!< Card is receiving operation information */
// #define HAL_SD_CARD_PROGRAMMING 0x00000007U /*!< Card is in programming state */
// #define HAL_SD_CARD_DISCONNECTED 0x00000008U /*!< Card is disconnected */
// #define HAL_SD_CARD_ERROR 0x000000FFU /*!< Card response Error */
check_error(HAL_SD_GetCardCID(&hsd, &pCID), "getCID");
printf("pCID.ManufacturerID = %02X\n", pCID.ManufacturerID);
printf("pCID.OEM_AppliID = %04X\n", pCID.OEM_AppliID);
printf("pCID.ProdName1 = %08X\n", pCID.ProdName1);
printf("pCID.ProdName2 = %02X\n", pCID.ProdName2);
printf("pCID.ProdRev = %02X\n", pCID.ProdRev);
printf("pCID.ProdSN = %08X\n", pCID.ProdSN);
printf("pCID.ManufactDate = %04X\n", pCID.ManufactDate);
printf("pCID.CID_CRC = %02X\n", pCID.CID_CRC);
check_error(HAL_SD_GetCardCSD(&hsd, &pCSD), "getCSD");
printf("pCSD.SysSpecVersion = %d\n", pCSD.SysSpecVersion);
printf("pCSD.CardComdClasses = %d\n", pCSD.CardComdClasses);
printf("pCSD.DeviceSize = %d\n", pCSD.DeviceSize);
printf("pCSD.DeviceSizeMul = %d\n", pCSD.DeviceSizeMul);
printf("pCSD.MaxBusClkFrec = %d\n", pCSD.MaxBusClkFrec);
printf("pCSD.TAAC (read time) = %d\n", pCSD.TAAC);
printf("pCSD.RdBlockLen = %d\n", pCSD.RdBlockLen);
printf("pCSD.MaxWrBlockLen = %d\n", pCSD.MaxWrBlockLen);
printf("pCSD.EraseGrSize = %d\n", pCSD.EraseGrSize);
printf("pCSD.EraseGrMul = %d\n", pCSD.EraseGrMul);
printf("pCSD.WrProtectGrSize = %d\n", pCSD.EraseGrSize);
printf("pCSD.WrProtectGrEnable = %d\n", pCSD.WrProtectGrEnable);
// check_error(HAL_SD_GetCardStatus(&hsd, &pStatus), "getStatus");
check_error(HAL_SD_GetCardInfo(&hsd, &pCardInfo), "getInfo");
printf("info.CardType = %d\n", pCardInfo.CardType);
printf("info.CardVersion = %d\n", pCardInfo.CardVersion);
printf("info.Class = %d\n", pCardInfo.Class);
printf("info.RelCardAdd = %d\n", pCardInfo.RelCardAdd);
printf("info.BlockNbr = %d\n", pCardInfo.BlockNbr);
printf("info.BlockSize = %d\n", pCardInfo.BlockSize);
printf("info.LogBlockNbr = %d\n", pCardInfo.LogBlockNbr);
printf("info.LogBlockSize = %d\n", pCardInfo.LogBlockSize);
// printf("info.CardSpeed = %d\n", pCardInfo.CardSpeed);
}
// main() runs in its own thread in the OS
int main()
{
printf("\nRESTART delayer\n");
HAL_StatusTypeDef state = sd_init();
if (state != HAL_OK){
printf("FAIL!\n");
while(1);
}
t.start();
HAL_SD_WriteBlocks(&hsd, blocks, ADDRESS, BLOCK_N, 1000000);
t.stop();
printf("The time taken was %llu usec\n", duration_cast<microseconds>(t.elapsed_time()).count());
t.reset();
t.start();
HAL_SD_ReadBlocks(&hsd, blocks, ADDRESS, BLOCK_N, 1000000);
t.stop();
printf("The time taken was %llu usec\n", duration_cast<microseconds>(t.elapsed_time()).count());
printf("%s\n", blocks);
while(1){
}
sd_deinit();
}
@tschiemer
Copy link
Author

electrical hookup as shown in many places

sparkfun sd card breakout: https://www.sparkfun.com/products/12941

cmd, d0/1/2/3 pulled to 3v3 through 47k resistors connected to sdmmc_cmd/d0/1/2/3
ck sdmmc_ck
cd NC
wp GND

also see (amongst others) https://karo-electronics.github.io/docs/hardware-documentation/txguide/PinAssignments/SDIO.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment