STM32F4xx 에서 USB MSC 를 테스트하다 보니 가끔 파일이 깨지는 현상이 발생했습니다.


여러가지 개선책을 생각하고 적용을 해 봤습니다.
1. DMA 적용.
: 많이 깨짐 현상이 줄어들었다. 
그래도 가끔 깨지는 파일이 발생했고, DMA를 적용하기 전에는 여러 곳이 깨졌는데, DMA를 적용하니 꼭 파일의 끝 부분이 깨진다.
2. 섹터 크기 일치 시킴.
STM32F4xx 의 f/w 에서 설정된 파일 시스템은 FAT32,Sector 크기는 512, 
이에 연결되어 있는 SD Card 의 format을 보면 파일시스템는 FAT였고 Sector 크기가 8192 였다.
이것을 일치 시켜서 테스트해 봤는데, 깨짐 현상이 아직까지 나타나지 않았다.
SD Card 의 포맷을 다음과 같이 FAT32,Sector Size = 512 로 수정.

[CUBEMX 툴에서 설정된 섹터 크기 정보]






[MSC 에서 설정을 바꿔서 다시 포맷]




SDIO - 4Bit 방식으로 SD-Card 를 STM32F4xx 와 연결했고,

USB MSC(Mass Storage Class) 로 PC 와 연결하면 USB Drive 가 생성되는 프로그램에서 테스트한 결과입니다.

우선, SDIO-4bit , SDIO CLK-42MHz 로 설정했는데 USB MSC로 동작 시키면 생각 보다 너무 느립니다.
몇 MByte/s 쯤 나오지 않을까 생각 했는데 1MByte/s 도 안나옵니다.

기본적인 원인은 USB_FS 최대 속도가 12Mbit/sec (1.5MByte/s) 라는 점입니다.
여러가지 콘트롤을 위한 패킷을 빼면 1Mbyte 정도 나오리라 예상이 되는데, 그래서 몇 MByte/s 는 나올리가 없는 것이죠.




다음은 SDIO CLK 을 여러가지 값으로 변경시의 속도 입니다. 
위의 USB Speed 를 SDIO CLK 테스트를 해 보고 나서 알아서, 왜 이런 쓰잘떼기 없는 테스트를 했나 하는 자괴감이 드네요. ㅜㅜ
[아래 그림의 번호 설명]
1. 약 3MByte 의 파일 5개를 PC에서 STM32F4xx 의 USBMSC 로 Copy.
2. 약 3MByte 의 파일 5개를 STM32F4xx 의 USBMSC에서  PC로 Copy.
3. 약 250MByte 의 파일 1개를 PC에서 STM32F4xx 의 USBMSC 로 Copy.
4. 약 250MByte 의 파일 1개를 STM32F4xx 의 USBMSC에서  PC로 Copy.




혹시 Sector 의 크기와 속도의 관계는 없는 것인가 하는 생각에, 섹터 크기를 CUBEMX 툴 설정에서 512 에서 4096으로 늘려서 테스트 해 봤습니다. 




별 차이 없었습니다.




섹터를 변경할 때에, MSC 에 의해 생성된 USB Drive 포맷도 할당 단위 크기를 변경해서 수행했습니다.
섹터의 크기는 디스크의 반응 속도에 영향을 미쳤습니다.
섹터가 많으면 그 만큼 파일을 검색하는 시간이 많이 걸리는 듯 하여, 더 많은 시간이 지나야 USB Drive 가 PC에서 나타나네요.
즉, USB MSC 의 할당 단위가 크면(512바이트 보다 4096 바이트가) 응답 속도가 빠르다.





참고 : [USB 버전 별 속도 비교]




안녕하세요, 오랜만입니다.

전광판 프로젝트가 얼추 마무리되어 다시 활동을 재개 합니다. ^^

전광판 프로젝트에서 파일을 변환하는 기능이 필요해서 검토를 하다보니,
파일을 변환하려면, 읽을 파일과 쓸 파일, 이렇게 2개의 파일을 열어야 하네요.

그럼 먼저 여러 개의 파일을 열려면 어디를 고쳐야 하는지 찾아 봤는데,
다행히도 CUBEMX 는 기본으로 2개의 파일을 열 수 있도록 셋팅이 되어 있었습니다.
총 255 개의 파일을 열 수 있다고 합니다.

셋팅 방법은 CUBEMX 의 Configuration TAB 에서, Middleware 의 FATFS 를 선택합니다.




그 다음으로, FATFS Configuration 의 Set Define 에서 FS_LOCK 값을 수정하면 동시에 열 수 있는 파일의 갯수를 조정할 수 있는데, 설정값에 따라 총 255개의 파일을 동시에 열 수 있습니다. 저는 동시에 2개의 파일만 열려고 하니, 디폴트로 2로 그냥 놔두겠습니다.
혹시나 해서 이 값을 1로 바꾸니, 나중에 열도록 한 파일이 안 열리더군요. 
따라서 이 값이 동시에 열 수 있는 파일의 갯수를 의미함이 틀림없습니다. ^^




