안녕하세요,


요 근래, 파일 시스템 테스트 하다가 안되서 고생고생 했네요.

그 고생한 내용은 다음과 같습니다.

파일 관련 함수는 거의 다 테스트를 했는데, f_read() 함수만 유독 실행이 될 때도 있고, 
어떤 때는 계속 실행이 안되고 Hardfault Error 인터럽트로 점프해서 멈춰 있었습니다.

같은(거의 비슷한?) 입력 파라메터를 갖고 있는 f_write() 함수는 또 잘 된다.
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */

하~.., 그래서 사용자 함수를 하나 만들어서 파라메터를 f_read() 와 같이 정의를 해서 테스트를 해보니 f_read()함수와 같은 증상이 발생. ㅜㅜ

안되는 원인을 찾아 보려고 f_read()함수의 파라메터의 입력을 각각 1가지씩 사용자 함수에 적용해서 테스트를 해 봤더니,
입력 인자 중 void* buff 가 문제였습니다.

원인은 입력 인자인 배열(void *buff)을 f_write()에서 보다 많이 잡아서 Stack overflow 가 걸리는 것이었습니다.

에러가 나면 스택 오버플로우 인지 확인도 못하고(혹시 아시는 분 있으시면 방법을 알려 주시면 감사하겠습니다),
그냥 프로그램이 멈춰버리니.. 며칠을 밤새고 나서야 찾을 수 있었습니다.

이번 글의 요점은, 파일 시스템 관련 함수를 사용할 경우에는 스텍을 충분히 확보하라 입니다.

그럼, 다음은 스텍을 늘리는 방법입니다. 방법은 2가지가 있네요.
1. startup_stm32f411xe.s 파일을 열어서 다음과 같이 스택 사이즈를 변경합니다.



2. CUBE 툴에서 project > Setting 메뉴에서 수정할 수도 있습니다. (저는 큐브 툴로 KEIL 소스를 만들어서 이런 방법도 있음을 알려 드립니다)



스택이 0x400 일 때 멈춰버린 프로그램이..



스택을 0x800 으로 고치니까 아주 잘 됩니다. 잘 되서 f_lseek()함수도 사용해서 파일의 offset를 막 이동해 가면서 읽어서 UART로 마구마구 뿌려 댔습니다. ^^



그런데, 나중에 알게된 사실인데... 

큐브 툴의 Help 메뉴를 누르면 UM1718.pdf 파일이 열리는데, 이 곳의 7번의 제목의 글에 스택을 충분이 할당하라는 내용이 약간 나옵니다. ㅜㅜ 저와 같은 문제로 고생하는 사람이 많진 않지만 약간 있나 봅니다.
많았으면 반드시 스택을 많이 잡고 시작하라고 했을 텐데.





그리고 이 PDF의 파일 시스템 예제가 인터넷에 돌아다니는 예제 보다, 가장 잘 나온 것 같습니다.
참고하시면 도움이 될 것 같습니다.


STM32F4xx에서 uart2 로 printf()함수를 사용하는 방법을 테스트 해보고 정보를 공유합니다.


다음과 같은 코드를 main.c 파일에 추가하면 됩니다.

/* USER CODE BEGIN 0 */
#ifdef __GNUC__
 #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
 #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
 
PUTCHAR_PROTOTYPE
{
 HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 1);
 return ch;
}
/* USER CODE END 0 */



이 코드를 추가하기 전에 huart2 는 정의를 해야겠죠.
그리고 uart HAL 함수 HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 1); 를 사용해서 폴링으로 1Byte 전송하도록 되어 잇습니다.


이번에는 GPIO에 여러핀을 동시에 출력하는 방법을 알아보겠습니다.


핀 설정도 동시에 할 순 있는데, 좀 귀찮아서 핀설정은 각 핀마다 했습니다.

이제 제가 프로젝트를 시작해야 해서, 프로젝트에서 사용해야 할 핀들을 다 포함시킨 상태에서
GPIO 병렬 포트로 사용할 핀을 지정을 해 봤습니다.

GPIO 설정은 Cube 툴에서 할 수 없으니까, 핀이 표시가 되지 않아서 따로 사용할 핀을 정리해 봤습니다.




그리고 현재 사용한 Nucleo 보드에서 핀헤더의 위치도 표시해 봤습니다.



자.. 그럼 핀 설정은 정리해 보자면,

DATA[7:2] : PA15,14,13,12,11,10

DATA[1:0] : PC7,6

SYNC CLK : PB5


핀 설정 코드는 1핀마다 설정을 했더니 너무 길어서 소스코드 첨부해 놓을 테니 참고하시기 바람니다.

main.c 파일 안에 static void MX_GPIO_Init(void) 함수 보시면 되겠습니다.


