zigbee zstack 串口,按键,消息,定时器

协议栈中的串口接收流程
串口在底层使用的是中断,轮询还是DMA呢??答案从下面这个文件定义的宏可以得知
在C:\Texas Instruments\ZStack-CC2530-2.3.0-1.4.0\Components\hal\target\CC2530EB\hal_board_cfg.h

//默认开启DMA
#ifndef HAL_DMA
       #define HAL_DMA TRUE
#endif

//只要定义了以下4个宏,就相当于需要使用串口
#ifndef HAL_UART
	#if (defined ZAPP_P1) || (defined ZAPP_P2) || (defined ZTOOL_P1) || (defined ZTOOL_P2)
		#define HAL_UART TRUE
	#else
		#define HAL_UART FALSE
	#endif
#endif

// HAL_UART和HAL_DMA都已经定义过了,所以协议栈里的串口默认使用的是DMA,而不是中断
#if HAL_UART
	// Always prefer to use DMA over ISR.
	#if HAL_DMA
		#ifndef HAL_UART_DMA
			#if (defined ZAPP_P1) || (defined ZTOOL_P1)
				#define HAL_UART_DMA  1
			#elif (defined ZAPP_P2) || (defined ZTOOL_P2)
				#define HAL_UART_DMA  2
			#else
				#define HAL_UART_DMA  1
			#endif
		#endif
		#define HAL_UART_ISR  0
	#else
		#ifndef HAL_UART_ISR
			#if (defined ZAPP_P1) || (defined ZTOOL_P1)
			#define HAL_UART_ISR  1
			#elif (defined ZAPP_P2) || (defined ZTOOL_P2)
			#define HAL_UART_ISR  2
			#else
			#define HAL_UART_ISR  1
			#endif
		#endif
		#define HAL_UART_DMA  0
	#endif

	// Used to set P2 priority - USART0 over USART1 if both are defined.
	#if ((HAL_UART_DMA == 1) || (HAL_UART_ISR == 1))
		#define HAL_UART_PRIPO             0x00
		#else
		#define HAL_UART_PRIPO             0x40
	#endif

#else//否则
	#define HAL_UART_DMA  0
	#define HAL_UART_ISR  0
#endif

在某个应用的初始化函数中顺序执行下面两个函数,比如在SampleApp_Init函数中,
  MT_UartInit();
  MT_UartRegisterTaskID(task_id);
MT_UartRegisterTaskID//用于给SampleApp注册串口,这样当串口在MT_UartProcessZToolData(mt_uart.c)发数据到上层时,会发到SampleApp。如果多个app都执行了MT_UartRegisterTaskID,则最后一个注册的有效,因为前面的被覆盖掉了。
void MT_UartRegisterTaskID( byte taskID )
{
  App_TaskID = taskID;
}

比如,在osalInitTasks函数中
void osalInitTasks( void )
{
  uint8 taskID = 0;

  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

  macTaskInit( taskID++ );
  nwk_init( taskID++ );
  Hal_Init( taskID++ );
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );//会执行MT_UartRegisterTaskID函数
#endif
  APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_Init( taskID++ );
#endif
  ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_Init( taskID++ );
#endif
  SampleApp_Init( taskID );//会执行MT_UartRegisterTaskID函数,把MT_TaskInit函数注册的串口覆盖掉了。串口有数据发给上层时,SampleApp会收到消息。
}

MT_UartInit将会指定串口有数据到来时,调用哪个callback函数来处理。如果在option的preprocessor中定义了ZTOOL_P1即串口0(cc2530有2个串口,ZTOOL_P2或ZAPP_P2代表串口1),则调用的是MT_UartProcessZToolData,见下面

