Difference between revisions of "Contiki Programming Guide"
Line 328: | Line 328: | ||
[2]The Contiki OS, http://www.sics.se/contiki/ | [2]The Contiki OS, http://www.sics.se/contiki/ | ||
[2]Contiki Programming, 2011. http://wenku.baidu.com/view/ | [2]Contiki Programming, 2011. http://wenku.baidu.com/view/ | ||
+ | |||
[[Contiki_tutorials | Back to Contiki Tutorials]] | [[Contiki_tutorials | Back to Contiki Tutorials]] |
Latest revision as of 11:05, 4 November 2016
Contents
Introduction
• Contiki is developed by a group of developers from industry and academia lead by Adam Dunkels from the Swedish Institute of Computer Science. The Contiki team currently consists of tens of developers from SICS, SAP AG, Cisco, Atmel, NewAE and TU Munich(Technical University of Munich).
• Contiki is multi-tasking operating system, especially designed for microcontrollers with small amount of memory(35KB of ROM and around 3K of RAM), which are used in networked embedded systems and wireless sensor networks.
• Contiki is written in the C programming language.
• It is freely available as open source under a BSD-style license.
• Contiki was the first OS which introduced IP communication in low-power sensor networks
You will learn
Understand the idea of threads and Events for Contiki
Code Review for Protothreads and Events
Define system processes
procinit(&etimer_process, &mac_process, &tcpip_process);
#define PROCINIT(...)
const struct process *prooinit[] = {__VA_ARGS__, NULL}
void procinit_init(void){
int i;
for(i = 0; procinit[i] != NULL; ++i){
process_start((struct process *)procinit[i], NULL);
}
}
int main(void){
/*initialize hardware*/
intit_lowlevel();
clock_init();
process_init();
procinit_init();
autostart_start(autostart_processes);
printf("*****************BOOTING CONTIKI****************\n");
while(1){
process_run();
}
return 0;
}
The process thread in this part
PROCESS_THREAD(tcpip_process, ev, data);
PROCESS_THREAD(mac_process, ev, data);
PROCESS_THREAD(etimer_process, ev, data){
struct etimer *t, *u;
PROCESS_BEGIN();
timerlist = NULL;
while(1){
PROCESS_YIELD();
}
}
Start system processes
void
process_start(struct process *p, cnst char *arg){
struct process *q;
/*first make sure that we do not try to start a process that is already running*/
for(q = process_list; q != p && q != NULL; q = q->next);
/*If we found the process on the process list, we will bail out*/
if(q == p){
return;
}
/*Put on the process list*/
p->next = process_list;
process_list = p;
p->state = PROCESS_STATE_RUNNING;
PT_INIT(&p->pt);
printf("process: starting '%s'\n", p->name);
/*Post a synchronous initialization event to the process*/
process_post_synch(p, PROCESS_EVENT_INIT, (process_data_t)arg);
}
/*----------------------------------------------------------------------------------*/
/* PROCESS_POST_SYNCH will execute code between PROCESS_BEGIN(); and PROCESS_YIELD();*/
PROCESS_THREAD(etimer_process, ev, data){
struct etimer *t, *u;
PROCESS_BEGIN();
timerlist = NULL;
while(1){
PROCESS_YIELD();
if(ev == PROCESS_EVENT_EXITED){
struct process *p = data;
while(timerlist != NULL && timerlist -> p == p){
timerlist = timerlist -> next;
}
if(timerlist != NULL){
t = timerlist;
while(t -> next != NULL){
t = timerlist -> next;
}
}
}
}
}
Start user processes
/*PROCINIT(&etimer_process, &mac_process, &tcpip_process);*/
/*follow by define system processes main function*/
AUTOSTART_PROCESSES(&temp_measure_cmd_sender, &serial_cmd_process){
int i;
for(i = 0; processes[i] != NULL; ++i){
process_start(processes[i], NULL);
PRINTF("autostart_start: starting process '%s'\n", processes[i]->name);
}
}
The process thread in this part
PROCESS_THREAD(temp_measure_cmd_sender, ev, data);
PROCESS_THREAD(serial_cmd_process, ev, data){
PROCESS_BEGIN();
while(1){
PROCESS_WAIT_EVEN();
if(ev = SERIAL_CMD){
process_post(&temp_measure_cmd_sender, TEMP_CMD, 0);
}
}
PROCESS_END();
}
Main scheduler loop
/*loop until poll_requested == 1 || nevents > 0*/
int process_run(void){
/*process poll events*/
if(pull_requested){
do_poll();
}
/*Process one event from the queue*/
do_event();
return nevents + poll_requested;
}
static void do_event(void){
/*
if there are any events in the queue, take the first one and walk through the
list of processes to see if the event should be delivered to any of them. If
so, we call the event handler function for the process. We only process one
event at a time and call the poll handlers inbetween.
*/
if(nevents >0){
...
call_process(receiver, ev, data);
}
}
Method: POLL
ISR(AVR_OUTPUT_COMPARE_INT){
++count;
if(etimer_pending()){
etimer_request_poll();
}
}
void etimer_request_poll(void){
process_poll(&etimer_process);
}
void process_poll(struct process *p){
if(p != NULL){
if(p->state == PROCESS_STATE_RUNNING ||
p->state == PROCESS_STATE_CALLED){
p->needspoll = 1;
poll_requested = 1;
}
}
}
int process_run(void){
/*process poll events*/
if(poll_requested){
do_poll();
}
/*Process one event from the queue*/
do_event();
return nevents + poll_requested;
}
static void do_poll(void){
struct process *p;
poll_requested = 0;
/*Call the processes that needs to be polled*/
for(p = process_list; p != NULL; p = p->next){
if(p->needspoll){
p->state = PROCESS_STATE_RUNNING;
p->needspoll = 0;
call_process(p, PROCESS_EVENT_POLL, NULL);
}
}
}
Method: POST
PROCESS_THREAD(etimer_process, ev, data){
...
for(t = timerlist; t != NULL; t = t->next){
if(timer_expired(&t->timer)){
if(process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK)
...
}
}
}
int process_post(struct process *p, process_event_t ev, process_data_t data){
...
anum = (fevent + nevents) % PROCESS_CONF_NUMEVENTS;
events[snum].ev = ev;
events[snum].data = data;
events[snum].p = p;
++ nevents;
...
}
int process_run(void){
/*Process poll events*/
if(poll_requested){
do_poll();
}
/*Process one event from the queue*/
do_event();
return nevents + poll_requested;
}
static void do_event(void){
...
/*
if there are any events in the queue, take the first one and walk through the
list of processes to see if the event should be delivered to any of them. If
so, we call the event handler function for the process. We only process one
event at a time and call the poll handlers inbetween.
*/
if(nevents > 0){
...
call_process(reveiver, ev, data);
}
}
It is clear to see according to the figure 1
Code Style: Names in Contiki In Contiki, all names are prefixed with the module name (example: process_start(), rime_init(), clock_time(), memb_alloc(), list_add()...) Prefix makes it possible to mentally locate all function calls All code must abide by this: There are a few exceptions in the current code, but those are to be removed in the future
Code Style: What to avoid
-noCamelCase() -No_Capital_letters() -#define EXCEPT_FOR_MACROS()
Code Sytle: File names Contiki uses hyphenated-file-names rather than underscore_in_file_names
The Motivation – Safe Tiny OS
The Safe Tiny OS tool chain is shown in Figure 2. The most immediate benefit of Safe TinyOS was that during its development 4 severe bugs in TinyOS could be found out and were corrected.
Building A Safe Application
To build an application Safe, we have to provide the parameter ‘safe’ in the command line of ‘make’.
cd $TOSROOT/apps/Blink
make micaz safe
Making Safety Optional
Implementation Overview
The source code of Contiki consists of nearly 1200 files, including both the OS core and the applications. The OS core itself consists of around 300 files. The work comprises of annotating each pointer access in all these files and recompiling with Deputy. Once the core is free of Safety errors, we can go for the Safety of applications. After this step, we set Deputy as the default compiler of Contiki. Programmers will have to adapt to the Annotations of Deputy, which are very simple and easy to learn.
The flow graph of Safe Contiki development is given in Figure 3
Modifying the Makefiles
The implementation was started by editing the makefiles associated with the ‘native’ processor. The Makefile.native mainly contains definitions for the C compiler used for the native processor. The file is located in contiki-2.x/cpu/native/ directory. The current contents of the 6th and 7th lines are
- CC = gcc - LD = gcc
which indicates that for the native CPU, the compiler and the linker are the same, the gcc. We replaced this with
- CC = deputy - LD = deputy
A New Argument To Make
As an illustration of how the SAFETY argument is used, the following commands will create the Safe version of the ‘hello-world’ program for the ‘native’ platform.
cd /home/user/contiki-2.x/examples/hello-world/
make TARGET=native SAFETY=yes
Safety is an optional feature; programs are made Unsafe, by default. The following command makes the program without Safety features, for the ‘native’ platform.
make TARGET=native
Conditional Making And The gcc -D Flag
In order to make ‘making’ conditional, depending on the value of the variable SAFETY, we further modified the Makefile.native file in the contiki-2.x/cpu/native/ directory. Makefile.native is shown below
ifeq ($(SAFETY),yes)
CC = deputy
LD = deputy
else
CC = gcc
LD = gcc
CFLAGS += -DNTS='' -DSAFE='' \
-DCOUNT\(x\)='' \
-DTC\(x\)=x -DBOUND\(x,y\)='' \
-DTRUSTED='' -DNTDROP\(x\)=x \
-DNTEXPAND\(x\)=x
endif
Reference
[1]The Safe TinyOS, 2008. http://docs.tinyos.net/index.php/Safe_TinyOS [2]The Contiki OS, http://www.sics.se/contiki/ [2]Contiki Programming, 2011. http://wenku.baidu.com/view/