이렇게 설정을 했습니다. 간단히 설명을 해 보자면, SYNC CLK 의 Rising Edge 마다 8비트 데이터를 출력 하는 테스트 입니다.

8비트 병렬 통신을 구현할 생각이고, STM 칩으로 8비트 데이터를 보내고 SYNC 출력을 1번 내보내는 겁니다.


8번 출력(0x01->0x02->0x04->0x08->0x10->0x20->0x40->0x80) 을 반복해서 내보내도록 했습니다.


다음은 main.c 의 main()함수의 주요 동작 코드입니다.


  for (i=0;i<8;i++)

  {

par_data[i] = (1<< i);

      // (0x01->0x02->0x04->0x08->0x10->0x20->0x40->0x80) 데이터 생성

  }


  while (1)

  {

par_ReAssemble(par_data,8);

       // (0x01->0x02->0x04->0x08->0x10->0x20->0x40->0x80) 데이터 출력 + SYNC_CLK 출력

HAL_Delay(1);

  }




현재 핀을 여러용도록 사용했더니, 출력할 포트가 2개로 나뉘어 버려서 8비트 데이터를 재정렬하도록 프로그램을 만들었습니다.

재정렬 함수의 코드는 다음과 같습니다.

void par_ReAssemble(uint8_t *par_data,uint16_t cnt_data)

{

uint16_t i;

for (i=0;i<8;i++)

{

// DATA[7:2] : PA15,14,13,12,11,10

GPIOA->ODR = (((uint32_t)par_data[i]) << 8)&0xFC00;

// DATA[1:0] : PC7,6

GPIOC->ODR = (((uint32_t)par_data[i]) << 6)&0x00C0;

//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_SET);

//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_RESET);

GPIOB->BSRR = GPIO_PIN_5;

GPIOB->BSRR = (GPIO_PIN_5<<16);

}

}


위의 함수에서 GPIOA->ODR 이 보이시죠?

이 레지스터가 각각 16비트폭으로서, 병렬 포트를 출력하도록 하는 레지스터입니다.

쉬프트 연산과 마스크 연산을 해서 병렬포트 출력 레지스터에 값을 쓰면 해단 핀들로 0또는 1이 출력 됩니다.


1비트 출력은 2가지 방법이 있습니다.

1. HAL 드라이버에서 제공하는 함수를 사용.

HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_SET);

2. 직접 레지스터에 쓰기.

GPIOB->BSRR = GPIO_PIN_5;

GPIOB->BSRR = (GPIO_PIN_5<<16);


HAL_GPIO_WritePin 함수는 누구나 인식하기 쉬워서 보면 아시겠고,

BSRR 레지스터는 32비트 구조인데, 1을 해당 비트에 써주면 하위 16비트는 SET, 상위 16비트는 RESET 기능을 합니다.




위에서 2가지 방법이 있다고 했는데, 사실은 HAL.. 함수에서 BSRR 레지스터를 사용하고 있어서 본래는 1가지 방법입니다.

그런데 제가 왜 레지스터에 집접 썼는가 하면 HAL.. 함수가 너무 느려서 입니다. 

테스트를 해 본 결과, 레지스터를 사용한 방법보다 2배나 느리더군요.


다음은 같은 동작을 수행했을 때, 왼쪽이 HAL 함수, 오른쪽이 레지스터에 직접 쓴 방법을 사용한 결과 입니다.



다음은 8비트로 1바이트를 보냈을 때, 직접 속도를 계산해 봤습니다. 



나중에 설명을 하려고 했는데, 먼저 해 버렸네요.

이게 제가 알려드리고자 한 핵심이고 앞으로 설명할 나머지는 크게 중요하진 않습니다.


사용자의 편의성이냐? 속도냐? 둘 중 하나를 선택하시면 됩니다.


그런데.. 저 파형이 링잉이 발생하는게 거슬리네요. 파형이 원래 그런것인지 오실로 스코프가 문제인지.. 궁금하네요. 내일 오실로 스코프 바꿔서 테스트 해 봐야겠습니다.



다음은 HAL 함수를 사용해서 프로그램을 돌려 본 결과 입니다.

검증을 위해 4채널 오실로 스코프를 이용해서 1로 예상되는 포인트만 찍었습니다. 9개의 신호를 한번에 찍을 수가 없어서 3개의 프로브를 나눠서 측정을 했습니다.


[ SYNC_CLK 와 DATA 0,1,2 ]



[ SYNC_CLK 와 DATA 2,3,4 ]



[ SYNC_CLK 와 DATA 5,6,7 ]



다행이 예상대로 잘 나왔네요.


그럼 이만 마치겠습니다.


소스 코드 첨부 합니다.

perallel_GPIO.zip



+ Recent posts