contiki 之 button sensor

原文: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)

struct sensors_sensor {
  char *       type;
  int          (* value)     (int type);
  int          (* configure) (int type, int value);
  int          (* status)    (int type);
};
 
 
  • 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

#define SENSORS_SENSOR(name, type, value, configure, status)        \
const struct sensors_sensor name = { type, value, configure, status }
 
 
  • 1
  • 2
  • 1
  • 2

很明显,该宏定义用于创建一个名字为name的具体的sensors_sensor结构体。

part3

#define SENSORS_NUM (sizeof(sensors) / sizeof(struct sensors_sensor *))

#define SENSORS(...) \
const struct sensors_sensor *sensors[] = {__VA_ARGS__, NULL};       \
unsigned char sensors_flags[SENSORS_NUM]
 
 
  • 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
static int
get_sensor_index(const struct sensors_sensor *s)
{
  int i;
  for(i = 0; i < num_sensors; ++i) {
    if(sensors[i] == s) {
      return i;
    }
  }
  return i;
}
 
 
  • 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
const struct sensors_sensor *
sensors_first(void)
{
  return sensors[0];
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

该函数用于查找数组const struct sensors_sensor * sensors[]的第一个结构体,并返回该sensor的结构体指针。

sensors_next
const struct sensors_sensor *
sensors_next(const struct sensors_sensor *s)
{
  return sensors[get_sensor_index(s) + 1];
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

找到下一个sensor的结构体,并返回该sensor的结构体指针。

sensors_changed
#define FLAG_CHANGED    0x80
void
sensors_changed(const struct sensors_sensor *s)
{
  sensors_flags[get_sensor_index(s)] |= FLAG_CHANGED;
  process_poll(&sensors_process);
}
 
 
  • 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)

PROCESS_THREAD(sensors_process, ev, data)
{
  static int i;
  static int events;

  PROCESS_BEGIN();

  sensors_event = process_alloc_event();

  for(i = 0; sensors[i] != NULL; ++i) {
    sensors_flags[i] = 0;
    sensors[i]->configure(SENSORS_HW_INIT, 0);
  }
  num_sensors = i;

    while(1)
    {
        PROCESS_WAIT_EVENT();
        do
        {
            events = 0;
            for(i = 0; i < num_sensors; ++i)
            {
                if(sensors_flags[i] & FLAG_CHANGED)
                {
                    if(process_post(PROCESS_BROADCAST, sensors_event, (void *)sensors[i]) == \ PROCESS_ERR_OK)
                    {

                        PROCESS_WAIT_EVENT_UNTIL(ev == sensors_event);
                    }
                    sensors_flags[i] &= ~FLAG_CHANGED;
                    events++;
                }
            }
        } while(events);
  }

  PROCESS_END();
}
 
 
  • 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()的调用

  1. 当该线程被驱动后,则开始遍历sensors_flags[],查看到底是哪个sensor的动作(FLAG_CHANGED)。找到那个sensor后,就调用process_post()函数,以sensors_event事件的名义广播给所有的process(线程)。广播完成后会把相应的sensors_flags[i]数组中的内容清除。events++, 自加,然后并不会跳出do循环,而是接着进行第二次循环,events = 0; 后再确认确实没有sensor发生FLAG_CHANGED的动作后,才退出循环。
  2. 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)

SENSORS_SENSOR(button_select_sensor, BUTTON_SENSOR, value_select,
               config_select, status_select);

#define BUTTON_SENSOR "Button"
#define SENSORS_SENSOR(name, type, value, configure, status)        \
const struct sensors_sensor name = { type, value, configure, status }
 
 
  • 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。

static int
value_select(int type)
{
  if(type == BUTTON_SENSOR_VALUE_STATE) {
    return ti_lib_gpio_read_dio(BOARD_IOID_KEY_SELECT) == 0 ?
           BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED;
  } else if(type == BUTTON_SENSOR_VALUE_DURATION) {
    return (int)sel_timer.duration;
  }
  return 0;
}
 
 
  • 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,则会返回当前值是按下了多久。

static int
config_select(int type, int value)
{
  config_buttons(type, value, BOARD_IOID_KEY_SELECT);

  return 1;
}


/*---------------------------------------------------------------------------*/
/**
 * \brief Configuration function for the button sensor for all buttons.
 *
 * \param type This function does nothing unless type == SENSORS_ACTIVE
 * \param c 0: disable the button, non-zero: enable
 * \param key: One of BOARD_KEY_LEFT, BOARD_KEY_RIGHT etc
 */
