STM32/STM32_RTOS

STM32 RTOS 알아보기 2편(xQueueCreate,xQueueSend,xQueueReceive)

원원 2024. 8. 18. 21:27

안녕하세요. 오늘은 RTOS의 Queue에 대해 알아보겠습니다.
Queue는 먼저 집어넣은 데이터가 먼저 나오는 FIFO 구조의 저장형식입니다. RTOS에서는 TASK나 ISR에서 Queue에 데이터를 저장&불러오기를 할 수 있습니다. TASK랑 ISR에서 Queue에 접근할때 사용하는 함수가 다릅니다. ISR에서 접근할때는 함수명뒤에 FromISR()이 붙습니다. 다른 이유는 ISR은 짧은시간안에 실행되어야 하며 다른 태스크나 ISR에 의해 중단되지 않아야 합니다. 이로 인해 ISR에서 사용하는 함수들은 블로킹이 없고 간단하고 빠르게  동작됩니다. 
예시) TASK에서 큐에 데이터를 보내는 함수 :  xQueueSend(),
ISR에서 큐에 데이터를 보내는 함수 : xQueueSendFromISR();


*xQueueCreate함수
QueueHandle_t xQueueCreate(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize );
큐를 만드는 함수입니다.
uxQueueLength : 큐에 저장될 최대 갯수입니다.
uxItemSize : 큐에 저장될 자료형입니다.
QueueHandle_t  : 큐 생성이 실패하면 NULL을 리턴하고 큐 생성에 성공하면 큐의 핸들(참조)을 반환합니다.

QueueHandle_t xQueue; //큐 선언
xQueue = xQueueCreate(5, sizeof(uint8_t)); // 최대 5개 저장, 큐에 저장할 자료형의 크기


*xQueueSend함수
BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
큐에 데이터를 저장하는 함수입니다.
xQueue :큐 핸들
pvItemToQueue : 큐에 보낼 아이템
xTicksTowait : 큐가 꽉 차서 데이터가 들어갈 수 없을 때, 대기할 시간(틱 수). 0이면 대기 없이 즉시 pdFAIL이 반환됨
BaseType_t : 큐가 전송이 성공되었으면 pdPASS, 실패했으면 pdFAIL이 나옵니다.

테스트로 큐를 만들고, task1에서 버튼이 눌렸을때마다 큐를 보내보겠습니다. uxQueueMessagesWaiting 함수는 현재 큐에 저장된 갯수를 리턴해줍니다.

void task1(void *pvParameters);
QueueHandle_t xQueue;
void myTask()
{

  xQueue = xQueueCreate(5, sizeof(uint8_t));
  xTaskCreate(task1, "Task1", 256, NULL, 1, NULL);
}

void task1(void *pvParameters)
{
  uint8_t i = 0;
  GPIO_PinState last_state = HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin);
  GPIO_PinState current_state;
  printf("Queue number : %lu \n",uxQueueMessagesWaiting(xQueue));
  while(1)
  {
    current_state = HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin);
    if(last_state == GPIO_PIN_SET && current_state == GPIO_PIN_RESET)
    {
      printf("BTN1 is clicked : %d \n",i);
      if(xQueueSend(xQueue,&i,0) != pdPASS)
      {
        printf("FAIL ! \n");
      }
      printf("Queue number : %lu \n",uxQueueMessagesWaiting(xQueue));
      i++;
    }
    last_state = current_state;
    vTaskDelay(10);
  }
}

버튼을 6번 클릭했을때부터 "FAIL !"이 나와서 큐에 데이터가 저장이 안되는걸 볼 수 있습니다.


*xQueueReceive함수
BaseType_t xQueueReceive(
QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait );
xQueue :큐 핸들
pvBuffer: 큐에서 읽어서 저장될 변수
xTicksTowait : 큐가 비어있는경우 대기할 시간. 0이면 데이터가 읽을 수 없는경우에 즉시 pdFAIL을 리턴하고 portMAX_DELAY면 무한정 대기합니다.
BaseType_t : 데이터 수신여부에따라 pdPASS/pdFAIL가 옵니다.


task1은 큐에 데이터를 전달해주는 태스크이고 task2는 큐에서 데이터를 읽는 태스크입니다. 큐에 데이터가 있다면 task2에서 읽어서 화면에 보여줍니다.

#include <stdio.h>
#include "cmsis_os.h"
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"

void task1(void *pvParameters);
void task2(void *pvParameters);
QueueHandle_t xQueue;

void myTask()
{

  xQueue = xQueueCreate(5, sizeof(uint8_t));
  xTaskCreate(task1, "Task1", 256, NULL, 1, NULL);
  xTaskCreate(task2, "Task2", 256, NULL, 1, NULL);
}

void task1(void *pvParameters)
{
  uint8_t i = 0;
  GPIO_PinState last_state = HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin);
  GPIO_PinState current_state;
  while(1)
  {
    current_state = HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin);
    if(last_state == GPIO_PIN_SET && current_state == GPIO_PIN_RESET)
    {
      printf("BTN1 is clicked : %d \n",i);
      if(xQueueSend(xQueue,&i,0) != pdPASS)
      {
        printf("FAIL ! \n");
      }
      i++;
    }
    last_state = current_state;
    vTaskDelay(10);
  }
}

void task2(void *pvParameters)
{
  uint8_t i=0;
  while(1)
  {
    //if(uxQueueMessagesWaiting(xQueue) > 0)
    {
      if(xQueueReceive(xQueue, &i, 2000) == pdPASS)
      {
        printf("Queue received : %d \n",i);
      }
      else
      {
        printf("Queue received FAIL\n");
      }
    }
    vTaskDelay(10);
  }
}

버튼을 클릭하면 Queue received가 되고, 버튼을 2초이상 클릭하지않으면 Queue received FAIL이 나옵니다.

소스코드(Commits: Queue TEST1)
https://github.com/yhunterr/STM32_RTOS_STUDY/commits/main/

큐를 ISR에서 사용할 경우는 다음에 알아보겠습니다.