/***************************************************************************************************
 * @fn      MT_UartInit
 *
 * @brief   Initialize MT with UART support
 *
 * @param   None
 *
 * @return  None
***************************************************************************************************/
void MT_UartInit ()
{
  halUARTCfg_t uartConfig;

  /* Initialize APP ID */
  App_TaskID = 0;

  /* UART Configuration */
  uartConfig.configured           = TRUE;
  uartConfig.baudRate             = MT_UART_DEFAULT_BAUDRATE;
  uartConfig.flowControl          = MT_UART_DEFAULT_OVERFLOW;
  uartConfig.flowControlThreshold = MT_UART_DEFAULT_THRESHOLD;
  uartConfig.rx.maxBufSize        = MT_UART_DEFAULT_MAX_RX_BUFF;
  uartConfig.tx.maxBufSize        = MT_UART_DEFAULT_MAX_TX_BUFF;
  uartConfig.idleTimeout          = MT_UART_DEFAULT_IDLE_TIMEOUT;
  uartConfig.intEnable            = TRUE;
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
  uartConfig.callBackFunc         = MT_UartProcessZToolData;
#elif defined (ZAPP_P1) || defined (ZAPP_P2)
  uartConfig.callBackFunc         = MT_UartProcessZAppData;
#else
  uartConfig.callBackFunc         = NULL;
#endif

  /* Start UART */
#if defined (MT_UART_DEFAULT_PORT)//默认串口,如果定义了ZTOOL_P1则是串口0,ZTOOL_P2则是串口1
  HalUARTOpen (MT_UART_DEFAULT_PORT, &uartConfig);
#else
  /* Silence IAR compiler warning */
  (void)uartConfig;
#endif

  /* Initialize for ZApp */
#if defined (ZAPP_P1) || defined (ZAPP_P2)
  /* Default max bytes that ZAPP can take */
  MT_UartMaxZAppBufLen  = 1;
  MT_UartZAppRxStatus   = MT_UART_ZAPP_RX_READY;
#endif

}

MT_UartProcessZToolData用于处理串口接收缓冲区的数据,pMsg->msg的结构体的数据如下

/***************************************************************************************************
 * @fn      MT_UartProcessZToolData
 *
 * @brief   | SOP | Data Length  |   CMD   |   Data   |  FCS  |
 *          |  1  |         1    |      2  |  0-Len   |   1   |
 *
 *          Parses the data and determine either is SPI or just simply serial data
 *          then send the data to correct place (MT or APP)
 *
 * @param   port     - UART port
 *          event    - Event that causes the callback
 *
 *
 * @return  None
 ***************************************************************************************************/
void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
  uint8  ch;
  uint8  bytesInRxBuffer;
  
  (void)event;  // Intentionally unreferenced parameter

  while (Hal_UART_RxBufLen(port))
  {
    HalUARTRead (port, &ch, 1);//每次读一个字节

    switch (state)
    {
      case SOP_STATE://0
        if (ch == MT_UART_SOF)// 如果读到的字符时Start-of-frame即0xFE,则进入LEN_STAT状态,进行读LEN
          state = LEN_STATE;
        break;

      case LEN_STATE://1
        LEN_Token = ch;//消息的长度

        tempDataLen = 0;

        /* Allocate memory for the data */
        pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
                                                        MT_RPC_FRAME_HDR_SZ + LEN_Token );

        if (pMsg)
        {
          /* Fill up what we can */
          pMsg->hdr.event = CMD_SERIAL_MSG;
          pMsg->msg = (uint8*)(pMsg+1);
          pMsg->msg[MT_RPC_POS_LEN] = LEN_Token;//pMsg->msg[0]=len
          state = CMD_STATE1;//读完长度,就进入CMD_STATE1状态,进行读命令
        }
        else
        {
          state = SOP_STATE;
          return;
        }
        break;

      case CMD_STATE1:
        pMsg->msg[MT_RPC_POS_CMD0] = ch;//pMsg->msg[1]=命令低字节
        state = CMD_STATE2;
        break;

      case CMD_STATE2:
        pMsg->msg[MT_RPC_POS_CMD1] = ch;//pMsg->msg[2]=命令高字节
        /* If there is no data, skip to FCS state */
        if (LEN_Token)
        {
          state = DATA_STATE;//读完命令,如果数据长度不为0,就进入DATA_STATE状态,进行读数据
        }
        else
        {
          state = FCS_STATE;//否则进入FCS_STATE状态,进行校验
        }
        break;

      case DATA_STATE:

        /* Fill in the buffer the first byte of the data */
        pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen++] = ch;

        /* Check number of bytes left in the Rx buffer */
        bytesInRxBuffer = Hal_UART_RxBufLen(port);

        /* If the remain of the data is there, read them all, otherwise, just read enough,读数量较小的数据 */
        if (bytesInRxBuffer <= LEN_Token - tempDataLen)//如果剩余数据<=len-已读长度,全部读出
        {
          HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], bytesInRxBuffer);
          tempDataLen += bytesInRxBuffer;
        }
        else//否则只读出(len-已读长度)的数据
        {
          HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], LEN_Token - tempDataLen);
          tempDataLen += (LEN_Token - tempDataLen);
        }

        /* If number of bytes read is equal to data length, time to move on to FCS */
        if ( tempDataLen == LEN_Token )
            state = FCS_STATE;

        break;

      case FCS_STATE:

        FSC_Token = ch;

        /* Make sure it's correct */
        if ((MT_UartCalcFCS ((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ + LEN_Token) == FSC_Token))//计算fcs
        {
          osal_msg_send( App_TaskID, (byte *)pMsg );
        }
        else
        {
          /* deallocate the msg */
          osal_msg_deallocate ( (uint8 *)pMsg );
        }

        /* Reset the state, send or discard the buffers at this point */
        state = SOP_STATE;

        break;

      default:
       break;
    }
  }
}

