Embedded

ARM Cortex M4 core - (3) UART 키보드로 LED제어

MiddleJo 2024. 9. 4. 18:25

UART.zip
8.55MB

목차

1. 배경

2. 보드 구성 및 소스코드

3. 실습결과

 

 

1. 배경

 

모든 칩에는 통신 모듈이 들어가 있습니다.

그래야 정보를 교환할 수 있으니까요.

UART는 PC와 소통할 때 가장 많이 사용하고,

직관적이고 간단합니다.

다른 작업에도 PC로 보기 위해 알아둘 필요가 있습니다.

 

 

제가 사용하는 STM32 F429 보드는 위와 같은 방식으로 PC와 통신합니다.

F103 파트에 데이터를 전달하고, USB로 바꿔주는 방식인데요.

 

 

 

회로도를 확인해 봐도, STLK_RX와 STLK_TX로 연결되어 있음을 확인할 수 있습니다.

따라서, 저는 많은 포트 중 UART 3번 포트를 활용하려고 합니다.

 

 

2. 보드 구성 및 소스코드

기본적으로, Board Select 선택 시 불필요한 기능이 포함되어

코드가 길어지고 보기 불편합니다.

따라서 MCU 방법으로 직접 세팅하겠습니다.

기본 상태에서 코딩이 필요한 부분만을 언급할 예정이며,

자세한 것은 첨부된 파일을 참고하시면 됩니다.

 

- 보드 세팅

 

 

- RCC 세팅

 

Nucleo 보드에서는 내부 클럭보다 외부 클럭이 더 정확하다고 하고,

ST-LINK MCO는 8 MHz를 사용합니다.

따라서, 위 사진과 같이 LSE를 사용해 줄 필요가 있습니다.

 

 

- NVIC 세팅

 

 

UART Interrupt를 사용하기 위해 세팅해 줍니다.

저희는 테스트를 위해 이 인터럽트가 가장 우선순위가 되도록 설정하겠습니다.

 

 

- UART 설정

 

이후에 코드에서도 바꿀 수 있지만, 여기서 통신 방식에 대해 설정하고 갈 수 있습니다.

 

 

- (참고) printf 사용을 위한 세팅

printf를 사용하기 위해서는 Syscalls.c에서 extern 처리되어 있는

위 _write 함수 혹은 __io_putchar 함수를 수정하여 main에 가져와 사용할 수 있습니다.

제가 마지막으로 보여드릴 예시에는 printf 함수가 필요하지 않습니다.

 

 

- UART_RxCPltCallback

 

stm32 f4 xx_hal_uart.c 에서 위와 같은 함수를 찾을 수 있습니다.

이것을 main으로 가져와 사용할 것입니다.

 

 

 

- Main

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();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART3_UART_Init();

  /* Initialize interrupts */
  MX_NVIC_Init();
  /* USER CODE BEGIN 2 */

  HAL_UART_Receive_IT(&huart3, (uint8_t *)&rx_data, 1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
//	  // printf Test
//	  printf("Hello, STM32World %d\n", i++);
//	  HAL_Delay(1000);
//
//	  // Create string test
//	  memset(data, 0, sizeof(data));
//	  sprintf(data, "%d. Hello STM32F249 World !!! \n", i++);
//	  HAL_UART_Transmit(&huart3, (uint8_t *)data, strlen(data), 500);
//	  HAL_Delay(1000);

// 	  // Echo Test
// 	  if (HAL_UART_Receive(&huart3, (uint8_t *)&data_echo, sizeof(data), 500))
//	  {
//	    HAL_UART_Transmit(&huart3, (uint8_t *)&data_echo, sizeof(data), 500);
//	  }

//	  // Polling Test
//	  if (HAL_UART_Receive(&huart3, (uint8_t *)&data, 1, 1000) == HAL_OK)
//	  {
//	  	HAL_UART_Transmit(&huart3, (uint8_t *)&data, 1, 1000);
//	  }
	  //
	  switch (led_mode)
	  {
	  	  case 1 : GPIOB -> ODR = 0x0001; break;
	  	  case 2 : GPIOB -> ODR = 0x0080; break;
	  	  case 3 : GPIOB -> ODR = 0x4000; break;
	  	  case 0 : GPIOB -> ODR = 0x0000; break;
	  }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

주석처리된 것에서 다른 예시를 확인할 수 있습니다.

while문 앞에 HAL_UART_Receive_IT 함수를 한번 초기화했습니다.

 

인터럽트로부터 변한 led_mode 변수에 따라

PB0, PB7, PB14에 HIGH 신호를 주게 됩니다.

 

ODR에 직접 값을 입력하면, 굉장히 빠르지만

어떤 경우에는 빠른 것이 좋지 않을 수 있습니다.

현재는 상관없으므로, ODR 방법을 사용하였습니다.

 

Reference Manual을 보면, ODR의 어떤 비트가 어떤 제어를 하는지 알 수 있고,

저는 상황에 맞게 16진수로 명령을 넣어주었습니다.

 

 

- HAL_UART_RxCpltCallback

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart -> Instance == USART3)
  {
	  HAL_UART_Receive_IT(&huart3, (uint8_t *)&rx_data, 1);
	  HAL_UART_Transmit(&huart3, (uint8_t *)&rx_data, 1, 500);
	  switch (rx_data)
	  {
	  case '1': led_mode = 1; break;
	  case '2': led_mode = 2; break;
	  case '3': led_mode = 3; break;
	  default: led_mode = 0;
	  }

  }

}

 

인터럽트는 UART통신을 통해 받은 문자가 1, 2, 3일 경우

led_mode 변수를 변화시키고, 이것이 main 함수의 작동을 돕습니다.

 

 

- PV

 

 

 

3. 실습결과

 

 

1을 입력하면 초록불(PB0)이,

2를 입력하면 파란불(PB7)이,

3을 입력하면 빨간불(PB14)이 켜집니다.

PC와 잘 통신하고 있음을 알 수 있습니다.