STM32でSPIでSDを操作するとかいう時代遅れなことをしたいのか?
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にします。
デフォルトだとSDカードの最大セクタが512になっているので、4069に変更。
SPI
SPI1を有効化し、分周周期をおとします。SDカードはクロックが早すぎると初期の接続プロセスで失敗するので、10M以下くらいにするとよいと思われます。
また、CS用にGPIOをひとつ立ち上げておきましょう。
Code Generate
作成した設定をコードに書き出します。ビルドしてみてもOKです。
コードの変更
こちらのサイトのサンプルコードを使わせていただきます。(UPDATE2)
https://controllerstech.com/sd-card-using-spi-in-stm32/?
zipを解凍したら、Inc、Src下のfatfs_sd.cと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カード書き込みに対応させます。
/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"
+ #include "fatfs_sd.h"
/* 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にピン設定を書き込む
#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
タイマの変更
/* 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 */
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にテスト用プログラムを記述
インクルードファイルの記述
/* USER CODE BEGIN Includes */
+ #include "fatfs_sd.h"
+ #include "stdio.h"
+ #include "string.h"
/* USER CODE END Includes */
/* 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 */
/* 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