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文件,输入如下代码:
#include "contiki.h"
#include <stdio.h>
PROCESS(HW, "HWP");
AUTOSTART_PROCESSES(&HW);
PROCESS_THREAD(HW, ev, data)
{
PROCESS_BEGIN();
printf("Hello world!\n"); //此处放自己的代码
PROCESS_END();
}
接下来回到user根目录,然后进入contiki目录,敲pwd命令,记下当前路径,等下要用。重新进入helloworld目录,新建一个Makefile文件,输入如下代码:
CONTIKI_PROJECT = hello-world
all: $(CONTIKI_PROJECT)
/* Contiki源文件根目录,使用前面记下的路径 */
CONTIKI = /home/user/contiki
include $(CONTIKI)/Makefile.include
准备工作完成,敲入命令make,编译、生成可执行文件。此处相当坑爹,代码写错几处,编译不过,要删除生成的文件再编译,折磨死我了。先将就着,以后要换个工具写代码。生成完后,如图所示,生成很多文件。


先看看PROCESS源码,就在前一篇process结构体上面:
#if PROCESS_CONF_NO_PROCESS_NAMES
#define PROCESS(name, strname) \
PROCESS_THREAD(name, ev, data); \
struct process name = { NULL, \
process_thread_##name }
#else
#define PROCESS(name, strname) \
PROCESS_THREAD(name, ev, data); \
struct process name = { NULL, strname, \
process_thread_##name }
/**
* Define the body of a process.
*定义process主体
* This macro is used to define the body (protothread) of a
* process. The process is called whenever an event occurs in the
* system, A process always start with the PROCESS_BEGIN() macro and
* end with the PROCESS_END() macro.
*此宏用于定义一个process的主体,当某事件发生时,process被调用。process总是从PROCESS_BEGIN()宏开始,并结束于
*PROCESS_END() 宏
*/
#define PROCESS_THREAD(name, ev, data) \
static PT_THREAD(process_thread_##name(struct pt *process_pt, \
process_event_t ev, \
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 = ; if (PT_YIELD_FLAG) {;} LC_RESUME((process_pt)->lc)
整下容,变为
{
char PT_YIELD_FLAG = ;
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 = ;
if (PT_YIELD_FLAG)
{;}
switch((process_pt)->lc)
{
case :;
#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 = ; \
PT_INIT(process_pt); return PT_ENDED; }
整下容变成:
LC_END((process_pt)->lc);
PT_YIELD_FLAG = ; \
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 = ; \
(process_pt)->lc = ;
return ;
}
凌乱了,整理下思绪,休息一下把Helloworld.c全部展开看看
脑袋有点不够用了,慢慢展开吧,看看庐山真面目:
static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data)
struct process HW= { NULL, "HWP", process_thread_HW }
struct process * const autostart_processes[] = {&HW, NULL}
static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data)
{
char PT_YIELD_FLAG = ;
if (PT_YIELD_FLAG)
{;}
switch((process_pt)->lc)
{
case :
printf("Hello world!\n");
};
PT_YIELD_FLAG = ; \
(process_pt)->lc = ;
return ;
}
下面给代码加上我自己的理解
//声明一个函数原型,用于process所执行的方法
static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data)
//声明代表进程的结构体,并把之前的函数原型做为其参数代入
struct process HW= { NULL, "HWP", process_thread_HW }
//声明一个process的指针数组,用于存放多个process(此程序只有一个),最后放入NULL只是为了方便查找到数组结尾。这
//里没有用链表,说明不需要删除process(个人猜测)
struct process * const autostart_processes[] = {&HW, NULL}
//函数主体,对应上面的函数原型
static char process_thread_HW(struct pt *process_pt, process_event_t ev, process_data_t data)
{
//由于这个程序没用到事件,此参数无用,所以下面三句都是废话
char PT_YIELD_FLAG = ;
if (PT_YIELD_FLAG)
{;}
//process_pt为函数第一个参数,并无赋值,此时值为0
switch((process_pt)->lc)
{
case :
printf("Hello world!\n"); //由于process_pt的值为0,所以执行此句
};
PT_YIELD_FLAG = ; //此处无用
(process_pt)->lc = ; //此处无用
return ; //返回PT_ENDED,从字面意义上理解protothread_ended,指示此process已经game over。
}
有点凌乱,但也只能如此理解。这个程序只打印一句话,没用到事件,所以产生了一些无用语句。只能等下次代入事件,看看会不会有什么新的理解。
Contiki学习笔记 第一个程序:Hello World的更多相关文章
- libevent学习笔记 —— 第一个程序:计时器
用libevent写个定时器其实步骤不多: 1.初始化libevent 2.设置事件 3.添加事件 4.进入循环 由于定时事件触发之后,默认自动删除,所以如果要一直计时,则要在回调函数中重新添加定时事 ...
- 简单的玩玩etimer <contiki学习笔记之九 补充>
这幅图片是对前面 <<contiki学习笔记之九>> 的一个补充说明. 简单的玩玩etimer <contiki学习笔记之九> 或许,自己正在掀开contiki ...
- 简单的玩玩etimer <contiki学习笔记之九>
好吧,我承认etimer有点小复杂,主要是它似乎和contiki的process搅在一起,到处都在call_process.那就先搜搜contiki下的etimer的example看看,然后再试着写一 ...
- ActionBarSherlock学习笔记 第一篇——部署
ActionBarSherlock学习笔记 第一篇--部署 ActionBarSherlock是JakeWharton编写的一个开源框架,使用这个框架,可以实现在所有的Android ...
- oracle学习笔记第一天
oracle学习笔记第一天 --oracle学习的第一天 --一.几个基础的关键字 1.select select (挑选) 挑选出显示的--列--(可以多列,用“,”隔开,*表示所有列),为一条 ...
- QT学习之第一个程序
QT学习之第一个程序 目录 手动创建主窗口 居中显示 添加窗口图标 显示提示文本 Message Box的应用 手动连接信号与槽 手动创建主窗口 窗口类型 QMainWindow: 可以包含菜单栏.工 ...
- AndroidStudio学习笔记-第一个安卓程序
要带一个本科生做一部分跟安卓有点关系的项目,于是趁着机会学习一下编写安卓程序. 第一篇材料来自谷歌官方,传送门:https://developer.android.com/training/basic ...
- Django学习笔记---第一天
Django学习笔记 1.Django的安装 //如果不指定版本号,默认安装最新版 pip3 install django==1.11.8 关于Django的版本和python的版本依赖关系,请看下图 ...
- 微信小程序学习笔记一 小程序介绍 & 前置知识
微信小程序学习笔记一 1. 什么是小程序? 2017年度百度百科十大热词之一 微信小程序, 简称小程序, 英文名 Mini Program, 是一种不需要下载安装即可使用的应用 ( 张小龙对其的定义是 ...
- ASP.NET Core 学习笔记 第一篇 ASP.NET Core初探
前言 因为工作原因博客断断续续更新,其实在很早以前就有想法做一套关于ASP.NET CORE整体学习度路线,整体来说国内的环境的.NET生态环境还是相对比较严峻的,但是干一行爱一行,还是希望更多人加入 ...
随机推荐
- Alignment trap 解决方法 【转 结合上一篇
前几天交叉编译crtmpserver到arm9下.编译通过,但是运行的时候,总是提示Alignment trap,但是并不影响程序的运行.这依然很令人不爽,因为不知道是什么原因引起的,这就像一颗定时炸 ...
- SEO优化
SEO是由英文Search Engine Optimization缩写而来, 中文意译为“搜索引擎优化”.SEO是指从自然搜索结果获得网站流量的技术和过程,是在了解搜索引擎自然排名机制的基础上, 对网 ...
- 如何快速在当前目录打开cmd命令提示符
对于稍微熟悉电脑一些的朋友来说.cmd绝对是个很方便的东西.但是每次使用cmd都要cd半天才能到当前目录.怎么快速打开当前目录呢? 当前目录按住shift再右键.然后会看到右键菜单里有一个" ...
- 用jQuery做一个三级菜单,鼠标移动到二级菜单的选项上,然后再迅速离开后,当鼠标再移动到该一级菜单或其他二级菜单选项,三级菜单也会显示。
用jQuery做一个三级菜单,鼠标移动到二级菜单的选项上,然后再迅速离开后,当鼠标再移动到该一级菜单或其他二级菜单选项,三级菜单也会显示. 原因:在为一个元素绑定hover事件之后,用户把光标移入元素 ...
- Ngui中Sprite,SlicedSprite,Tiled Sprite,FilledSprite的区别
Sprite:标准Sprite控件,自适应图片大小. Sliced Sprite:一个含有9个切片的Sprite,创建固定边框的控件最佳选择,固定大小,不会随图片大小而改变,可做人物头像等. Tile ...
- docker基本操作
centos 7 安装docker 目前,CentOS 仅发行版本中的内核支持 Docker. Docker 运行在 CentOS 7 上,要求系统为64位.系统内核版本为 3.10 以上. Dock ...
- grep 命令操作
linux grep命令 1.作用Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来.grep全称是Global Regular Expressi ...
- HTTP 响应头信息
HTTP 响应头信息 HTTP请求头提供了关于请求,响应或者其他的发送实体的信息. 在本章节中我们将具体来介绍HTTP响应头信息.
- SqlServer性能优化 提高并发性能二(九)
补充上一篇修改用非聚集索引: update Employee set age=age+1 from Employee with(index=nc_Employee_Age) where age< ...
- Maven项目pom.xml文件详解
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...