간단한 임베디드 시스템이라면 앞에서 설명했던 counter만 가지고도 필요한 기능을 수행할 수 있겠지만, 일반적인 임베디드 시스템에서는 좀더 다양한 형태의 이벤트 처리 기능을 요구하게 된다. 인터럽트가 발생했을때 적절한 처리를 하도록 타스크에게 알려주기 위해서나, 타스크간의 정보 교환이 필요한 경우가 있는데 이를 동기화(Synchronization)라고 한다. megaOS에서는 동기화 기능중 semaphore, flag, mailbox를 만들어 보도록 하겠다. Synchronization을 지원하기 위해서 TCB 구조체에 sync_list를 추가하여야 한다. 당연히 os_task_create() 함수에서는 sync_list 초기화 코드도 추가하여야 한다. 이 부분은 이 글의 마지막 부분에 첨부되어 있는 ..
RTOS 뿐만 아니라 모든 OS는 커널에서 사용하기 위한 타이머가 있다. 그리고 OS에서 사용할 수 있도록 많은 CPU에는 전용의 타이머를 제공하기도 한다. 이번 글에서는 커널에서 사용하는 system timer에 대해서 설명하도록 하겠다. 먼저 os.h 파일에 필요한 내용을 추가해 보도록 하겠다. #ifdef BOARD_TICKS_PER_SEC #define OS_TICKS_PER_SEC BOARD_TICKS_PER_SEC #else #define OS_TICKS_PER_SEC 100 #endif #define OS_SCHED_TIMESLICE_TICKS (OS_TICKS_PER_SEC/10) OS_TICKS_PER_SEC은 1초에 몇번의 tick을 발생 시킬것인지 설정하는 역할을 한다. OS 타이머는..
임베디드 시스템을 개발하다보면 어떤 이벤트가 몇번 발생했는지 알고 싶을때가 있다. 이미 입문편에서도 설명하였지만 이벤트의 발생간격이 일정하게 연속되어 발생한다면 타이머로도 사용할 수 있다고 하였다. 이번 글에서는 megaOS에서 제공하는 Counter에 대해서 설명하도록 하겠다. 이전에 설명하던 방식과 다르게 Counter에 대한 내용을 모두 설명한 뒤에 예제 코드를 작성해서 잘 동작되는지 확인해 보도록 하겠다. 먼저, os.h에 Counter와 관련된 내용을 추가해 보도록 하겠다. Counter에는 이벤트가 발생했을때 그 사실을 알려주기 위하여 Alarm을 반드시 연결하여 사용하도록 되어있다. 따라서 Counter와 Alarm을 같이 설명하도록 하겠다. struct os_counter { os_tick..
지금까지 진행된 내용을 정리하면, 프로그램이 시작되어 여러가지 초기화를 진행한 후, idle task를 생성하여 실행 시키고, idle task에서는 main task를 생성, 실행 시켰다. idle task나 main task 둘 다 while(1); 로 되어 있으므로 두 타스크 모두 무한 루프 상태로 타스크가 실행된다. 하지만, 실제로는 main task가 우선 순위가 높으므로 main task만 CPU를 독점하고 idle task는 단 한 순간도 CPU를 점유하지 못하여 기아 상태에 빠지게 된다. RTOS를 사용하는 이유가 CPU를 효율적으로 이용하기 위해서이므로 어떤 이벤트가 발생하였을 때 그 이벤트를 처리하기 위한 타스크가 동작하고 다른 이벤트를 수행하기 위하여 CPU 점유권을 넘겨 주는 구조를..
앞의 글에서는 idle task의 context들을 스택메모리에 초기화 시킨 다음, arch_load_context() 함수를 불러서 스택메모리에 있던 context data들을 CPU 내부 레지스터로 복사하여 idle_task()라는 함수를 호출하는 과정에 대해서 설명하였다. 이번 글에서는 동작 중이던 어떤 타스크를 중지하고, 다른 타스크를 수행하는 과정에 대해서 설명하도록 하겠다. 이렇게 타스크를 바꾸는 동작을 task switching 또는 context switching이라고 한다. 먼저, TCB 구조체에 몇가지 필드를 추가해 보도록 하겠다. struct os_tcb { os_addr_t stack_ptr; os_addr_t stack_base; uint16_t stack_size; os_task..
이번글에서는 바로 앞의 글에서 생성한 Idle task에 대한 context를 실제로 CPU에 load 하여 예상대로 동작되는지 확인해 보도록 하겠다. 먼저 task_init() 함수에 다음과 같은 코드를 추가한다. void task_init(void) { struct os_tcb *idle_tcb = os_task_create(idle_task, 0x1234, OS_MIN_PRIORITY, OS_MIN_STACK_SIZE, "Idle"); arch_load_context(idle_tcb); } arch_load_context() 함수 호출을 추가하였는데, arch_load_context()는 어셈블러 코드로 작성된다. context.S 파일을 만들고 다음과 같은 코드를 추가한다. #include .gl..
이번 글에서는 타스크와 관련된 내용인 TCB와 context에 대한 설명과 함께 실제로 어떻게 새로운 타스크를 만드는지 구현해 보도록 하겠다. 위의 그림은 uC/OS-II 문서에 나와 있는 그림이다. 이 그림을 보면 전체적인 타스크와 관련된 내용을 한눈에 볼수가 있다. Task Control Block이라고 하는 TCB는 각 타스크에 대한 정보를 가지고 있다. RTOS 글에서 나오는 아무추어 바둑선수들의 프로필 정보라고 생각하면 될것이다. TCB에는 타스크의 우선순위, 상태, 스택 포인터 등등 많은 정보를 가지고 있다. 그림에서 볼수있듯이 각 타스크는 별도의 스택 영역을 가지고 있어야 한다. 스택의 크기는 타스크의 기능에 따라 그 크기를 알맞게 설정할 수 있다. Context라고 하는것은 위의 그림에 표..
OS에서 가장 핵심이 되는 부분을 커널이라고 부르고, 그리고 그 커널안에서도 가장 중요한 부분을 굳이 말하라고 한다면 스케줄러일것이다. 스케줄러가 무엇인지 한줄로 요약한다면 여러 타스크들중에 어떤 타스크를 실행 시킬것인지 선택하여 그 타스크에게 CPU 점유권을 넘겨주는 기능을 수행하는 블럭이라 하겠다. 문제는 어떤 기준을 가지고 많은 타스크중에 하나를 선택할 것인가이다. 이러한 선택의 알고리듬의 차이가 각각의 RTOS의 특징이 되고 장단점이 되는 것이다. 이번 글에서는 megaOS의 스케줄러가 어떤식으로 동작되는지 설명하고, 스케줄러의 초기화 부분을 구현해 보도록 하겠다. megaOS는 모두 8개의 우선순위(Priority)를 제공한다. 그러므로 각 타스크는 0~7 중에 하나의 우선순위 값을 가질 수 있..
megaOS는 자료 관리를 위하여 double linked list를 사용한다. megaOS의 동작을 이해하기 위해서는 반드시 double linked list가 어떻게 동작되는지 정확히 알고 있어야 한다. 따라서 이번 글에서는 본격적으로 megaOS 코드를 구현하기 전에 앞서서 megaOS에서 사용하는 double linked list에 대한 설명을 하기로 하겠다. os.h 파일에 다음과 같은 구조체를 선언한다. /*------------------------------------------------------------------------------ * Base types and Macros *----------------------------------------------------------..
이번 단계에서 설명하고자 하는 내용은 startup 코드가 제대로 작성되었는지, 그리고 간단하지만 회로에는 문제가 없는지 확인해 보기위한 초기화 코드를 만들어 보는 것이다. megaOS 프로젝트를 진행하기 위하여 세개의 header 파일이 필요하다. OS에서 사용하는 각종 매크로와 구조체, 함수 prototype을 정의하기 위한 os.h 파일과, ATmega328이라는 MCU에 해당되는 내용을 담고있는 arch.h 파일, 그리고 보드 의존적인 내용을 정의하기 위한 board.h 파일이다. 이들 header 파일은 inc 라는 폴더에 분리시켜 작성하도록 하겠다. 코드 작성의 편의성을 위하여 프로젝트 속성 창에 이들 header파일이 있는 폴더를 등록하도록 하겠다. 위의 그림처럼 프로젝트 속성창에서 Tool..