CS/Embedded System

[실습] FreeRTOS - Signaling & Mutual Exclusion

WakaraNai 2021. 12. 8. 23:20
728x90
반응형

LCD 화면에 task1와 task2가 동시에 출력하려면

shared data problem으로 인하여

글씨가 깨진다

 

이는 두 task가

LCD API를 호출할 때마다 systick interrupt에 의해

선점되기 때문이다

 

그래서 shared data에 접근할 때

1) interrupt를 껐다가 켜줘야 한다.

//Disable/Enable interrupt in FreeRTOS
taskENTER_CRITICAL();  // disable
taskEXIT_CRITICAL(); // enable
// Handling Shared Data Problem in FreeRTOS
void Task1( void *pvParameters )
{
    int i=0;
    char buffer[64];
    while (1)
    {
        sprintf(buffer,"Task1: %3d", i++);
        taskENTER_CRITICAL();
        Graphics_drawString(&g_sContext,
                        (int8_t *)buffer,
                        AUTO_STRING_LENGTH,
                        12,
                        24,
                        OPAQUE_TEXT);
        taskEXIT_CRITICAL();
        if (i==100) i=1;
    }
}

void Task2( void *pvParameters )
{
    int i=0;
    char buffer[64];
    while (1)
    {
        sprintf(buffer,"Task2: %3d", i++);
        taskENTER_CRITICAL();
        Graphics_drawString(&g_sContext,
                    (int8_t *)buffer,
                    AUTO_STRING_LENGTH,
                    12,
                    32,
                    OPAQUE_TEXT);
        taskEXIT_CRITICAL();
        if (i==100) i=1;
    }
}

 

아니면 2) synchronization primitives를 사용한다

 


Synchronization Primitives in FreeRTOS

<queue: FIFO>

항목을 full queue로 보내는 과정은

queue가 꽉 찼거나 시간 초과가 만료되기 전까지 blocked됨

empty queue에서 가져오려고 하면

queue가 비어있지 않을 때까지, 또는 시간초과가 만료될 때까지 blocked 됨

 

Binary Semaphore

이는 길이가 1인 큐와 비슷함

(하나에 full, 이 반대는 empty)

  • task는 semaphore에서 제공될 때까지 blocked state로 대기
  • ISR이 발생하면 이러한 방식으로 blocked를 해제한 뒤 semaphore 제공
  • task는 semaphore를 가져와서 지연처리를 수행

  • xSemaphoreTake() : semaphore가 낮아질 때까지 semaphore 대기. (즉, task가 blocked 상태로 쭉). 낮아지지 않는다면 진행하여 sempahore를 내림
  • xSemaphoreGive() : semaphore 올리기
#include <semphr.h>

SemaphoreHandle_t xSemaphoreCreateBinary( void );

BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, 
	TickType_t xTicksToWait );

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore);

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
  • xTicksToWait
    • 0: semaphore를 이용할 수 없다면 즉시 반환
    • portMAX_DELAY 는 timeout 없이, 계속 task가 대기하도록 하게 함

 

문제점

만약 ISR에서 보류 중인 (pending) task가 blocked 되면 어떻게 해야하나?

  • ISR의 응답시간이 지연됨

그래서 이 방식은 Real-time(실시간) system에 적합하지 않음

심지어 시스템이 halting state(정지 상태)에 있게 될 수도 있음

그래서 blocking operation은 ISR에서 허용되지 않음

 

 


Task Signaling

semaphore로 event 발생 여부를 알리는 flag로 사용해보자

그 이벤트가 다른 task일 수도 ISR일 수도

 

실습

위의 그림에서 task 간 상황에 대해서 해보자

 

Task 1

  • Poll S1 button
  • button을 땠을 때 task2에게  signal 보내기

Task 2

  • signal 기다리기
  • signal 받으면 Red LED on/off

 

Bilateral Rendezvous

이것만 보고 button에 대해서만 semaphore signalling을 하면 작동이 안 됨

led on/off에 대해서도 signalling이 필요

불가능

 

Bilateral Rendezvous란

두 task가 2개의 semaphore를 사용하여 그들의 활동을 synchronize 하는 것

(이 방법은 task와 ISR 간에서는 불가능. 왜냐면 ISR은 blocked되어 버려서. 그럼 안 되는데)

/* global variables */
Graphics_Context g_sContext;
SemaphoreHandle_t s1but_sem;  // 추가
SemaphoreHandle_t leddone_sem;   // 추가 (led done)
...
void main(void)
{
  ...
  s1but_sem=xSemaphoreCreateBinary();   // 추가
  leddone_sem=xSemaphoreCreateBinary();   // 추가
  ...
}


