라벨이 FreeRTOS인 게시물 표시

[FreeRTOS] SOFTWARE TIMER (소프트웨어 타이머)

# Software Timer Config configUSE_TIMERS 는 디폴트로 0이지만 타이머를 사용하기 위해서는 반드시  1 로 변경 되어야 한다. 그외 나머지는 적당히 알아서 셋팅 하도록 하자. // FreeRTOSConfig.h /* Software timer definitions. */ # define configUSE_TIMERS 1 // Required # define configTIMER_TASK_PRIORITY 3 # define configTIMER_QUEUE_LENGTH 10 # define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE # Software Timer API xTimerCreate : 타이머 생성 # include “timers.h” TimerHandle_t xTimerCreate ( const char *pcTimerName, const TickType_t xTimerPeriod, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction) ; // # Parameters // pcTimerName : 타이머 이름 // xTimerPeriod : 타이머 주기 (Tick 단위) // 단위를 ms 로 변경하기위해 pdMS_TO_TICKS 매크로를 사용 함. // ex) 500ms => pdMS_TO_TICKS(500) // uxAutoReload : 타이머 작동 방식 설정 // pdFALSE : one-shot 타이머 // pdTRUE

[FreeRTOS] Message Queue (메시지 큐)

이미지
  메시지 큐는 RTOS 에서 가장 많이 사용되는 IPC 라고 봐도 무방 함. TASK나 ISR에서 또 다른 TASK로 포인터 변수나 데이터를 전달 함. xQueueSend 와 xQueueSendToBack 은 동일한 동작으로 FIFO를 수행 함. API xQueueCreate : 큐 생성 # include "queue.h" QueueHandle_t xQueueCreate ( UBaseType_t uxQueueLength, UBaseType_t uxItemSize ) ; // # Parameters // uxQueueLength : 큐 길이 // uxItemSize : 큐 아이템 사이즈 // # Return : 큐 핸들 xQueueReceive : 메시지 큐 대기 # include “queue.h” BaseType_t xQueueReceive (QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait) ; // # Parameters // xQueue : 큐 핸들 // pvBuffer : 수신 된 데이터가 복사 될 메모리 포인터 // xTicksToWait : 대기 시간 xQueueSend : 메시지 전송 # include "queue.h" BaseType_t xQueueSend (QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait) ; // # Parameters // xQueue : 큐 핸들 // pvItemToQueue : 큐에 복사할 데이터에 대한 포인터 // xTicksToWait : 대기열이 이미 가득 찬 경우, 대기열에서 큐를 사용할 수 있을 때 까지 // Task가 blocked 상태를 유지해야 하는 최대 시간 // # Return Values //

[FreeRTOS] EventGroup (이벤트 그룹)

이미지
Event Group을 사용하면 Task간 정보를 주고 받을 수 있다. API xEventGroupCreate : 이벤트 그룹 생성 # include "event_groups.h" EventGroupHandle_t xCreatedEventGroup; void TaskMain ( void ) { xCreatedEventGroup = xEventGroupCreate(); // Event Group 할당 실패에 대한 예외처리 if (xCreatedEventGroup == NULL ) { } } xEventGroupSetBits : 이벤트 전달 EventBits_t xEventGroupSetBits ( const EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet) ; // # Parameters // xEventGroup : Bit가 설정 될 이벤트 그룹 // uxBitsToSet : 이벤트 그룹에서 설정 할 비트를 나타내는 비트 값 // 예를들어, 0x0A는 1010(b) 이므로 비트3과 비트1을 설정 함 xEventGroupWaitBits : 이벤트 대기 # include "event_groups.h" EventBits_t xEventGroupWaitBits ( const EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits,

[FreeRTOS] configASSERT (프로그램 오류 감지)

