STM32 SPI SD card
사용 보드 : F429ZI
프로그램 : CubeIDE
서론
SD 카드 리더기로
핀이 9개가 있는 제품과
핀이 6개가 있는 제품이 있다.
SDIO 기능을 사용할 때 쓰려고 샀는데 보니까 [알리익스프레스 제품 9핀 링크]
SPI 통신으로 데이터를 주고 받는 것이였다. [알리익스프레스 제품 6핀 링크]
Setup
먼저 RCC 를 HSE 로 설정한다.
HSE Clock 설정은 아래와 같이 했다.
SD카드를 읽고 쓸 예정이기 때문에 Full-Duplex Master로 설정한다.
속도는 일단 테스트를 하기위해 11.25MBits/s 로 맞췄다.
파일 읽고 쓰는 과정을 확인하기 위해 UART3 설정을 한다.
FATFS 설정
USE_LFN 은 8 Bytes 이상의 파일 이름을 위해서 설정하였고
섹터 사이즈는 최대로 올렸다. [Sector : FAT32 관련 블로그 링크]
전체 핀
PB10 : USART3_TX
PB11 : USART3_RX
PB13 : SPI2_SCK
PB14 : SPI2_MISO
PB15 : SPI2_MOSI
PD8 : GPIO_OUTPUT (SD_CS, HIGH)
PD10 : GPIO_EXTI10 (인터럽트 용 버튼)
SDIO 할 때는 핀을 알아서 해줬는데
SPI 통신을 할 때는 장치마다 CS (Chip Select) 를 설정해줘야 한다.
GPIO_OUTPUT을 하나 고르고
초기 GPIO output level 을 HIGH로 해준다.
Maximum output speed => Medium
이름은 굳이 바꿀 필요는 없다. (SD카드 Chip Select 라서 SD_CS)
Code
- Github 에서 fatfs_sd.h 와 fatfs_sd.c 파일을 가져온다. [SPI_SD Github 링크]
1. fatfs_sd.h
빨간 테두리를 자신이 선택한 설정으로 바꾼다. (본인 : SPI2, SD_CS->PD8)
2. user_diskio.c
위에서 파란색 네모를 복사해서 user_diskio.c 파일의 return 값에 넣는다.
경로 : FATFS -> Target -> user_diskio.c
return SD_disk_initialize (pdrv);
return SD_disk_status (pdrv);
return SD_disk_read (pdrv, buff, sector, count);
return SD_disk_write (pdrv, buff, sector, count);
return SD_disk_ioctl (pdrv, cmd, buff);
3. stm32f1xx_it.c
경로 : Core -> Src -> stm32f1xx_it.c
- fatfs_sd.c -> uint16_t Timer1, Timer2
- stm32f4xx_hal_dma.h -> DMA_HandleTypeDef
- spi.c -> SPI_HandleTypeDef hspi2
추가 해야할 코드
extern uint16_t Timer1, Timer2; extern DMA_HandleTypeDef hdma_spi2_tx; extern SPI_HandleTypeDef hspi2;
추가 해야할 코드 2
if(Timer1 > 0) Timer1--; if(Timer2 > 0) Timer2--; HAL_SYSTICK_IRQHandler();
4. main.c
include 와 전역변수 설정이다.
CubeIDE 에서 printf 를 사용하기 위해서는 UART와 위 함수를 재정의 해줘야한다.
또한, 아래에서 만든 함수도 선언해줬다.
f_mount 같은 함수를 좌클릭을 하고 F3을 누르면 ff.c 파일로 들어가진다
내부가 궁금하면 찾아보자. 리턴값 자료형이 FRESULT 로 enum(열거형) 으로 둬서 여러가지 상태를 나타낼 수 있다.
USER CODE BEGIN 2 사이에 넣는 코드
▶ f_mount
SD 카드를 마운트, 언마운트하는 함수이다. 이것을 안하면 SD카드 메모리에 접근이 불가능
▶ f_open and f_close
특정 파일을 열고 닫는 함수이다.
▶ f_getfree
클러스터 개수와 크기를 연산하여 파일 사이즈를 계산해준다.
▶ f_write and f_puts
파일의 처음 처음 위치로 커서를 옮기고 데이터 한 줄을 쓰는 함수이다.
f_write 는 정해진 크기를 쓰기 때문에 여러 줄을 쓰기 위해서는 여러가지르 신경 써야한다.
◆ f_write 여러줄 작성sprintf((char*)buffer, "%s\r\n", text); uint16_t length = (uint16_t)strlen(text) + 2; // +2 는 \r\n 추가 for (uint8_t i = 0; i < 3; i++) { fres = f_write(&fil, buffer, length, (void*)&bw); }
\r\n 를 추가하여 나중에 파일시스템에서 읽을 때나 메모장에서 열 때 우리가 원하는 3줄을 얻을 수 있다.
그리고 f_write 의 크기를 버퍼의 크기[sizeof(buffer)]가 아니라 TEXT의 크기(length)로 변경해야 한다는 것을 꼭 기억해야 한다.
\r (carriage return) 을 쓴 이유는 f_read 를 사용할 때 똑같이 보이게 하기 위해서다.
sprintf((char*)buffer, "%s\n", text); for (uint8_t i = 0; i < 3; i++) { fres = f_puts(buffer, &fil); }
만약 f_write 를 말고 f_puts 를 사용한다면 \0 (NULL) 까지 쓰기 때문에 크기를 따로 입력할 필요가 없다.
▶ f_read and f_gets
Read 함수는 길어서 코드블럭으로 작성
한줄을 읽어오는 코드인데
파일을 fres = f_open(&fil, fileName, FA_READ); 의 FA_READ 를 안하면
읽어온 바이트의 크기를 읽어오지 못해서 Flag를 통해서
먼저 열려있던 파일을 닫고 다시 열었다.
void ReadFile(char* fileName)
{
if (closedFlag == 0) CloseFile();
fres = f_open(&fil, fileName, FA_READ);
if (fres == FR_OK) {
printf("File opened for reading.\r\n");
} else if (fres != FR_OK) {
printf("File was not opened for reading!\r\n");
}
fres = f_read(&fil, buffer, sizeof(buffer), (void*)&br);
char mRd[100];
if (fres == FR_OK)
{
sprintf((char*)mRd, "%s", buffer);
printf("-----------READING_TEXT----------\r\n");
printf("%s", mRd); // already existed \r\n
printf("-----------READING_TEXT---------\r\n");
sprintf((char*)str, "%3d bytes Read", (int)br);
printf("%s\r\n", str);
}
else if (fres != FR_OK || br == 0)
{
printf("Can't read~!\r\n");
}
}
f_gets 는 \n (Line Feed) 또는 \0 까지 읽는다. 그 리턴값을 버퍼의 포인터를 반환하는데
버퍼 길이 끝까지 다 읽은 경우 0을 반환하여 while문에 넣어서 자주 사용한다.
위의 saveFile()은
위 Youtube 의 연습 자료를 함수로 만든 것이다. [Code 링크]
# 해결 하고 싶은 것
EXTI 외부 인터럽트를 통해서 FATFS 에 접근하는 것을 하고싶다.
예) 버튼을 누르면 파일이 저장되는 기능
잘 안되어서 고민 중.
Author And Source
이 문제에 관하여(STM32 SPI SD card), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@lcooldong/STM32-SPI-SD-card저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)