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)

**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);
}
}
```