STM32F411 에서는 SDIO DMA 로 SD-Card가 잘 읽혔습니다.
방법이 어렵지 않아서 금방 테스트가 끝났는데, STM32F446 에서는 DMA를 사용하면 f_mount() 함수에서 멈춰버렸습니다.
DMA를 사용할 경우 sd_diskio.c 의 SD_read 함수 내의
BSP_SD_ReadBlocks() 함수를 BSP_SD_ReadBlocks_DMA() 함수로 바꿔주면 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | /** * @brief Reads Sector(s) * @param lun : not used * @param *buff: Data buffer to store read data * @param sector: Sector address (LBA) * @param count: Number of sectors to read (1..128) * @retval DRESULT: Operation result */ DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count) { DRESULT res = RES_OK; /*if(BSP_SD_ReadBlocks((uint32_t*)buff, (uint64_t) (sector * BLOCK_SIZE), BLOCK_SIZE, count) != MSD_OK)*/ if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff, (uint64_t) (sector * BLOCK_SIZE), BLOCK_SIZE, count) != MSD_OK) { res = RES_ERROR; } return res; } /** * @brief Writes Sector(s) * @param lun : not used * @param *buff: Data to be written * @param sector: Sector address (LBA) * @param count: Number of sectors to write (1..128) * @retval DRESULT: Operation result */ #if _USE_WRITE == 1 DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count) { DRESULT res = RES_OK; /*if(BSP_SD_WriteBlocks((uint32_t*)buff, (uint64_t)(sector * BLOCK_SIZE), BLOCK_SIZE, count) != MSD_OK)*/ if(BSP_SD_WriteBlocks_DMA((uint32_t*)buff, (uint64_t)(sector * BLOCK_SIZE), BLOCK_SIZE, count) != MSD_OK) { res = RES_ERROR; } return res; } | cs |
STM32F446 Nucleo 보드에서 DMA를 사용하지 않으면 동작은 잘 되는데,
가끔 Error이 발생해서 SD-Card 와의 선을 짧게 하니 잘 동작 했습니다.
보드의 아트웍 영향인건지는 확실히 모르겠는데, 배선도 좀 짧으면 좋은 것 같습니다.