이미지
  프로그램이 구동 중에 갑자기 멈추는데 무엇이 문제인지 알려주지 않는 경우엔 답답하다. 이를 조금이나마 해소할 수 있는 방법을 알아보자. FreeRTOSConfig.h 파일에서 #define configASSERT 부분을 찾아서 매크로를 수정하고, // FreeRTOSConfig.h ... /* Normal assert() semantics without relying on the provision of an assert.h header file. */ /* USER CODE BEGIN 1 */ extern void vAssertCalled ( const char *filename, unsigned int line ) ; # define configASSERT( x ) if ((x) == 0) vAssertCalled( __FILENAME__, __LINE__ ) /* USER CODE END 1 */ ... main.c 에 vAssertCalled 함수를 정의 하자. // main.c void vAssertCalled ( const char *filename, unsigned int line ) { taskDISABLE_INTERRUPTS(); printf ( "\n\nAssertion failed in %s:%d\n" , filename, line ); for (;;); } 예제 ) Task 종료 전 vTaskDelete(NULL) 을 호출 하지 않을경우, 아래와 같은 에러 메시지를 확인 할 수 있다. Assertion failed in port.c:202

[FreeRTOS] MUTEX (뮤텍스)

이미지
 임계영역(Critical Section)을 보호 하는 방법에는 세마포어를 사용하는 방법과 뮤텍스를 사용하는 방법이 있는데 둘의 차이는 뭘까? 아래와 같이 Task1, Task2, Task3 이 있는 경우에, Task1의 우선순위가 높은데도 불구하고 Task2에 밀리는 경우가 발생한다. 이를 우선순위 역전 현상이라고 한다. 이러한 우선순위 역전 현상을 방지 하기 위한 방법으로  우선순위 상속 이 있다. 높은 우선순위의 task가 block 상태인 동안, 그 task를 block 상태로 들어가게 만든 task의 우선 순위를 임시적으로 올려 다른 task로부터의 선점을 방지 하는 방법이다. 즉, 그러니까 Task1의 우선순위를 Task3이 상속 받게 되어 Task2로부터 선점을 당하지 않게 되는 것이다. 이러한 우선순위 상속의 메커니즘은 Mutex를 사용한다. Task3 가 임계영역을 벗어나, Mutex를 반납하게 되면 원래의 우선순위로 돌아가게 된다. 세마포어는 우선순위 상속을 하지 않으므로, 우선순위 역전 현상이 발생하게 된다. 따라서 뮤텍스(MUTEX) 는 다음과 같이 정리 할 수 있다. 상호배제를 목적으로 하는 '바이너리 세마포어 + 우선순위 상속' Mutex용 세마포어 사용 예제 SemaphoreHandle_t xSemaphore; void Task ( void *pvParameters) { xSemaphore = xSemaphoreCreateMutes(); /* Mutex 생성이 실패 할 수 있으므로 체크 필요 함*/ if (xSeamphore == NULL ) { /* There was insufficient heap memory available for the mutex to be created. */ } }

[FreeRTOS] Inter Task Communication

이미지
  보통 프로세스간 통신을 IPC (Inter Process Communication) 라고 많이 부르지만, RTOS 에서는 보통 프로세스 보다는 Task 라는 용어를 사용하므로 ITC (Inter Task Communication) 가 될 수 있겠다. 목적은 Task 또는 ISR(Interrupt Service Routine) 이 다른 Task에게  정보 전달  하는 것 이다. 이를 수행하는 방법에는 다음과 같은 방법들이 있다.  ⁜ 전역 변수 사용 ( 상호 배제에 유의 하여야 함 )  ⁜ 이벤트 플래그, 세마포어 : 이벤트 발생 여부만 알려 줌  ⁜ 메일박스, 메시지 큐 : 이벤트와 데이터도 함께 전달 세마포어로 구현하는 이벤트 전달 방법은 아래와 같을 수 있다. Task는 Semaphore Take를 통해 Blocked 상태에 진입하게 되고, 다른 Task 또는 ISR로부터 Semaphore를 Give 받았을 때 blocked 상태에서 깨어 나게 된다. 양방향의 경우, ISR → Task 는 일반적인 이벤트 전달 방향이지만, 그 반대 방향 (TASK → ISR) 은 보통 사용 되지 않는다.

[FreeRTOS] Semaphore (세마포어)

