一 main函数
-
#include "app_uart_fifo.h"
-
#include <stdint.h>
-
#include <stdio.h>
-
#include <sys/process.h>
-
#include <sys/procinit.h>
-
#include <etimer.h>
-
#include <sys/autostart.h>
-
#include <clock.h>
-
#include "bsp_imp.h"
-
-
static void CtimerTest_Callback(void *p)
-
{
-
printf("Ctimer Callback running\r\n");
-
}
-
-
-
-
static process_event_t event_data_ready;
-
-
PROCESS(print_hello_process, "Hello");
-
PROCESS(print_world_process, "world");
-
-
AUTOSTART_PROCESSES(&print_hello_process, &print_world_process);
-
-
-
PROCESS_THREAD(print_hello_process, ev, data)
-
{
-
PROCESS_BEGIN();
-
static struct etimer timer;
-
-
etimer_set(&timer, CLOCK_CONF_SECOND*5);
-
printf("***print hello process start***\r\n");
-
-
event_data_ready = process_alloc_event();
-
while (1)
-
{
-
-
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);
-
-
printf("Hello\r\n");
-
-
process_post(&print_world_process, event_data_ready, NULL);
-
-
etimer_reset(&timer);
-
}
-
-
PROCESS_END();
-
}
-
-
PROCESS_THREAD(print_world_process, ev, data)
-
{
-
PROCESS_BEGIN();
-
-
printf("***print world process start***\r\n");
-
-
-
while (1)
-
{
-
PROCESS_WAIT_EVENT_UNTIL(ev == event_data_ready);
-
printf("world\r\n");
-
}
-
-
PROCESS_END();
-
}
-
-
-
unsigned int idle_count = 0;
-
-
int main()
-
{
-
Bsp_Init();
-
printf("Initialising\r\n");
-
-
clock_init();
-
-
ctimer_init();
-
-
process_init();
-
process_start(&etimer_process, NULL);
-
autostart_start(autostart_processes);
-
printf("Processes running\r\n");
-
-
while(1) {
-
do {
-
-
} while(process_run() > 0);
-
idle_count++;
-
}
-
}
main函数创建两个进程,print_hello_process进程定时5S打印hello,同时发送事件给print_world_process打印world,并重启etimer定时器。
二 数据结构
2.1 进程数据结构
进程的数据结构为链表,代码中所有的进程使用进程链表管理。
-
struct process {
-
struct process *next;
-
#if PROCESS_CONF_NO_PROCESS_NAMES
-
#define PROCESS_NAME_STRING(process) ""
-
#else
-
const char *name;
-
#define PROCESS_NAME_STRING(process) (process)->name
-
#endif
-
PT_THREAD((* thread)(struct pt *, process_event_t, process_data_t));
-
struct pt pt;
-
unsigned char state, needspoll;
-
};
next 指向下一进程控制块,name为进程名,thred为进程的实体(函数指针),pt保存该进程执行的行数,实现进程切换,state为进程的运行状态,needpoll为进程的优先级。
进程创建完成后,需要其运行需要调用process_start,将进程控制块,挂载到进程链表中。注意每次挂载,从头开始挂载。
2.2 etimer数据结构
etimer也是用时间列表管理,结构代码如下。
-
struct etimer {
-
struct timer timer;
-
struct etimer *next;
-
struct process *p;
-
};
-
struct timer {
-
clock_time_t start;
-
clock_time_t interval;
-
}
由timer确定定时时间,next指向下一个etimer节点,p为绑定与etimer相关联的任务,在contiki中谁调用etimer_set函数,该进程绑定到p.
用户调用etimer_set -> add_timer将etimer挂载到时间管理链表,并将etimer与该进程关联起来,该链表的插入也是从头开始插入。
main函数启动一个etimer定时器,初始化后链表如下,
2.3 evt 事件结构
-
struct event_data {
-
process_event_t ev;
-
process_data_t data;
-
struct process *p;
-
};
ev为产生的事件,data产生事件所带的数据, p绑定获取该事件的进程。时间事件也是一个事件。事件管理使用一个环形FIFO队类管理,nevents表示队列里事件的总数(放入事件++,取出事件--),fevent表示取事件时下一个事件的下标(取事件++)。process_post_synch()向事件队列里发送一个事件,do_event()从事件队列中取出事件并处理绑定的进程。
2.3.1 广播事件
在事件中,会绑定获取该事件进程的指针,如果未绑定进程指针,则该事件是广播事件,所谓广播事件是所有进程均会获取该事件,所有进程均执行该事件。
-
if(receiver == PROCESS_BROADCAST) {
-
for(p = process_list; p != NULL; p = p->next) {
-
-
/* If we have been requested to poll a process, we do this in
-
between processing the broadcast event. */
-
if(poll_requested) {
-
do_poll();
-
}
-
call_process(p, ev, data);
-
}
-
}
2.4 进程,时间事件,事件关系
在contiki中etimer也会触发一个时间事件,在本工程中,etimer的数据结构如下,绑定该等待定时器事假的进程,这个etimer绑定print_hello_process进程。
当定时时间到,etimer进程调用
process_post_synch将定时事件和绑定进程发送到事件队列,事件队列结构如下:
主函数通过
do_event获取事件队列的事件,通过函数指针执行进程。
三 进程执行描述
process_start -> process_post_synch -> call_process(该函数中调用函数指针p->thread,执行具体的线程)。下面以etimer中的etimer_process线程分析。
-
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) {
-
if(t->next->p == p) {
-
t->next = t->next->next;
-
} else
-
t = t->next;
-
}
-
}
-
continue;
-
} else if(ev != PROCESS_EVENT_POLL) {
-
continue;
-
}
-
-
again:
-
-
u = NULL;
-
-
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) {
-
-
/* Reset the process ID of the event timer, to signal that the
-
etimer has expired. This is later checked in the
-
etimer_expired() function. */
-
t->p = PROCESS_NONE;
-
if(u != NULL) {
-
u->next = t->next;
-
} else {
-
timerlist = t->next;
-
}
-
t->next = NULL;
-
update_time();
-
goto again;
-
} else {
-
etimer_request_poll();
-
}
-
}
-
u = t;
-
}
-
-
}
-
-
PROCESS_END();
-
}
每次进入线程均要从PROCESS_BEGIN开始执行,该函数实现跳转功能,可以跳转到上次被打断点执行。该线程第一次运行到PROCESS_YIELD处会被挂起,该宏展开如下:
-
#define PT_YIELD(pt) \
-
do { \
-
PT_YIELD_FLAG = 0; \
-
LC_SET((pt)->lc); \
-
if(PT_YIELD_FLAG == 0) { \
-
return PT_YIELDED; \
-
} \
-
} while(0)
第一次运行
PROCESS_YIELD时,根据上面代码分析会return PT_YIELDED,此时该线程运行结束,函数被挂起,并保存此次运行的位置,本次保存到
LC_SET((pt)->lc);。下次再运行etimer_process,运行原因为:SysTick_Handler -> etimer_request_poll -> process_run() -> do_poll() -> call_process() -> p->thread(函数指针,指向etimer_process)。本次运行依旧从PROCESS_BEGIN开始运行,并跳转到第一次运行保存的位置。在
if(PT_YIELD_FLAG == 0)判断为假,函数继续执行(在
PROCESS_BEGIN中将PT_YIELD_FLAG 置1)。在
etimer_process,先判断事件类型,如果是退出事件类型,则将与该事件有关的定时器移除;在again后表示是定时时间到,将该定时器PROCESS_EVENT_TIMER放入事件队列,放入的信息包括事件类型,进程指针,在主函数查询事件队列,取出进程指针执行。例如当定时时间到执行print_hello_process进程。print_hello_process进程执行后,向事假队列post事件,该事件post给print_world_process进程。主函数 process_run() -> do_poll() -> call_process() -> p->thread执行print_world_process进程。
四 etimer注意事项
4.1 事件
contiki事件包含以下事件,包含定义事件的最大数目等,该部分可参看
http://blog.chinaunix.net/uid-9112803-id-2976348.html
-
#ifndef PROCESS_CONF_NUMEVENTS
-
#define PROCESS_CONF_NUMEVENTS 32
-
#endif /* PROCESS_CONF_NUMEVENTS */
-
-
#define PROCESS_EVENT_NONE 0x80
-
#define PROCESS_EVENT_INIT 0x81
-
#define PROCESS_EVENT_POLL 0x82
-
#define PROCESS_EVENT_EXIT 0x83
-
#define PROCESS_EVENT_SERVICE_REMOVED 0x84
-
#define PROCESS_EVENT_CONTINUE 0x85
-
#define PROCESS_EVENT_MSG 0x86
-
#define PROCESS_EVENT_EXITED 0x87
-
#define PROCESS_EVENT_TIMER 0x88
-
#define PROCESS_EVENT_COM 0x89
-
#define PROCESS_EVENT_MAX 0x8a
etimer 哪一个进程调用etimer_set在etimer管理中就绑定该进程,如果在main函数中调用,则绑定为NULL,可时间广播方式。
阅读(402) | 评论(0) | 转发(0) |