Tiny OS - 点对基站

一、基础知识

1.1 AMSenderC组件接口:AMPacket

活动消息访问器,提供AM本地地址和用于查询(querying)数据包的功能。 活动消息是单跳通信协议。 因此,诸如源和目标的字段表示单跳源和目标。 多跳源和目标由相应的(corresponding)多跳协议(如果有)定义。 另请参阅数据包接口。

command am_addr_t  address() 不清楚

command am_addr_t  address()
返回与此AM堆栈关联的节点的活动消息地址。

am_addr_t  destination(message_t *amsg) 返回AM数据包的AM目标地址

command am_addr_t  destination(message_t *amsg)
返回AM数据包的AM目标地址。

message_t* msg = xxx;

am_addr_t addr ;
addr = call AMPacket.destination(msg);

command am_addr_t  source(message_t *amsg) 返回AM数据包的AM源地址

command am_addr_t  source(message_t *amsg)
返回AM数据包的AM源地址。

message_t* msg = xxx;

am_addr_t source;
source = call AMPacket.source(msg);

am_id_t  type(message_t *amsg) 返回AM数据包的AM种类

command am_id_t  type(message_t *amsg)
返回AM数据包的AM种类。

message_t* msg = xxx;

am_id_t id;
id = call AMPacket.type(msg);

setSource(message_t *amsg, am_addr_t addr) 设置AM数据包的源地址。

command void  setSource(message_t *amsg, am_addr_t addr)
设置AM数据包的源地址。

setDestination(message_t *amsg, am_addr_t addr) 设置AM数据包的目标地址。

command void  setDestination(message_t *amsg, am_addr_t addr)
设置AM数据包的目标地址。

1.3 AMSenderC组件接口:Packet

clear(message_t *msg) 清除这个数据包

command void  clear(message_t *msg)
清除这个数据包

payloadLength(message_t *msg) 返回消息的有效载荷的长度

command uint8_t  payloadLength(message_t *msg)
Return the length of the payload of msg.返回消息的有效载荷的长度。

1.3 AMSenderC组件接口:AMSend

send(am_addr_t addr, message_t *msg, uint8_t len) 无线发送数据包

command error_t  send(am_addr_t addr, message_t *msg, uint8_t len)

message_t* msg = xxx;

len = call Packet.payloadLength(msg);
addr = call AMPacket.destination(msg);
source = call AMPacket.source(msg);
id = call AMPacket.type(msg);

call Packet.clear(msg);
call AMPacket.setSource(msg, source);
    
if (call RadioSend.send[id](addr, msg, len) == SUCCESS)
{
	//。。。
}

1.2 TinyOS中的任务

TinyOS中关键字 async 声明了可被硬件事件句柄执行的命令或事件,这意味着它们可在任何时候执行(可能抢占其他代码的执行),因此 async 命令和事件所做的工作应该尽可能地少而且应快速。

任务 task 用来执行更长时间的处理操作,如背景数据处理等,同时,任务可以被硬件事件句柄所抢占。

任务的声明:

task void taskname() { … }
其中
taskname()是程序员任意指定的任务名称标识。
任务的返回值类型必须是 void,而且不可以带参数。

例如:

SenseTaskM.nc, continued 
 task void processData() { 
 	int16_t i, sum=0; 
 	atomic { 
 		for (i=0; i < size; i++) 
 			sum += (rdata[i] >> 7); 
 	} 
	 display(sum >> log2size); 
 }

在任务 processData()中使用了关键字 atomic ,这样的语句在 nesC 中被称为“原子语句(atomic statement)。
代码中 atomic 的含义是其后花括号内的代码段在执行过程中不可以被抢占

注意:原子语句会推迟中断处理,从而使得系统的反应看起来不迅速。

任务的调用:

post taskname();
可以从一个命令、事件、甚至是另外一个任务内部“布置(post)”任务。
布置操作将任务放入一个以先进先出(FIFO)方式处理的内部任务队列。当某个任务执行时,它会一直运行直至结束,然后下一个任务开始执行。因此,任务不应该被挂起或阻塞太长时间。虽然任务之间不能够相互抢占,但任务可能被硬件事件句柄所抢占。如果要运行一系列较长的操作,应该为每个操作分配一个任务,而不是使用一个过大的任务。

