原文:http://blog.csdn.net/goodjobwubai/article/details/52905093?locationNum=11&fps=1
contiki 之 button sensor
contiki是以一款应用于单片机上的操作系统,主要服务于WSN(无线传感器网络)。它的强大之处在于对于网络方面的支持,包括6lowpan, IEEE802.15.4,rime 等网络协议。不仅如此,低内存占有,protothread的设计也为人们津津乐道。它的作者是大名鼎鼎的Adam,同时也是uip和lwip的作者。还有就是完全开源,并辅以许多高质量论文,这对于有兴趣研究的同学,可算是关键问题了。
————————————
Sensor 简介
sensor 顾名思义,就是生活中无处不在的传感器。可以用于检测温度、湿度、电流、压力等等参数。由于contiki主要服务于WSN(无线传感器网络),其中关于sensor方面的设计自然是重中之重。那么这些sensor检测到的数据是怎么传输给单片机处理的呢?下面将会以德州仪器的cc1310作为例子,介绍button sensor的工作原理。
数据结构
part1
路径&文件名 (contiki-3.0\core\lib\sensor.h)
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
这个结构体用于定义每一个sensor属性。
- type:一个char类型的指针,一般会用一个字符串定义这个sensor的类型。
- value:一个输入参数为( int ) 类型,返回值也为int类型的函数指针。这个函数主要用于取得该sensor的值。
- configure:一个输入参数为( int ,int ) 类型,返回值也为int类型的函数指针。主要用于该sensor的初始化配置。
- status:主要用于查看该sensor的状态。
part2
- 1
- 2
- 1
- 2
很明显,该宏定义用于创建一个名字为name的具体的sensors_sensor结构体。
part3
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
刚才的SENSORS_SENSOR是为了创建一个具体的sensors_sensor,之所以每个sensor都用一个结构体来定义就是方便管理和使用。最终是需要把他们统一起来的。这里,SENSORS(…) 的作用就是把它们都统一到一个数组里面方便管理。但,这并不是单纯地把结构体都放在数组里面,而是把代表每个sensor的结构体指针放在数组里面。这样既不影响每个sensor的单独定义,又能够把它们放在一起集中管理。
宏定义的SENSORS_NUM能取到sensor的数量。利用它,能够定义一个相同数量的sensor_flags的数组,用于作为每个sensor的标记。
Sensor模版设计
sensor的整体设计大致就是每个sensor触发后,就会交给sensor_process线程(PROCESS_THREAD)进行处理。下面先介绍sensor_process线程(PROCESS_THREAD)用到的几个函数。
sensors.c 内的几个函数
路径&文件名 (contiki-3.0\core\lib\sensor.c)
get_sensor_index
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
它的输入参数为类型为sensors_sensor的结构体。也就是每个sensor在使用之初都会利用sensors_sensor结构体来定义自己的属性,详见 数据结构 part1。该函数遍历数组const struct sensors_sensor * sensors[]中的结构体,并找到代表该sensor的结构体在指针数组中的索引(index)位置。
sensors_first
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
该函数用于查找数组const struct sensors_sensor * sensors[]的第一个结构体,并返回该sensor的结构体指针。
sensors_next
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
找到下一个sensor的结构体,并返回该sensor的结构体指针。
sensors_changed
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这个函数是给具体的sensor写应用时调用的,sensor通过该函数传递信号给sensors_process。sensors_process会做出相应的处理。可以看到函数的内部,会调用get_sensor_index找到该sensor结构体的索引位置,然后把该位置的sensors_flags标记置成FLAG_CHANGED。接着调用process_poll,驱动一次sensors_process线程。
关于利用process_poll而不是process_post_synch,是一个值得讨论的问题。
sensors_process
路径&文件名 (contiki-3.0\core\lib\sensor.c)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
线程的初始化工作
sensors_event = process_alloc_event(); 分配了sensor类型的
接着遍历了所有sensor的结构体指针,并把他们的flag置为0,同时调用configure函数对sensor初始化配置。
接着,num_sensors得到sensor的总数。
线程的主体工作
1. PROCESS_WAIT_EVENT();等待事件的发生,即等待sensors_changed()的调用
- 当该线程被驱动后,则开始遍历sensors_flags[],查看到底是哪个sensor的动作(FLAG_CHANGED)。找到那个sensor后,就调用process_post()函数,以sensors_event事件的名义广播给所有的process(线程)。广播完成后会把相应的sensors_flags[i]数组中的内容清除。events++, 自加,然后并不会跳出do循环,而是接着进行第二次循环,events = 0; 后再确认确实没有sensor发生FLAG_CHANGED的动作后,才退出循环。
- PROCESS_WAIT_EVENT_UNTIL(ev == sensors_event); 这一句其实是我比较感兴趣的。既然都已经广播出去了,还要等自己的事件干什么呢。经过测试发现如果不这一句的话,PROCESS_WAIT_EVENT();会收到两次事件,一次是FLAG_CHANGED,另外一次则是自身的广播。所以加上这一句相当于少检测了一次。
猜测部分:
另外,也能够避免当多个sensor的change事件并发的时候,连续process_post相同的事件,只是传递的sensors指针不同。导致前一次的传递被后一次覆盖。结果是前一次的process_post并没有执行。所以加上这一句也能保证该部分确实执行了。当然这部分有待考证了。
Button 初始化设计
下面以功能为select的button(按键)为例,介绍button的初始化部分的设计。路径&文件名:(contiki-master\platform\srf06-cc26xx\srf06\button-sensor.c ; button-sensor.h)
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
根据上面的介绍,对于SENSORS_SENSOR的作用应该比较清楚了。所以定义的结构体的(name)名字为button_select_sensor,type 为 Button, value函数为value_select, configure函数为 config_select,status函数为status_select。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
输入参数为type, 如果type为BUTTON_SENSOR_VALUE_STATE,则会返回当前按键的状态是按下的还是,松开的。
如果type为BUTTON_SENSOR_VALUE_DURATION,则会返回当前值是按下了多久。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
这是初始化button的函数。在sensors_process(线程)里面,用到了sensors[i]->configure(SENSORS_HW_INIT, 0);对其硬件方面进行初始化。其他的则是SENSORS_ACTIVATE(激活)和SENSORS_DEACTIVATE(失活)sensor。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
status_select()用于检查该按键的状态(边沿触发的中断是否被使能)。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 静态变量如果没有初始化,则会自动初始化为0。所以sel_timer初始值为0。
- 按键按下,下降沿,进入button_press_handler
timer_expired(&sel_timer.debounce)为1程序继续往下走,timer_set(),设置保持按下的时间为DEBOUNCE_DURATION (1/32 秒)。
检测到该键的值为0,保存按键开始按下的时间。 - 按键松开,上升沿,进入button_press_handler
如果按键按下的时间超过DEBOUNCE_DURATION (1/32 秒),timer_expired(&sel_timer.debounce)为1,程序继续往下走。timer_set()这一次的设置不重要,只要比当前时间小就行。
接着读到按键引脚的状态为1,怎进入else,计算实际按键按下的时间,并通过sensors_changed(&button_select_sensor);告知sensors_process(线程),已经有select按键按下,需要做出处理。
Button sensor的整体工作流程
总结
contiki中sensor部分的代码分析就是如上所述的内容了。初看虽然有一点难以理解,仔细分析还是能一点一点地找到思路的。sensor的框架设计方面是非常值得参考的。把sensor的相关功能完全都抽象出来了,然后用结构体指针数组把所有的sensor都放在一起管理。这和高级语言中类的思路其实是一样的,但是C在使用上就没有高级语言那么方便了。