Contiki学习笔记 第一个程序:Hello World
想来想去,还是得先写一个程序,找下感觉,增强一下自信心,那就国际惯例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文件,输入如下代码:
CONTIKI_PROJECT = hello-world all: $(CONTIKI_PROJECT) /* Contiki源文件根目录,使用前面记下的路径 */ CONTIKI = /home/user/contiki include $(CONTIKI)/Makefile.include
准备工作完成,敲入命令make,编译、生成可执行文件。此处相当坑爹,代码写错几处,编译不过,要删除生成的文件再编译,折磨死我了。先将就着,以后要换个工具写代码。生成完后,如图所示,生成很多文件。
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
这个……这个上一篇日志中刚接触过,用于把一个东西变成函数指针,先代入看看:
static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data)
这回没变成函数指针,而是一个方法,看来PT_THREAD这个宏定义专门用来生成函数,它有注释,看看怎么说:
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头文件中
#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 }
有点凌乱,但也只能如此理解。这个程序只打印一句话,没用到事件,所以产生了一些无用语句。只能等下次代入事件,看看会不会有什么新的理解。