SenseTaskM.nc 
 // ADC data ready event handler 
 async event result_t ADC.dataReady(uint16_t data) { 
	putdata(data); //将新的样本数据插入到缓冲区中
 	post processData(); //启动任务 processData()进行数据的处理
 	return SUCCESS; 
 }

二、源码分析

点源码

BlinkToRadioC.nc

发送流程:

  1. 上电后开启无线通信的组件
  2. 开启无线通信组件成功后,开启定时器并且设置周期250ms
  3. 定时器周期达到后,发送数据包(包含自身节点标识TOS_NODE_ID,counter数值),锁定发送,发送成功后解锁。

接收流程:

  1. 开启无线通信
  2. 过滤长度不符合的数据
  3. 给数据起别名,并且转化数据类型
  4. 根据rcm->counter ,执行相应的灯操作。
#include <Timer.h>
#include "BlinkToRadio.h"

configuration BlinkToRadioAppC {
}
implementation {
  components MainC;
  components LedsC;
  components BlinkToRadioC as App;
  components new TimerMilliC() as Timer0;
  components ActiveMessageC;
  components new AMSenderC(AM_BLINKTORADIO);
  components new AMReceiverC(AM_BLINKTORADIO);

  App.Boot -> MainC;
  App.Leds -> LedsC;
  App.Timer0 -> Timer0;
  App.Packet -> AMSenderC;
  App.AMPacket -> AMSenderC;
  App.AMControl -> ActiveMessageC;
  App.AMSend -> AMSenderC;
  App.Receive -> AMReceiverC;
}

#include <Timer.h>
#include "BlinkToRadio.h"
module BlinkToRadioC {
  uses interface Boot;
  uses interface Leds;
  uses interface Timer<TMilli> as Timer0;
  uses interface Packet;
  uses interface AMPacket;
  uses interface AMSend;
  uses interface Receive;
  uses interface SplitControl as AMControl;
}
implementation {

  uint16_t counter;
  message_t pkt;
  bool busy = FALSE;

  void setLeds(uint16_t val) {
    if (val & 0x01)
      call Leds.led0On();
    else 
      call Leds.led0Off();
    if (val & 0x02)
      call Leds.led1On();
    else
      call Leds.led1Off();
    if (val & 0x04)
      call Leds.led2On();
    else
      call Leds.led2Off();
  }

  event void Boot.booted() {
    call AMControl.start();
  }

  event void AMControl.startDone(error_t err) {
    if (err == SUCCESS) {
      
     call Timer0.startPeriodic(TIMER_PERIOD_MILLI);
    }
    else {
      call AMControl.start();
    }
  }

  event void AMControl.stopDone(error_t err) {
  }

  event void Timer0.fired() {
    counter++;
    if (!busy) {
      BlinkToRadioMsg* btrpkt = 
	(BlinkToRadioMsg*)(call Packet.getPayload(&pkt, sizeof(BlinkToRadioMsg)));
      if (btrpkt == NULL) {
	return;
      }
      btrpkt->nodeid = TOS_NODE_ID;
      btrpkt->counter = counter;
      if (call AMSend.send(AM_BROADCAST_ADDR, 
          &pkt, sizeof(BlinkToRadioMsg)) == SUCCESS) {
        busy = TRUE;                                  
      }
    }
  }

  event void AMSend.sendDone(message_t* msg, error_t err) {
    if (&pkt == msg) {
      busy = FALSE;
    }
  }

  event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len){
    if (len == sizeof(BlinkToRadioMsg)) {
      BlinkToRadioMsg* btrpkt = (BlinkToRadioMsg*)payload;
      setLeds(btrpkt->counter);
    }
    return msg;
  }
}

基站源码:

Makefile

CFLAGS += -DCC2420_NO_ACKNOWLEDGEMENTS //不回复ack
CFLAGS += -DCC2420_NO_ADDRESS_RECOGNITION //不开启地址识别

表示开始混杂模式。

COMPONENT=BaseStationC
CFLAGS += -DCC2420_NO_ACKNOWLEDGEMENTS
CFLAGS += -DCC2420_NO_ADDRESS_RECOGNITION
CFLAGS += -DTASKLET_IS_TASK
CFLAGS += -DCC2420_DEF_CHANNEL=14
include $(MAKERULES)

