🎃

STM32でSPIでSDを操作するとかいう時代遅れなことをしたいのか?

2023/04/28に公開

STM32のSDカード事情

最近のSTMにはSDIOとかSDMMCといった専用ペリフェラルが用意されていて、SPIを使うよりずっと高速にSDカードと通信することが可能です。しかしどういうわけかSDIOのHALには謎のバグが大量にあり、動かせはしましたがそれも1bitモードだけで4bit Wideは使えませんでした。(原因究明中)

と、いうわけで大変時代遅れな手段ではありますが、SPIを使ってSDカードのR/Wにトライします。

環境

  • STM32F401RDT6
  • STM32Cube FW_F4. V1.27.1

SDカードをSPIで接続して、クロック以外のピンをプルアップすること。

プログラム

書き込み用のJTAG、SDカードと通信するSPI、FATFS、CS用のGPIOを設定していきます

ピンの設定

今回は、JTAG5pinを書き込み用に用意しました。
SPIはSPI1を設定、CSにはPD2を使います。

FATFS

FATFSを有効化します。user-definedをチェックすればOKです。

USE_LFNをEnableにします。
https://ja.wikipedia.org/wiki/長いファイル名

デフォルトだとSDカードの最大セクタが512になっているので、4069に変更。
http://memes.sakura.ne.jp/memes/?page_id=2225

SPI

SPI1を有効化し、分周周期をおとします。SDカードはクロックが早すぎると初期の接続プロセスで失敗するので、10M以下くらいにするとよいと思われます。

また、CS用にGPIOをひとつ立ち上げておきましょう。

Code Generate

作成した設定をコードに書き出します。ビルドしてみてもOKです。

コードの変更

こちらのサイトのサンプルコードを使わせていただきます。(UPDATE2)
https://controllerstech.com/sd-card-using-spi-in-stm32/?

https://controllerstech.com/wp-content/uploads/2020/07/SDCARD_SPI_OLD_F103.zip

zipを解凍したら、Inc、Src下のfatfs_sd.cとfatfs_sd.hをそれぞれコピーし自分の環境に取り込みます。

Core/Inc/fatfs_sd.h
#ifndef INC_FATFS_SD_H_
#define INC_FATFS_SD_H_

#ifndef __FATFS_SD_H
#define __FATFS_SD_H

+ #include "diskio.h"

/* Definitions for MMC/SDC command */
#define CMD0     (0x40+0)     /* GO_IDLE_STATE */
#define CMD1     (0x40+1)     /* SEND_OP_COND */
#define CMD8     (0x40+8)     /* SEND_IF_COND */
#define CMD9     (0x40+9)     /* SEND_CSD */

user_disk.ioを編集してSDカード書き込みに対応させます。

FATFS/Target/user_diskio.c
/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"
+ #include "fatfs_sd.h"
FATFS/Target/user_diskio.c
/* Private functions ---------------------------------------------------------*/

