임베디드 시스템을 개발하다보면 어떤 이벤트가 몇번 발생했는지 알고 싶을때가 있다. 이미 입문편에서도 설명하였지만 이벤트의 발생간격이 일정하게 연속되어 발생한다면 타이머로도 사용할 수 있다고 하였다. 이번 글에서는 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..
가장 먼저 할일은 AtmelStudio에서 megaOS 프로젝트를 생성하는 것으로부터 시작하겠다. 프로젝트를 만들면 AtmelStudio 7의 경우 자동으로 main.c 파일이 생성되는데 이 파일을 삭제하여 완전히 텅 빈 상태로 만들어 둔다. 이제 링커 스크립트 파일을 만드는것으로부터 코딩을 시작하도록 하겠다. megaOS.lds라는 이름의 파일을 새로 만든 후 다음과 같이 입력한다. OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr") OUTPUT_ARCH(avr:5) MEMORY { flash (rx) : org = 0, len = 32K sram (rw!x) : org = 0x800100, len = 2K } SECTIONS { .text : { KEEP(*(...
지금부터 ATmega328에서 동작 되는 RTOS를 만들어 보겠다. 편의상 이 OS의 이름을 "megaOS"라고 하겠다. ATmega328만을 목표로 OS를 만들겠지만 ATmega128과 같은 다른 ATmega 계열의 MCU는 조금만 수정하면 쉽게 해당 MCU에서 동작시켜 볼 수 있을것이다. 여력이 된다면 실전 과정에서 ATmega뿐만 아니라 다른 종류의 MCU에도 쉽게 포팅할 수 있는 구조를 만들어 볼 생각이다. megaOS의 기본 개념은 "eCos"라는 RTOS에서 가지고 왔다. 그럴리는 없겠지만 혹시라도 megaOS를 상용제품에 적용할 생각이 있다면 eCos 라이센스를 잘 확인하여 문제가 발생하지 않도록 주의하기 바란다. http://ecos.sourceware.org/ 에 들어가면 eCos의 소스..