Presentation is loading. Please wait.

Presentation is loading. Please wait.

TinyOS Tutorial Lesson 3 : Introducing Tasks for Application Data Processing (Hanback’s zigbeX & TinyOS ver.1.1.7) 3번째 시간입니다. Lesson 2는 생략하고 건너 뛰었습니다.

Similar presentations


Presentation on theme: "TinyOS Tutorial Lesson 3 : Introducing Tasks for Application Data Processing (Hanback’s zigbeX & TinyOS ver.1.1.7) 3번째 시간입니다. Lesson 2는 생략하고 건너 뛰었습니다."— Presentation transcript:

1 TinyOS Tutorial Lesson 3 : Introducing Tasks for Application Data Processing (Hanback’s zigbeX & TinyOS ver.1.1.7) 3번째 시간입니다. Lesson 2는 생략하고 건너 뛰었습니다. 자료도 많이 나오는 편이고, 이벤트에 대한 소개를 다루는데 있어서, 쉽게 잘 올라온 것이 많아서 점프 했습니다. 그리고 한백전자용 zigbeX를 사용하는데 TinyOSver 에서 Lesoon4가 오동작하는 것을 발견하였기 때문에 1.1.7(원래 제공하는) 버전으로 하는게 좋을 듯 싶습니다. 저 때문에 을 설치하신분들께는 죄송하지만, 아무 문제 없이 사용하시길 원하신다면 1.1.7을 설치하시기 바랍니다. 그럼 시작하겠습니다. 이번 Lesson 3은 TinyOS에서의 스케줄링에 대해서 알아보겠습니다. Embedded System Lab. HyounSoo. Kim

2 Contents 1 Task creation and scheduling 2 The SenseTask Application 3
Introduction to SenseTask SenseTask Configuration SenseTask Module 순서는 TinyOS 스케줄러가 어떻게 스케줄링 되는지에 대해 알아보고, 예제인 SenseTask 프로그램을 통해 어떤 방식으로 스케줄링 되는지 살펴보겠습니다. 그리고 요약하는 것으로 마치겠습니다. 3 Summary

3 1. Task Creation and scheduling
A two-level scheduling hierarchy Tasks(태스크) 보통 백그라운드 데이터 처리와 같은 보다 긴 처리 작업들을 수행 Hardware event handlers에 의해 선점될 수 있다. Hardware event handlers(하드웨어 이벤트 핸들러) keyword로 async 사용하여 command나 event로 소량의 작업을 수행 이것은 언제나(다른 코드들을 선점하여) 실행이 가능함 따라서 수행 시간이 짧아야 하고, data race 가능성을 항상 염두 해야 함 Task의 운용 task void taskname() { … } // taskname에 실제 Task 이름 삽입하여 선언 Return type은 항상 void형이며, 어떤 argument를 받지 않는다. post taskname(); // task는 command, event 또는 또 다른 태스크에 의해 뒤로 미루어질 수 있다. post 작업은 FIFO 명령을 처리하는 내부의 태스크 큐에 태스크를 위치시키며, 태스크가 실행될 때, 태스크는 다음 태스크가 실행되기 전에 완전히 종료되어야 한다. 태스크가 큰 경우 각각의 작업들을 분리된 태스크로 처리시켜 준다. 첫번째 장으로 TinyOS의 스케줄링입니다. TinyOS는 우선순위가 부여되지는 않지만 스케줄링을 합니다. TinyOS는 Task들이 Task queue에 들어온 순서대로 처리되어지는 First-In-First-Out(FIFO) 방식에 의해서 처리되어집니다. 이 와중에 Task들간에는 서로 선점이 없고 하나가 다 종료되면, 다음 Task가 들어와서 수행되고, 다시 또 Task가 들어오면 수행되고, 이러다가 더 이상 수행될 Task가 queue에 남아 있지 않다면, sleep 모드로 들어가게 됩니다. 하지만, 도중에 ISR 즉, 여기서는 hardware event handler에 의해서 Task들이 선점당할 수 있으며, event 역시 선점 당할 수 있습니다. 이렇게 Task가 FIFO로 처리되는 가운데 event가 수행되고 이런식으로 TinyOS가 돌아갑니다. 그래서 evnet의 경우 Task 작업을 너무 연기시키지 않게끔 수행 시간이 짧아야 하고, 따라서 공유 데이터에 대한 동시 접근에 대한 문제들도 염두해 둬야 합니다. 그리고 Task에 관한 설명으로 Task는 직접 만들수 있고, 이것을 배정시킬 수가 있습니다. 따라서 보통 특정 작업이 끝남기 전에 특정 Task를 수행시켜 주기 위해서 post 시켜 줄 수 있습니다.(Task queue에다가 넣어주는 겁니다.) 단, 이때의 Task는 되도록이면 빨리 종료될 수 있도록 하고, 그 크기가 클 경우 작은 단위로 분리해 주는게 좋습니다.

