페이지

글목록

2016년 11월 30일 수요일

[STM32F4xx] Nucleo 보드 테스트 #19 (SDIO uSD-Card 읽기.STM32F446 : KEIL)

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

[한번에 많은 데이터를 읽어서 빨라지도록 수정된 코드]
1
2
3
4
5
6
7
8
9
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개 출력(오실로 스코프로 시간이 얼마나 걸리는지 체크를 위해)



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)로 설정한 코드 내용]
1
2
3
4
5
6
7
8
9
10
11
12
/* 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, 10);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 20);
  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, 20);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 30);
  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, 10);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 20);
  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, 00);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 00);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
}
cs

댓글 없음:

댓글 쓰기