출처: https://jeonhj.tistory.com/46
MCU에서 인터럽트 핸들러 안에서는 동작을 최대한 짧게 가져가야 합니다.
그래서 USART와 같은 통신 인터페이스를 사용할때에는 Queue Buffer 구조를 사용합니다.
큐(Queue)는 FIFO(First-In, First-Out)로 가장 먼저 들어온 데이터가 가장 먼저 나가는 자료 구조입니다.
Queue 구조, 먼저 들어간 데이터가 가장 먼저 나온다.
그 중에서도 원형큐(Circular Queue) 또는 링 버퍼(Ring Buffer)는 다음과 같은 구조를 가지고 있습니다.
만약 원이 다 차서 새로운 데이터가 들어오게 된다면 어떻게 될까요?
-> Data1 자리에 덮어쓰게 됩니다. 그 다음 데이터는 Data2 자리에..
원으로 계속 반복하게 된다고 해서 원형 큐라고 합니다.
원형큐를 다음과 같이 구현할 수 있습니다.
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 |
/* * 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 데이터를 처리하는 방식을 알아봅시다.
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 |
/* 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 반복
이 되겠습니다.
데이터 처리는 제품에 맞는 프로토콜에 따라 프로그램하면 되겠습니다.
이를 모듈화 하기 위해서 구조체를 사용하여 구조를 잡아주면 좋을거 같습니다.
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 |
// 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); } } } |