LoginSignup
3
2

More than 1 year has passed since last update.

F767ZIのSDMMC機能を使用してSDカードにデータを書き込んだ話

Posted at

はじめに

STM32マイコンの一部にはSDIO,SDMMCといったSDカードとの通信を行える機能があるそうです.

↓ ST公式によるSDMMCの資料
https://www.st.com/resource/en/product_presentation/35.stm32l4-peripheral-sdmmc-wspc-interface-wspc-(sdmmc)-wspc-final_jp.pdf

今回はNucleo-F767ZIでSDMMC機能を用い,SDカードにデータを書き込むことに成功したのでその方法を紹介したいと思います.

開発環境

使用ボード:Nucleo-F767ZI
CubeIDEバージョン: 1.6.1
SDカードスロット:これ

プロジェクト作成からコード生成まで

プロジェクト作成

・プロジェクト名を適当に設定.
・「Initialize all peripherals with their default Mode?」では「Yes」を選択.

Ethernetを無効化

・「Connectivity」->「ETH」を選択
・「Mode」->「Disable」を選択

私の環境ではEthernetの初期化でエラーを吐いてしまったのでEthernetを無効化しました.
Ethernet無効化.png

SDMMCの設定

・「Connectivity」->「SDMMC1」を選択
・「Mode」->「SD 4 bits Wide bus」を選択
・「Configuration」->「NVIC Settings」->「SDMMC1 global interrupt」を有効化
SDMMC設定1.png

ここで,Clock Configurationが×マークを表示するのでクロックの設定を行う.
「自動でクロックを設定するか」といった表示が出た場合は「OK」を選択.

SDMMC1へのクロック周波数を確認.今回は48MHzに設定されていた.
SDMMC設定2.png

「Pinout & Configuration」に再び戻り,
・「Configuration」->「DMA Settings」で「Add」ボタンから
「SDMMC1_RX」「SDMMC_TX」を追加.
(DMA Request SettingsはデフォルトのままでOK)

SDMMC設定3.png

・「Configuration」->「GPIO Settings」でSDカードとの接続に使用するピンが確認できる.
(使用するピンは計6つ)

SDMMC設定5.png

FATFSの設定

FatFsはChaN氏によるFATファイルシステム用ライブラリ[1]のこと.
STM32CubeIDEではそれをサポートしている.

・「Middleware」->「FATFS」を選択.
・「Mode」->「SD Card」をチェック.
・「Configuration」->「Advanced Settings」から「Use dma template」で「Enabled」を選択.

FATFS設定6.png

ヒープ・スタック領域の拡大

「Project Manager」で
・「Minimum Heap Size」を「0x400」
・「Minimum Stack Size」を「0x800」
にそれぞれ変更.
Middlewareを使用するため,この設定は必須とのこと.
Project Manager.png

コード生成

以上で設定は完了.コードを生成する.
その際に下図のような表示が出るが「Yes」を選択.
(SDカードの検出に関する設定らしい)
Code Generation.png

ソースコード

インクルード設定

必要なライブラリをインクルードします.

main.c
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */

使用する変数の宣言

「USER CODE BEGIN/END PV」内に以下を記述.

main.c
/* USER CODE BEGIN PV */
FRESULT res;
uint32_t byteswritten, bytesread;
uint8_t wtext[] = "STM32 FATFS works great!\r\n";
char write_buffer[_MAX_SS];
uint8_t rtext[_MAX_SS];
int value = 10;
float value2 = 2.3;
static const char* filename = "STM32.TXT";
/* USER CODE END PV */

main文

今回はSDカードに5回文字列を書き込んでみます.
書き込みが終わったらwhile文内でLチカさせて終了を知らせるようにしました.

main.c
int main(void)
{
  // (中略)

  /* USER CODE BEGIN 2 */
  // mount SD
  if(f_mount(&SDFatFS, (TCHAR const*)SDPath, 0) != FR_OK){
            Error_Handler();
    }else{
          //Open file for writing (Create)
          if(f_open(&SDFile, filename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK){
              Error_Handler();
          }else{
              // Write to the text file
              res = f_write(&SDFile, wtext, strlen((char *)wtext), (void *)&byteswritten);

              // Write int value
              sprintf(write_buffer, "value is %d\r\n", value);
              res = f_write(&SDFile, write_buffer, strlen((char *)write_buffer), (void *)&byteswritten);

              // write float value
              // Do not forget to set "-u _printf_float" argument
              sprintf(write_buffer, "value2 is %f\r\n", value2);
              res = f_write(&SDFile, write_buffer, strlen((char *)write_buffer), (void *)&byteswritten);

              // whether writing is completed or not
              if((byteswritten == 0) || (res != FR_OK)){
                  Error_Handler();
              }else{
                  f_close(&SDFile);
              }
          }
    }

    f_mount(&SDFatFS, (TCHAR const*)NULL, 0);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
      HAL_Delay(1000);
  }
  /* USER CODE END 3 */
}