이 상태로 13KByte 의 데이터를 읽는데 10ms 정도의 시간이 걸렸으니
거의 1MB/sec 의 속도밖에 안나와서 SDIO를 왜 쓰나 싶더군요.
그래서 처음부터 다시 프로젝트를 만들어서 동작을 시켜봤는데, 역시나 결과는 DMA 기능은 구현이 안되네요.
그런데, 뭔가 틀려진 점이 있는데, DMA를 안 썼을 때 13KB를 읽는데 10ms 걸렸던 것이 2ms 로 대폭 줄었습니다.
24KB를 읽었더니 3ms 고요. 마치 DMA가 동작되는 듯이 느껴질 정도로 빨라졌지만 DMA는 동작되지 않는 상황.
왜 빨라졌는지 원인은 못 찾았지만 CUBEMX 툴이 아직 안정화가 덜 됐나 봅니다.
빨라진 이유는 제가 코드를 잘 못 만들어서 그렇네요. f_read()함수가 512 Byte씩 읽어야 되는 줄 알고 24K 파일을
512Byte 단위로 f_read()함수를 이용해 여러번 나눠서 읽었더니 느리고 한번에 24KB를 다 읽으니 빠르네요.
예전에 테스트를 했을 때, 512-Byte 이상 읽으면 안 읽혔었는데 이번에 해 보니 한번에 많은 데이터도 읽기 가능하네요.
[512 Byte 씩 여러번 읽어서 느렸던 코드내용]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | uint8_t read_f_dat(uint8_t *pf_dat,FIL* fp) { UINT i,testBytes; uint32_t f_size; f_size = 13000; // #define _MAX_SS 512 for (i=0;i<f_size;i+=_MAX_SS) { if ((f_size - i) < _MAX_SS) { res = f_read(fp, &pf_dat[i],(f_size - i), &testBytes); } else { res = f_read(fp, &pf_dat[i],_MAX_SS, &testBytes); } } } | cs |
[한번에 많은 데이터를 읽어서 빨라지도록 수정된 코드]
| uint8_t read_f_dat(uint8_t *pf_dat,FIL* fp) { UINT testBytes; uint32_t f_size; f_size = 13000; res = f_read(fp, &pf_dat[0],f_size, &testBytes); } | cs |
아뭏든 현재 첨부한 프로젝트는 DMA는 동작이 안되는 것으로 보이고, 계속해서 방법을 찾아보다가
잘 되면 DMA SDIO-4bit 관련 글을 다시 올려 보겠습니다.
다음은 Nucleo - STM32F446 보드에서 DMA가 안되서 새로 프로젝트를 만들었던 과정입니다.
1. 먼저 MCU를 STM32F446로 선택하고, SDIO,UART2,GPIO 1개 출력(오실로 스코프로 시간이 얼마나 걸리는지 체크를 위해)

2. SDIO 신호와 u-SD-Card를 점퍼선으로 연결.

3. SDIO 를 DMA 로 동작하도록 CUBEMX 에서 설정.

4. sd_diskio.c 파일에서 SD_read/SD_write 함수 안의 BSP_SD_ReadBlocks/BSP_SD_WriteBlocks 함수를 바꾸지 않고 그대로 사용했는데 24KB 읽는데 걸리는 시간이 3ms 걸렸음.

5. 혹시 다 읽힌 것 맞는지 알아보려고 해리포터 소설을 UART로 뿌려보니 제대로 읽혀 있었습니다.
DMA가 동작한 것으로 믿어야겠죠? (제가 미쳤나 봅니다. 이런 상황을 가지고 DMA가 된다고 생각했다니.. 사기꾼이 될 뻔한 것을 글을 다시 보고 수정하면서 정정합니다.)
SDIO는 많은 양을 읽을수록 속도가 점점 더 빨라지는 듯 합니다.
그리고 STM32F411 은 42MHz 로도 DMA가 동작하는데, STM32F446 은 45MHz로 일반 방식에서는 동작하지 않았습니다.
(차후 DMA 구현되면 다시 비교해 볼 예정)
[STM32F4xx 에서 SDIO CLK를 최대(411:42MHz,446:45MHz)로 설정한 코드 내용]
| /* SDIO init function */ static void MX_SDIO_SD_Init(void) { hsd.Instance = SDIO; hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_ENABLE; hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE; hsd.Init.BusWide = SDIO_BUS_WIDE_1B; hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; hsd.Init.ClockDiv = 0; } | cs |
STM32F446 이 DMA가 안된 상태에서 비교를 하니 의미가 많이 없지만,
또한 411은 SDIO_CLK=42MHz 이고 446은 22.5MHz 이기때문에,
이 상태에서는 (당연하게도)STM32F411 이 더 빨랐습니다.
STM32F446(SDIO_CLK:22.5MHz)은 13KB 읽는데 2ms, 24KB 3ms가 걸렸는데,
STM32F411(SDIO_CLK:42MHz) 은 13KB/1.4ms , 24KB/2ms 밖에 안걸립니다.
24KB 읽는 것을 기준으로 STM32F411 은 최대 속도 12MB/s , STM32F446 은 최대속도 8MB/s 나오네요.
이 속도는 앞서서도 말씀 드렸듯이 한번에 많이 읽으면 더 빨라집니다.
6. 해리포터 소설을 24KB 만큼 읽어서 출력해 봄.

7. main() 주요 동작 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_SDIO_SD_Init(); MX_USART2_UART_Init(); MX_FATFS_Init(); /* USER CODE BEGIN 2 */ read_TAR_file_DMA(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /* SDIO init function */ static void MX_SDIO_SD_Init(void) { hsd.Instance = SDIO; hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; //hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_ENABLE; hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE; hsd.Init.BusWide = SDIO_BUS_WIDE_1B; hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; hsd.Init.ClockDiv = 0; } /** * Enable DMA controller clock */ static void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA interrupt init */ /* DMA2_Stream3_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 1, 0); HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); /* DMA2_Stream6_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 2, 0); HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); } /* USER CODE BEGIN 4 */ void read_TAR_file_DMA(void) { #if _USE_LFN TCHAR lfn[_MAX_LFN + 1]; fno_t.lfname = lfn; fno_t.lfsize = sizeof lfn; #endif BYTE testBuffer[24000]; uint8_t path[13]; DWORD cnt_rd_Byte=24000; UINT testBytes; DWORD cnt_i; UINT i,cnt_512; uint32_t image_offset; //sprintf(path,"1.tar"); sprintf(path,"aaa.txt"); printf("%s\n\r",path); res_t = f_mount(&fs32_t,SD_Path,1); printf("SD Mount : res f_mount : %02X\n\r",res_t); res_t = f_open(&fil_t, (char*)path, FA_READ); printf("res f_open : %02X, fil_t.fsize : %d\n\r",res_t,fil_t.fsize); printf("Read Txt File Test\n\r"); GPIOB->ODR ^= GPIO_PIN_5; res_t = f_read(&fil_t, testBuffer, cnt_rd_Byte, &testBytes); GPIOB->ODR ^= GPIO_PIN_5; printf("res_t = f_read : %02X\n\r",res_t); for(i=0;i<24000;i++) printf("%c",testBuffer[i]); } /* USER CODE END 4 */ | cs |
추가로 DMA 인터럽트도 DMA 동작이 안되는 것과 관련이 있을 수도 있다 싶어서 인터럽트 설정 부분도 올려 봅니다.
혹시 왜 DMA가 안되는지 아시는분 알려주시면 대단히 고맙겠습니다.
아래와 같이 인터럽트를 여러가지로 변경해 봤는데, DMA 동작은 여전히 안됐습니다.
그런데 DMA를 사용하지 않아도 SDIO_CLK 22.5MHz의 속도로써 늦은 속도는 아닌 것 같습니다.
(STM32F411 SDIO_CLK 42MHz(DMA)에서 13KB 전송시 1.4ms 이고, STM32F446 SDIO_CLK 22.5MHz(No DMA) 에서 2ms 걸렸기 때문에)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /** * Enable DMA controller clock */ static void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA interrupt init */ /* DMA2_Stream3_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 2, 0); HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); /* DMA2_Stream6_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 3, 0); HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); } static void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA interrupt init */ /* DMA2_Stream3_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 1, 0); HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); /* DMA2_Stream6_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 2, 0); HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); } static void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA interrupt init */ /* DMA2_Stream3_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); /* DMA2_Stream6_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); } | cs |
다음의 소스코드 첨부합니다.
sdio_pure_v1.zip