TI zigbee UART的兩種模式

文章來自:http://wjf88223.blog.163.com/blog/static/3516800120104179327286/


協議棧中UART有兩種模式:

1、中斷
2、DMA


對於這兩種模式具體運用在哪一步,糾結了很久.通過UART配置結構:
typedef struct
{
  uint8 *rxBuf;
  uint8 rxHead;
  uint8 rxTail;
  uint8 rxMax;
  uint8 rxCnt;
  uint8 rxTick;
  uint8 rxHigh;


  uint8 *txBuf;
#if HAL_UART_BIG_TX_BUF
  uint16 txHead;
  uint16 txTail;
  uint16 txMax;
  uint16 txCnt;
#else
  uint8 txHead;
  uint8 txTail;
  uint8 txMax;
  uint8 txCnt;
#endif
  uint8 txTick;


  uint8 flag;


  halUARTCBack_t rxCB;
} uartCfg_t;


可以看到協議棧爲串口收發分別配置了一塊內存空間rxBuf和txBuf,具體在HalUARTOpen()裏配置.
而中斷與DMA這兩種模式具體就運用於 數據在串口緩存U0_1DBUF與rxBuf/txBuf之間傳送 的過程.


串口接收DMA模式:(data) —> U0DBUF —(DMA)—> rxBuf —> HalUARTRead()讀取rxBuf數據進行處理
串口接收中斷模式:(data) —> U0DBUF —(中斷)—> rxBuf —> HalUARTRead()讀取rxBuf數據進行處理




串口發送DMA模式:(data) <— U0DBUF <—(DMA)— txBuf
串口發送中斷模式:(data) <— U0DBUF <—(中斷)— txBuf


協議中UART的兩種模式 - 小峯 - happy~我覺得這樣理解好像還有問題,本身對DMA不瞭解,網上又幾乎查不到關於協議棧有關UART_DMA模式具體


流程資料,卡在這裏很久,所以打算建立個大概印象就跳出來,以後再看.協議中UART的兩種模式 - 小峯 - happy~


 


先記錄下中斷模式吧~
###########################################################################


###########################################################################
1、中斷模式(UART接收)


當1寫入UxCSR.RE位時,在UART上數據接收就開始了.然後UART會在輸入引腳RXDx中尋找有效起始位,並且設置UxCSR.ACTIVE位爲1.當檢測出有效起始位時,收到的字節就傳入接收寄存器,UxCSR.RX_BUTE位設置爲1.該操作完成時,產生接收中斷。通過寄存器UxBUF提供收到的數據字節。當UxBUF讀出時,UxCSR.RX_BUTE位由硬件清零.當產生中斷時,自然進入中斷程序,看下UART0接收中斷函數:


 ***************************************
#if HAL_UART_0_ENABLE
HAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR )
{
  cfg0->rxBuf[cfg0->rxHead] = U0DBUF;


  if ( cfg0->rxHead == cfg0->rxMax )
  {
    cfg0->rxHead = 0;
  }
  else
  {
    cfg0->rxHead++;
  }
}
#endif


/**************************************


中斷函數完成了把U0DBUF裏一字節的數據傳送到rxBuf[ ]存儲空間去.這裏rxHead是指向rxBuf[ ]的指針,看單詞像是指在數組的頭,其實應理解爲rxBuf[ ]接收數據的個數(以字節爲單位).rxMax是rxBuf[ ]可以存儲最大字節數,爲128.而後面當用HalUARTRead()來讀取rxBuf[ ]時,rxTail應理解爲rxBuf[]轉移出去數據的個數(同樣以字節爲單位).那數據傳送到rxBuf[ ]存儲空間去後呢?先看下pollISR()


 *****************************************************************************/