// (中略)

void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
      HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
  }
  /* USER CODE END Error_Handler_Debug */
}

詳細

詳細について説明します.

/* mount SD-card */
if(f_mount(&SDFatFS, (TCHAR const*)SDPath, 0) != FR_OK){
    Error_Handler();
}

SDカードをマウントします.
失敗したらError_Handler()関数が呼ばれます.
Error_Handler()関数はmain.cの最下部にあるのでデバッグの際は何かしら処理を記述しておくと役に立つかも.
(自分はエラー検出用のLEDを光らす処理を入れたりしています)

/* Open file for writing (Create) */
if(f_open(&SDFile, filename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK){    // error
    Error_Handler();    // could not open / create the file
}

ファイルを新たに作成し,書き込みモードで開く処理です.

// Write to the text file
res = f_write(&SDFile, wtext, strlen((char *)wtext), (void *)&byteswritten);

// Write int value
sprintf(write_buffer, "value is %d\r\n", value);
res = f_write(&SDFile, write_buffer, strlen((char *)write_buffer), (void *)&byteswritten);

// write float value
// Do not forget to set "-u _printf_float" argument
sprintf(write_buffer, "value2 is %f\r\n", value2);
res = f_write(&SDFile, write_buffer, strlen((char *)write_buffer), (void *)&byteswritten);

ファイルに書き込む処理です.
変数の値をSDカードに書き込みたい場合はsprintf()関数で書き込むバッファ(write_buffer)内にデータを詰め,f_write()関数でSDカードに書き込みを行います.
この時の第三引数は”何バイト書き込みを行うか”を与えるのでstrlen()関数で文字列の長さを渡しています.
また,第四引数には”実際に何バイト書き込まれたか”が記録されます.

// whether writing is completed or not
if((byteswritten == 0) || (res != FR_OK)){
    Error_Handler();
}else{
    f_close(&SDFile);
}

書き込み処理が終了したら,エラーチェックを行います.
1バイトも書き込めなかった,もしくは正常でない状態を返した場合はError_Handler()を実行させます.

正常に書き込めていたらf_close()関数でファイルを閉じます.

/* close the file */
f_close(&SDFile);

/* unmount the SD-card */
f_mount(&SDFatFS, (TCHAR const*)NULL, 0);

SDカード内のファイルを閉じ,SDカードをアンマウントする処理です.
この処理を行わないとデータが保存されないので注意.

/* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
      HAL_Delay(1000);
  }

LEDを1秒間隔で点滅させます.SDカードの処理はこの処理が行われる前に完了しているので,完了の合図としています.

void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
      HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
  }
  /* USER CODE END Error_Handler_Debug */
}

エラーが起きた際に実行する関数です.定義はmain.cの一番下部にあります.
今回はエラーが起きたらLD2を光らせるようにしています.

浮動小数点を出力するための設定

浮動小数点を出力するための設定を行います.
上部のタブから「Project」->「Properties」をクリック
properties.png

・「C/C++ Build」->「Settings」をクリック
・「MCU G++ Linker」->「Miscellaneous」->「Other flags」に
-u _printf_float」を追加
・「Apply and Close」をクリックして終了
properties2.png

以上で設定は完了です.
金槌のマークをクリックしてビルドしてみます.
エラーが無いことを確認しましょう.
build_icon.png

実行

ビルドに成功したら実際にSDカードへ書き込んでみます.
まずはSDカードとF767ZIを接続します.

「SDMMC1」の「GPIO Setting」で確認したピンと,SDカードスロットの対応するピンを接続します.
VDDに3.3Vを,VSSとGNDを接続するのを忘れないように注意.

この時,
・D0
・D1
・D2
・D3
・CMD
5つのピンはプルアップを行うこと.
今回は10kΩのプルアップ抵抗を使用しました.
P_20210611_130238.jpg

また,念のためにSDカードもフォーマットしておきましょう.
形式は「FAT32」です.

結果

マイコンボードとPCをUSBで接続し,「Run」ボタンからプログラムを実行します.
実行する前にSDカードをスロットに挿入するのを忘れずに!
run_icon.png

プログラムを実行したら,LEDが点滅するまで待ちます.
点滅し始めたら無事SDカードに書き込めたので,マイコンボードからUSBケーブルを外し,SDカードもスロットから抜いておきます.

SDカードをPCに接続し,中身を見てみましょう.
SDカード内に「STM32.txt」というファイルがあり,画像のような文字が出力されているはずです.
result1.png
result2.png

おわりに

今回はSDMMC機能を使用してSDカードにデータを書き込んでみました.
SPIによる方法よりも高速かつCPUに負担がかからない方法とのことなので,FreeRTOSと組み合わせることで高速なデータロガーが作れるのではないでしょうか.

参考資料

[1] http://elm-chan.org/fsw/ff/00index_e.html
[2] https://youtu.be/I9KDN1o6924(ST公式による解説動画)
[3] https://programmersought.com/article/47326972171/

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2