BaseStationC.nc

ActiveMessageC 组件也有AMSend,Receive,Packet,AMPacket接口。
以后可以直接使用ActiveMessageC组件,抛弃AMSenderC,AMReceiverC组件。

configuration BaseStationC {
}
implementation {
  components MainC, BaseStationP, LedsC;
  components ActiveMessageC as Radio, SerialActiveMessageC as Serial;
  
  MainC.Boot <- BaseStationP;

  BaseStationP.RadioControl -> Radio;
  BaseStationP.SerialControl -> Serial;
  
  BaseStationP.UartSend -> Serial;
  BaseStationP.UartReceive -> Serial.Receive;
  BaseStationP.UartPacket -> Serial;
  BaseStationP.UartAMPacket -> Serial;
  
  BaseStationP.RadioSend -> Radio;
  BaseStationP.RadioReceive -> Radio.Receive;
  BaseStationP.RadioSnoop -> Radio.Snoop;
  BaseStationP.RadioPacket -> Radio;
  BaseStationP.RadioAMPacket -> Radio;
  
  BaseStationP.Leds -> LedsC;
}

BaseStationP.nc

流程:

  1. 上电后,初始化数据数组,然后打开无线通信,串口通信。
  2. 等待无线接收数据事件被触发。(正常接收到的包,窃听接收到的包)
#include "AM.h"
#include "Serial.h"

module BaseStationP @safe() {
  uses {
    interface Boot;
    interface SplitControl as SerialControl;
    interface SplitControl as RadioControl;

    interface AMSend as UartSend[am_id_t id];
    interface Receive as UartReceive[am_id_t id];
    interface Packet as UartPacket;
    interface AMPacket as UartAMPacket;
    
    interface AMSend as RadioSend[am_id_t id];
    interface Receive as RadioReceive[am_id_t id];
    interface Receive as RadioSnoop[am_id_t id];
    interface Packet as RadioPacket;
    interface AMPacket as RadioAMPacket;

    interface Leds;
  }
}