//大概每200ms調用pollISR()函數.當串口UxDBUF接收到一字節數據產生中斷,在中斷
//程序中把UxDBUF中數據傳送到rxbuf[ ]中(這有個坎要跨過來,pollISR()200ms才被調用一次,而不是每次中斷後都調用一次,如果串口接收的是大的數據包,則200ms內rxbuf[ ]已經接收了約48字節(這個後面分析),中斷了48次??.當然如果串口沒有接收到數據,也就是說沒有發生串口接收中斷,cfg應爲是爲空的,則cnt=0).此後pollISR()進行輪詢,主要是重置超時時間和計算rxbuf[ ]還有多少數據沒有讀走(即cnt).然後再跳回到HalUARTPoll()函數進行下一步處理.
static void pollISR( uartCfg_t *cfg )
{
  uint8 cnt = UART_RX_AVAIL( cfg ); //計算rxBuf[]中還有多少數據沒有讀出(以字節爲單位)


  if ( !(cfg->flag & UART_CFG_RXF) ) //UART_CFG_RXF:Rx flow is disabled.rx流控制未關閉
  {
    // If anything received, reset the Rx idle timer.
    //如果又有新的數據接收到,則重置超時時間
    if ( cfg->rxCnt != cnt )
    {
      cfg->rxTick = HAL_UART_RX_IDLE;
      cfg->rxCnt = cnt;
    }


    /* It is necessary to stop Rx flow in advance of a full Rx buffer because
     * bytes can keep coming while sending H/W fifo flushes.
     */
     //當接收數據超過安全界限的時候,通過硬件流控制停止接收數據
    if ( cfg->rxCnt >= (cfg->rxMax - SAFE_RX_MIN) )
    {
      RX_STOP_FLOW( cfg );
    }
  }
}
#endif


/******************************************************************************
pollISR()函數主要就是設置rxTick和rxCnt,發生接收中斷並且接收中斷函數處理完後,這些參數都會改變.
pollISR()執行完成後就跳回到HalUARTPoll()中.
看下 HalUARTPoll()
/**************************************
void HalUARTPoll( void )
{
#if ( HAL_UART_0_ENABLE | HAL_UART_1_ENABLE )
  static uint8 tickShdw;
  uartCfg_t *cfg;
  uint8 tick;


//---------以下設置cfg
#if HAL_UART_0_ENABLE
  if ( cfg0 )  //當串口接收到數據時,在中斷函數或DMA程序中會改變cfg0值,如果沒有接收到數據,cfg0


爲空.
  {
    cfg = cfg0;
  }
#endif
//---------
#if HAL_UART_1_ENABLE
  if ( cfg1 )
  {
    cfg = cfg1;//同上
  }
#endif
//---------
  
  // Use the LSB of the sleep timer (ST0 must be read first anyway).
  //睡眠定時器.ST0爲睡眠定時器計數值的低8位
  tick = ST0 - tickShdw;
  tickShdw = ST0;


  do
  {
    //------------------------發送超時時間
    if ( cfg->txTick > tick )
    {
      cfg->txTick -= tick;
    }
    else
    {
      cfg->txTick = 0;
    }
    //------------------------接收超時時間
    if ( cfg->rxTick > tick )
    {
      cfg->rxTick -= tick;
    }
    else
    {
      cfg->rxTick = 0;
    }
    //------------------------
#if HAL_UART_ISR
#if HAL_UART_DMA
    if ( cfg->flag & UART_CFG_DMA )
    {                             
      pollDMA( cfg ); //pollDMA( cfg )
    } 
    else
#endif
      {
      pollISR( cfg );  //pollISR( cfg )  
      }
#elif HAL_UART_DMA   
    pollDMA( cfg );
#endif


    /* The following logic makes continuous callbacks on any eligible flag(合法標誌)
     * until the condition corresponding to the flag is rectified.
     * So even if new data is not received, continuous callbacks are made.
     */
      if ( cfg->rxHead != cfg->rxTail ) //初始化時rxHead=rxTail=0,不相等,rxbuf[ ]中有數據
      {
      uint8 evt;


      if ( cfg->rxHead >= (cfg->rxMax - SAFE_RX_MIN) )
      {
        evt = HAL_UART_RX_FULL; //rxBuf接收[ ]滿,則觸發事件
      }
      else if ( cfg->rxHigh && (cfg->rxHead >= cfg->rxHigh) )
      {
        evt = HAL_UART_RX_ABOUT_FULL; //rxBuf[ ]接收到預設值(默認80字節),則觸發事件
    }
      else if ( cfg->rxTick == 0 )
    {
        evt = HAL_UART_RX_TIMEOUT;  //RX接收事件超時,則觸發事件
    }
    else
    {
        evt = 0;
    }


    if ( evt && cfg->rxCB )  //有事件並且有回調函數
    {
        cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt );  //調用回調函數處理這些事件
    }
    }


  ………………


  } while ( TRUE );
#else
  return;
#endif
}