이미지
  Semaphore (세마포어) 로 할 수 있는 일 1. 상호 배제 (Mutual Exclusion) 2. 프로세스간 통신 (IPC) 3. 동기화 (Synchronization) 아래 예제는 유명한 생산자-소비자 동기화를 다루고 있음. 우선순위가 높은 소비자 프로세스가 먼저 실행 되어   세마포어 획득 시도를 하지만  생산 된 아이템이 없으므로  blocked  상태로 들어가게 됨 . 그 다음에 생산자  프로세스가 깨어나 아이템 생산 후 ,   V  연산을  하면   소비자 프로세스를 깨어나게  되고 , 소비자 프로세스는 아이템을 가져와서 소비한 뒤 다시 세마포어 획득 시도를 하게 되고   blocked  상태로 들어가게 됨 . 즉 ,  우선순위가 낮은 생산자 프로세스에서 로직을 컨트롤 할 수 있게 된다 . Semaphore API 세마포어 생성 void vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore ) 또는 SemaphoreHandle_t xSemaphoreCreateBinary( ) SemaphoreHandle_t sem_handle; void Task ( void *pvParameters) { vSemaphoreCreateBinary(sem_handle); // or sem_handle = xSemaphoreCreateBinary( ); // 세마포어 할당에 실패하는 경우도 있으니, 꼭 체크 해 주자. if (sem_handle == NULL ) { /* There was insufficient FreeRTOS heap available for the semaphore to be created succesffully. */ } } P 연산 (세마포어 획득) BaseType_t xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicks

[FreeRTOS] Interrupt

이미지
ISR (인터럽트 서비스 루틴) 내에서 시간이 오래 소요되는 작업을 처리하게 되면, 그 동안 다른 인터럽트를 받지 못하고 놓치게 되는 문제가 발생한다. 따라서 ISR 에서는 인터럽트 발생 여부만 체크하고, 실제 작업을 처리하는건 Task에서 수행하도록 하면 인터럽트를 놓치지 않을 수 있다. 간단하게 비교하자면 다음과 같다. 코드레벨에서는 다음과 같이 구현 할 수 있다. TaskHandle_t xHandlingTask; void vHandlingTask ( void *pvParameters ) { uint32_t ulNotifiedValue; xHandlingTask = xTaskGetCurrentTaskHandle(); /* Store the handle of the calling task. */ while ( 1 ) { ulNotifiedValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY /* Block indefinitely. */ ); while (ulNotifiedValue > 0 ) { 처리해야 할 작업( ); ulNotifiedValue--; } } } void vANInterruptHandler ( void ) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; vTaskNotifyGiveFromISR(xHandlingTask, &xHigherPriorityTaskWoken); /* Unblocking Task */ portYIELD_FROM_ISR(&xHigherPriorityTaskWoken); /* Context Switch */ } TaskHandle_t xHandlingTask; void vHandlingTask ( void *pvParamet

[FreeRTOS] Critical Section (임계 영역)

  # Critical Section 공유 자원 을 사용 중인 함수내의 일부 혹은 전체 영역. 공유 자원이라 함은  전역 변수  또는  하드웨어 주변장치  가 될 수 있다. 따라서 공유 자원에 접근해서 조작을 하는 중간에 컨텍스트 스위칭이 발생하면 데이터의 무결성이 깨지게 된다. int 전역변수; void Task1 ( void ) { ... /* CRITICAL SCTION BEGIN */ 전역변수++; -> // 공유 자원에 접근하여 조작하는 코드 코드에서는 1 줄이지만, 실제로는 아래처럼 여러줄의 어셈블리 코드로 이루어져있다. 1. 전역변수 메모리주소에서 값을 읽어오는 라인, 2. 메모리에서 값을 읽어 레지스터에 할당하는 라인, 3. 레지스터에서 값을 조작 (증가연산) 하는 라인, 4. 레지스터의 값을 다시 메모리에 Write하는 라인 /* CRITICAL SECTION END */ ... } void Task2 ( void ) { ... /* CRITICAL SCTION BEGIN */ // 공유 자원에 접근하여 조작하는 코드 전역변수++; -> // 공유 자원에 접근하여 조작하는 코드 /* CRITICAL SECTION END */ ... } # 재 진입 (Reentrancy) 다수의 Task에서 호출하여 사용 할 수 있기 위해서 해당 함수는  재 진입 가능  하도록 작성 되어져야 함.  그렇다면 재 진입 가능 하다는 것은 무엇을 의미할까? 단순히 함수 호출을 통해 함수 Entry Point에 진입하는게 아니라, 위 예제 코드에서처럼, C 코드 한줄은 여러줄의 어셈블리 코드로 이루어져있는데, 어셈블리어 실행 중간에 태스크 스위칭이 발생하더라도 문제가 없음을 의미 한다. 재 진입 함수를 만드는 방법은 다음과 같다. Task에서 공유자