ANT無線通信技術(5) ANT與MCU的SPI通信時序分析及相關程序設計


ANT與MCU可以使用異步UART或同步SPI兩種方式連接。異步通信與同步通信的各自特點這里不贅述,總之我們選擇使用同步方式進行連接。

 

一、SPI簡介

      SPI(Serial Peripheral Interface),串行外設接口。是摩托羅拉公司開發的一種同步全雙工通信協議。依靠收發兩端的移位寄存器,以及主機master提供的時鍾信號,雙方可以實現較高速率的同步全雙工傳輸。

標准的SPI是3/4根線,分別用於一主一從/多主從的情況。4根線分別是:

 

    1. MOSI 主機發,從機收 master out slave in 
    2. MISO 主機收,從機發 master in slave out 
    3. SCLK 主機時鍾輸出,從機時鍾輸入
    4. CS   片選信號,用於多主從情況 

有些命名可能有區別,不過意思相同。

 

二、ANT中的SPI

  SPI固然很強大,然而,由於協議中缺少流控,面對后來出現的各種各樣的外設,他漸漸開始力不從心了。例如,在一些高速應用中,收發雙發需要進行嚴格的握手確認機制,已保證可靠的數據傳輸,然而標准SPI協議中,通過僅有的幾根數據線,master甚至無法知道slave的存在!再比如,萬一MOSI線和MISO線短路到一起了,主機有可能仍然正常工作,而無法發覺從機已掛!這正是我們在調試單機SPI的時候那種做法。因此,現實的情況是,我們所見的很多的SPI設備,其實都並沒有使用標准的SPI協議,而是紛紛開發了多種多樣的SPI兼容的傳輸方式。ANT就是這樣,借用了SPI“擠牙膏”式的數據傳輸機制,配上了自己的一套流控方法,最后全雙工簡化成了半雙工。

      在連接MCU與ANT時,可以使用硬件SPI接口。借助另外三根信號線SRDY,MRDY,EN,便可以完成與ANT模塊的所有通信。當然,對於沒有硬件SPI的低成本單片機,也可以使用軟件模擬SPI來完成通信。

1.MRDY        a Message is ReaDY to ANT

2.EN            ANT modual is ENabled to response

3.SRDY        MCU is ready to receive message from ANT

4.SCLK        same as SPI

5.MOSI        from ANT to MCU

6.MISO        from MCU to ANT

 

      需要指出的是,雖然在功能上十分類似,但由於一些細小的差異,這里的SRDY不能使用SPI中的CS信號線來代替。所以說,我們使用SPI的3pin從模式就可以了。ANT模塊作為SPI主機,MCU做SPI從機。(官方文檔中說到EPSON有一個系列的單片機可以用硬件SPI的片選信號直接作SRDY,我猜測很可能就是那款單片機內置了ANT的這種SPI兼容協議)

      我的Launchpad是MSP430G2553,恰好具有一個硬件SPI,所以接下來這里我們實現硬件SPI接口的連接。既然是同步通信,自然少不了時序。TIME IS MONEY!

      nRF24AP2的SPI通信機制基本描述如下:

    • 半雙工模式的SPI,ANT為主,MCU為從
    • MRDY,EN,外加SRDY,共同完成握手以及流控
    • nRF24AP2作主節點時,自動轉發所有收到的消息至MCU
    • MCU使用串口,必須在請求得到nRF24AP2回應后才可以

 

 

1.同步

      ANT模塊上電后,需要與MCU進行一次同步操作,才能正常開始SPI通信。同步可以通過兩種方式完成,一種是拉一下ANT上面的RESET-Pin,簡單可靠,但需要設計硬件按鈕,或者占用一個MCU的接口。另一種就是通過MRDY和SRDY,EN三根線完成一個握手,純軟件實現,成本低廉。下面的圖描述了完成一次SPI同步初始化握手的時序圖,我們也可以認為這個過程是實現了一個同步序列Synchronous Sequence。

      藍色的箭頭標明了數據的流向,下同。我簡潔地表述下同步的整個過程:

a) 最初,MCU拉高MRDY,SRDY

b) 首先,MCU拉低SRDY

c) 間隔至少250us,然后MCU拉低MRDY

d) 若SEN為低,等待ANT拉高SEN

e) ANT拉高SEN后會再次拉低SEN,表示同步完成

f) MCU最后拉高SRDY,同步結束


 

2.MCU接收 (SYNC=0xA4)

      通過此過程,ANT得以發送消息至MCU。須知,一個消息的完整傳輸,第一個字節也就是SYNC字節,總是由ANT發起。SYNC字節(的最低位)標明了后續字節的流向。

    

簡潔描述如下:

a) ANT自動拉低SEN,提示MCU有消息給你

b) MCU發覺后,待自己准備好了,便發送一個SRDY的負脈沖告訴ANT,脈沖持續至少2.5us

c) ANT感知到負脈沖,隨后將使能SCLK信號,發送SYNC字節0xA4

d) MCU再次准備好后,便可繼續來一發SRDY負脈沖,進行后續字節的通信

 

3.MCU發送 (SYNC=0xA5)

      當MCU有消息要發送給ANT模塊的時候,進行這個操作。同樣的,第一個SYNC字節仍是來自ANT。正確的SYNC,標明ANT已經准備好接收消息。

 

簡潔描述如下:

a) MCU拉低MRDY,表示自己有消息要給ANT

b) ANT發覺后,拉低SEN回應

c) MCU得到回應,准備完畢便發送一個SRDY負脈沖,至少2.5us

d) ANT聽到負脈沖,便發出SCLK信號,SYNC字節0xA5隨之而來

e) 確認SYNC字節無誤后,MCU再次給出一個SRDY負脈沖,表示自己隨時可以發送數據