/**************************************
可以看到,初始化時rxHead=rxTail=0,如果發生接收中斷,在中斷服務函數中把UxDBUF中的數據傳送到rxbuf[ ]中(這一步也可以在DMA服務程序中完成),則rxHead與rxTail值不等(rxHead是rxBuf[ ]接收數據的個數,rxTail是rxBuf[ ]轉移出去數據個數),再根據兩值的差值,判斷具體事件evt,最後再調用回調函數:cfg->rxCB();回調函數在哪?回調函數 cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt ),在cfg中有halUARTCBack_t rxCB;則肯定有個地方配置cfg這個結構體,這個地方是HalUARTOpen( uint8 port, halUARTCfg_t *config );在這個函數中可以看到,結構體config的內容一項項賦給了結構體cfg,其中包括cfg->rxCB = config->callBackFunc;那肯定有個地方配置config這個結構體,這個地方是SPIMgr_Init (),這個函數有一句HalUARTOpen (SPI_MGR_DEFAULT_PORT, &uartConfig),*config與&uartConfig是同一halUARTCfg_t類型結構體,而且SPIMgr_Init ()函數中對uartConfig進行了定義,如下:


  /* UART Configuration */
  uartConfig.configured           = TRUE;
  uartConfig.baudRate             = SPI_MGR_DEFAULT_BAUDRATE;
  uartConfig.flowControl          = 0;//SPI_MGR_DEFAULT_OVERFLOW;
  uartConfig.flowControlThreshold = SPI_MGR_DEFAULT_THRESHOLD;
  uartConfig.rx.maxBufSize        = SPI_MGR_DEFAULT_MAX_RX_BUFF;
  uartConfig.tx.maxBufSize        = SPI_MGR_DEFAULT_MAX_TX_BUFF;
  uartConfig.idleTimeout          = SPI_MGR_DEFAULT_IDLE_TIMEOUT;
  uartConfig.intEnable            = TRUE;
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
  uartConfig.callBackFunc         = SPIMgr_ProcessZToolData;  //回調函數
#elif defined (ZAPP_P1) || defined (ZAPP_P2)
  uartConfig.callBackFunc         = SPIMgr_ProcessZAppData;  //回調函數
#else
  uartConfig.callBackFunc         = NULL;
#endif




來看下SPIMgr_ProcessZToolData ( uint8 port, uint8 event )
/*****************************************************
 * @fn      SPIMgr_ProcessZToolRxData
 *
 * @brief   | SOP | CMD  |   Data Length   | FSC  |     //幀格式
 *                |  1     |    2    |             1             |  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   pBuffer  - pointer to the buffer that contains the data
 *          length   - length of the buffer
 *
 *
 * @return  None
 
void SPIMgr_ProcessZToolData ( uint8 port, uint8 event )
{
  uint8  ch;


  /* Verify events */
  if (event == HAL_UART_TX_FULL)
  {
    // Do something when TX if full
    return;
  }


  if (event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT))
  {
    while (Hal_UART_RxBufLen(SPI_MGR_DEFAULT_PORT)) //RxBuf[ ]中字節數,即有數據
    {
      HalUARTRead (SPI_MGR_DEFAULT_PORT, &ch, 1); //每次讀取1字節到ch所指空間
                                                                                              //接下來對所讀字節的state進行判斷
      switch (state) //state 參見上面宏定義/* State values for ZTool protocal */
      {
        case SOP_STATE:
          if (ch == SOP_VALUE)  //SOP_VALUE=0x02
            state = CMD_STATE1;
          break;


        case CMD_STATE1:
          CMD_Token[0] = ch;
          state = CMD_STATE2;
          break;


        case CMD_STATE2:
          CMD_Token[1] = ch;
          state = LEN_STATE;
          break;


        case LEN_STATE:
          LEN_Token = ch;
          if (ch == 0)
            state = FCS_STATE;
          else
            state = DATA_STATE;  //state = DATA_STATE=0x04


          tempDataLen = 0;


          /* Allocate memory for the data */
          SPI_Msg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +


2+1+LEN_Token );


          if (SPI_Msg)  //構造消息:把SOP字節和FCS字節去掉,剩下2字節的CMD和1字節的Data
          {
            /* Fill up what we can */
            SPI_Msg->hdr.event = CMD_SERIAL_MSG;  //CMD_SERIAL_MSG
            SPI_Msg->msg = (uint8*)(SPI_Msg+1);
            SPI_Msg->msg[0] = CMD_Token[0];
            SPI_Msg->msg[1] = CMD_Token[1];
            SPI_Msg->msg[2] = LEN_Token;
          }
          else
          {
            state = SOP_STATE;
            return;
          }


          break;


        case DATA_STATE:
            SPI_Msg->msg[3 + tempDataLen++] = ch;
            if ( tempDataLen == LEN_Token )
              state = FCS_STATE;
          break;


        case FCS_STATE:  //幀校驗序列(FCS),則判斷是接收數據是否正確


          FSC_Token = ch;


          /* Make sure it's correct */
          if ((SPIMgr_CalcFCS ((uint8*)&SPI_Msg->msg[0], 2 + 1 + LEN_Token) == FSC_Token))
          {
            osal_msg_send( MT_TaskID, (byte *)SPI_Msg );  //接收數據正確則發送系統信息觸發MT層任


務.事件爲CMD_SERIAL_MSG
          }
          else
          {
            /* deallocate the msg */
            osal_msg_deallocate ( (uint8 *)SPI_Msg);
          }


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


         break;


        default:
         break;
      }




    }
  }
}
#endif*********************************************************
(這個函數只是總體上了解了下)
回調函數SPIMgr_ProcessZToolData()對rxbuf[]中的每一字節數據進行分析,並且構造一個發往系統的消息包(具體事件爲SPI_Msg->hdr.event = CMD_SERIAL_MSG)然後調用osal_msg_send( MT_TaskID, (byte *)SPI_Msg )給系統發送消息,而下面肯定又會調用osal_set_event()來設置任務事件發生標誌,最後調用MT層的事件處理函數,下面來看下MT層的事件處理函數:
/**********************************************************
 * @fn      MT_ProcessEvent
 *
 * @brief
 *
 *   MonitorTest Task Event Processor.  This task is put into the
 *   task table.
 *
 * @param   byte task_id - task ID of the MT Task
 * @param   UINT16 events - event(s) for the MT Task
 *
 * @return  void
 */