所以如果PC发数据给zigbee串口,需要先发送FE...或者修改这个MT_UartProcessZToolData函数以适应自己的应用

协议栈中的串口发送流程

在某个app_init()中初始化和注册串口(如果此app不需要接收数据,可以不用注册)
直接使用如下代码发送数据即可
 HalUARTWrite(0,"Hello World\n",12);
如果在opention中定义了MT_TASK,则在发送实际的数据之前会发送一个11字节的数据头(下行红色数据),在PC接收到如下形式的数据:
FE 06 41 80 01 02 00 02 03 00 C5 48 65 6C 6C 6F 20 57 6F 72 6C 64 0A 
后面黑色字体的12个字节才是真正的数据

MT_TASK取消定义则直接发送真实的数据


关于串口buffer的和应用层的串口帧大小的关系:
cc2530的串口buffer是1字节,可以看出串口帧的大小与串口buffer没啥关系,因为帧大小一般总是大于1字节的。
U0DBUF (0xC1) – USART 0 Receive/Transmit Data Buffer
这也说明串口在最低层发送的时候,是把上层给的数据一个字节一个字节的塞进buffer里面发出去的)
但是在协议栈中只要发送的串口帧大于128字节,就发不出去,这是因为单片机内存分给帧最大长度是128字节造成的。位于OnBoard.h。可修改之。

#define MT_UART_TX_BUFF_MAX  128
#define MT_UART_RX_BUFF_MAX  128

如果你想要发送大于128字节的帧而不愿意修改上面的宏,可以在while里面用HalUARTWrite一个字节一个字节的发,每发出一个字节执行一次HalUARTPoll。(相当于flush)。



协议栈中的按键流程
以其为例C:\Texas Instruments\ZStack-CC2530-2.3.0-1.4.0\Projects\zstack\Samples\SampleApp
1.在main函数中会执行InitBoard(),用于初始化led和按键
OnBoard.c
void InitBoard( uint8 level )
{
  if ( level == OB_COLD )
  {
    // Interrupts off
    osal_int_disable( INTS_ALL );
    // Turn all LEDs off
    HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
    // Check for Brown-Out reset
    ChkReset();
  }
  else  // !OB_COLD
  {
    /* Initialize Key stuff */
    OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;//此处需要使能按键中断
    HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);//配置按键终端产生之后,触发OnBoard_KeyCallback函数
  }
}

配置函数HalKeyConfig,用于配置哪个按键(哪个端口)
回调函数OnBoard_KeyCallback,此函数执行OnBoard_SendKeys,将按键事件发给上层注册按键的应用

2.在SampleApp_Init()函数中注册按键,这样以后有按键事件发生时,才会将消息发送到SampleApp应用
RegisterForKeys( SampleApp_TaskID );
如果按键已被注册,则后来注册的就会失败。
uint8 RegisterForKeys( uint8 task_id )
{
  // Allow only the first task
  if ( registeredKeysTaskID == NO_TASK_ID )
  {
    registeredKeysTaskID = task_id;
    return ( true );
  }
  else
    return ( false );
}
类似于注册串口,但串口刚好相反。


不管有没有使用中断,对于button1和joystick的按键,都会响应
如果按键没有启用中断,则会每隔100ms轮询一次按键状态,见下
如果注册了中断,则在按键中断发生时,会触发hal_key.c中的中断处理函数,注意kal_key.c中只注册了p2和p0口的中断处理函数,见下
 **************************************************************************************************/