4 2. The SenseTask Application
Introduction to SenseTask (1 of 2) "SenseTask" 조도 Sensor를 통해 읽어온 값을 LED로 표시 빛의 세기에 따라 0~7단계의 LED 상태 변화 SenseTask application is composed of a component: configuration : “SenseTask.nc” SenseTask 프로그램에서 컴포넌트 간의 전체적인 흐름을 이어주는 역할 module : “SenseTaskM.nc" 실질적인 SenseTask 프로그램에 대한 구현을 제공한 모듈 그럼 예제를 살펴보겠습니다. 예제는 Lesson2와 결과는 비슷합니다. 하지만, 내부 동작은 이전의 것과는 다르다는걸 알아야만 합니다. 그럼 뭐가 틀린지 자세히 알아보도록 하죠.

5 2. The SenseTask Application
Introduction to SenseTask (2 of 2) Overview RealMain Pot HardwareInit StdControl Pot Init PotC HPLInit Main StdControl Pot StdControl PotM SenseTaskM HPLPot Timer Leds ADC 일단 전체적인 구조입니다. 이런걸 그래프로 된걸로 보실려면 make zigbex docs로 문서 만들어서 직접 확인할 수도 있습니다. (이경우 /opt/tinyos-1.x/docs/nesdoc/zigebex 로 가시면 파일이 있습니다.) 우선 전체적인 모습입니다. Lesson 1에서 설명한 RealMain은 건너가고 Main부터 알아보겠습니다. HPLPotC HPLPot StdControl Timer[id] Leds ADC StdControl TimerC LedsC DemoSensor 타이머 초기화 및 설정 Leds 초기화 및 이용 photo 센서 초기화 및 ADC 이용

