stm32 can總線 基礎

選自stm32 canhtml

最近在搞stm32實驗板的can現場總線實驗,以前只是搞過STC51的串口通訊,相比之下,發覺can總線都挺複雜的。開始時,知道本身是新手,只知道can總線跟串行通訊,485通訊,I2C通訊同樣都是用來傳輸數據通訊的,對其工做原理一竅不通,仍是從基礎開始看書看資料,先了解它的基本原理吧。編程


原來can總線有如下特色:less

主要特色函數

支持CAN協議2.0A和2.0B主動模式oop

波特率最高可達1兆位/秒spa

支持時間觸發通訊功能debug

發送設計

3個發送郵箱orm

發送報文的優先級特性可軟件配置htm

記錄發送SOF時刻的時間戳

接收

3級深度的2個接收FIFO

14個位寬可變的過濾器組-由整個CAN共享

標識符列表

FIFO溢出處理方式可配置

記錄接收SOF時刻的時間戳

可支持時間觸發通訊模式

禁止自動重傳模式

16位自由運行定時器

定時器分辨率可配置

可在最後2個數據字節發送時間戳

管理

中斷可屏蔽

郵箱佔用單獨1塊地址空間,便於提升軟件效率



看完這些特色後,疑問一個一個地出現,

1. 什麼是時間觸發功能?

2. 發送郵箱是什麼來的?

3. 報文是什麼來的?

4. 什麼叫時間戳?

5. 什麼叫接收FIFO?

6. 什麼叫過濾器?

好了,帶着疑問往下看,看完一遍後,

報文:

報文包含了將要發送的完整的數據信息

發送郵箱:

共有3個發送郵箱供軟件來發送報文。發送調度器根據優先級決定哪一個郵箱的報文先被髮送。

接收過濾器:

共有14個位寬可變/可配置的標識符過濾器組,軟件經過對它們編程,從而在引腳收到的報文中選擇它須要的報文,而把其它報文丟棄掉。


接收FIFO

共有2個接收FIFO,每一個FIFO均可以存放3個完整的報文。它們徹底由硬件來管理


工做模式

bxCAN有3個主要的工做模式:初始化、正常和睡眠模式。


初始化模式

*軟件經過對CAN_MCR寄存器的INRQ位置1,來請求bxCAN進入初始化模式,而後等待硬件對CAN_MSR寄存器的INAK位置1來進行確認

*軟件經過對CAN_MCR寄存器的INRQ位清0,來請求bxCAN退出初始化模式,當硬件對

CAN_MSR寄存器的INAK位清0就確認了初始化模式的退出。

*當bxCAN處於初始化模式時,報文的接收和發送都被禁止,而且CANTX引腳輸出隱性位(高電平)

正常模式

在初始化完成後,軟件應該讓硬件進入正常模式,以便正常接收和發送報文。軟件能夠經過

對CAN_MCR寄存器的INRQ位清0,來請求從初始化模式進入正常模式,而後要等待硬件對

CAN_MSR寄存器的INAK位置1的確認。在跟CAN總線取得同步,即在CANRX引腳上監測到11個連續的隱性位(等效於總線空閒)後,bxCAN才能正常接收和發送報文。

過濾器初值的設置不須要在初始化模式下進行,但必須在它處在非激活狀態下完成(相應的FACT位爲0)。而過濾器的位寬和模式的設置,則必須在初始化模式下,進入正常模式前完成。

睡眠模式(低功耗)

*軟件經過對CAN_MCR寄存器的SLEEP位置1,來請求進入這一模式。在該模式下,bxCAN的時鐘中止了,但軟件仍然能夠訪問郵箱寄存器。

*當bxCAN處於睡眠模式,軟件想經過對CAN_MCR寄存器的INRQ位置1,來進入初始化式,

那麼軟件必須同時對SLEEP位清0才行

*有2種方式能夠喚醒(退出睡眠模式)bxCAN:經過軟件對SLEEP位清0,或硬件檢測CAN

總線的活動。


工做流程

那麼究竟can是怎樣發送報文的呢?

發送報文的流程爲:

應用程序選擇1個空發送郵箱;設置標識符(接收的時候須要過濾掉,過濾器只過濾本身須要的數據,其餘的報文丟掉),數據長度和待發送數據;

而後對CAN_TIxR寄存器的TXRQ位置1,來請求發送。TXRQ位置1後,郵箱就再也不是空郵箱;而一旦郵箱再也不爲空,軟件對郵箱寄存器就再也不有寫的權限。TXRQ位置1後,郵箱立刻進入掛號狀態,並等待成爲最高優先級的郵箱,參見發送優先級。一旦郵箱成爲最高優先級的郵箱,其狀態就變爲預約發送狀態。一旦CAN總線進入空閒狀態,預約發送郵箱中的報文就立刻被髮送(進入發送狀態)。一旦郵箱中的報文被成功發送後,它立刻變爲空郵箱;硬件相應地對CAN_TSR寄存器的RQCP和TXOK位置1,來代表一次成功發送。