UINT16 MT_ProcessEvent( byte task_id, UINT16 events )
{
  uint8 *msg_ptr;


   ………………


  if ( events & SYS_EVENT_MSG )
  {
    while ( (msg_ptr = osal_msg_receive( MT_TaskID )) )
    {
      MT_ProcessCommand( (mtOSALSerialData_t *)msg_ptr );
    }
   ………………
/*********************************************************************
可以看到它調用了MT_ProcessCommand( (mtOSALSerialData_t *)msg_ptr )來進行下一步的操作,來看下MT_ProcessCommand()這個函數:


/*********************************************************************
 * @fn      MT_ProcessCommand *
 * @brief


 *
 *   Process Event Messages.
 *
 * @param   byte *msg - pointer to event message
 *
 * @return
 */
void MT_ProcessCommand( mtOSALSerialData_t *msg )
{
   ……………………
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
    case CMD_SERIAL_MSG:
      MT_ProcessSerialCommand( msg->msg );
      break;
   ……………………
}


/*********************************************************************
通過上面的MT_ProcessCommand(),再根據先前SPI_Msg->hdr.event = CMD_SERIAL_MSG,可以得到它調用MT_ProcessSerialCommand( msg->msg )這個函數,而MT_ProcessSerialCommand()函數根據參數cmd來選擇命令,包括讀RAM,寫RAM等等很多命令指令來對接收到的數據進行下一步操作(先不鑽了……),對於這個cmd,cmd = BUILD_UINT16( msg[1], msg[0] ),SPIMgr_ProcessZToolData()對msg[1], msg[0]進行了配置:
            SPI_Msg->msg[0] = CMD_Token[0];
            SPI_Msg->msg[1] = CMD_Token[1];
而CMD_Token[0]與CMD_Token[1]的值又與SPIMgr_ProcessZToolData()中的參數ch密切相關,對於這個ch具體的定義,只是看到個uint8  ch;,沒看到具體初始化爲什麼值.個人在想,是不是通過對參數ch的自由設定來選擇串口命令cmd,通過cmd來實現對數據的不同處理……【現在發現這個應該是跟ZTOOL有關,晴天那天說ZTOOL上面可以發送不同命令的】
迷糊中!~協議中UART的兩種模式 - 小峯 - happy~~糾結中!~協議中UART的兩種模式 - 小峯 - happy~


上面分析了UART中斷模式下,從接收到數據進入中斷函數,把數據寫入rxbuf[ ],200ms調用一次pollISR()輪詢,跳回到HalUARTPoll()進行數據處理……………….那這個具體輪詢時間rxTick約爲200ms是怎麼得出來的?借用《Z-STACK問題之串口結構uartCfg_t亂說》這篇文章的分析:
************
 cfg->rxTick = HAL_UART_RX_IDLE
 #define HAL_UART_RX_IDLE  (6 * RX_MSECS_TO_TICKS)
#define RX_MSECS_TO_TICKS  33 (// The timeout tick is at 32-kHz, so multiply msecs by 33.)
(這個超時計數採用的是睡眠定時器,32KHZ時鐘,一個微秒計數約33次)
因此cfg->rxTick=198,即大約200ms調用一次pollISR();
論證 #define SAFE_RX_MIN  48  // bytes - max expected per poll @ 115.2k
因爲CC2430串口波特率爲38400bps下,一個字節需要時間約爲:4.16ms(這裏……這?),那麼198/4.16等於47.6(198ms是中斷處理數據的間隔,這期間串口是不斷接收數據,那在這期間能接收到多少數據呢?爲47.6字節,因此要保證至少48字節的存儲空間),約爲48.所以這也就是上面定義這個48的原因。因爲 cfg->rxTick定義了多久處理一次串口緩存區,而爲了保證串口緩衝區不被新的數據覆蓋,那麼在這個間隔期間就必須保證大於48字節存儲空間空閒。
*************
hal_uart.c有這段話:
/* Need to leave enough of the Rx buffer free to handle the incoming bytes
 * after asserting flow control, but before the transmitter has obeyed it.
 * At the max expected baud rate of 115.2k, 16 bytes will only take ~1.3 msecs,
 * but at the min expected baud rate of 38.4k, they could take ~4.2 msecs.
 * SAFE_RX_MIN and DMA_RX_DLY must both be consistent according to
 * the min & max expected baud rate.
 */
我算了半天也算不出上面幾個數來……悲劇!先這樣吧……
對於串口裏面的幾個參數,後面直接轉載《Z-STACK問題之串口結構uartCfg_t亂說》文章好了.
********************************************************
UART接收數據中斷模式流程:(純屬個人理解,還有很多地方不清楚!協議中UART的兩種模式 - 小峯 - happy~)
(1)
ZMain.c調用HalDriverInit(),HalDriverInit調用HalUARTInit()初始化串口驅動程序任務初始化函數,MT_TaskInit()調用SPIMgr_Init()初始化串口配置
(2)
串口接收到數據產生中斷,進入接收中斷服務程序,把串口緩存UxDBUF中的數據傳送到rxbuf[]中去.UxDBUF只能存放1字節,rxbuf[]能存放128字節,但要空出48字節作爲安全區,因此接收數據達到80字節時作爲一個臨界值rxHigh,可以觸發事件HAL_UART_RX_ABOUT_FUL.
(3)


系統主循環函數進入HalUARTPoll()輪詢串口,大約每200ms調用一次pollISR(),然後跳回HalUARTPoll()對rxbuf[]中的數據進行處理——>調用回調函數SPIMgr_ProcessZToolData()分析rxbuf[]中每一字節,構造發往系統的消息——>調用osal_msg_send( MT_TaskID, (byte *)SPI_Msg )發送消息——>觸發MT任務事件,調用MT任務事件處理函數 MT_ProcessEvent()——>調用MT_ProcessCommand(),根據msg->hdr.event——>調用MT_ProcessSerialCommand(),根據參數cmd來進行最終處理.
********************************************************
UART發送數據中斷模式暫時不分析了,一頭霧水……


############################################(下面小段於2010.5.25更新)


對於串口的回調函數,SPIMgr_Init()中配置了兩個:


#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
  uartConfig.callBackFunc         = SPIMgr_ProcessZToolData;  //回調函數
#elif defined (ZAPP_P1) || defined (ZAPP_P2)
  uartConfig.callBackFunc         = SPIMgr_ProcessZAppData;  //回調函數
#else
  uartConfig.callBackFunc         = NULL;


有什麼區別呢?在《Zigbee技術規範與協議棧分析》這篇文章中看到過一段話:
    作爲協調器,如果程序使用了串口調試助手,則DMA將上位機的數據按照一個字節波特率加一個字節數據的形式組裝到cfg->rxBuf中供其他函數調用,並且通過回調函數SPIMgr_ProcessZToolData ( uint8 port, uint8 event )將任務的ID和強制事件發送到任務列表中,供主循環處理函數掃描;作爲終端節點和路由設備,無法使用串口調試助手,則通過回調函數 SPIMgr_ProcessZAppData ( uint8 port, uint8 event ) 將任務的ID和強制事件發送到任務列表中。當掃描至參數events=1,則進入相應層的處理程序進行任務ID和events的約定比對,完成相應的功能。
根據這段話個人在想:ZTOOL上可以發命令數據到ZC??,協調器調用SPIMgr_ProcessZToolData()進行處理,那ZR和ED的串口接收到數據(怎麼接收?)調用SPIMgr_ProcessZAppData(),SPIMgr_ProcessZAppData()最終會調用osal_msg_send():
/**************************************
void SPIMgr_ProcessZAppData ( uint8 port, uint8 event )
{


     …………
        if ( msg_ptr )
        {
          msg_ptr->event = SPI_INCOMING_ZAPP_DATA;//SPI_INCOMING_ZAPP_DATA
          msg_ptr->status = length;


          /* Read the data of Rx buffer *///Rx buffer是讀到上面爲msg_ptr 分配的空間去的
          HalUARTRead( SPI_MGR_DEFAULT_PORT, (uint8 *)(msg_ptr + 1), length );


          /* Send the raw data to application...or where ever */
          osal_msg_send( App_TaskID, (uint8 *)msg_ptr );
        }
     …………
}
/**************************************


至於把串口接收到的數據傳送到哪裏去,這就跟App_TaskID有關,而App_TaskID是通過下面這個函數來定義的:


/**************************************
 * @fn      MT_SerialRegisterTaskID
 *
 * @brief
 *
 *   This function registers the taskID of the application so it knows
 *   where to send the messages whent they come in.
 *
 * @param   void
 *
 * @return  void
 **************************************
void SPIMgr_RegisterTaskID( byte taskID )
{
  App_TaskID = taskID;
}


/*************************************
可以看到,如果想把串口接收到的數據送到應用層任務(比如SampleApp)中,那這裏應該註冊SPIMgr_RegisterTaskID( SampleApp_TaskID ).這樣就可以觸發應用層任務(SampleApp)的事件(SPI_INCOMING_ZAPP_DATA),在事件處理函數中添加一個對事件SPI_INCOMING_ZAPP_DATA進行處理的程序就可以.
但是,我好像看到協調器設備的預編譯裏都是ZTOOL_P1,終端和路由功能的設備的預編譯爲XZTOOL_P1,沒有編譯ZAPP_P1和ZAPP_P2,也就是沒有回調函數,是不是就不能進行串口傳輸了?
那如果我在終端和路由節點上面編譯ZTOOL_P1是否可行??
那如果我在協調器上編譯ZAPP_P1,再在應用任務事件處理函數中添加相應SPI_INCOMING_ZAPP_DATA事件的處理程序,串口接收的數據是否可以順利傳送到應用層??
——等有了實驗套件,我再驗證下!


個人在想,協調器串口接收的數據送到應用層任務中的方法有:
(1)通過默認的SPIMgr_ProcessZToolData()處理函數把數據發送到應用層供應用層使用(這點只是猜測,還沒找着,畢竟MT_ProcessSerialCommand()函數這麼多命令)
(2)通過SPIMgr_RegisterTaskID()註冊相應TaskID,再通過SPIMgr_ProcessZAppData()處理函數把數據發送到相應任務中供其使用(也只是猜測,沒有驗證)
(3)通過串口讀寫函數HalUARTRead()和HalUARTWrite(),這兩個函數可以在應用層中直接調用來讀取串口接收到的數據以及通過串口發送數據。


HalUARTRead( uint8 port, uint8 *buf, uint16 len )//從rxbuf[ ]讀取len長度的數據到*buf所指空間
HalUARTWrite( uint8 port, uint8 *buf, uint16 len )//把len長度的數據從*buf所指空間發送到txbuf[ ]


  以上問題見《協議棧中串口的兩種回調函數》記錄。。。。。。。。                                           
純屬個人想法,有待驗證!


###########################################################################


###########################################################################


2、DMA模式(UART接收)


hal_board_cfg.h中有這段話:


/* The preferred method of implementation is by DMA for faster buad rate
   * support. Customer may prefer to use the DMA channels for something else,
   * in which case USART can be driven by ISR. Also, if the 2nd USART is to be
   * used, this module does not currently support using 2 more DMA channels for
   * it, so it must use ISR.
   */