f)ANT聽到負脈沖,便發出SCLK信號,指引MCU發送下一個字節的數據

e) MCU根據SCLK的節奏,隨即發出下一個字節

g) 后續操作相同

 

三、基本程序設計

      根據以上過程,可以歸納出大致的程序流程圖。

接下來是CODE TIME!

1.先是同步的部分。

 1 ///////////////////////////////////////////////////////////////////////////////////
 2 //完成同步序列的發送
 3 //返回值:    IS_EN_ASSERTED()
 4 BOOL ByteSyncSerial_SyncSequenceReset(void)
 5 {
 6     SYNC_SRDY_DEASSERT();                                                            //先拉高SRDY,MRDY
 7     SYNC_MRDY_DEASSERT();
 8     Timer_DelayTime(300);                                                            //等待ANT初始化,約1ms
 9 
10     //正式開始同步序列
11     SYNC_SRDY_ASSERT();                                                                //先拉低SRDY
12 
13     Timer_DelayTime(10);                                                            //等待10*30us=300us》250us
14 
15     SYNC_MRDY_ASSERT();                                                                //再拉低MRDY
16 
17     Timer_DelayTime(1000);                                                            //等待ANT拉低EN響應
18 
19     SYNC_SRDY_ASSERT();                                                                //拉低SRDY
20     //同步結束
21 
22     return IS_EN_ASSERTED();                                                        //返回EN是否已拉低
23 }

 

 

2.然后是接收和發送消息的部分。

 1 ////////////////////////////////////////////////////////////////////////////////////
 2 //用於接收消息或發送消息,發送的消息用指針引自發送緩存pucTxMsgBuffer,接收的消息用指針保存至接收緩存pucRxMsgBuffer
 3 //參數:    pucTxMsgBuffer
 4 //        pucRxMsgBuffer
 5 //返回值:    bRxMessage
 6 BOOL ByteSyncSerial_Transaction(UCHAR *pucTxMsgBuffer, UCHAR *pucRxMsgBuffer)
 7 {
 8    BOOL  bRxMessage = FALSE;                                                        //成功接收消息標志
 9    UCHAR ucCheckSum;
10    UCHAR ucMesgLen;
11    UCHAR ucIndex;
12    UCHAR ucByte;
13 
14    if (pucTxMsgBuffer != NULL)                                                       //MCU有消息發給ANT
15    {
16       SYNC_MRDY_ASSERT();                                                              //拉高MRDY通知ANT
17 
18       {
19          UCHAR ucEnTimeout = EN_ASSERT_TIMEOUT;
20 
21          do                                                                          //循環等待ANT拉低EN來響應
22          {
23             if (!(ucEnTimeout--))
24             {
25                ucEnTimeout = EN_ASSERT_TIMEOUT;
26 
27                SYNC_MRDY_DEASSERT();                                                   //重試拉低MRDY
28                TIMER_EN_DELAY();
29                SYNC_MRDY_ASSERT();
30             }
31 
32             TIMER_EN_DELAY();                                                          //等待10us
33          }
34          while (!IS_EN_ASSERTED());                                                 //直到EN拉低
35       }
36    }
37 
38    while (IS_MRDY_ASSERTED() || (IS_EN_ASSERTED() && (!bRxMessage)))                 //MCU有消息至ANT,或者ANT已響應即將有個消息要發過來且還沒收到
39    {
40       while (!IS_EN_ASSERTED());
41 
42       ucByte     = ReadByte();                                                            //讀取SYNC字節
43       ucCheckSum = ucByte;                                                            //初始化校驗和
44 
45       if (ucByte == MESG_RX_SYNC)                                                     //若SYNC=0xA5 MCU->ANT
46       {
47          SYNC_MRDY_DEASSERT();                                                         //拉高MRDY信號,MCU有話要說
48 
49          ucMesgLen = pucTxMsgBuffer[0] + MESG_SAVED_FRAME_SIZE;                     //讀取待發送信息的長度,並根據DataLength位和ID位+2
50          ucIndex   = 0;
51 
52          SYNC_SRDY_ASSERT();                                                         //准備好了可以開始寫了
53 
54          do                                                                            //循環寫完信息(不含校驗和),並更新校驗和
55          {
56             WriteByte(pucTxMsgBuffer[ucIndex]);
57             ucCheckSum ^= pucTxMsgBuffer[ucIndex];
58          }
59          while (++ucIndex < ucMesgLen);
60 
61          WriteByte(ucCheckSum);                                                       //寫出校驗和
62       }
63       else if (ucByte == MESG_TX_SYNC)                                               //若SYNC=0xA4 MCU<-ANT
64       {
65          SYNC_SRDY_ASSERT();                                                        //確認完畢,准備好接受下一個字節
66          ucByte    = ReadByte();                                                    //第二個字節發過來的就是DataLength
67          ucMesgLen = ucByte + MESG_SAVED_FRAME_SIZE;                                  //DataLength+2得到整體長度
68          ucIndex   = 0;
69 
70          do                                                                            //循環讀完剩下的消息
71          {
72             pucRxMsgBuffer[ucIndex] = ucByte;                                        //存入接收緩存
73             ucCheckSum ^= ucByte;                                                    //更新校驗和
74             ucByte      = ReadByte();
75          }
76          while (++ucIndex < ucMesgLen);
77 
78          if (ucCheckSum == ucByte)                                                    //確認校驗和無誤
79             {
80                bRxMessage = TRUE;                                                    //置位成功接收標志
81                SYNC_SRDY_ASSERT();                                                    //處理完畢,等待下一個字節到來
82             }
83       }
84    }
85 
86    return bRxMessage;                                                                 //返回接收信息標志
87 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM