基於CAN 匯流排的資料傳輸與分析討論
阿新 • • 發佈:2018-12-16
基於CAN匯流排的資料傳輸與分析討論
首先我們來討論一下為什麼要使用CAN通訊?以及使用CAN通訊能給我們帶來什麼樣的方便?
首先不得不說的是CAN通訊現在被應用於很多的領域,比如說汽車And so on........CAN通訊的好處很簡單,它可以以最少的CPU消耗來處理一大批的檔案,當然這裡所說的檔案是指傳輸的資料之類的,為什麼要傳輸資料呢?因為有些資料你想管理,哈哈哈!
為什麼說CAN通訊可以減少CPU的消耗呢?因為很簡單,CAN把你要處理的特殊資料幫你給處理好了,就好像別人幫你做好了一樣,你只要告訴CAN你想要哪些資料就好了,它就會幫你把你想要的資料給你挑選出來,來供你使用。
而且最重要的一點就是CAN的資料傳輸只是需要兩根線就OK了,哈哈哈 ,你很難想像兩根線來完成那麼多資料的處理吧。通過這兩根線,就可以輕鬆搭建一個最小區域網,簡單來說就是讓這些控制器通過CAN匯流排相互打交道,讓它們自己嗨起來!
當下我們來分析一下STM32中的CAN通訊原理,我所使用的是一款32的微控制器(ZET6),不過都一樣了,不管使用什麼樣的控制器,原理都是一樣了。
首先來讓我們看看CAN的工作框圖
哈哈 是不是覺得還是比較簡單的,首先允許我說一下那兩個電阻是什麼東東,這兩個電阻如果你學習過射頻的話,估計都不用我多說啥,這裡照顧一下那些沒有接觸過射頻的童鞋了,這兩個電阻其實它有一個好聽的名字是叫做終端電阻,用途是用來做阻抗匹配的,減少回波反射的。 (不懂的話可以自己查查) CAN總線上有兩個電平,一個顯性,一個是隱性電平。也就是CAN_High,CAN_Low,這兩個電平在工作的時候電壓差是2.5V。這裡不難理解了。如果對這個電平不太瞭解的你可以看下TGA1050這款晶片,是CAN的一個收發晶片。(你值得了解) 好了我們接下來開始進入重點學習了
CAN呢!它的協議主要是通過5種類型餓幀構成的。
幀型別
幀用途
資料幀
用於傳送單元向接收單元傳送資料的幀
遙控幀
用於接收單元向具有相同ID的傳送單元請求資料的幀
錯誤幀
用於當檢查出錯時向其他單元通知錯誤的幀
過載幀
用於接收單元通知其尚未做好接收準備的幀
間隔幀
用於將資料幀及遙控幀與前面的幀分離開來的幀
由於本人比較懶啦,所以以最重要的一個數據幀來講解一下
資料幀有7個段構成,即
1、幀起始。表示資料幀開始的段。
2、仲裁段。表示該幀優先順序的段。
3、控制段。表示資料的位元組數及保留位的段。
4、資料段。資料的內容,一幀可以傳送0~8個位元組的資料。
5、CRC段。檢查幀的傳輸錯誤的段。
6、ACK段。表示確認正常接收的段。
7、幀結束。表示資料幀結束的段。
一下是資料幀的兩種形式,一種是標準幀,一種是擴充套件幀。
這裡提一下,不要將ID視為高7位全部設定為隱性位。RTR表示是否為遠端幀,(0:資料幀。1:遠端幀)IDE位表示表示符選擇位(0:使用標準識別符號。1:表示使用擴充套件識別符號)r0和r1為保留位,必須全部以顯性電平發出。SRR位為代替遠端請求位,為隱性位,它代替了標準幀中RTR,DLC是資料長度表示段,DLC段的有效值為0~8。資料段最多可以有8個位元組,從最高位MSB位開始送出。CRC段,該段用於檢查幀傳輸錯誤,由15個位的CRC順序和1個位的CRC界定符(用於分隔的位)組成。CRC的計算範圍包括幀起始,仲裁段,控制段和資料段。接收方以同樣的方式進行計算,兩者不一致時會通報錯誤。ACK段有ACK曹和ACK界定符兩個位組成,傳送單元的ACK,傳送兩個隱性位,人而接收單元在ACK槽f傳送顯性位,EOF是幀結束,由7個隱性位組成。 至此資料幀的7個段就介紹完成了。 好了下面我們再來介紹一下什麼叫做位速率?
由傳送單元在非同步的情況下發送的每秒鐘的位數稱為位速率。一個位可以分為4段。
1、同步段(ss)
2、傳播時間段(PTS)
3、相位緩衝段1(PBS1)
4、相位緩衝段2(PBS2)
這些段又可稱為Time Quantum(簡稱Tq)的最小時間單位構成。
1位分為4個段,每個段又由若干個Tq構成,這稱為位時序。
一個位又由多少個Tq構成、每個段又由多少個Tq構成等,可以任意設定位時序。通過設定位時序,多個單元可同時取樣,也可以任意設定取樣點。各段的作用和Tq可如下表示
這些位的設定可以用來設定CAN的波特率,具體計算如下:
OK到此就把CAN通訊的波特率以及工作模式和幀型別介紹的差不多了。 接下來開始真正的進入CAN最為關鍵而且最為有意思的CAN的過濾器組。 首先來介紹一下CAN的一些資源,CAN支援時間觸發通訊,具有3個傳送郵箱,具有3級深度的2個接收FIFO和可變的過濾器組(最多有28個)。 讓我們來看一下CAN過濾器的工作情況
這裡我用的晶片是STM32ZET6的一款,只有一個CAN控制器, 這個就是CAN的有趣之處了,就是說它可以佔用CPU資源然後自己去篩選合適的來進行過濾。哈哈哈 每個過濾器組都有2個32位的暫存器,CAN_FxR1和CAN_FxR2組成。 每個過濾器組的位寬都可以獨立配置,以滿足不同應用程式的不同需求。 1個32位的過濾器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位 2個16位的過濾器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位 過濾器可以配置為遮蔽位模式和表示符列表模式。 遮蔽位模式就是說可以對識別符號的一些位進行遮蔽,可以讓過濾器組不去匹配這些位,然後匹配通過後才讓資料進來。 識別符號列表模式,收到報文的識別符號的每一位都必須和過濾器識別符號相同,才能讓你進來,否則不讓通過。 可以通過相關暫存器配置它的模式和位寬。如下
好了所有的條件都已經準備好了,接下來我們可以進入CAN的接收和傳送流程了 CAN的傳送流程為:總共有三個郵箱,程式選擇1個空的郵箱(TME-1)->設定識別符號的ID,資料長度和傳送的資料->設定CAN_TIxR的TXRQ位為1,請求傳送->郵箱掛號(等待成為最高的優先順序)->預定傳送(等待匯流排空閒)->傳送->郵箱空。流程如下圖:
CAN接收到的有效報文,被存放在3級郵箱深度的FIFO中。FIFO完全由硬體管理,從而節省了CPU的處理負荷。 CAN的接收流程為:FIFO空->收到有效報文->掛號_1(存入FIFO的一個郵箱,這個是由硬體控制,我們不需要理會)->收到有效的報文->掛號_2->收到有效報文->收到有效報文->掛號_3->收到有效報文->溢位。每次讀取一個報文時掛號就減1,直到FIFO空,CAN的接收流程圖如下:
好了至此我們已經將CAN的重要關卡都打通了, 下面來看一下軟體的配置 #include "CAN.H"
/*
CAN 初始化
Fclk初始化時鐘設定為36Mhz
tsjw:重新同步跳躍時間單元.範圍:CAN_SJW_1tq~ CAN_SJW_4tq
tbs2:時間段 2 的時間單元. 範圍:CAN_BS2_1tq~CAN_BS2_8tq;
tbs1:時間段 1 的時間單元. 範圍:CAN_BS1_1tq ~CAN_BS1_16tq
brp :波特率分頻器.範圍:1~1024; tq=(brp)*tpclk1
波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,迴環模式;
Fpclk1 的時鐘在初始化的時候設定為 36M,如果設定
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
則波特率為:36M/((8+9+1)*4)=500Kbps
返回值:0,初始化 OK;
其他,初始化失敗;
*/
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);//使能CAN時鐘
//判斷是否使能CAN的接收中斷?
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//設定為複用推輓輸出,用來輸出CAN的資料
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉輸入,用來對資料的接收
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//CAN的單元設計
CAN_InitStructure.CAN_TTCM=DISABLE;//非時間觸發通訊
CAN_InitStructure.CAN_ABOM=DISABLE;//軟體自動離線管理
CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通過軟體喚醒
CAN_InitStructure.CAN_NART=ENABLE;//禁止報文自動傳輸
CAN_InitStructure.CAN_RFLM=DISABLE;//報文不鎖定,新的覆蓋舊的
CAN_InitStructure.CAN_TXFP=DISABLE;//優先順序由報文識別符號決定
CAN_InitStructure.CAN_Mode=mode;//模式設定 mode:0,普通模式 1,迴環模式
//設定波特率
CAN_InitStructure.CAN_SJW=tsjw;//設定從新跳躍寬度
CAN_InitStructure.CAN_BS1=tbs1;//時間段1設定
CAN_InitStructure.CAN_BS2=tbs2;//時間段2設定
CAN_InitStructure.CAN_Prescaler=brp;//分頻係數
CAN_Init(CAN1,&CAN_InitStructure);
//設定濾波器的相關係數
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//過濾器0
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//關聯到FIFO0這個接收郵箱上
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;//32位寬的暫存器
CAN_FilterInit(&CAN_FilterInitStructure);
#if CAN_RX0_INT_ENABLE
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0訊息掛號中斷允許
NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//主優先順序為1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//次優先順序為0
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
/*
接收中斷函式管理 目前沒有做其它事情
*/
#if CAN_RX0_INT_ENABLE
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
CAN_Receive(CAN1,0,&RxMessage);
}
#endif
上面的一段程式是CAN的一個初始化,可以看到我把過濾器設定為遮蔽位模式,而且全部都是不去關心識別符號,也就是說所有的報文都可以通過濾波器組0進來了。 下面配置的是接收和傳送函式,不過這裡用的是32的庫做開發了,有興趣的也可以用暫存器開發了 #include "CAN.H"
/*
can 傳送一組資料(固定格式為:ID 0X12,標準幀,資料幀)
len 資料長度
msg 資料指標 最多為八個位元組
返回值 0 表示成功
其它 表示失敗
*/
u8 Can_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=0x12; //標準識別符號為0
TxMessage.ExtId=0x12; //設定擴充套件識別符號(29位)
TxMessage.IDE=CAN_Id_Standard; //標準幀
TxMessage.RTR=CAN_RTR_Data; //資料幀
TxMessage.DLC=len; //要傳送的資料長度
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i];
mbox=CAN_Transmit(CAN1,&TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1,mbox)!=CAN_TxStatus_Ok)&&(i<0xfff)) i++;//等待結束
if(i>=0xfff)
return 1;
return 0;
}
/*
can 口接收資料查詢
buf 資料快取區
返回值 0 表示無資料接收
其它 資料接收的資料長度
*/
u8 Can_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if(CAN_MessagePending(CAN1,CAN_FIFO0)==0) return 0;//沒有接收到資料直接退出
CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);//讀取資料
for(i=0;i<8;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC;//返回接收到的資料長度
}
好了在此就講解完了CAN的通訊設計了。這裡我強調一點 CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);//讀取資料,這個函式在進行收據接收的時候,同時也對FIFO中的郵箱進行釋放。好了 。想繼續瞭解的同學們可以具體看看CAN的相關協議,或者讀一讀32的相關章節,還是很有幫助的。
哈哈 是不是覺得還是比較簡單的,首先允許我說一下那兩個電阻是什麼東東,這兩個電阻如果你學習過射頻的話,估計都不用我多說啥,這裡照顧一下那些沒有接觸過射頻的童鞋了,這兩個電阻其實它有一個好聽的名字是叫做終端電阻,用途是用來做阻抗匹配的,減少回波反射的。 (不懂的話可以自己查查) CAN總線上有兩個電平,一個顯性,一個是隱性電平。也就是CAN_High,CAN_Low,這兩個電平在工作的時候電壓差是2.5V。這裡不難理解了。如果對這個電平不太瞭解的你可以看下TGA1050這款晶片,是CAN的一個收發晶片。(你值得了解) 好了我們接下來開始進入重點學習了
這裡提一下,不要將ID視為高7位全部設定為隱性位。RTR表示是否為遠端幀,(0:資料幀。1:遠端幀)IDE位表示表示符選擇位(0:使用標準識別符號。1:表示使用擴充套件識別符號)r0和r1為保留位,必須全部以顯性電平發出。SRR位為代替遠端請求位,為隱性位,它代替了標準幀中RTR,DLC是資料長度表示段,DLC段的有效值為0~8。資料段最多可以有8個位元組,從最高位MSB位開始送出。CRC段,該段用於檢查幀傳輸錯誤,由15個位的CRC順序和1個位的CRC界定符(用於分隔的位)組成。CRC的計算範圍包括幀起始,仲裁段,控制段和資料段。接收方以同樣的方式進行計算,兩者不一致時會通報錯誤。ACK段有ACK曹和ACK界定符兩個位組成,傳送單元的ACK,傳送兩個隱性位,人而接收單元在ACK槽f傳送顯性位,EOF是幀結束,由7個隱性位組成。 至此資料幀的7個段就介紹完成了。 好了下面我們再來介紹一下什麼叫做位速率?
這些位的設定可以用來設定CAN的波特率,具體計算如下:
OK到此就把CAN通訊的波特率以及工作模式和幀型別介紹的差不多了。 接下來開始真正的進入CAN最為關鍵而且最為有意思的CAN的過濾器組。 首先來介紹一下CAN的一些資源,CAN支援時間觸發通訊,具有3個傳送郵箱,具有3級深度的2個接收FIFO和可變的過濾器組(最多有28個)。 讓我們來看一下CAN過濾器的工作情況
這裡我用的晶片是STM32ZET6的一款,只有一個CAN控制器, 這個就是CAN的有趣之處了,就是說它可以佔用CPU資源然後自己去篩選合適的來進行過濾。哈哈哈 每個過濾器組都有2個32位的暫存器,CAN_FxR1和CAN_FxR2組成。 每個過濾器組的位寬都可以獨立配置,以滿足不同應用程式的不同需求。 1個32位的過濾器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位 2個16位的過濾器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位 過濾器可以配置為遮蔽位模式和表示符列表模式。 遮蔽位模式就是說可以對識別符號的一些位進行遮蔽,可以讓過濾器組不去匹配這些位,然後匹配通過後才讓資料進來。 識別符號列表模式,收到報文的識別符號的每一位都必須和過濾器識別符號相同,才能讓你進來,否則不讓通過。 可以通過相關暫存器配置它的模式和位寬。如下
好了所有的條件都已經準備好了,接下來我們可以進入CAN的接收和傳送流程了 CAN的傳送流程為:總共有三個郵箱,程式選擇1個空的郵箱(TME-1)->設定識別符號的ID,資料長度和傳送的資料->設定CAN_TIxR的TXRQ位為1,請求傳送->郵箱掛號(等待成為最高的優先順序)->預定傳送(等待匯流排空閒)->傳送->郵箱空。流程如下圖:
CAN接收到的有效報文,被存放在3級郵箱深度的FIFO中。FIFO完全由硬體管理,從而節省了CPU的處理負荷。 CAN的接收流程為:FIFO空->收到有效報文->掛號_1(存入FIFO的一個郵箱,這個是由硬體控制,我們不需要理會)->收到有效的報文->掛號_2->收到有效報文->收到有效報文->掛號_3->收到有效報文->溢位。每次讀取一個報文時掛號就減1,直到FIFO空,CAN的接收流程圖如下:
好了至此我們已經將CAN的重要關卡都打通了, 下面來看一下軟體的配置 #include "CAN.H"
/*
CAN 初始化
Fclk初始化時鐘設定為36Mhz
tsjw:重新同步跳躍時間單元.範圍:CAN_SJW_1tq~ CAN_SJW_4tq
tbs2:時間段 2 的時間單元. 範圍:CAN_BS2_1tq~CAN_BS2_8tq;
tbs1:時間段 1 的時間單元. 範圍:CAN_BS1_1tq ~CAN_BS1_16tq
brp :波特率分頻器.範圍:1~1024; tq=(brp)*tpclk1
波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,迴環模式;
Fpclk1 的時鐘在初始化的時候設定為 36M,如果設定
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
則波特率為:36M/((8+9+1)*4)=500Kbps
返回值:0,初始化 OK;
其他,初始化失敗;
*/
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);//使能CAN時鐘
//判斷是否使能CAN的接收中斷?
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//設定為複用推輓輸出,用來輸出CAN的資料
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉輸入,用來對資料的接收
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//CAN的單元設計
CAN_InitStructure.CAN_TTCM=DISABLE;//非時間觸發通訊
CAN_InitStructure.CAN_ABOM=DISABLE;//軟體自動離線管理
CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通過軟體喚醒
CAN_InitStructure.CAN_NART=ENABLE;//禁止報文自動傳輸
CAN_InitStructure.CAN_RFLM=DISABLE;//報文不鎖定,新的覆蓋舊的
CAN_InitStructure.CAN_TXFP=DISABLE;//優先順序由報文識別符號決定
CAN_InitStructure.CAN_Mode=mode;//模式設定 mode:0,普通模式 1,迴環模式
//設定波特率
CAN_InitStructure.CAN_SJW=tsjw;//設定從新跳躍寬度
CAN_InitStructure.CAN_BS1=tbs1;//時間段1設定
CAN_InitStructure.CAN_BS2=tbs2;//時間段2設定
CAN_InitStructure.CAN_Prescaler=brp;//分頻係數
CAN_Init(CAN1,&CAN_InitStructure);
//設定濾波器的相關係數
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//過濾器0
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//關聯到FIFO0這個接收郵箱上
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;//32位寬的暫存器
CAN_FilterInit(&CAN_FilterInitStructure);
#if CAN_RX0_INT_ENABLE
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0訊息掛號中斷允許
NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//主優先順序為1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//次優先順序為0
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
/*
接收中斷函式管理 目前沒有做其它事情
*/
#if CAN_RX0_INT_ENABLE
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
CAN_Receive(CAN1,0,&RxMessage);
}
#endif
上面的一段程式是CAN的一個初始化,可以看到我把過濾器設定為遮蔽位模式,而且全部都是不去關心識別符號,也就是說所有的報文都可以通過濾波器組0進來了。 下面配置的是接收和傳送函式,不過這裡用的是32的庫做開發了,有興趣的也可以用暫存器開發了 #include "CAN.H"
/*
can 傳送一組資料(固定格式為:ID 0X12,標準幀,資料幀)
len 資料長度
msg 資料指標 最多為八個位元組
返回值 0 表示成功
其它 表示失敗
*/
u8 Can_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=0x12; //標準識別符號為0
TxMessage.ExtId=0x12; //設定擴充套件識別符號(29位)
TxMessage.IDE=CAN_Id_Standard; //標準幀
TxMessage.RTR=CAN_RTR_Data; //資料幀
TxMessage.DLC=len; //要傳送的資料長度
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i];
mbox=CAN_Transmit(CAN1,&TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1,mbox)!=CAN_TxStatus_Ok)&&(i<0xfff)) i++;//等待結束
if(i>=0xfff)
return 1;
return 0;
}
/*
can 口接收資料查詢
buf 資料快取區
返回值 0 表示無資料接收
其它 資料接收的資料長度
*/
u8 Can_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if(CAN_MessagePending(CAN1,CAN_FIFO0)==0) return 0;//沒有接收到資料直接退出
CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);//讀取資料
for(i=0;i<8;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC;//返回接收到的資料長度
}
好了在此就講解完了CAN的通訊設計了。這裡我強調一點 CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);//讀取資料,這個函式在進行收據接收的時候,同時也對FIFO中的郵箱進行釋放。好了 。想繼續瞭解的同學們可以具體看看CAN的相關協議,或者讀一讀32的相關章節,還是很有幫助的。