對於USART操作,波特率高的情況下推薦使用DMA驅動模式.用戶可能習慣把DMA應用在其它地方,這種情況下可以用中斷模式.同樣,如果第二個USART也被使用了,由於USART模塊不能在同一時間支持兩條以上DMA通道,因此這個時候必須使用中斷來驅動.




********************
DMA:
允許不同速度的硬件裝置來溝通,而不需要依於CPU 的大量中斷負載。否則,CPU需要從來源把每一片段的資料複製到暫存器,然後把他們再次寫回到新的地方。在這個時間中,CPU 對於其他的工作來說就無法使用。DMA傳輸方式無需CPU直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢復現場的過程,通過硬件爲RAM與I/O設備開闢一條直接傳送數據的通路,使CPU的效率大爲提高。
********************
typedef struct {
  uint8 srcAddrH;
  uint8 srcAddrL;
  uint8 dstAddrH;
  uint8 dstAddrL;
  uint8 xferLenV;
  uint8 xferLenL;
  uint8 ctrlA;
  uint8 ctrlB;
} halDMADesc_t; //DMA描述符結構體


每一個通道都有相應的描述符.DMA傳送首先要進行DMA參數配置,需要配置以下參數:
(1)源地址:DMA信道要讀的數據的首地址
(2)目標地址:DMA信道從源地址讀出的要寫數據的首地址.目標地址要可寫
(3)傳送長度
(4)可變長度(VLEN)設置
(5)優先級別
(6)觸發事件
(7)源地址和目標地址增量
(8)DMA傳送模式
(9)字節傳送或字傳送
(10)中斷屏蔽
(11)設置M8模式


