안녕하세요.

 

MCU에서 인터럽트 핸들러 안에서는 동작을 최대한 짧게 가져가야 합니다.

 

그래서 USART와 같은 통신 인터페이스를 사용할때에는 Queue Buffer 구조를 사용합니다.

 

큐(Queue)는 FIFO(First-In, First-Out)로 가장 먼저 들어온 데이터가 가장 먼저 나가는 자료 구조입니다.

Queue 구조, 먼저 들어간 데이터가 가장 먼저 나온다.

 

그 중에서도 원형큐(Circular Queue) 또는 링 버퍼(Ring Buffer)는 다음과 같은 구조를 가지고 있습니다.

원형 큐 구조

 

만약 원이 다 차서 새로운 데이터가 들어오게 된다면 어떻게 될까요?

-> Data1 자리에 덮어쓰게 됩니다. 그 다음 데이터는 Data2 자리에..

 

원으로 계속 반복하게 된다고 해서 원형 큐라고 합니다.

 

원형큐를 다음과 같이 구현할 수 있습니다.

/*
*  Queue 구조 예시
*  만약 head가 tail을 역전할 경우 처리는..?
*/

#define MAX_QUEUE_SIZE		(255)

uint8_t queue_buffer[MAX_QUEUE_SIZE] = {0,};

uint8_t head;
uint8_t tail;

// push: 새로운 데이터를 Queue buffer에 담는다.
void push(uint8_t new_data)
{
  // 새로운 데이터를 queue에 넣는다.
  queue_buffer[head] = new_data;
  
  head++;
  
  // head가 queue 마지막 위치에 도달했다면, 0으로 초기화
  if (head >= MAX_QUEUE_SIZE) {
    head = 0;
  }
}

// pop: 가장 오래된 데이터를 가져온다.
uint8_t pop(void)
{
  // tail의 위치에 데이터를 가져온다
  uint8_t pop_data = queue_buffer[tail];
  
  tail++;
  
  // tail이 queue 마지막 위치에 도달했다면, 0으로 초기화
  if (tail >= MAX_QUEUE_SIZE) {
    tail = 0;
  }
  
  return pop_data;
}

uint8_t isEmpty(void)
{
  // head와 tail의 위치가 같으면 queue가 비어있음
  return head == tail;
}

 

 

위의 코드를 이용하여 USART 데이터를 처리하는 방식을 알아봅시다.

 


/* USER CODE BEGIN PFP */

uint8_t rx_data;

/*Interrupts RX Callback************************************************************/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1) {
    push(rx_data);
    HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  }
}

int main(void)
{
  /* USER CODE BEGIN 1 */
  uint8_t data;
  /* 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_USART1_UART_Init();

  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (isEmpty() == 0) {
      data = pop();
      
      // 데이터 처리
      // ...
    }
    
    /* USER CODE END WHILE */

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

 

상세히 살펴볼게요

HAL_UART_RxCpltCallback 함수는 Uart Rx 인터럽트가 발생하면 호출되는 함수입니다.

 

1. huart 인스턴스가 USART1로부터 Rx 인터럽트가 발생되었다면

2. rx_data를 queue buffer에 넣고

3. 1개 USART 데이터를 수신했다면 인터럽트를 발생시켜라

 

가 되겠습니다.

 

main을 분석해보면

 

1. 1개 데이터를 수신했다면 인터럽트를 발생시켜라

2. 만약 Queue Buffer에 데이터가 있다면

3. 데이터를 꺼내와라

4. - 데이터 처리 -

5. 2 반복

 

이 되겠습니다.

 

데이터 처리는 제품에 맞는 프로토콜에 따라 프로그램하면 되겠습니다.

 

 

 

이를 모듈화 하기 위해서 구조체를 사용하여 구조를 잡아주면 좋을거 같습니다.

// module_uart.h

#define MAX_BUFFER_SIZE    (255)

typedef struct{
  uint8_t head;
  uint8_t tail;
  uint8_t buffer[MAX_BUFFER_SIZE];
}uart_t;

void push(uart_t*, uint8_t);
uint8_t pop(uart_t*);
uint8_t isEmpty(uart_t*);



// module_uart.c

void init_uart(uart_t* u)
{
  u->head = 0;
  u->tail = 0;
  memset(u->buffer, 0, sizeof(u->buffer));
}

void push(uart_t* u, uint8_t data)
{
  u->buffer[u->head] = data;
  
  u->head++;
  
  if (u->head >= MAX_BUFFER_SIZE) {
    u->head = 0;
  }
}

uint8_t pop(uart_t* u)
{
  uint8_t data = u->buffer[u->tail];
  
  u->tail++;
  
  if (u->tail >= MAX_BUFFER_SIZE) {
    u->tail = 0;
  }
  
  return data;
}

uint8_t isEmpty(uart_t* u)
{
  return u->head == u->tail;
}




// main.c

#include "module_uart.h"

uint8_t rx_data;
uart_t uart;

/*Interrupts RX Callback************************************************************/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1) {
    push(&uart, rx_data);
    HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  }
}

void main() {
  uint8_t data;


  // ..
  
  
  init_uart(&uart);
  
  HAL_UART_Receive_IT(&huart1, &rx_data, 1);
    
  while(1) {
    if (isEmpty(&uart) == 0) {
      data = pop(&uart);
    
    }
  }
}

 

반응형

+ Recent posts