HAL_ISR_FUNCTION( halKeyPort2Isr, P2INT_VECTOR )
{
  if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)
  {
    halProcessKeyInterrupt();
  }
  /*
    Clear the CPU interrupt flag for Port_2
    PxIFG has to be cleared before PxIF
    Notes: P2_1 and P2_2 are debug lines.
  */
  HAL_KEY_JOY_MOVE_PXIFG = 0;
  HAL_KEY_CPU_PORT_2_IF = 0;
}

HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
{
  if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)
  {
    halProcessKeyInterrupt();
  }
  /*
    Clear the CPU interrupt flag for Port_0
    PxIFG has to be cleared before PxIF
  */

  HAL_KEY_SW_6_PXIFG = 0;
  HAL_KEY_CPU_PORT_0_IF = 0;
}


halProcessKeyInterrupt 只是中断标志。没有对中断做具体处理,同时延时25ms设置一个HAL_KEY_EVENT事件,让Hal_TaskID去处理。延时的目的是去抖动。
void halProcessKeyInterrupt (void)
{
  bool valid=FALSE;
  if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)  /* Interrupt Flag has been set */
  {
    HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */
    valid = TRUE;
  }
  if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)  /* Interrupt Flag has been set */
  {
    HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */
    valid = TRUE;
  }
  if (valid)
  {
    osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);//
  }
}

在循环任务中有一个Hal_ProcessEvent (hal_driver.c)

  if (events & HAL_KEY_EVENT)
  {
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
    /* Check for keys */
    HalKeyPoll();//读取时按个按键
    /* if interrupt disabled, do next polling */
    if (!Hal_KeyIntEnable)//如果没有使用中断,就重新延时100ms设置事件HAL_KEY_EVENT。从此处可以看出,如果按键没有启用中断,则会每隔100ms轮询一次按键状态
    {
      osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
    }
#endif // HAL_KEY
    return events ^ HAL_KEY_EVENT;
  }



其调用了HalKeyPoll(hal_key.c),在这个函数中
 **************************************************************************************************/
void HalKeyPoll (void)
{
  uint8 keys = 0;
//读取joystick的状态
  if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /* Key is active HIGH */
  {
    keys = halGetJoyKeyInput();//返回joystick的哪个按键按下
  }
  /* If interrupts are not enabled, previous key status and current key status
   * are compared to find out if a key has changed status.
   */
  if (!Hal_KeyIntEnable)//如果没有启动中断
  {
    if (keys == halKeySavedKeys)
    {
      /* Exit - since no keys have changed */
      return;
    }
    /* Store the current keys for comparation next time */
    halKeySavedKeys = keys;
  }
  else
  {
    /* Key interrupt handled here */
  }

//读取button1状态,
  if (HAL_PUSH_BUTTON1())//返回button1是否按下
  {
    keys |= HAL_KEY_SW_6;
  }

//如果按键按下设置了回调函数,就调用回调函数,如下
  /* Invoke Callback if new keys were depressed */
  if (keys && (pHalKeyProcessFunction))
  {
    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  }
}

onboard.c中定义了回调函数
 *********************************************************************/
void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
  uint8 shift;
  (void)state;

  /* Get shift key status */
  shift = ((keys & HAL_KEY_SW_6) ? true : false);

  if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )//如果上传消息失败(即应用层没有注册按键事件),就就地处理。
  {
    // Process SW1 here
    if ( keys & HAL_KEY_SW_1 )  // Switch 1
    {
    }
    // Process SW2 here
    if ( keys & HAL_KEY_SW_2 )  // Switch 2
    {
    }
    // Process SW3 here
    if ( keys & HAL_KEY_SW_3 )  // Switch 3
    {
    }
    // Process SW4 here
    if ( keys & HAL_KEY_SW_4 )  // Switch 4
    {
    }
    // Process SW5 here
    if ( keys & HAL_KEY_SW_5 )  // Switch 5
    {
    }
    // Process SW6 here
    if ( keys & HAL_KEY_SW_6 )  // Switch 6
    {
    }
  }
}


由上可知,
/* Switches (keys) */
#define HAL_KEY_SW_1 0x01  // Joystick up
#define HAL_KEY_SW_2 0x02  // Joystick right
#define HAL_KEY_SW_5 0x04  // Joystick center
#define HAL_KEY_SW_4 0x08  // Joystick left
#define HAL_KEY_SW_3 0x10  // Joystick down
#define HAL_KEY_SW_6 0x20  // Button S1 if available
协议栈中只对这6个按键由响应。而HalKeyConfig (hal_key.c) 函数中也只是对这6个按键进行了配置。
协议栈中的消息流程