CC2430中DMA有5條通道,UART默認的是哪個通道呢?
********************
HalUARTInit()中初始化爲:
  // Setup Tx by DMA.
  ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_TX );
  // Setup Rx by DMA.
  ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_RX );


#define HAL_DMA_GET_DESC1234( a )     (dmaCh1234+((a)-1))


// Used by DMA macros to shift 1 to create a mask for DMA registers.
#define HAL_DMA_CH_TX    3  //協議棧默認的用於UART發送的DMA通道 映射後爲DMA通道2
#define HAL_DMA_CH_RX    4  //協議棧默認的用於UART接收的DMA通道 映射後爲DMA通道3


********************
對這兩條分別用於發送與接收的DMA通道的配置是如何的?
********************
在HalUARTInit()中初始化爲:
#if HAL_UART_DMA


//---------------------配置發送爲DMA模式;數據從內存空間傳送到DMA_UDBUF???
  /*選擇DMA通道*/
  // Setup Tx by DMA.
  ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_TX ); //協議棧默認的用於UART發送的DMA通道映射後爲DMA通道2


  /*配置ch->dstAddrH/L*/
  // The start address of the destination.目的地的起始地址
  HAL_DMA_SET_DEST( ch, DMA_UDBUF ); //DMA_UDBUF爲目的地,通過這個函數把DMA_UDBUF地址的高8位
                                                                         //賦給ch->dstAddrH,低8位賦給ch->dstAddrL
  /*配置ch->xferLenV*/
  // Using the length field to determine how many bytes to transfer.
  HAL_DMA_SET_VLEN( ch, HAL_DMA_VLEN_USE_LEN ); //HAL_DMA_VLEN_USE_LEN=0x00 //配置VLEN域使用LEN字段


 


  /*配置ch->ctrlA(通過多次配置ctrlA的每一位,同理包括ctrlB)*/
  // One byte is transferred each time. //初始化爲一次傳輸一字節 
  HAL_DMA_SET_WORD_SIZE( ch, HAL_DMA_WORDSIZE_BYTE );


 


  /*配置ch->ctrlA*/
  // The bytes are transferred 1-by-1 on Tx Complete trigger. //一次觸發只傳輸一個字節,觸發源爲UART0 TX完成 
  HAL_DMA_SET_TRIG_MODE( ch, HAL_DMA_TMODE_SINGLE );
  HAL_DMA_SET_TRIG_SRC( ch, DMATRIG_TX );


 


  /*配置ch->ctrlB*/
  // The source address is decremented by 1 byte after each transfer.每次傳送後源地址減1
  HAL_DMA_SET_SRC_INC( ch, HAL_DMA_SRCINC_1 );


 


  /*配置ch->ctrlB*/
  // The destination address is constant - the Tx Data Buffer.目的地址不變,始終爲Tx Data Buffer
  HAL_DMA_SET_DST_INC( ch, HAL_DMA_DSTINC_0 );


 


  /*配置ch->ctrlB*/
  // The DMA is to be polled and shall not issue an IRQ upon completion.傳送完成後不傳送中斷請求
  HAL_DMA_SET_IRQ( ch, HAL_DMA_IRQMASK_DISABLE );


 


  /*配置ch->ctrlB*/
  // Xfer all 8 bits of a byte xfer. 8位一字節
  HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS );


 


  /*配置ch->ctrlB*/
  // DMA Tx has shared priority for memory access - every other one. 高優先級
  HAL_DMA_SET_PRIORITY( ch, HAL_DMA_PRI_HIGH );




  