static void
config_buttons(int type, int c, uint32_t key)
{
  switch(type) {
  case SENSORS_HW_INIT:
    ti_lib_gpio_clear_event_dio(key);
    ti_lib_rom_ioc_pin_type_gpio_input(key);
    ti_lib_rom_ioc_port_configure_set(key, IOC_PORT_GPIO, BUTTON_GPIO_CFG);
    gpio_interrupt_register_handler(key, button_press_handler);
    break;
  case SENSORS_ACTIVE:
    if(c) {
      ti_lib_gpio_clear_event_dio(key);
      ti_lib_rom_ioc_pin_type_gpio_input(key);
      ti_lib_rom_ioc_port_configure_set(key, IOC_PORT_GPIO, BUTTON_GPIO_CFG);
      ti_lib_rom_ioc_int_enable(key);
    } else {
      ti_lib_rom_ioc_int_disable(key);
    }
    break;
  default:
    break;
  }
}

sensors[i]->configure(SENSORS_HW_INIT, 0);
#define SENSORS_ACTIVATE(sensor) (sensor).configure(SENSORS_ACTIVE, 1)
#define SENSORS_DEACTIVATE(sensor) (sensor).configure(SENSORS_ACTIVE, 0)
/* some constants for the configure API */
#define SENSORS_HW_INIT 128 /* internal - used only for initialization */
#define SENSORS_ACTIVE 129 /* ACTIVE => 0 -> turn off, 1 -> turn on */
#define SENSORS_READY 130 /* read only */
 
 
  • 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。

/*---------------------------------------------------------------------------*/
/**
 * \brief Status function for the select button.
 * \param type SENSORS_ACTIVE or SENSORS_READY
 * \return 1 if the button's port interrupt is enabled (edge detect)
 *
 * This function will call status. It will pass type verbatim and it will also
 * pass the correct key_io_id
 */
static int
status_select(int type)
{
  return status(type, BOARD_IOID_KEY_SELECT);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Status function for all buttons
 * \param type SENSORS_ACTIVE or SENSORS_READY
 * \param key_io_id BOARD_IOID_KEY_LEFT, BOARD_IOID_KEY_RIGHT etc
 * \return 1 if the button's port interrupt is enabled (edge detect)
 *
 * This function will only be called by status_left, status_right and the
 * called will pass the correct key_io_id
 */
static int
status(int type, uint32_t key_io_id)
{
  switch(type) {
  case SENSORS_ACTIVE:
  case SENSORS_READY:
    if(ti_lib_ioc_port_configure_get(key_io_id) & IOC_INT_ENABLE) {
      return 1;
    }
    break;
  default:
    break;
  }
  return 0;
}
 
 
  • 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()用于检查该按键的状态(边沿触发的中断是否被使能)。

/**
 * A timer.
 *
 * This structure is used for declaring a timer. The timer must be set
 * with timer_set() before it can be used.
 *
 * \hideinitializer
 */
struct timer {
  clock_time_t start;
  clock_time_t interval;
};
struct btn_timer {
  struct timer debounce;
  clock_time_t start;
  clock_time_t duration;
};

static struct btn_timer sel_timer;
/*---------------------------------------------------------------------------*/
/**
 * \brief Handler for SmartRF button presses
 */
static void
button_press_handler(uint8_t ioid)
{
  if(ioid == BOARD_IOID_KEY_SELECT) {
    if(!timer_expired(&sel_timer.debounce)) {
      return;
    }

    timer_set(&sel_timer.debounce, DEBOUNCE_DURATION);

    /*
     * Start press duration counter on press (falling), notify on release
     * (rising)
     */
    if(ti_lib_gpio_read_dio(BOARD_IOID_KEY_SELECT) == 0) {
      sel_timer.start = clock_time();
      sel_timer.duration = 0;
    } else {
      sel_timer.duration = clock_time() - sel_timer.start;
      sensors_changed(&button_select_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
  • 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
  1. 静态变量如果没有初始化,则会自动初始化为0。所以sel_timer初始值为0。
  2. 按键按下,下降沿,进入button_press_handler 
    timer_expired(&sel_timer.debounce)为1程序继续往下走,timer_set(),设置保持按下的时间为DEBOUNCE_DURATION (1/32 秒)。 
    检测到该键的值为0,保存按键开始按下的时间。
  3. 按键松开,上升沿,进入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的整体工作流程

Startsensors_process初始化button_sensor按键检测是否有按键按下sensors_process通知应用程序Endyesno

总结

contiki中sensor部分的代码分析就是如上所述的内容了。初看虽然有一点难以理解,仔细分析还是能一点一点地找到思路的。sensor的框架设计方面是非常值得参考的。把sensor的相关功能完全都抽象出来了,然后用结构体指针数组把所有的sensor都放在一起管理。这和高级语言中类的思路其实是一样的,但是C在使用上就没有高级语言那么方便了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值