6 2. The SenseTask Application
SenseTask configuration SenseTask.nc putdata( int16_t val); Global Variable Main head(8bit) rdata[buffersize](16bit) StdControl configuration SenseTask { // this module does not provide any interface } implementation { components Main, SenseTaskM, LedsC, TimerC, DemoSensorC as Sensor; Main.StdControl  Sensor; Main.StdControl  TimerC; Main.StdControl  SenseTaskM; SenseTaskM.ADC  Sensor; SenseTaskM.Leds  LedsC; SenseTaskM.Timer  TimerC.Timer[unique("Timer")]; init(); start(); stop(); processData(); StdControl display(); SenseTaskM Timer Leds ADC fired(); getData(); dataReady( uint16_t data); init(); 먼저 전체적인 컴포넌트간의 연결이 나와 있는 configuration을 봐야죠. 그림과 같이 동작됩니다. 이해가 쉽게 되나요?(Lesson1 보다 좀 더 신경을 썼습니다.) 아기자기하게 연결이 참 잘 되어 있네요. 당장 쓰이는 눈에 보이는 함수들 위주로만 편집해 놓았습니다. 우선 SenseTask에서 사용될 전역변수인 head와 전역버퍼 rdata를 선언합니다. 그리고 StdControl을 통해 SenseTask와 TimerC와 DemoSensor를 초기화 시킵니다. 그리고 나서의 동작 순서대로 보자면, Timer에서 0.5초마다 fired() 되었다는 signal이 날아오면, SenseTaskM에서는 ADC Interface를 통해 getData()를 호출하고, DemoSensor와 하위 컴포넌트들에서 다시 조도에 대한 값을 받아 dataReady()라고 signal을 올려주면, SenseTaskM에서는 그 signal을 받아 전역버퍼인 rdata에 putdata() 함수를 통해 데이터를 넣어주고 processData()라는 태스크를 실행하여 현재 버퍼안에 있는 총 데이터를 평균화시켜 실제 값을 display() 함수를 통해 LedsC라는 컴포넌트를 통해 LED로 표시해 주게 됩니다. 이 설명으로 이미 예제 설명은 끝난것과 마찬가지입니다. start(); init(); init(); redOn(); / Off(); stop(); greenOn(); / Off(); yellowOn(); / Off(); StdControl Timer Leds ADC StdControl TimerC LedsC DemoSensor

7 2. The SenseTask Application
SenseTask module (1 of 3) SenseTaskM.nc module SenseTaskM { provides { interface StdControl; } uses { interface Timer; interface ADC; interface Leds; implementation { enum { log2size = 3, // 버퍼공간은 3bit임(bit는 2진수 표현이므로 log2) size=1 << log2size, // 즉 1의 값을 3bit만큼 쉬프트 시킨 8이 버퍼 크기 sizemask=size - 1, // 버퍼 크기만큼만 가져오기 위한 bitmask (0111) }; // 전역변수에 대한 선언 int8_t head; // 원형 버퍼를 구현하기 위해 사용될 index 변수 int16_t rdata[size]; // 원형버퍼에 대한 메모리 공간 확보 (ATmega의 ADC가 10bit로 처리하기 때문에 16bit 선언) command result_t StdControl.init() { atomic head =0; // 버퍼의 index를 나타낼 head 초기화 return call Leds.init(); // LED를 사용할 수 있게 초기화 } command result_t StdControl.start() { // 0.5초 단위로 조도 값을 가져오도록 return call Timer.start(TIMER_REPEAT, 500); // 타이머 구동 command result_t StdControl.stop() { return call Timer.stop(); // stop()이 실행되면 타이머를 정지 시킴 SenseTaskM Timer StdControl Leds ADC 앞에서 설명한 초기화 부분에 대한 예입니다. 본래의 소스가 동작 순서대로 되어 있지 않고, 구조적으로 짜여진 느낌이 들었는데, 제가 설명하기 좋게끔 순서가 바뀌어져 있습니다. 결국 내용은 같은 것임을 명시합니다. 따라서 컴포넌트 위주의 설명이 아닌, 연계되어 실제 돌아가는 과정에 맞추어 작성되었습니다. 자세한 설명은 생략하는 대신 소스와 주석을 통해서 순서대로 넘기면서 이해하시면 됩니다.

8 2. The SenseTask Application
SenseTask module (2 of 3) SenseTaskM.nc Implementation event result_t Timer.fired() { return call ADC.getData(); // fired() signal 수신시 조도값 가져오기 } // 조도 값에 대한 signal이 오게 될때 실행되는 함수 async event result_t ADC.dataReady(uint16_t data) { putdata(data); // 받아온 조도 값을 원형버퍼에 채워줌 post processData(); // processData Task를 실행시킬 수 있도록 post(Task 큐에 삽입) 시켜줌 return SUCCESS; // 원형 버퍼에다가 조도 값을 입력하는 함수 inline void putdata(int16_t val) { int16_t p; atomic { // 계산되는 동안 자원이 공유되지 않도록 설정 p = head; head = (p+1) & sizemask; // 다음 버퍼 위치(0~7)를 지정해줌, 즉 haed 값이 7보다 큰 수가 되어도 결국 다시 0이 되도록 bitmask를 이용 rdata[p] = val; // 해당 위치의 버퍼에 조도 값을 넣어줌 Global Variable Head(8bit) rdata[buffersize](16bit) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bit 이번장도 역시 주석에 내용이 담겨져 있습니다. 여기서 자칫 헷갈리거나 착각할 수 있는 부분이 rdata버퍼인데 이번 장과 다음 장을 차례대로 읽어보시면 이해가 쉬울겁니다. 저 역시 잘 모르는 부분이 많아서 되도록 많이 조사해봤는데, 그런 부분이 있으셨다면 참고하세요. (실제 Lesson 3은 단순 소스에 대한 해석만 몇 줄 나오고 왜 그런지 안 나와서 이해가 잘 안 되었는데, 하드웨어적인 부분에 제가 약해서 그렇게 느낀것 같습니다. 많이 참고하였습니다.) 이걸 보다보니 느낀점이 있는데, TinyOS 개발자는 소프트웨어 개발자가 아닌 하드웨어 개발자인것 같습니다. 왠지 성격이 뭍어나는 것 같습니다. OS가 칩을 전선으로 이어나가듯(wiring)이 구성되는 것부터가 독특합니다. 그리고 shift 시키는 부분이 간혹 나오는데 이 부분도 하드웨어적인 느낌이 드는데… 저만 그런지도 모르겠네요. 우선 이쪽 관련에는 shift 연산이 많이 사용되니 충분히 숙지하셔야 겠습니다. 보통 little-endian 표기방법을 많이 쓰는것 같고 이것 역시 그렇습니다. 게다가 보통 하나의 변수가 다용도로 bit별로 잘라져서 사용되는 경우가 많기 때문에 shift 연산은 반드시 이해하면서 넘어가셔야 겠습니다. 설명은 주석 참고하시면 됩니다.

9 2. The SenseTask Application
SenseTask module (3 of 3) SenseTaskM.nc Implementation // 빛에 대한 Digital화된 값들의 평균값을 계산하여 LED에 display 시킴 task void processData() { int16_t i, sum=0; atomic // 값을 계산하는 동안 버퍼를 공유할 수 없도록 함 for (i=0; i<size; i++) sum += (rdata[i] >> 7); // 평균값을 구하기 위해 버퍼크기만큼 합계를 구함 // 여기서 ATmega ADC는 10bit단위 처리를 하기 때문에 // 16bit 값중 상위 6bit는 단순히 0으로 채워져 사용되지 않고, // 하위 10bit중 7개bit는 사용하지 않기 때문에 버려줍니다. display(sum >> log2size); // 합의 평균을 위해 하위 3bit 버림 } // 조도 값에 따른 빛의 세기를 LED에 표시 result_t display(uint16_t value) { // 위의 결과에 따라서 3bit 데이터 값이 넘어옴 if (value &1) call Leds.yellowOn(); else call Leds.yellowOff(); if (value &2) call Leds.greenOn(); else call Leds.greenOff(); if (value &4) call Leds.redOn(); else call Leds.redOff(); return SUCCESS; Global Variable Head(8bit) rdata[buffersize](16bit) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bit 자세히 보시면 오른쪽 버퍼 공간에 10, 9, 8 자리 bit만 사용한다고 했죠. 즉 16, 15, 14, ㆍ ㆍ ㆍ , 3, 2, 1 이런식으로 16bit로 되어 있으면 버퍼 공간에서 10, 9, 8자리만 실제로 사용한다는 이야기입니다. 이것 때문에 많이 고민했습니다. 왜 그런가? 근데, 그냥 하드웨어적인 성격이 짙어서 그렇게 사용한다고 나오는군요. 실제로 상위 6비트는 그냥 0으로 채워지고, 하위 10bit만 사용하는데 먼저 그 이유가 ATmega ADC가 10bit를 쓰니깐 그렇다는 것이고 그 중에서 조도 값은 10bit중 최상위 3bit만 가지고도 충분히 빛에 대한 크기 조절을 할 수 있기 때문에 하위 7개 bit는 shift 시키는 군요. 그리고 이렇게 shift 시킨 값을 버퍼 크기만큼 버퍼안의 값(8개)을 더해서 평균치로 display() 시켜줍니다. 이때도… 또 재미나게 3bit를 shift하게 되는데 이건 8개의 조도 값을 버퍼에서 구해서 더했기 때문에 8로 나누어 줘야 평균값이 나오고, 2진 표현에서는 하위 3bit를 버리는 것이 바로 그것과 같다고 생각하시면 됩니다. 즉, 갑자기 환해 진다고 퍽 환해졌어요~하고 LED가 알려주는게 아니라 이전 상태에서 차츰차츰 변하도록 구성된다고 보면 될 것 같습니다. (이거 아니라면 제가 거짓말 하는게 되는데… 아니라면 댓글 달아주세요. 이 부분만 사용

10 3. Summary TinyOS Kernel Scheduler에 의한 스케줄링을 도식화한 그림입니다.(도식화 자료는 KETI 자료입니다.) 보시는대로 TinyOS Kernel Scheduler 안에서 실제로 작업되는 것은 Task와 Event입니다. Task Queue에는 실제 Task는 FIFO 구조로 순서대로 실행되어집니다. Task Queue에 Task가 모두 비어질때까지 계속 Task를 수행합니다. 이러다가 Interupt를 발생시킬 수 있는 요인들에 의해서 ISR이 수행되어지면서 Task는 수행중이던 일을 멈추고 바로 처리를 해주어야 하는 Event Handler가 수행되면서 Event 처리가 이루어집니다. 이때 Task와 Event가 서로 같은 전역변수에 접근을 하게 되는 문제가 발생될 수 있습니다. 이것을 동시성 문제라고 해서 nesC에서 다루고 있는데, 이 문제를 해결해 주기 위해서 해당 변수를 사용하는 동안에는 Context-switch가 발생하지 않게끔 특별한 영역으로 선언해 주고 사용할 수 있습니다.. 그 역할을 하는 것이 바로 atomic 키워드입니다. 이렇게 atomic 키워드가 사용되는 공간은 동시에 접근하는 문제에 대해서 보호되어질 수 있습니다. 그리고 nesC 컴파일러에 의해 이런 race condition이 발생할 수 있는 문제에 대해서는 미리 경고하는 문구를 출력하게 되는데, 이런 문제가 발생하지 않을 것이라는 확신이 있을경우 norace 키워드를 통해서 이런 경고에 대해 무시하고 컴파일을 진행 할 수 있습니다. 따라서 이 norace 키워드는 신중하게 사용하여야 합니다. Event 처리가 끝나면 다시 원래 Task로 돌아와서 Task의 수행을 하게 됩니다. Event 처리중에 Task가 Task queue에 넣어줘야 할 경우가 발생하는데 이럴 경우에 post를 하여 넣어주게 됩니다. 그리고 Task queue에 Task가 모두 수행되고 비어있게 되면 Sleep() 모드로 있으면서 Interupt가 발생되기를 기다리면서 자게 됩니다.

11 E N D 이로써 간단하게 TinyOS가 스케줄링 되는 방식에 대해 알아봤습니다. 간단하게 요약해 놓았습니다.
Lesson 1에서와 같은 방법으로 make를 하여 실제 모트에 올려서 실행해 보고 나서 왜 이게 이런식으로 동작하는걸까 하고 생각해본 후에 그리고 나서 소스를 보면 좀 더 이해가 쉬워지지 않을까 생각이 듭니다. 아참… 정리를 하자면, 여기 Lesson3에서 말하고자 했던 스케줄링~ 즉 원래 Task가 돌아가다가 Timer의 fierd(); signal에 대해 Hardware event handler가 동작되고, 이로 인해 ADC에 저장된 data 값을 읽어오도록 신호를 보내고, 그래서 받게되는 data에 대한 signal이 오게 되면, 이 값을 전역 버퍼인 rdata에 넣게 되고, 이 데이터들의 실제 평균값을 계산하여 LED에 display()하는 Task인 processData()를 post(Task Queue에 삽입)하여 사용할 수 있음을 배웠습니다. 물론 Event가 종료되면 원래 Task가 FIFO 방식에 의거해서 순서대로 실행되겠죠? OS에 대한 개념이 있었다면 이해가 좀 더 쉬웠을거 같습니다. 다음 시간인 Lesson4에서는 무선 통신에 대해서 접근해 보겠습니다. 원래 Lesson3랑 같은 날(다음날) 발표인데… 지금 새벽 2시인데 이제 mote의 무선 통신 관련쪽을 보고 있습니다. 최초 제작 및 발표일 (목) 수정일


Download ppt "TinyOS Tutorial Lesson 3 : Introducing Tasks for Application Data Processing (Hanback’s zigbeX & TinyOS ver.1.1.7) 3번째 시간입니다. Lesson 2는 생략하고 건너 뛰었습니다."

Similar presentations


Ads by Google