3个不得不知道的结构体
typedef struct
{
  void   *next;
  uint16 len;
  uint8  dest_id;
} osal_msg_hdr_t;

typedef struct
{
  uint8  event;
  uint8  status;
} osal_event_hdr_t;

typedef void * osal_msg_q_t;

//无线收到数据向上层发送的消息
typedef struct
{
  osal_event_hdr_t hdr;     /* OSAL Message header */
  uint16 groupId;           /* Message's group ID - 0 if not set */
  uint16 clusterId;         /* Message's cluster ID */
  afAddrType_t srcAddr;     /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP,
                               it's an InterPAN message */
  uint16 macDestAddr;       /* MAC header destination short address */
  uint8 endPoint;           /* destination endpoint */
  uint8 wasBroadcast;       /* TRUE if network destination was a broadcast address */
  uint8 LinkQuality;        /* The link quality of the received data frame */
  uint8 correlation;        /* The raw correlation value of the received data frame */
  int8  rssi;               /* The received RF power in units dBm */
  uint8 SecurityUse;        /* deprecated */
  uint32 timestamp;         /* receipt timestamp from MAC */
  afMSGCommandFormat_t cmd; /* Application Data */
} afIncomingMSGPacket_t;

//按键按下时向上层发送的消息格式
typedef struct
{
  osal_event_hdr_t hdr;
  uint8             state; // shift
  uint8             keys;  // keys
} keyChange_t;

//串口接到数据向上层发送的消息
typedef struct
{
  osal_event_hdr_t  hdr;
  uint8             *msg;
} mtOSALSerialData_t;


定时器
cc2530-ZStack-CC2530-2.3.0-1.4.0

Hal_timer.c
 NOTE: The following mapping is done between the logical timer
       names defined in HAL_TIMER.H and the physical HW timer.


       HAL_TIMER_0 --> HW Timer 3  (8-bits)
       HAL_TIMER_2 --> HW Timer 4  (8-bits)
       HAL_TIMER_3 --> HW Timer 1  (16-bits)


 NOTE: The timer code assumes only one channel, CHANNEL 0, is used
       for each timer.  There is currently no support for other
       channels.


HAL_ISR_FUNCTION( halTimer1Isr, T1_VECTOR )
{
  halProcessTimer1 ();//是真实的硬件定时器1处理函数,默认没有开启
}

HAL_ISR_FUNCTION( halTimer3Isr, T3_VECTOR )
{
  halProcessTimer3 ();//是真实的硬件定时器3处理函数,默认没有开启
}

HAL_ISR_FUNCTION( halTimer4Isr, T4_VECTOR )
{
  halProcessTimer4 ();//是真实的硬件定时器4处理函数,默认没有开启
}


还有一个硬件定时器2,也是mac timer,已被协议栈占用
mac_mcu.c
/**************************************************************************************************
 * @fn          macMcuTimer2Isr
 *
 * @brief       Interrupt service routine for timer2, the MAC timer.
 *
 * @param       none
 *
 * @return      none
 **************************************************************************************************
 */
HAL_ISR_FUNCTION( macMcuTimer2Isr, T2_VECTOR )


osal_start_timerEx使用的是
mac timer
但是osal_start_timerEx最快可以实现1ms的定时,而我测在mac timer的中断处理函数中使用P1_1=!P1_1测得的中断周期却大于100ms.?????
是使用的mac tiemr吗???
如下是说是使用的MAC backoff timer,这个timer又是啥玩意?
/*********************************************************************
 * @fn      osalTimeUpdate
 *
 * @brief   Uses the free running rollover count of the MAC backoff timer;
 *          this timer runs freely with a constant 320 usec interval.  The
 *          count of 320-usec ticks is converted to msecs and used to update
 *          the OSAL clock and Timers by invoking osalClockUpdate() and
 *          osalTimerUpdate().  This function is intended to be invoked 
 *          from the background, not interrupt level.
 *
 * @param   None.
 *
 * @return  None.
 */
void osalTimeUpdate( void )

posted on 2012-12-02 13:41  _song  阅读(1617)  评论(0编辑  收藏  举报