USB MSC(Mass Storage Class) 는 예전에 SD_Card 를 USB Drive 로 동작시켰던 글에서,
귀찮아서 DMA를 동작 시키지 않도록 프로그램했습니다. 
DMA를 사용하지 않는 방법은, 예전 글에서 DMA 를 설정하는 부분과 인터럽트 우선 순위를 설정하는 작업을 생략하고,
usbd_storage_if.c 파일의 STORAGE_Read_FS() 함수와 STORAGE_Write_FS()함수의 내부에 쓰인 함수를 일반 폴링 함수로 고치면 됩니다.
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
/*******************************************************************************
* Function Name  : STORAGE_Read_FS
* Description    : 
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int8_t STORAGE_Read_FS (uint8_t lun, 
                        uint8_t *buf, 
                        uint32_t blk_addr,                       
                        uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */ 
  BSP_SD_ReadBlocks((uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len);
  //BSP_SD_ReadBlocks_DMA((uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len);
  return (USBD_OK);
  /* USER CODE END 6 */ 
}
 
/*******************************************************************************
* Function Name  : STORAGE_Write_FS
* Description    :
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int8_t STORAGE_Write_FS (uint8_t lun, 
                         uint8_t *buf, 
                         uint32_t blk_addr,
                         uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */ 
  BSP_SD_WriteBlocks((uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len);
  //BSP_SD_WriteBlocks_DMA((uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len);
  return (USBD_OK);
  /* USER CODE END 7 */ 
}
cs

그럼 다음으로, 파일을 2개 열어서 테스트하기 전에 USB MSC 로 읽을 파일을 PC에서 STM32F4 에 연결된 SD-Card 로 Copy 했습니다.
인터넷에서 돌아다니는 해리포터 소설 1장 만 띠어다 herry-1.txt 로 만들어 Sd card 에 복사 했습니다. 
혹시 몰라 2장도 넣는데 .. 파일 3개 여는 테스트는 귀찮네요. ^^. 2개가 되면 3개도 되겠죠 뭐.




다음은, herry-1.txt 파일에서 512 바이트를 읽어서 herry-cnv.txt 파일에 쓰는 소스 코드입니다.
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
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_SDIO_SD_Init();
  MX_USART1_UART_Init();
  MX_FATFS_Init();
  MX_USB_DEVICE_Init();
 
  /* USER CODE BEGIN 2 */
    HAL_SD_ErrorTypedef res_sd;
    FRESULT res;
    FILINFO fno;
    FIL fil_1,fil_2;
    DIR dir;
    FATFS fs32;
    uint32_t byteswritten, bytesread;                     /* File write/read counts */
    uint8_t read_buf[1000];
    
    
    char path[200];
    #if _USE_LFN
    TCHAR lfn[_MAX_LFN + 1];
    fno.lfname = lfn;
    fno.lfsize = sizeof lfn;
    #endif
    
    res = f_mount(&fs32,SD_Path,1);
    sprintf(path,"herry-1.txt");
    res = f_open(&fil_1, (char*)path,FA_READ);
    sprintf(path,"herry-cnv.txt");
    res = f_open(&fil_2, (char*)path, FA_WRITE | FA_CREATE_ALWAYS);
    
    res = f_read(&fil_1, &read_buf[0],512&bytesread);
    res = f_write(&fil_2, &read_buf[0], 512&byteswritten);
    
    res = f_close(&fil_1);
    res = f_close(&fil_2);
    
    res = f_mount(0,SD_Path,0);
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
 
  }
  /* USER CODE END 3 */
 
}
cs

위의 코드를 컴파일해서 다운로드 후 실행하면, herry-cnv.txt 파일이 생깁니다.




다음과 같이 각 파일을 열어 보니, 2개의 파일을 열어서(1개는 raed,1개는 Write) 512 바이트를 읽고 쓰는 테스트가 완료됐음을 확인했습니다.




첨부한 파일 이름이 프로젝트 진행 중인 파일 이름으로 되어 있어서 길이가 긴데, 이름은 아무 의미 없습니다. ^^

usb_msc_rf_sdi_fcv_nv102.zip


이전에 올린 글 "[STM32F4xx] uSD-Card USB MSC(Mass Storage Class) CUBEMX 설정 (1/2)" 에 이어서,

프로그램 수정 사항을 올립니다.

MSC 의 수정할 부분은 의외로 별로 없습니다.
CUBEMX 에서 설정만 잘 했으면, usbd_storage_if.c 파일만 손보면 됩니다.

1. #include 추가.
1
2
3
4
5
6
/* Includes ------------------------------------------------------------------*/
#include "usbd_storage_if.h"
/* USER CODE BEGIN INCLUDE */
#include "bsp_driver_sd.h"
/* USER CODE END INCLUDE */
 
cs


2. STORAGE_Init_FS() 함수 수정.
1
2
3
4
5
6
7
int8_t STORAGE_Init_FS (uint8_t lun)
{
  /* USER CODE BEGIN 2 */ 
    BSP_SD_Init();
    return (USBD_OK);
  /* USER CODE END 2 */ 
}
cs


