想來想去,仍是得先寫一個程序,找下感受,加強一下自信心,那就國際慣例Hello World吧。
先到這個網址下一個Instant Contiki 2.7。之因此沒用3.0的,是由於有些問題,我源碼是下的3.0的。
http://sourceforge.net/projects/contiki/files/Instant%20Contiki/
下完後裝個VMWear,載入Instant Contiki 2.7虛擬機,就能夠在Ubuntu上用contiki了。
打開終端,默認是用user用戶名登陸,密碼也是user。ls一下,看見有contiki目錄就對了。接下來在user根目錄下建一個demo目錄用來存放本身的工程,而後在demo目錄下建一個helloworld目錄,而後進去。
建一個hello-world.c文件,輸入以下代碼:數組
1 #include "contiki.h" 2 #include <stdio.h> 3 PROCESS(HW, "HWP"); 4 AUTOSTART_PROCESSES(&HW); 5 PROCESS_THREAD(HW, ev, data) 6 { 7 PROCESS_BEGIN(); 8 printf("Hello world!\n"); //此處放本身的代碼 9 PROCESS_END(); 10 }
接下來回到user根目錄,而後進入contiki目錄,敲pwd命令,記下當前路徑,等下要用。從新進入helloworld目錄,新建一個Makefile文件,輸入以下代碼:app
CONTIKI_PROJECT = hello-world all: $(CONTIKI_PROJECT) /* Contiki源文件根目錄,使用前面記下的路徑 */ CONTIKI = /home/user/contiki include $(CONTIKI)/Makefile.include
準備工做完成,敲入命令make,編譯、生成可執行文件。此處至關坑爹,代碼寫錯幾處,編譯不過,要刪除生成的文件再編譯,折磨死我了。先將就着,之後要換個工具寫代碼。生成完後,如圖所示,生成不少文件。ide
1 #if PROCESS_CONF_NO_PROCESS_NAMES 2 #define PROCESS(name, strname) \ 3 PROCESS_THREAD(name, ev, data); \ 4 struct process name = { NULL, \ 5 process_thread_##name } 6 #else 7 #define PROCESS(name, strname) \ 8 PROCESS_THREAD(name, ev, data); \ 9 struct process name = { NULL, strname, \ 10 process_thread_##name }
1 /** 2 * Define the body of a process. 3 *定義process主體 4 * This macro is used to define the body (protothread) of a 5 * process. The process is called whenever an event occurs in the 6 * system, A process always start with the PROCESS_BEGIN() macro and 7 * end with the PROCESS_END() macro. 8 *此宏用於定義一個process的主體,當某事件發生時,process被調用。process老是從PROCESS_BEGIN()宏開始,並結束於 9 *PROCESS_END() 宏 10 */ 11 #define PROCESS_THREAD(name, ev, data) \ 12 static PT_THREAD(process_thread_##name(struct pt *process_pt, \ 13 process_event_t ev, \ 14 process_data_t data))
愈來愈複雜了,繼續代入吧 PROCESS_THREAD(HW, ev, data); 變爲:函數
static PT_THREAD(process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data))
還沒完,還得跟蹤PT_THREAD,在Pt.h頭文件中,先看看定義:工具
#define PT_THREAD(name_args) char name_args
這個……這個上一篇日誌中剛接觸過,用於把一個東西變成函數指針,先代入看看:this
static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data)
這回沒變成函數指針,而是一個方法,看來PT_THREAD這個宏定義專門用來生成函數,它有註釋,看看怎麼說:spa
struct process { struct process *next; const char *name; char (* thread)(struct pt *, process_event_t, process_data_t) struct pt pt; unsigned char state; unsigned char needspoll; };
AUTOSTART_PROCESSES(&HW);
先找到AUTOSTART_PROCESSES定義,在Autostart.h頭文件中.net
#define AUTOSTART_PROCESSES(...) \
struct process * const autostart_processes[] = {__VA_ARGS__, NULL}
struct process * const autostart_processes[] = {&HW, NULL}
static char process_thread_HW(struct pt *process_pt, \ process_event_t ev, \ process_data_t data)
/** * Define the beginning of a process. *定義process的開始部分 * This macro defines the beginning of a process, and must always * appear in a PROCESS_THREAD() definition. The PROCESS_END() macro * must come at the end of the process. *此宏用於定義一個process的開始部分,並只能在PROCESS_THREAD() 函數體中定義。在process結尾處必須緊接着定義 *PROCESS_END() 宏。 */
#define PROCESS_BEGIN() PT_BEGIN(process_pt)
繼續代入吧,有啥可說的呢,語句變爲:線程
PT_BEGIN(process_pt);
接下來找PT_BEGIN宏,Pt.h頭文件中,原型以下:指針
/** * Declare the start of a protothread inside the C function * implementing the protothread. *用於在線程原型函數主體中聲明一個線程的開始部分 * This macro is used to declare the starting point of a * protothread. It should be placed at the start of the function in * which the protothread runs. All C statements above the PT_BEGIN() * invokation will be executed each time the protothread is scheduled. *此宏放在線程運行的開始部分。線程將會根據執行在PT_BEGIN()中聲明的調用。 * \param pt A pointer to the protothread control structure. * \hideinitializer */ #define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) {;} LC_RESUME((pt)->lc);
代入,語句變爲:
{ char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) {;} LC_RESUME((process_pt)->lc)
整下容,變爲
{ char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) {;} LC_RESUME((process_pt)->lc);
繼續追蹤LC_RESUME宏:
#define LC_RESUME(s) switch(s) { case 0:
代入上式,最終PROCESS_BEGIN();變成:
{ char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) {;} switch((process_pt)->lc) { case 0:;
#define PROCESS_END() PT_END(process_pt)
再找PT_END
#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \ PT_INIT(pt); return PT_ENDED; }
最終,語句變爲:
LC_END((process_pt)->lc); PT_YIELD_FLAG = 0; \ PT_INIT(process_pt); return PT_ENDED; } 整下容變成: LC_END((process_pt)->lc); PT_YIELD_FLAG = 0; \ PT_INIT(process_pt); return PT_ENDED; }
LC_END定義爲:
#define LC_END(s) }
PT_INIT定義爲:
#define PT_INIT(pt) LC_INIT((pt)->lc)
LC_INIT定義爲:
#define LC_INIT(s) s = 0;
PT_ENDED定義爲:
#define PT_ENDED 3
一層層代入,最終PROCESS_END()變成:
} PT_YIELD_FLAG = 0; \ (process_pt)->lc = 0; return 3; }
凌亂了,整理下思緒,休息一下把Helloworld.c所有展開看看
腦殼有點不夠用了,慢慢展開吧,看看廬山真面目:
1 static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data) 2 struct process HW= { NULL, "HWP", process_thread_HW } 3 struct process * const autostart_processes[] = {&HW, NULL} 4 static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data) 5 { 6 char PT_YIELD_FLAG = 1; 7 if (PT_YIELD_FLAG) 8 {;} 9 switch((process_pt)->lc) 10 { 11 case 0: 12 printf("Hello world!\n"); 13 }; 14 PT_YIELD_FLAG = 0; \ 15 (process_pt)->lc = 0; 16 return 3; 17 }
下面給代碼加上我本身的理解
1 //聲明一個函數原型,用於process所執行的方法 2 static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data) 3 //聲明表明進程的結構體,並把以前的函數原型作爲其參數代入 4 struct process HW= { NULL, "HWP", process_thread_HW } 5 //聲明一個process的指針數組,用於存放多個process(此程序只有一個),最後放入NULL只是爲了方便查找到數組結尾。這 6 //裏沒有用鏈表,說明不須要刪除process(我的猜想) 7 struct process * const autostart_processes[] = {&HW, NULL} 8 //函數主體,對應上面的函數原型 9 static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data) 10 { 11 //因爲這個程序沒用到事件,此參數無用,因此下面三句都是廢話 12 char PT_YIELD_FLAG = 1; 13 if (PT_YIELD_FLAG) 14 {;} 15 //process_pt爲函數第一個參數,並沒有賦值,此時值爲0 16 switch((process_pt)->lc) 17 { 18 case 0: 19 printf("Hello world!\n"); //因爲process_pt的值爲0,因此執行此句 20 }; 21 PT_YIELD_FLAG = 0; //此處無用 22 (process_pt)->lc = 0; //此處無用 23 return 3; //返回PT_ENDED,從字面意義上理解protothread_ended,指示此process已經game over。 24 }
有點凌亂,但也只能如此理解。這個程序只打印一句話,沒用到事件,因此產生了一些無用語句。只能等下次代入事件,看看會不會有什麼新的理解。