DSTATUS USER_initialize (
	BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{
  /* USER CODE BEGIN INIT */
-    Stat = STA_NOINIT;
-    return Stat;
+    return SD_disk_initialize(pdrv);
  /* USER CODE END INIT */
}

DSTATUS USER_status (
	BYTE pdrv       /* Physical drive number to identify the drive */
)
{
  /* USER CODE BEGIN STATUS */
-    Stat = STA_NOINIT;
-    return Stat;
+    return SD_disk_status(pdrv);
  /* USER CODE END STATUS */
}

DRESULT USER_read (
	BYTE pdrv,      /* Physical drive nmuber to identify the drive */
	BYTE *buff,     /* Data buffer to store read data */
	DWORD sector,   /* Sector address in LBA */
	UINT count      /* Number of sectors to read */
)
{
  /* USER CODE BEGIN READ */
-    return RES_OK;
+    return SD_disk_read(pdrv, buff, sector, count);
  /* USER CODE END READ */
}

#if _USE_WRITE == 1
DRESULT USER_write (
	BYTE pdrv,          /* Physical drive nmuber to identify the drive */
	const BYTE *buff,   /* Data to be written */
	DWORD sector,       /* Sector address in LBA */
	UINT count          /* Number of sectors to write */
)
{
  /* USER CODE BEGIN WRITE */
  /* USER CODE HERE */
-    return RES_OK;
+    return SD_disk_write(pdrv, buff, sector, count);
  /* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */

#if _USE_IOCTL == 1
DRESULT USER_ioctl (
	BYTE pdrv,      /* Physical drive nmuber (0..) */
	BYTE cmd,       /* Control code */
	void *buff      /* Buffer to send/receive control data */
)
{
  /* USER CODE BEGIN IOCTL */
-    DRESULT res = RES_ERROR;
-    return res;
+    return SD_disk_ioctl(pdrv, cmd, buff);
  /* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */

fatfs_sd.cにピン設定を書き込む

Core/Src/fatfs_sd.c
#define TRUE  1
#define FALSE 0
#define bool BYTE

- #include "stm32f1xx_hal.h"
+ #include "stm32f4xx_hal.h"

#include "diskio.h"
#include "fatfs_sd.h"

+ extern SPI_HandleTypeDef hspi1;
extern volatile uint8_t Timer1, Timer2;
static volatile DSTATUS Stat = STA_NOINIT;
static uint8_t CardType;
static uint8_t PowerFlag = 0;

- #define SD_CS_GPIO_Port GPIOB
- #define SD_CS_Pin GPIO_PIN_0
+ #define SD_CS_GPIO_Port GPIOD
+ #define SD_CS_Pin GPIO_PIN_2

タイマの変更

Core/Src/stm32f4xx_it.c
/* Private user code ---------------------------------------*/
/* USER CODE BEGIN 0 */
+ volatile uint8_t FatFsCnt = 0;
+ volatile uint8_t Timer1, Timer2;

+ void SDTimer_Handler(void)
+ {
+ 	if(Timer1 > 0)
+ 		Timer1--;
+ 	if(Timer2 > 0)
+ 		Timer2--;
+ }
/* USER CODE END 0 */
Core/Src/stm32f4xx_it.c
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
+   FatFsCnt++;
+   if(FatFsCnt >= 10)
+   {
+ 	  FatFsCnt = 0;
+ 	  SDTimer_Handler();
+   }
  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

main.cにテスト用プログラムを記述

インクルードファイルの記述

Core/Src/main.c
/* USER CODE BEGIN Includes */
+ #include "fatfs_sd.h"
+ #include "stdio.h"
+ #include "string.h"
/* USER CODE END Includes */
Core/Src/main.c
/* USER CODE BEGIN 0 */
FATFS fs;
FIL fil;
FRESULT fresult;
char buffer[1024];
char buffer2[1024];
UINT br, bw;

FATFS *pfs;
uint32_t fre_clust, tot_size, fre_size;

void send_uart(char *s)
{
	HAL_UART_Transmit(&huart2, (uint8_t *) s, strlen(s), 2000);
}
/* USER CODE END 0 */
Core/Src/main.c
/* USER CODE BEGIN 2 */
  fresult = f_mount(&fs, "/", 1);
  if(fresult == FR_OK){
	  send_uart("SD CARD mounted successfully.");
  }else{
	  send_uart("ERROR : SD CARD mounted");
  }

  if(f_getfree("", &fre_clust, &pfs) == FR_OK){
	  tot_size = (pfs->n_fatent - 2) * pfs->csize * 0.5;
	  sprintf(buffer, "Total size: %15lu B\n", tot_size * 1024);
	  send_uart(buffer);
	  fre_size = fre_clust * pfs->csize * 0.5;
	  sprintf(buffer, "Free size: %15lu B\n", fre_size * 1024);
	  send_uart(buffer);
  }

  if(f_open(&fil, "file1.txt", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK){
	  strcpy(buffer, "qwertyuiop\n");
	  f_write(&fil, buffer, strlen(buffer), &bw);
	  f_close(&fil);
  }

  if(f_open(&fil, "file1.txt", FA_READ) == FR_OK){
	  send_uart("file1 open.\n");
  	  f_read(&fil, buffer2, f_size(&fil), &br);
  	  send_uart(buffer2);
  	  f_close(&fil);
    }
  /* USER CODE END 2 */

書き込みできました

Discussion