若是發送失敗,因爲仲裁引發的就對CAN_TSR寄存器的ALST位置1,因爲發送錯誤引發的

就對TERR位置1。


原來發送的優先級能夠由標識符和發送請求次序決定:

由標識符決定

當有超過1個發送郵箱在掛號時,發送順序由郵箱中報文的標識符決定。根據CAN協議,標

識符數值最低的報文具備最高的優先級。若是標識符的值相等,那麼郵箱號小的報文先被髮

送。

由發送請求次序決定

經過對CAN_MCR寄存器的TXFP位置1,能夠把發送郵箱配置爲發送FIFO。在該模式下,發送的優先級由發送請求次序決定。

該模式對分段發送頗有用。


時間觸發通訊模式

在該模式下,CAN硬件的內部定時器被激活,而且被用於產生時間戳,分別存儲在

CAN_RDTxR/CAN_TDTxR寄存器中。內部定時器在接收和發送的幀起始位的採樣點位置被採樣,並生成時間戳(標有時間的數據)。



接着又是怎樣接收報文的呢?

接收管理

接收到的報文,被存儲在3級郵箱深度的FIFO中。FIFO徹底由硬件來管理,從而節省了CPU

的處理負荷,簡化了軟件並保證了數據的一致性。應用程序只能經過讀取FIFO輸出郵箱,來讀取FIFO中最早收到的報文。


有效報文

根據CAN協議,當報文被正確接收(直到EOF域的最後1位都沒有錯誤),且經過了標識符

過濾,那麼該報文被認爲是有效報文。


接收相關的中斷條件

* 一旦往FIFO存入1個報文,硬件就會更新FMP[1:0]位,而且若是CAN_IER寄存器的FMPIE位爲1,那麼就會產生一箇中斷請求。

* 當FIFO變滿時(即第3個報文被存入),CAN_RFxR寄存器的FULL位就被置1,而且若是CAN_IER寄存器的FFIE位爲1,那麼就會產生一個滿中斷請求。

* 在溢出的狀況下,FOVR位被置1,而且若是CAN_IER寄存器的FOVIE位爲1,那麼就會產生一個溢出中斷請求



標識符過濾

在CAN協議裏,報文的標識符不表明節點的地址,而是跟報文的內容相關的。所以,發送者以廣播的形式把報文發送給全部的接收者。(注:不是一對一通訊,而是多機通訊)節點在接收報文時-根據標識符的值-決定軟件是否須要該報文;若是須要,就拷貝到SRAM裏;若是不須要,報文就被丟棄且無需軟件的干預。

爲知足這一需求,bxCAN爲應用程序提供了14個位寬可變的、可配置的過濾器組(13~0),

以便只接收那些軟件須要的報文。硬件過濾的作法節省了CPU開銷,不然就必須由軟件過濾從而佔用必定的CPU開銷。每一個過濾器組x由2個32位寄存器,CAN_FxR0和CAN_FxR1組成。



過濾器的模式的設置

經過設置CAN_FM0R的FBMx位,能夠配置過濾器組爲標識符列表模式屏蔽位模式

爲了過濾出一組標識符,應該設置過濾器組工做在屏蔽位模式

爲了過濾出一個標識符,應該設置過濾器組工做在標識符列表模式

應用程序不用的過濾器組,應該保持在禁用狀態。


過濾器優先級規則

位寬爲32位的過濾器,優先級高於位寬爲16位的過濾器

對於位寬相同的過濾器,標識符列表模式的優先級高於屏蔽位模式

位寬和模式都相同的過濾器,優先級由過濾器號決定,過濾器號小的優先級高


圖128 過濾器機制的例子

file:///C:/Users/skl/AppData/Local/Temp/ksohtml/wps_clip_image-17227.png


上面的例子說明了bxCAN的過濾器規則:在接收一個報文時,其標識符首先與配置在標識符

列表模式下的過濾器相比較;若是匹配上,報文就被存放到相關聯的FIFO中,而且所匹配的

過濾器的序號被存入過濾器匹配序號中。如同例子中所顯示,報文標識符跟#4標識符匹配,

所以報文內容和FMI4被存入FIFO。

若是沒有匹配,報文標識符接着與配置在屏蔽位模式下的過濾器進行比較。

若是報文標識符沒有跟過濾器中的任何標識符相匹配,那麼硬件就丟棄該報文,且不會對軟

件有任何打擾。


接收郵箱(FIFO)

在接收到一個報文後,軟件就能夠訪問接收FIFO的輸出郵箱來讀取它。一旦軟件處理了報文(如把它讀出來),軟件就應該對CAN_RFxR寄存器的RFOM位進行置1,來釋放該報文,