//---------------------配置接收爲DMA模式:數據從DMA_UDBUF傳送到內存空間???
  // Setup Rx by DMA.
  ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_RX );//


協議棧默認的用於UART接收的DMA通道 映射後爲DMA通道3


 
  // The start address of the source. 發源地起始地址
  HAL_DMA_SET_SOURCE( ch, DMA_UDBUF );


  // Using the length field to determine how many bytes to transfer.
  HAL_DMA_SET_VLEN( ch, HAL_DMA_VLEN_USE_LEN );


  /* The trick is to cfg DMA to xfer 2 bytes for every 1 byte of Rx.
   * The byte after the Rx Data Buffer is the Baud Cfg Register,
   * which always has a known value. So init Rx buffer to inverse of that
   * known value. DMA word xfer will flip the bytes, so every valid Rx byte
   * in the Rx buffer will be preceded by a DMA_PAD char equal to the
   * Baud Cfg Register value.
   */
  HAL_DMA_SET_WORD_SIZE( ch, HAL_DMA_WORDSIZE_WORD );


  // The bytes are transferred 1-by-1 on Rx Complete trigger.
  HAL_DMA_SET_TRIG_MODE( ch, HAL_DMA_TMODE_SINGLE );
  HAL_DMA_SET_TRIG_SRC( ch, DMATRIG_RX );


  // The source address is constant - the Rx Data Buffer.
  HAL_DMA_SET_SRC_INC( ch, HAL_DMA_SRCINC_0 );


  // The destination address is incremented by 1 word after each transfer.
  HAL_DMA_SET_DST_INC( ch, HAL_DMA_DSTINC_1 );


  // The DMA is to be polled and shall not issue an IRQ upon completion.
  HAL_DMA_SET_IRQ( ch, HAL_DMA_IRQMASK_DISABLE );


  // Xfer all 8 bits of a byte xfer.
  HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS );


  // DMA has highest priority for memory access.
  HAL_DMA_SET_PRIORITY( ch, HAL_DMA_PRI_HIGH );