void Task1( void *pvParameters )
{
  int s1but_bs=BS_INIT;
  while (1) {
    if (S1BUT) { // button down
      s1but_bs=BS_DOWN;
    } else { // button up
      if (s1but_bs==BS_DOWN) {
        xSemaphoreGive(s1but_sem); // 수정 - Task2의 xSemaphoreTake()로 signal 전송
        xSemaphoreTake(leddone_sem, portMAX_DELAY); // 추가 - Task2의 xSemaphoreGive()에게 signal 받음     
      }
      s1but_bs=BS_UP;
    }
  }
}

void Task2( void *pvParameters )
{
  while (1)
  {
    xSemaphoreTake(s1but_sem, portMAX_DELAY); // 수정
    P1->OUT^=BIT0;
    xSemaphoreGive(leddone_sem); // 추가
  }
}

Mutual Exclusion Semaphore

task와 ISR은 변수처럼 공동 resource를 공유할 수 있다

  • 둘 이상의 task 또는 ISR이 동시에 shared resource를 사용하려고 할 때, 일치하지 않는 잘못된 결과가 발생할 수 있음
  • shared resource에 접근하는 코드의 critical section이 resource에 대한 독점적(exclusive) 접근권한을 갖는지 확인하자
  • semaphore로 상호 배제(mutual exclusion)을 보장
  • FreeRTOS는 mutual exclusion을 위한 mutex semaphore를 제공함
#include <semphr.h>

SemaphoreHandle_t xSemaphoreCreateMutex( void );
SemaphoreHandle_t mtx;


int main(void) {
  ...
  mtx = xSemaphoreCreateMutex(); // mutex semaphore 생성 함수
  ...
  vTaskStartScheduler();
  ...
}

void vTaskA(void *pv) {
  while(1) {
    xSemaphoreTake(mtx, portMAX_DELAY);
    // The critical section begins
    ...
    // The critical section ends
    xSemaphoreGive(mtx);
  }
}

void vTaskB(void *pv) {
  while(1) {
    xSemaphoreTake(mtx, portMAX_DELAY);
    // The critical section begins
    ...
    // The critical section ends
    xSemaphoreGive(mtx);
  }
}
/* Driver configuration */
#include <ti/devices/msp432p4xx/inc/msp.h>
#include <ti/devices/msp432p4xx/driverlib/driverlib.h>
#include <ti/drivers/Board.h>
#include <ti/grlib/grlib.h>
#include "LcdDriver/Crystalfontz128x128_ST7735.h"
#include "LcdDriver/msp432p4111_classic.h"

/* RTOS header files */
#include <FreeRTOS.h>
#include <task.h>
#include <semphr.h>


/* function prototypes */
void Task1( void *pvParameters );
void Task2( void *pvParameters );

/* global variables */
Graphics_Context g_sContext;
SemaphoreHandle_t mtx;


void main(void)
{
	...
     xTaskCreate( Task1,                   /* The function that implements the task. */
                 "Task 1",                /* The text name assigned to the task - for debug only as it is not used by the kernel. */
                 configMINIMAL_STACK_SIZE,/* The size of the stack to allocate to the task. */
                 ( void * ) 0,            /* The parameter passed to the task - just to check the functionality. */
                 5,                      /* The priority assigned to the task. */
                 NULL );                  /* The task handle is not required, so NULL is passed. */

    xTaskCreate( Task2,                   /* The function that implements the task. */
                 "Task 2",                /* The text name assigned to the task - for debug only as it is not used by the kernel. */
                 configMINIMAL_STACK_SIZE,/* The size of the stack to allocate to the task. */
                 ( void * ) 0,            /* The parameter passed to the task - just to check the functionality. */
                 5,                      /* The priority assigned to the task. */
                 NULL );                  /* The task handle is not required, so NULL is passed. */

    mtx = xSemaphoreCreateMutex();
    /* Start the tasks and timer running. */
    vTaskStartScheduler();

    while (1)
    {
    }
}




void Task1( void *pvParameters )
{
    int i=0;
    char buffer[64];
    while (1)
    {
        sprintf(buffer,"Task1: %3d", i++);
        xSemaphoreTake(mtx, portMAX_DELAY);
        Graphics_drawString(&g_sContext,
                                        (int8_t *)buffer,
                                        AUTO_STRING_LENGTH,
                                        12,
                                        24,
                                        OPAQUE_TEXT);
        xSemaphoreGive(mtx);
        if (i==100) i=1;
    }
}

void Task2( void *pvParameters )
{
    int i=0;
    char buffer[64];
    while (1)
    {
        sprintf(buffer,"Task2: %3d",i++);
        xSemaphoreTake(mtx, portMAX_DELAY);
        Graphics_drawString(&g_sContext,
                                        (int8_t *)buffer,
                                        AUTO_STRING_LENGTH,
                                        12,
                                        32,
                                        OPAQUE_TEXT);
        xSemaphoreGive(mtx);
        if (i==100) i=1;

    }
}
728x90
반응형