[FreeRTOS] Task

seopppio·2024년 10월 31일

FreeRTOS

목록 보기
2/14

XTaskCreate

XtaskCreate라는 FreeRTOS 함수로, 태스크를 만들 수 있따.
이 함수에 대해서는, 메뉴얼을 읽어봐도 좋으나, 들어가는 인자에 대해 알려주겠다
xTaskCreate(테스크 함수, "테스크 이름", 테스크 파라미터, 테스크 우선순위, 태스크핸들)

파라미터와 핸들은 제외하고, 일단 테스크 우선순위를 보자.


이렇게 테스크1 함수를 바탕으로 테스크를 만들어야하는데, 우선순위가더 높은 태스크가 있는 경우 만들어지지만 실행은 안 된다


USER_THREADS() -> 테스크 리소스를 만든다
osKernelStart() -> 스케줄러 동작시킨다, 일종의 멀티테스크 시작 함수

CMSIS나 HAL 라이브러리 사용할때 사용할때 초기값

  • HAL 라이브러리
    하드웨어 레지스터를 직접 접근하기 보다, 함수를 통해 접근한다, 즉 하드웨어 추상화 과정을 가지게 한다
    GPIO, UART, ADC등 상위 수준의 추상화된 API

  • CMSIS 마이크로컨트롤러 소프트웨어 인터페이스 표준
    코어의 기능을 위한 API
    (ex, 인터럽트, Systick 타이머 등등)

xTaskcreate로 테스크 만들어도, osKernelStart()라는 스케줄러 동작시키는 함수가 메인에서 안 돌아가면 그냥 메모리에만 테스크가 남아 있게 된다

테스크 바꿀 때

만약 우선순위가 높은 테스크가 실행 중이고, 우선순위가 낮은 테스크가 ready에 있을 때, 우선순위가 낮은 테스크가 실행되려면, 높은 테스크가 blocked로 변경되거나 kill되면 된다

  • vTaskSuspend(핸들포인터) : blocked로 변경

  • vTaskDelay : 지정시간동안 blocked에 있다 ready걸쳐서 다시 running됨(FreeRTOS의 함수 인수가 틱 단위여서, pdMS_TO_TICKS 함수를 이용해 초 단위를 틱으로 바꿔준다)

    이때 blocked로 갔을 때, running에 공백기가 생기니 Idle task가 그 자리를 매꾼다,
    !!! 얘를 이용해 멀티테스킹 구현 가능

  • vResume : blocked에 있는거 ready로 옮겨줌

RTOS의 특징

우선 순위가 높다고 오래 실행되는 것이 아니다

ex)
Task1 우선순위10, 태스크 함수에 vTaskDelay(pdMS_TO_TICKS(1000))존재
Task2 우선순위5, 태스크 함수에
vTaskDelay(pdMS_TO_TICKS(100))존재

이렇게 되면, 우선순위가 높은 Task1이 먼저 실행될 지 언정, 오랫동안 blocked 상태에 존재하고, 그 사이에 우선순위가 낮은 Task2가 running되면서 더 많이 실행이 된다

  • 즉 휴면을 적게 할수록 오래 실행된다

헤더


이게 0이면, 선점한걸 놓아주지 않는다

틱 인터럽트


타이머 인터럽트는 주기적으로 발생하는데, 그것을 틱주기라 하며, Hz가 1000이면 1초에 1000개의 타이머 인터럽트가 발생한다는 것

타이머 인터럽트가 발생할 때 마다, 선점하는게 작동해야하는지 안 해야하는지 틱 인터럽트에서 확인한다


타임슬라이스를 1로하고 선점을 0로하면, 우선순위 기반 태스크 전환 일어나지 않음

printf와 fflush

기존 printf는, 문자를 버퍼에 담고 가득 차면 선입선출 형식으로 내보낸다
fflush는 강제로 버퍼를 비우게 한다

fflush와 함께 UART 하드웨어 장치를 작동해서 문자를 배출하게 되는데, UART의 경우 느린 장치다.
fflush 없이 printf는 uart동작을 유발하지 않아서 공평하게 작동되는게 눈으로 보이지만, fflush가 있는 경우 uart 동작을 작동시켜서, 공평하게 테스크가 실행됨에도 불구하고 착시현상이 보인다(실제로, 우선순위 같게하면 동시에 실행되면서 라운드 로빈이 실행된다, but fflush 함수가 uart를 출력하는데 오래 걸려서 착시현상이 나타날뿐)

IDLE 테스크


configUSE_IDLE_HOOK 을 1로 하면
정의된 후크테스크를 아이들 테스크에서 부르게 한다

테스크와 메모리