#endif
********************
對於發送,觸發事件爲HAL_DMA_TRIG_UTX1/0 :USART1/0 TX complete UART中數據發送完成就觸發DMA從內存空間txbuf[ ]傳送數據到串口??
對於接收,觸發事件爲HAL_DMA_TRIG_URX1/0:USART1/0 RX complete UART中數據接收完成就觸發DMA把數據從串口緩存傳送到內存空間rxbuf[ ]??
——我不敢確定!
事實上這一步所完成的任務和中斷服務程序完成的任務是一樣的,發送中斷服務函數:把數據從txbuf[]傳送到串口UDBUF;接收中斷服務函數:把數據從UDBUF傳送到rxbuf[ ];


UART接收數據DMA模式流程:(純屬個人理解,還有很多地方不清楚!協議中UART的兩種模式 - 小峯 - happy~)
(1)
ZMain.c調用HalDriverInit(),HalDriverInit調用HalDmaInit()初始化DMA通道01234的地址:


    halDMADesc_t dmaCh0;      
    halDMADesc_t dmaCh1234[4];
    HAL_DMA_SET_ADDR_DESC0( &dmaCh0 );
    HAL_DMA_SET_ADDR_DESC1234( dmaCh1234 );


(2)
HalUARTInit()函數中對通道2和3進行配置.
(3)
系統主循環函數進入HalUARTPoll()輪詢串口,大約每200ms調用一次 pollDMA()???,然後跳回HalUARTPoll()對rxbuf[]中的數據進行處理——>調用回調函數SPIMgr_ProcessZToolData()分析rxbuf[]中每一字節,構造發往系統的消息——>調用osal_msg_send( MT_TaskID, (byte *)SPI_Msg )發送消息——>觸發MT任務事件,調用MT任務事件處理函數 MT_ProcessEvent()——>調用MT_ProcessCommand(),根據msg->hdr.event——>調用MT_ProcessSerialCommand(),根據參數cmd來進行最終處理.


DMA模式UART發送的暫不鑽了.
###########################################################################


###########################################################################
對於UART的兩種方式,實在有好多地方不搞不懂,以上個人記錄也肯定有錯誤之處,希望哪位對這部分內容熟悉的高手能指點一下具體流程,或是在哪裏看到相關資料給個鏈接提示,感激不盡!協議中UART的兩種模式 - 小峯 - happy~


###########################################################################


###########################################################################


說明:本文作者所記錄,以上基本爲個人見解,錯誤之處還請高手指點,本人隨時更新,轉載請註明出處,謝謝!


參考資料:《Z-STACK問題之串口結構uartCfg_t亂說》:http://hi.baidu.com/dapaopao/blog/item/ccf242900ef2e985a877a48a.html  已轉載.


                                                                                                                      2010.5.17   ~XF
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章