以便爲後面收到的報文留出存儲空間。



中斷

bxCAN佔用4個專用的中斷向量。經過設置CAN中斷容許寄存器(CAN_IER),每一箇中斷源都

能夠單獨容許和禁用。


發送中斷可由下列事件產生:

─ 發送郵箱0變爲空,CAN_TSR寄存器的RQCP0位被置1。

─ 發送郵箱1變爲空,CAN_TSR寄存器的RQCP1位被置1。

─ 發送郵箱2變爲空,CAN_TSR寄存器的RQCP2位被置1。

FIFO0中斷可由下列事件產生:

─ FIFO0接收到一個新報文,CAN_RF0R寄存器的FMP0位再也不是‘00’。

─ FIFO0變爲滿的狀況,CAN_RF0R寄存器的FULL0位被置1。

─ FIFO0發生溢出的狀況,CAN_RF0R寄存器的FOVR0位被置1。

FIFO1中斷可由下列事件產生:

─ FIFO1接收到一個新報文,CAN_RF1R寄存器的FMP1位再也不是‘00’。

─ FIFO1變爲滿的狀況,CAN_RF1R寄存器的FULL1位被置1。

─ FIFO1發生溢出的狀況,CAN_RF1R寄存器的FOVR1位被置1。

��� 錯誤和狀態變化中斷可由下列事件產生:

─ 出錯狀況,關於出錯狀況的詳細信息請參考CAN錯誤狀態寄存器(CAN_ESR)。

─ 喚醒狀況,在CAN接收引腳上監視到幀起始位(SOF)。

─ CAN進入睡眠模式。


工做流程大概就是這個樣子,接着就是一大堆煩人的can寄存器,看了一遍總算有了大概的瞭解,何況這麼多的寄存器要一會兒把他們都記住是不可能的。根據以往的經驗,只要用多幾回,對寄存器的功能就能記住。


好了,到讀具體實驗程序的時候了,這時候就要打開「STM32庫函數」的資料

由於它裏面有STM32打包好的庫函數的解釋,對讀程序頗有幫助。


下面是主程序:

int main(void)

{

//  int press_count = 0;

char data = '0';

int sent = FALSE;


#ifdef DEBUG

  debug();

#endif


  /* System Clocks Configuration */

  RCC_Configuration();


  /* NVIC Configuration */

  NVIC_Configuration();


  /* GPIO ports pins Configuration */

  GPIO_Configuration();


  USART_Configuration();


  CAN_Configuration();


  Serial_PutString("\r\n偉研科技 http://www.gzweiyan.com\r\n");

  Serial_PutString("CAN test\r\n");


  while(1){

    if(GPIO_Keypress(GPIO_KEY, BUT_RIGHT)){

      GPIO_SetBits(GPIO_LED, GPIO_LD1);//檢測到按鍵按下


      if(sent == TRUE)

        continue;

      sent = TRUE;

      data++;

      if(data > 'z')

        data = '0';

      CAN_TxData(data);

    }

    else{//按鍵放開

      GPIO_ResetBits(GPIO_LED, GPIO_LD1);  

      sent = FALSE;

    }

  }

}


前面的RCC、NVIC、GPIO、USART配置和以前的實驗大同小異,關鍵是分析

CAN_Configuration()

函數以下:

void CAN_Configuration(void)//CAN配置函數

{

  CAN_InitTypeDef        CAN_InitStructure;

  CAN_FilterInitTypeDef  CAN_FilterInitStructure;


  /* CAN register init */

  CAN_DeInit();        //can初始化

//  CAN_StructInit(&CAN_InitStructure);


  /* CAN cell init */

  CAN_InitStructure.CAN_TTCM=DISABLE;//禁止時間觸發通訊模式

  CAN_InitStructure.CAN_ABOM=DISABLE;//,軟件對CAN_MCR寄存器的INRQ位進行置1隨後清0後,一旦硬件檢測

                                     //到128次11位連續的隱性位,就退出離線狀態。


  CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式經過清除CAN_MCR寄存器的SLEEP位,由軟件喚醒


  CAN_InitStructure.CAN_NART=ENABLE;//DISABLE;CAN報文只被發送1次,無論發送的結果如何(成功、出錯或仲裁丟失)


  CAN_InitStructure.CAN_RFLM=DISABLE;//在接收溢出時FIFO未被鎖定,當接收FIFO的報文未被讀出,下一個收到的報文會覆蓋原有

                                                            //的報文


  CAN_InitStructure.CAN_TXFP=DISABLE;//發送FIFO優先級由報文的標識符來決定

//  CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack;

  CAN_InitStructure.CAN_Mode=CAN_Mode_Normal; //CAN硬件工做在正常模式

  CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;//從新同步跳躍寬度1個時間單位

  CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;//時間段1爲8個時間單位

  CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;//時間段2爲7個時間單位

  CAN_InitStructure.CAN_Prescaler = 9; //(pclk1/((1+8+7)*9)) = 36Mhz/16/9 = 250Kbits設定了一個時間單位的長度9

  CAN_Init(&CAN_InitStructure);


  /* CAN filter init 過濾器初始化*/

  CAN_FilterInitStructure.CAN_FilterNumber=0;//指定了待初始化的過濾器0

  CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;//指定了過濾器將被初始化到的模式爲標識符屏蔽位模式

  CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;//給出了過濾器位寬1個32位過濾器


  CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;//用來設定過濾器標識符(32位位寬時爲其高段位,16位位寬時爲第一個)


  CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;//用來設定過濾器標識符(32位位寬時爲其低段位,16位位寬時爲第二個


  CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//用來設定過濾器屏蔽標識符或者過濾器標識符(32位位寬時爲其高段位,16位位寬時爲第一個


  CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;//用來設定過濾器屏蔽標識符或者過濾器標識符(32位位寬時爲其低段位,16位位寬時爲第二個


  CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;//設定了指向過濾器的FIFO0


  CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//使能過濾器

  CAN_FilterInit(&CAN_FilterInitStructure);


  /* CAN FIFO0 message pending interrupt enable */

  CAN_ITConfig(CAN_IT_FMP0, ENABLE);//使能指定的CAN中斷

}