메모리에 단편화 현상이 생길 수 있는데, 가급적 테스크 스택 사이즈를 크게 할당하는 것이 좋다

크기 결정할 때

지역 변수의 개수
테스크 호출 함수의 중복 호출 횟수

정적 메모리로 테스크 스택을 생성할지, 동적으로 생성할지 결정

정적으로 하면 .bss메모리에, 동적이면 .heap에 할당 된다

스택 오버플로우 검사


옵션이 1, 2 있는데 2를 사용하자. 1은 detect이 잘 안 된다. 사실 2도 안 된다

사용하기전에 스택 메모리에 특별한 문자 패턴을 넣는다(0은 아님, 특별한 용이 패턴)
모니터링 해보니까 원래 있어야할 패턴이 아니라 다른 패턴이 있다면 스택 메모리가 다 사용됐다고 판단.

동적 할당 테스크 스택 메모리 커널 코드 분석

시작하면 pvPortMalloc으로 메모리 할당하고, 테스크 생성할 때 인자의 usStackDepth 크기로 스택 크기 잡는다

ARM은 메모리를 높은주소부터 낮은 주소로 쓰기 때문에, 맨 마지막 주소로보면 된다
즉 메모리를 할당하면 시작 주소를 주는데 스택 관점에서는 마지막 주소다

스택 메모리 최적화

스택메모리 만들어지자마자 빈 부분에 0을 넣는다(FILL Zero)
0이 어디부터 어디까지 나타나는지 확인하기만 하면, 테스크가 스택 메모리를 어디까지 사용했는지 판단 가능 이후 더하거나 빼서 알맞게 최적화 한다

테스크 관련 API

vTakDelete(테스크핸들)

테스크 삭제 함수, 자기 자신을 넣고 싶으면 핸들에 Null 넣으면 된다
IDLE 테스크는 삭제 불가능

vTaskPrioritySet(테스크 핸들, 새로운 우선순위)

우선순위 바꿈

vTaskSuspend, vTaskResume

테스크를 blocked 상태로 둠, 다시 Ready 상태로 둠

TCB

  • 컨택스트 복원 : 각 Task마다 context 가지고 있고 이것은 CPU레지스터를 저장하고 있다
  • 컨택스트 저장 : 레지스터를 Task context에 저장. CPU 레지스터의 복사본을 해당 테스크 전용 스택 메모리 공간에 저장

TCB는 Task Control Block이라고 한다. 여기에는 태스크에 있는 모든 정보가 저장되어 있다.
CPU레지스터가 저장되어 있는 컨텍스트 위치를 찾아낼 때, 프로그램이 어디에서 실행되고 있는지, 어떤 라인이 실행되고 있는지 모두 담고 있다
테스크가 러닝인지, 블록인지, 레디인지 확인 가능한 Status
스택 포인터 레지스터도 TCB 내부에 있다.
테스크 우선순위도 TCB에 저장된다

어떤 테스크의 TCB를 읽어내고 싶으면 kernel-api 이용하면 된다
(vTaskGetTaskInfo 사용)

vTaskDelayUntil()

까다로운 함수다. 어디다 사용하는지를 기억해두자

vTaskDelay는 절대시간이 아닌 상대 시간으로 딜레이를 해준다.
vTaskDelayUntil은 절대 시간을 보장한다.

주기적 테스크의 정확한 실행 주기를 보장해주니, 하드 리얼타임을 위해 적용하자

FreeRTOSconfig.h


어떤 함수 쓰려면 이렇게 메크로로 미리 헤더파일에 설정해둬야 한다

Coretex-M3 프로세서


이 헤더에서 정의한 1000은

이 포인터셋업 인터럽트 함수에 사용된다.
그래서 나중에 vTaskDelay()여기에 그냥 틱을 넣어서 사용하는 경우, 헤더의 Tick_Rate를 알아야한다. 그래서 보통

셋업된 틱과 무관하게, 시간을 설정해줄 수 있는 pdMS_TO_TICKS 사용한다. 즉 Delay함수에 틱의 단위가 아닌, 초 단위로 전달인자 넣을 수 있다

vTaskDelay(1000)이면 타이머 인터럽트가 1000개 발생했을때 시간이고, 이걸 저 헤더에서 1000으로 설정하면 1초인거다. 근데 이런걸 헤더도 찾아보면서 숫자를 적는건, 타이머 인터럽트 설정을 바꾸면 모두 틀어질 수 있어서, 틱 대신 시간으로 인자를 넣을 수 있는 방법을 추천하는 것이다


NVIC는 인터럽트 컨트롤러, 시스틱이라는 하드웨어 타이머도 존재
이 시스틱이 1000hz로 설정했으면 1초에 1000번 Tick을 발생시키는 거다

0개의 댓글