Here’s a practical, copy-pasteable path to write data to a microSD card from an [STM32](https://www.ampheo.com/search/STM32). Two mainstream options: * SDMMC/SDIO 4-bit bus (fast; recommended if your MCU has SDMMC) * SPI mode (universal; works on any STM32 with SPI) ![module-wired](https://hackmd.io/_uploads/SyrY0Bvy-e.jpg) **1) Hardware checklist** **Common to both** * Voltage: microSD is 3.3 V only (I/O too). Do not connect to 5 V pins. * Decoupling: ≥1× 100 nF close to socket + 10–47 µF bulk nearby. * ESD/EMI: place a low-capacitance ESD array at the socket. * Pull-ups: CMD/DAT lines need pull-ups [resistor](https://www.onzuu.com/category/resistors) (typ. 47 kΩ–100 kΩ; many sockets/boards already have them). * Card detect (CD) and write protect (WP) pins are optional switches on some sockets. **SDMMC/SDIO (preferred if available)** * Lines: CLK, CMD, D0–D3 (+ VDD, GND). Start at ≤25 MHz, then up to 50 MHz in high-speed mode. * Keep traces short/clean; route CLK carefully. **SPI mode** * Lines: SCK, MOSI, MISO, CS (plus power/ground). Start at 100–400 kHz for init, then raise (e.g., 12–18 MHz). * Put a series 22–33 Ω on SCK if edges ring. **2) CubeMX setup (FatFS)** **A. SDMMC path** 1. Enable SDMMC1 (or SDMMC2) in CubeMX; pick your pins. 2. Enable DMA for Rx/Tx if available. 3. Add FATFS middleware → set SD Card interface = SDMMC. 4. Generate code; you’ll get fatfs.c, user_diskio.c, and SD drivers. **B. SPI path** 1. Enable SPIx in Full-Duplex Master. 2. Add FATFS → set SD Card interface = SPI. 3. Provide/keep the diskio glue (CubeMX template uses SPI to talk to the card). On M7/H7 with D-Cache: use cache-safe, 32-byte aligned buffers or clean/invalidate cache around DMA I/O (see Tips §6). **3) Minimal code: create and write a file (FatFS)** ``` #include "fatfs.h" #include "stdio.h" static FATFS fs; static FIL file; static char line[64]; void sdcard_example_write(void) { FRESULT fr; /* 1) Mount (auto-mount: set 1st param to "", 3rd to 1 for immediate) */ fr = f_mount(&fs, "", 1); if (fr != FR_OK) { /* handle error */ return; } /* 2) (Optional) create a folder */ f_mkdir("logs"); /* ignore FR_EXIST */ /* 3) Open or create a CSV for append */ fr = f_open(&file, "logs/data.csv", FA_OPEN_APPEND | FA_WRITE); if (fr == FR_NO_FILE) { /* If path missing, create new */ fr = f_open(&file, "logs/data.csv", FA_CREATE_ALWAYS | FA_WRITE); if (fr != FR_OK) { /* handle error */ goto unmount; } f_puts("time_ms,temperature,voltage\r\n", &file); /* header */ } else if (fr != FR_OK) { /* handle error */ goto unmount; } /* 4) Write one line */ snprintf(line, sizeof(line), "%lu,%.2f,%.3f\r\n", (unsigned long)HAL_GetTick(), 23.75f, 3.301f); UINT bw = 0; fr = f_write(&file, line, strlen(line), &bw); if (fr != FR_OK || bw != strlen(line)) { /* handle error */ } /* 5) Flush metadata to reduce corruption risk on power loss */ f_sync(&file); /* 6) Close and unmount */ f_close(&file); unmount: f_mount(NULL, "", 0); } ``` Linker note: enable long filenames (LFN) in ffconf.h if you need them, and provide a working buffer (static or heap). **4) SPI-mode specifics (if you don’t have SDMMC)** * In CubeMX, choose FATFS: SD Card (SPI) and map CS to a GPIO. * Ensure the generated user_diskio_spi.c (name may vary) calls your SPI transmit/receive (ideally with DMA). * Start SPI clock low (100–400 kHz) during card init; raise after FatFS mounts. **5) Formatting & card types** * Block size is 512 bytes; FatFS handles it. * Card sizes: SDSC ≤2 GB (FAT16), SDHC 4–32 GB (FAT32), SDXC ≥64 GB (exFAT). * For broad compatibility, format as FAT32 on PC for ≤32 GB. * exFAT needs FF_SUPPORT_EXFAT in ffconf.h (and larger workspace). **6) Robustness, performance & caches** * Power-loss safety: write to a temp file, f_sync(), then rename to final name; or append with periodic f_sync(). * Batch writes: buffer data in RAM and write in multiples of 512 B to reduce wear/speed up. * M7/H7 D-Cache: * Use alignas(32) uint8_t io_buf[512]; for I/O buffers. * Before DMA Tx: clean cache (SCB_CleanDCache_by_Addr). * After DMA Rx: invalidate cache (SCB_InvalidateDCache_by_Addr). * Or configure MPU region for SD DMA buffers as non-cacheable. * Task-safety (RTOS): enable reentrancy in ffconf.h and provide mutex hooks, or serialize FS access. **7) Common errors & quick fixes** * FR_NOT_READY / mount fails: check wiring/pull-ups; lower clock; ensure card powered & inserted; verify diskio glue. * FR_NO_FILESYSTEM: format the card (FAT32) or call f_mkfs() (needs larger work buffer). * No SD card detected: CD pin polarity wrong or not wired; disable CD check in diskio. * Random write failures on [STM32H7](https://www.ampheoelec.de/search/STM32H7)/M7: fix cache coherency or lower SDMMC clock; verify DMA settings. * Very slow SPI writes: raise SPI speed after init; use DMA; write in 4–8 KB chunks. **8) Tiny logging loop (CSV)** ``` void log_sample(float t_c, float vbat) { if (f_open(&file, "logs/data.csv", FA_OPEN_APPEND | FA_WRITE) == FR_OK) { char buf[64]; UINT bw; int n = snprintf(buf, sizeof(buf), "%lu,%.2f,%.3f\r\n", (unsigned long)HAL_GetTick(), t_c, vbat); f_write(&file, buf, n, &bw); f_sync(&file); f_close(&file); } } ```