再看看發送程序:

TestStatus CAN_TxData(char data)

{

  CanTxMsg TxMessage;


  u32 i = 0;

  u8 TransmitMailbox = 0;

/*

  u32 dataLen;

  dataLen = strlen(data);

  if(dataLen > 8)

  dataLen = 8;

  */

  /* transmit 1 message生成一個信息 */

  TxMessage.StdId=0x00;// 設定標準標識符

  TxMessage.ExtId=0x1234;// 設定擴展標識符

  TxMessage.IDE=CAN_ID_EXT;// 設定消息標識符的類型,使用標準標識符+擴展標識符

  TxMessage.RTR=CAN_RTR_DATA;// 設定待傳輸消息的幀類型,數據幀

/*  TxMessage.DLC= dataLen;

for(i=0;i<dataLen;i++)

TxMessage.Data = data;

*/

  TxMessage.DLC= 1; //設定待傳輸消息的幀長度

  TxMessage.Data[0] = data;// 包含了待傳輸數據



  TransmitMailbox = CAN_Transmit(&TxMessage);//開始一個消息的傳輸


  i = 0;

  while((CAN_TransmitStatus(TransmitMailbox) != CANTXOK) && (i != 0xFF))//經過檢查CANTXOK位來確認發送是否成功

  {

    i++;

  }




  return (TestStatus)ret;

}



CAN_Transmit()函數的操做包括:

1. [選擇一個空的發送郵箱]

2. [設置Id]*

3. [設置DLC待傳輸消息的幀長度]

4. [請求發送]

請求發送語句:

CAN->sTxMailBox[TransmitMailbox].TIR |= TMIDxR_TXRQ;//對CAN_TIxR寄存器的TXRQ位置1,來請求發送




發送方面搞定了,但接收方面呢?好像在主程序裏看不到有接收的語句。馬上向師兄求救。

原來是用來中斷方式來接收數據,原來它和串口同樣能夠有兩種方式接收數據,一種是中斷方式一種是輪詢方式,若採用輪詢方式則要調用主函數的CAN_Polling(void)函數。


接着又遇到一個問題,爲何中斷函數CAN_Interrupt(void)的最後要關中斷呢?

由於一旦往FIFO存入1個報文,硬件就會更新FMP[1:0]位,而且若是CAN_IER寄存器的FMPIE位爲1,那麼就會產生一箇中斷請求。因此中斷函數執行完後就要清除FMPIE標誌位。這時我纔回想起來,原來我對CAN的理解還不夠,對程序設計的初衷不夠明確,因而我從新看了一遍CAN的工做原理,這時後我發現比之前容易理解了,多是由於看了程序之後知道了大概的流程,而後看資料就有了針對性。


發送者以廣播的形式把報文發送給全部的接收者(注:不是一對一通訊,而是多機通訊)節點在接收報文時-根據標識符的值-決定軟件是否須要該報文;若是須要,就拷貝到SRAM裏;若是不須要,報文就被丟棄且無需軟件的干預。一旦往FIFO存入1個報文,硬件就會更新FMP[1:0]位,而且若是CAN_IER寄存器的FMPIE位爲1,那麼就會產生一箇中斷請求。因此中斷函數執行完後就要清除FMPIE標誌位。


報文組成u32 標準標識符+u32擴展標識符+u8標識符類型+u8待傳輸消息的幀類型+u8幀長度+u8待傳輸數據

相關文章
相關標籤/搜索