3. STORAGE_GetCapacity_FS() 함수 수정.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */   
  HAL_SD_CardInfoTypedef info;
 
    BSP_SD_GetCardInfo(&info);
 
    *block_num = (info.CardCapacity)/STORAGE_BLK_SIZ  - 1;
    *block_size = STORAGE_BLK_SIZ;
//  *block_num  = STORAGE_BLK_NBR;
//  *block_size = STORAGE_BLK_SIZ;
  return (USBD_OK);
  /* USER CODE END 3 */ 
}
cs

4. STORAGE_Read_FS() 함수 수정.
1
2
3
4
5
6
7
8
9
10
int8_t STORAGE_Read_FS (uint8_t lun, 
                        uint8_t *buf, 
                        uint32_t blk_addr,                       
                        uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */ 
    BSP_SD_ReadBlocks_DMA((uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ,STORAGE_BLK_SIZ, blk_len);
  return (USBD_OK);
  /* USER CODE END 6 */ 
}
cs

5. STORAGE_Write_FS() 함수 수정.
1
2
3
4
5
6
7
8
9
10
int8_t STORAGE_Write_FS (uint8_t lun, 
                         uint8_t *buf, 
                         uint32_t blk_addr,
                         uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */ 
    BSP_SD_WriteBlocks_DMA((uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ,STORAGE_BLK_SIZ, blk_len);
  return (USBD_OK);
  /* USER CODE END 7 */ 
}
cs

이상이고, SDIO_CD (Card Detection) 신호를 사용하려면 위 함수들에 약간의 추가적인 코드가 필요합니다.
이전 글의 마지막에 링크된 웹사이트를 참고하셔서 수정하시면 됩니다.

다음은, 이 USB MSC 로 생성된 USB 드라이브 내용입니다.




엊그제 uSDcard 내용을 읽어서 USB CDC 로 터미널에 뿌렸던 내용과는 너무나 깔끔하고 좋네요.
파일을 PC에서 수정 가능하고, 쉽게 읽고 쓸 수 있습니다. ^^

[엊그제 uSDcard 내용을 읽어서 USB CDC 로 터미널에 뿌렸던 내용




요새 uSD-Card를 보드에 직접 납땜을 해서,

파일을 수정할 방법을 강구하다가 USB MSC(Mass Storage Device Class)를 공부하게 됐습니다.
2일 동안 회사에서 의자에 붙어서 열심히 팠습니다. ㅠㅠ

된다고 하는 여러 예제들이 있었는데, 왜 나만 안되는 것인지.. 참 답답했는데,
원인은 여러가지겠지만, 
가장 큰 원인은 인터럽트 우선순위를 설정하지 않고 모두 0으로 놓으니 동작이 안됐던 것 같았습니다.

그럼, 차근차근 설명을 해 보겠습니다.
제가 테스트하는 H/W 는 
1. SD-Card SDIO-4Bit DMA , 
2. USB Device Only , 
3. Embedded DFU 방식으로 F/W Upload. (BOOT0 핀과 GPIO INPUT 핀에 공통으로 스위치 사용)
4. BOOT1 을 GPIO OUT(LED OUT) 과 공통으로 사용.
5. RCC(HSE)로 24MHz Crystal 사용.
6. Middle Wares -> FATFS -> SD Card 설정.
7. SDPWR 은 필요 없는 기능이니 신경쓰지 마시고요.
으로 어제와 동일합니다.




어제 쓴 글에서는 디렉토리만 읽어서 USB CDC로 표시만 할 수 있었는데, 이번에는 MSC로 읽기 쓰기를 구현해 보고자,
CUBEMX 툴에서 USB_DEVICE 타입을 Mass Storage Class 로만 변경했습니다.






클락 탭의 설정은 다음과 같습니다.






다음은 CUBEMX 의 Configuration TAB의 상세 설정입니다. 
바꾼 것들만 설명을 할 것인데, 그 외의 설정은 디폴드 설정으로 건드리지 않았습니다.
NVIC 는 각각의 상세 설정에도 있고, 따로 NVIC 설정이 분리되어 있는데, 맨 마지막에 NVIC를 수정할 예정입니다.

















다음은 NIVC(인터럽트 우선순위) 설정으로 어떻게 정하는지 저도 잘 모르겠습니다만, MSC 안 되었던 이유 중 가장 큰 원인이, 이 우선순위를 수정하지 않고 모두 우선순위가 0(Default)으로 내버려 뒀기 때문이었습니다.
나중에 같은 실수를 방지하기 위해, 어떤 기준으로 정하는 것인지 확실히 정리해야 할 것 같습니다.






다음으로 HEAP 과 STACK 을 충분히 잡아 두라해서 일단은 최적화는 못했고, 무식하게 크게 잡아두었습니다.





글이 생각보다 너무 길어지고 시간이 너무 늦어서, S/W 쪽은 눈을 좀 붙이고 시간이 나면 작성하도록 하겠습니다.
제가 여러가지를 참고해서 S/W를 만들었는데, 일단 참고한 윕사이트를 링크해 드립니다.



+ Recent posts