implementation
{
  enum {
    UART_QUEUE_LEN = 12,
    RADIO_QUEUE_LEN = 12,
  };

  message_t  uartQueueBufs[UART_QUEUE_LEN];
  message_t  * ONE_NOK uartQueue[UART_QUEUE_LEN];
  uint8_t    uartIn, uartOut;
  bool       uartBusy, uartFull;

  message_t  radioQueueBufs[RADIO_QUEUE_LEN];
  message_t  * ONE_NOK radioQueue[RADIO_QUEUE_LEN];
  uint8_t    radioIn, radioOut;
  bool       radioBusy, radioFull;

  task void uartSendTask();
  task void radioSendTask();

  void dropBlink() {
    call Leds.led2Toggle();
  }

  void failBlink() {
    call Leds.led2Toggle();
  }

  event void Boot.booted() {
    uint8_t i;

    for (i = 0; i < UART_QUEUE_LEN; i++)
      uartQueue[i] = &uartQueueBufs[i];
    uartIn = uartOut = 0;
    uartBusy = FALSE;
    uartFull = TRUE;

    for (i = 0; i < RADIO_QUEUE_LEN; i++)
      radioQueue[i] = &radioQueueBufs[i];
    radioIn = radioOut = 0;
    radioBusy = FALSE;
    radioFull = TRUE;

    call RadioControl.start();
    call SerialControl.start();
  }

  event void RadioControl.startDone(error_t error) {
    if (error == SUCCESS) {
      radioFull = FALSE;
    }
  }

  event void SerialControl.startDone(error_t error) {
    if (error == SUCCESS) {
      uartFull = FALSE;
    }
  }

  event void SerialControl.stopDone(error_t error) {}
  event void RadioControl.stopDone(error_t error) {}

  uint8_t count = 0;

  message_t* ONE receive(message_t* ONE msg, void* payload, uint8_t len);
  
  event message_t *RadioSnoop.receive[am_id_t id](message_t *msg,
						    void *payload,
						    uint8_t len) {
    return receive(msg, payload, len);
  }
  
  event message_t *RadioReceive.receive[am_id_t id](message_t *msg,
						    void *payload,
						    uint8_t len) {
    return receive(msg, payload, len);
  }

  message_t* receive(message_t *msg, void *payload, uint8_t len) {
    message_t *ret = msg;

    atomic 
    {
      	if (!uartFull)//串口接收缓冲区没有满
		{
	  		ret = uartQueue[uartIn];//?????
	 		uartQueue[uartIn] = msg;//msg存入队列输入缓冲区

	  		uartIn = (uartIn + 1) % UART_QUEUE_LEN;//uartIn 增1,为下才存储作准备,并取余防止溢出
	
		  	if (uartIn == uartOut)
		    	uartFull = TRUE;
	
		  	if (!uartBusy)
		    {
		     	post uartSendTask();//调用发送到串口的任务
		      	uartBusy = TRUE;
		    }
		}
     	else
			dropBlink();
    }
    return ret;
  }

  uint8_t tmpLen;
  
  task void uartSendTask() {
	    uint8_t len;
	    am_id_t id;
	    am_addr_t addr, src;
	    message_t* msg;
	    atomic
	    //看不懂
	    if (uartIn == uartOut && !uartFull)
		{
		  	uartBusy = FALSE;
		  	return;
		}
	
	    msg = uartQueue[uartOut];
	    tmpLen = len = call RadioPacket.payloadLength(msg);
	    id = call RadioAMPacket.type(msg);
	    addr = call RadioAMPacket.destination(msg);
	    src = call RadioAMPacket.source(msg);
	    call UartPacket.clear(msg);
	    call UartAMPacket.setSource(msg, src);
	
		//串口发送数据
	    if (call UartSend.send[id](addr, uartQueue[uartOut], len) == SUCCESS)
	     	call Leds.led1Toggle();
	    else
	    {
			failBlink();
			post uartSendTask();
	    }
	}

  event void UartSend.sendDone[am_id_t id](message_t* msg, error_t error) {
    if (error != SUCCESS)
      	failBlink();
    else
      	atomic
		if (msg == uartQueue[uartOut])//确认串口发送的数据是否正确
	  	{
	    	if (++uartOut >= UART_QUEUE_LEN)//uartOut 队列下标+1,并作溢出复原处理
	      		uartOut = 0;
	      	//串口非满
	    	if (uartFull)
	      		uartFull = FALSE;
	  	}
    post uartSendTask();//调用串口发送任务
  }

  event message_t *UartReceive.receive[am_id_t id](message_t *msg,
						   void *payload,
						   uint8_t len) {
    message_t *ret = msg;
    bool reflectToken = FALSE;

    atomic
      if (!radioFull)
	{
	  reflectToken = TRUE;
	  ret = radioQueue[radioIn];
	  radioQueue[radioIn] = msg;
	  if (++radioIn >= RADIO_QUEUE_LEN)
	    radioIn = 0;
	  if (radioIn == radioOut)
	    radioFull = TRUE;

	  if (!radioBusy)
	    {
	      post radioSendTask();
	      radioBusy = TRUE;
	    }
	}
      else
	dropBlink();

    if (reflectToken) {
      //call UartTokenReceive.ReflectToken(Token);
    }
    
    return ret;
  }

  task void radioSendTask() {
    uint8_t len;
    am_id_t id;
    am_addr_t addr,source;
    message_t* msg;
    
    atomic
      if (radioIn == radioOut && !radioFull)
	{
	  radioBusy = FALSE;
	  return;
	}

    msg = radioQueue[radioOut];
    len = call UartPacket.payloadLength(msg);
    addr = call UartAMPacket.destination(msg);
    source = call UartAMPacket.source(msg);
    id = call UartAMPacket.type(msg);

    call RadioPacket.clear(msg);
    call RadioAMPacket.setSource(msg, source);
    
    if (call RadioSend.send[id](addr, msg, len) == SUCCESS)
      call Leds.led0Toggle();
    else
      {
	failBlink();
	post radioSendTask();
      }
  }

  event void RadioSend.sendDone[am_id_t id](message_t* msg, error_t error) {
    if (error != SUCCESS)
      failBlink();
    else
      atomic
	if (msg == radioQueue[radioOut])
	  {
	    if (++radioOut >= RADIO_QUEUE_LEN)
	      radioOut = 0;
	    if (radioFull)
	      radioFull = FALSE;
	  }
    
    post radioSendTask();
  }
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值