首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

STM32 FATFS 寫入 SD 卡顯示亂碼根因排查:編碼不匹配、緩衝區長度、檔案模式三大陷阱

     編輯:程式設計綜合問答

使用 STM32 + FATFS (官方中間件) 將 UART 接收資料寫入 SD 卡,電腦讀取卻顯示亂碼。本文系統性分析三大常見成因:f_write 長度參數錯誤、資料編碼 (ASCII/UTF-8/二進位) 不匹配、檔案開啟模式遺漏 FA_READ 導致無法驗證,並提供逐步除錯流程與修正代碼。

現象與環境

  • MCU:STM32F4/F1 系列
  • 檔案系統:FatFs R0.12b+ (官方 Middleware)
  • 症狀:UART 收發正常,f_write 回傳 FR_OK,但電腦用記事本/Notepad++ 開啟 test.txt 顯示亂碼或 NUL 字元。

原始代碼問題剖析

void WriteSDFile(unsigned char *USARTx_RX_BUF) {
    disk_initialize(0);
    f_mount(0, &fs);
    res = f_open(&fsrc, "test.txt", FA_OPEN_ALWAYS | FA_WRITE); // 缺少 FA_READ
    if (res == FR_OK) {
        f_lseek(&fsrc, fsrc.fsize); // 追加模式
        // ❌ 錯誤:傳遞指標位址 &USARTx_RX_BUF,長度用 NUM_SAVE-1 (常數)
        res = f_write(&fsrc, &USARTx_RX_BUF, NUM_SAVE-1, &br);
        f_close(&fsrc);
    }
}

三大核心錯誤與修正

1. f_write 緩衝區指標與長度錯誤 (最關鍵)

錯誤:&USARTx_RX_BUF 是 unsigned char** (指標的位址),而非資料起始位址。長度 NUM_SAVE-1 為固定常數,不等於實際接收長度。

修正:

// 假設實際接收長度存於全域變數 USARTx_RX_LEN
UINT bytesWritten;
res = f_write(&fsrc, USARTx_RX_BUF, USARTx_RX_LEN, &bytesWritten);
// 或使用 strlen (若確定以 '\0' 結尾且無二進位 0)
res = f_write(&fsrc, USARTx_RX_BUF, strlen((char*)USARTx_RX_BUF), &bytesWritten);

2. 編碼不匹配:二進位資料直接寫入文字檔

  • 若 UART 接收的是原始二進位 (如感測器 0x00~0xFF),直接寫入 .txt,文字編輯器嘗試以 UTF-8/ANSI 解碼會產生亂碼。
  • 對策 A:改寫二進位檔 test.bin,用十六進位編輯器 (HxD) 檢視。
  • 對策 B:若需人類可讀,MCU 端將資料轉為 ASCII 十六進位字串 (如 sprintf(buf, "%02X ", data[i])) 或 Base64 編碼後再寫入。

3. 檔案開啟模式缺少 FA_READ,無法驗證寫入內容

// 建議模式:可讀可寫、追加、不存在則建立
res = f_open(&fsrc, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);

具備 FA_READ 才能在 MCU 端用 f_read 回讀驗證,排除「寫入成功但資料錯誤」vs「寫入成功、讀取端解碼錯誤」。

完整修正版代碼

FRESULT WriteSDFile(const uint8_t *buf, uint16_t len) {
    FRESULT res;
    FATFS fs;
    FIL fil;
    UINT bw;

    res = f_mount(&fs, "0:", 1);
    if (res != FR_OK) return res;

    res = f_open(&fil, "0:/test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
    if (res != FR_OK) { f_mount(NULL, "0:", 1); return res; }

    res = f_lseek(&fil, f_size(&fil)); // 移至檔案末尾 (追加)
    if (res != FR_OK) { f_close(&fil); f_mount(NULL, "0:", 1); return res; }

    res = f_write(&fil, buf, len, &bw);
    f_close(&fil);
    f_mount(NULL, "0:", 1); // 釋放掛載
    return res;
}

// 呼叫範例
WriteSDFile(USARTx_RX_BUF, USARTx_RX_LEN);

除錯驗證清單 (按順序執行)

  1. MCU 端回讀驗證:寫入後立即 f_read 並透過 UART 印出十六進位,確認資料位元模式正確。
  2. 電腦端十六進位檢視:用 HxD 開啟 test.txt,查看原始位元組是否與 MCU 端一致。
  3. 編碼轉換測試:Notepad++ 選單 Encoding 切換 UTF-8/ANSI/UCS-2 LE 觀察是否可讀。
  4. 長度核對:比對 bw (實際寫入位元組數) 與 len、檔案大小是否一致。

常見誤區提醒

誤區正確認知
f_write(&fil, &buf, ...)應為 f_write(&fil, buf, ...),陣列名即起始位址
用 sizeof(buf) 當長度sizeof 取得指標大小 (4/8),需傳入實際資料長度變數
以為 FA_WRITE 含蓋讀取FatFs 權限位元獨立,需額外加 FA_READ
未呼叫 f_mount(NULL, ...) 卸載長時間運行會耗盡檔案系統物件,建議每次用完卸載

亂碼截圖

總結

SD 卡寫入亂碼 90% 源自 f_write 緩衝區指標/長度錯誤、編碼預期不符、缺乏讀回驗證。遵循「正確指標、實際長度、二進位存二進位檔/文字存文字檔、MCU 端先驗證」四原則,配合完整錯誤碼檢查與掛載管理,即可建立可靠的嵌入式檔案記錄功能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved