廣州網(wǎng)站建設(shè)推廣網(wǎng)絡(luò)營銷策劃
CAN總線(控制器局域網(wǎng)總線)
理論知識
CAN總線是由BOSCH公司開發(fā)的一種簡潔易用、傳輸速度快、易擴展、可靠性高的串行通信總線
CAN總線特征
- 兩根通信線(CAN_H、CAN_L),線路少,無需共地
- 差分信號通信(相對的是單端信號),抗干擾能力強(意思是當有干擾時,兩根線同時產(chǎn)生波動,但兩根線的差值不變)
- 高速CAN(ISO11898):125k~1Mbps,<40m
- 低速CAN(ISO11519):10k~12kbps,<1km
- 異步,無需時鐘線,通信速率由設(shè)備各自約定
- 半雙工,可掛載多設(shè)備,多設(shè)備同時發(fā)送數(shù)據(jù)時通過仲裁判斷先后順序
- 11位/29位報文ID,用于區(qū)分消息功能,同時決定優(yōu)先級
- 可配置1~8字節(jié)的有效載荷
- 可實現(xiàn)廣播式和請求式兩種傳輸方式
- 應(yīng)答、CRC校驗、位填充、位同步、錯誤處理等特性
名稱 | 引腳 | 雙工 | 時鐘 | 電平 | 設(shè)備 | 應(yīng)用場景 |
UART | TX、RX | 全雙工 | 異步 | 單端 | 點對點 | 兩個設(shè)備互相通信 |
I2C | SCL、SDA | 半雙工 | 同步 | 單端 | 多設(shè)備 | 單主控外掛多個模塊 |
SPI | SCK、MOSI、MISO、SS | 全雙 | 同步 | 單端 | 多設(shè)備 | 單模塊外掛多個模塊(高速,可達幾Mbps) |
CAN | CAN_H、CAN_L | 半雙工 | 異步 | 差分 | 多設(shè)備 | 多個主控互相通信 |
?CAN硬件電路
- 每個設(shè)備通過CAN收發(fā)器掛載在CAN總線網(wǎng)絡(luò)上
- 高速CAN使用閉環(huán)網(wǎng)絡(luò),CAN_H和CAN_L兩端添加120歐的終端電阻(第一個作用是防止回波反射,第二個作用是沒有設(shè)備操作時,將兩根差分線的電壓收緊)
- 低速CAN使用開環(huán)網(wǎng)絡(luò),CAN_H和CAN_L其中一端添加2.2千歐的終端網(wǎng)絡(luò)(有回波反射作用)
CAN電平標準
- CAN總線采用差分信號,即兩線電壓差(Vcan_h-Vcan_l)傳輸數(shù)據(jù)位
- 高速CAN規(guī)定:
電壓差為0V時表示邏輯1(隱性電平)
電壓差為2V時表示邏輯0(顯性電平)
- 低速CAN規(guī)定:
電壓差為-1.5V時表示邏輯1(隱性電平)
電壓差為3V時表示邏輯0(顯性電平)
CAN總線幀格式
幀類型 | 用途 |
數(shù)據(jù)幀 | 發(fā)送設(shè)備主動發(fā)送數(shù)據(jù)(廣播式) |
遙控幀 | 接收設(shè)備主動請求數(shù)據(jù)(請求式) |
錯誤幀 | 某個設(shè)備檢測出錯誤時向其他設(shè)備通知錯誤 |
過載幀 | 接收設(shè)備通知其尚未做好接收準備 |
幀間隔 | 用于將數(shù)據(jù)幀及遙控幀與前面的幀分離開 |
數(shù)據(jù)幀
總線空閑狀態(tài)-->幀起始SOF(發(fā)送隱性電平1)-->仲裁段(發(fā)送11位報文ID以及RTR【隱性電平1表示數(shù)據(jù)幀,顯性電平0表示遙控幀】)-->控制段(IDE:ID擴展標志位:用于區(qū)分標準格式還是擴展格式【標準格式發(fā)顯性電平0,擴展格式發(fā)隱性電平1】)、r0(保留位,必須為顯性電平0)、4位DLC(用于指定1~8位有效載荷)-->數(shù)據(jù)段(要發(fā)送的數(shù)據(jù),大小與前面的DLC對應(yīng))-->CRC段(校驗算法:CRC及CRC界定符)-->ACK段(收發(fā)應(yīng)答:ACK槽及ACK界定符)-->幀結(jié)束(EOF)
擴展格式:SRR替代RTR,協(xié)議升級時留下的無意義位
應(yīng)答的過程是發(fā)送方與接收方操縱總線的過程,發(fā)送方發(fā)完數(shù)據(jù)后釋放總線,等待讀取總線信息,如果讀到總線被拉緊,就說明有其他設(shè)備接收到數(shù)據(jù),并且,CAN設(shè)備是發(fā)送方發(fā)送一位,接收方接收一位的過程,兩個界定符的作用是給發(fā)送方和接收方留出操縱總線的時間。
遙控幀
遙控幀無數(shù)據(jù)段,RTR為隱性電平1,其他部分與數(shù)據(jù)幀相同
CAN的遙控幀(Remote Frame)的主要作用是請求其他節(jié)點發(fā)送具有特定ID的數(shù)據(jù)幀。具體來說,當一個節(jié)點需要從另一個節(jié)點獲取數(shù)據(jù)時,它可以發(fā)送一個遙控幀,而不是直接發(fā)送數(shù)據(jù)。這個遙控幀包含了請求數(shù)據(jù)的ID,但沒有包含數(shù)據(jù)本身。接收到遙控幀的節(jié)點如果擁有與遙控幀ID相匹配的數(shù)據(jù)幀,就會響應(yīng)并發(fā)送相應(yīng)的數(shù)據(jù)幀。這種方式允許節(jié)點僅請求需要的數(shù)據(jù),而不是不斷發(fā)送可能不需要的數(shù)據(jù),從而提高了網(wǎng)絡(luò)的效率和減少了不必要的數(shù)據(jù)傳輸。
位填充?
位填充規(guī)則:發(fā)送方每發(fā)送5個相同電平后,自動追加一個相反電平的填充位接收方檢測到填充位時,會自動移除填充位,恢復(fù)原始數(shù)據(jù)。
位填充作用:
- 增加波形的定時信息,利于接收方執(zhí)行“再同步”,防止波形長時間無變化,導致接收方不能精確掌握數(shù)據(jù)采樣時機
- 將正常數(shù)據(jù)流與“錯誤幀”和“過載幀”區(qū)分開,標志“錯誤幀”和“過載幀”的特異性(錯誤幀和過載幀回發(fā)送6個相同電平)
- 保持CAN總線早發(fā)送正常數(shù)據(jù)流時的活躍狀態(tài),防止被誤認為總線空閑
位時序
?為了靈活調(diào)整每個采樣點的位置,使采樣點對齊數(shù)據(jù)中心附近,CAN總線對每一個數(shù)據(jù)位的時長進行了更細的劃分,分為同步段(SS)、傳播時間段(PTS)、相對緩沖段1(PBS1)和相位緩沖段2(PBS2),每個段又由若干個最小時間單位(Tq)構(gòu)成
硬同步
- 每個設(shè)備都有一個位時序計時周期,當每個設(shè)備(發(fā)送方)率先發(fā)送報文,其他所有設(shè)備(接收方)收到SOF的下降沿時,接收方會將自己的位時序計時周期撥到SS段的位置,與發(fā)送方的位時序計時周期保持同步
- 硬同步只在幀的第一個下降沿(SOF下降沿)有效
- 經(jīng)過硬同步后,若發(fā)送方和接收方的時鐘沒有誤差,則后續(xù)所有數(shù)據(jù)位的采樣點必須都會對齊數(shù)據(jù)位中心附近
再同步
- 若發(fā)送方或接收方的時鐘有誤差,隨著誤差積累,數(shù)據(jù)位邊沿逐漸偏離SS段,則此時接收方根據(jù)再同步補償寬度值(SJW)通過加長PBS1段,或縮短PBS2段,以調(diào)整同步
- 再同步可以發(fā)生在第一個下降沿之后的每個數(shù)據(jù)位跳變邊沿
總線仲裁
CAN基本結(jié)構(gòu)
STM32有CAN外設(shè),微控制器做為CAN控制器,其內(nèi)部結(jié)構(gòu)如上圖所示,發(fā)送和接收控制器相當于管家,控制CAN的發(fā)送和接收,發(fā)送郵箱是發(fā)送緩沖區(qū),發(fā)送接收控制器可以根據(jù)先來后到或者ID優(yōu)先級兩種方式進行選擇發(fā)送報文,通過CAN_TX發(fā)送到外面的CAN收發(fā)器,CAN收發(fā)器將TTL電平轉(zhuǎn)換為差分電平CAN_H和CAN_L,傳輸?shù)紺AN總線上,發(fā)送接收控制器從CAN_RX引腳接收CAN總線發(fā)來的數(shù)據(jù)電平,先經(jīng)過14個過濾器篩選,通過過濾器的報文再存放到接收FIFO(先入先出寄存器,隊列)中,等待CPU讀取處理。
代碼
標準格式、擴展格式、數(shù)據(jù)幀、遙控幀
MyCAN.h
#ifndef __MYCAN_H
#define __MYCAN_H
#include "stdint.h"void MyCAN_Init(void);
void MyCAN_Transmit(CanTxMsg *TxMessage);
uint8_t MyCAN_ReceiveFlag(void);
void MyCAN_Receive(CanRxMsg *RxMessage);#endif
MyCAN.c
#include "stm32f10x.h" // Device headervoid MyCAN_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;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_InitTypeDef CAN_InitStructure;CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;//環(huán)回模式CAN_InitStructure.CAN_Prescaler = 48; //波特率 = 36M/48/(1+BS1Tq數(shù)+BS2Tq數(shù))=125KCAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;CAN_InitStructure.CAN_NART = DISABLE; //自動重傳CAN_InitStructure.CAN_TXFP = DISABLE; //發(fā)送郵箱優(yōu)先級,ENABLE先請求先發(fā)送,DISABLE,ID號小的先發(fā)送CAN_InitStructure.CAN_RFLM = DISABLE; //FIFO鎖定,ENABLE,FIFO溢出時,新報文丟棄,DISABLE,最后收到的報文被新報文覆蓋CAN_InitStructure.CAN_AWUM = DISABLE; //自動喚醒,ENABLE,自動,DISABLE,手動CAN_InitStructure.CAN_TTCM = DISABLE; //時間觸發(fā)通信模式CAN_InitStructure.CAN_ABOM = DISABLE; //離線自動恢復(fù),ENABLE自動恢復(fù),DISABLE手動恢復(fù)CAN_Init(CAN1,&CAN_InitStructure);CAN_FilterInitTypeDef CAN_FilterInitStructure;CAN_FilterInitStructure.CAN_FilterNumber = 0;CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; //全通模式CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //過濾器位寬:32位CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //過濾器模式:屏蔽模式CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //配置過濾器關(guān)聯(lián),進FIFO0排隊還是進FIFO1排隊CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活過濾器開關(guān)CAN_FilterInit(&CAN_FilterInitStructure);
}void MyCAN_Transmit(CanTxMsg *TxMessage)
{uint8_t TransmitMailbox = CAN_Transmit(CAN1,TxMessage);uint32_t Timeout = 0;while(CAN_TransmitStatus(CAN1,TransmitMailbox)!=CAN_TxStatus_Ok) //超時退出{Timeout ++;if(Timeout > 100000)break;}
}uint8_t MyCAN_ReceiveFlag(void)
{if(CAN_MessagePending(CAN1,CAN_FIFO0)>0) //檢查FIFO里是否有報文{return 1;}return 0;
}void MyCAN_Receive(CanRxMsg *RxMessage)
{CAN_Receive(CAN1,CAN_FIFO0,RxMessage);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyCAN.h"
#include "Key.h"CanTxMsg TxMsgArray[] = {
/* StdId ExtId IDE RTR DLC Data[8] */{0x555,0x00000000,CAN_Id_Standard,CAN_RTR_Data,4,{0x11,0x22,0x33,0x44}},{0x000,0x12345678,CAN_Id_Extended,CAN_RTR_Data,4,{0xAA,0xBB,0xCC,0xDD}},{0x666,0x00000000,CAN_Id_Standard,CAN_RTR_Remote,0,{0x00,0x00,0x00,0x00}},{0x000,0x0789ABCD,CAN_Id_Extended,CAN_RTR_Remote,4,{0x00,0x00,0x00,0x00}},
};CanRxMsg RxMsg;u8 pTMsgArray = 0;int main()
{u8 keyNum = 0;OLED_Init();MyCAN_Init();Key_Init();OLED_ShowString(1,1,"Rx :");OLED_ShowString(2,1,"RxID:");OLED_ShowString(3,1,"Leng:");OLED_ShowString(4,1,"Data:");while(1){keyNum = Key_GetNum();if(keyNum==1){ MyCAN_Transmit(&TxMsgArray[pTMsgArray]);pTMsgArray++;pTMsgArray%=sizeof(TxMsgArray)/sizeof(CanTxMsg);}if(MyCAN_ReceiveFlag()){MyCAN_Receive(&RxMsg);if(RxMsg.IDE == CAN_Id_Standard){OLED_ShowString(1,6,"Std");OLED_ShowHexNum(2,6,RxMsg.StdId,8);}else if(RxMsg.IDE == CAN_Id_Extended){OLED_ShowString(1,6,"Ext");OLED_ShowHexNum(2,6,RxMsg.ExtId,8);}if(RxMsg.RTR == CAN_RTR_Data){OLED_ShowString(1,10,"Data ");OLED_ShowHexNum(3,6,RxMsg.DLC,1);OLED_ShowHexNum(4,6, RxMsg.Data[0],2);OLED_ShowHexNum(4,9, RxMsg.Data[1],2);OLED_ShowHexNum(4,12,RxMsg.Data[2],2);OLED_ShowHexNum(4,15,RxMsg.Data[3],2); }else if(RxMsg.RTR == CAN_RTR_Remote){OLED_ShowString(1,10,"Remote");OLED_ShowHexNum(3,6,RxMsg.DLC,1);OLED_ShowHexNum(4,6, 0x00,2);OLED_ShowHexNum(4,9, 0x00,2);OLED_ShowHexNum(4,12,0x00,2);OLED_ShowHexNum(4,15,0x00,2); }}}
}
過濾器的使用
(1)16位列表模式
/*一共有14個過濾器,可配置多個*/CAN_FilterInitStructure.CAN_FilterNumber = 0;
// CAN_FilterInitStructure.CAN_FilterIdHigh = (0x234<<5)|0x10; //RTR在第五位,|0x10就能接收遙控幀CAN_FilterInitStructure.CAN_FilterIdHigh = 0x234<<5;CAN_FilterInitStructure.CAN_FilterIdLow = 0x345<<5;CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x567<<5;CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x000<<5; CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit; //過濾器位寬:16位CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList; //過濾器模式:列表CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //配置過濾器關(guān)聯(lián),進FIFO0排隊還是進FIFO1排隊CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活過濾器開關(guān)CAN_FilterInit(&CAN_FilterInitStructure);CAN_FilterInitStructure.CAN_FilterNumber = 1;CAN_FilterInitStructure.CAN_FilterIdHigh = 0x123<<5;CAN_FilterInitStructure.CAN_FilterIdLow = 0x678<<5;CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x000<<5;CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x000<<5; CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit; CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList; CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //配置過濾器關(guān)聯(lián),進FIFO0排隊還是進FIFO1排隊CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活過濾器開關(guān)CAN_FilterInit(&CAN_FilterInitStructure);
?(2)16屏蔽模式
CAN_FilterInitTypeDef CAN_FilterInitStructure;/*一共有14個過濾器,可配置多個*/CAN_FilterInitStructure.CAN_FilterNumber = 0;CAN_FilterInitStructure.CAN_FilterIdHigh = 0x200<<5; //第一組IDCAN_FilterInitStructure.CAN_FilterMaskIdHigh = (0x700<<5)|0x10|0x8; //對應(yīng)的屏蔽位CAN_FilterInitStructure.CAN_FilterIdLow = 0x320<<5; //第二組IDCAN_FilterInitStructure.CAN_FilterMaskIdLow = (0x7F0<<5)|0x10|0x8; //對應(yīng)的屏蔽位CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit; //過濾器位寬:16位CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //過濾器模式:屏蔽CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //配置過濾器關(guān)聯(lián),進FIFO0排隊還是進FIFO1排隊CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活過濾器開關(guān)CAN_FilterInit(&CAN_FilterInitStructure);
(3)32位列表模式
/*一共有14個過濾器,可配置多個*/CAN_FilterInitStructure.CAN_FilterNumber = 0;uint32_t ID1 = 0x123<<21;CAN_FilterInitStructure.CAN_FilterIdHigh = ID1>>16;CAN_FilterInitStructure.CAN_FilterIdLow =ID1;uint32_t ID2 = (0x12345678u<<3)|0x4;CAN_FilterInitStructure.CAN_FilterMaskIdHigh = ID2>>16;CAN_FilterInitStructure.CAN_FilterMaskIdLow = ID2; CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //過濾器位寬:16位CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList; //過濾器模式:列表CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //配置過濾器關(guān)聯(lián),進FIFO0排隊還是進FIFO1排隊CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活過濾器開關(guān)CAN_FilterInit(&CAN_FilterInitStructure);
(4)32位屏蔽模式
/*一共有14個過濾器,可配置多個*/CAN_FilterInitStructure.CAN_FilterNumber = 0;uint32_t ID = (0x12345600u<<3)|0x4;CAN_FilterInitStructure.CAN_FilterIdHigh = ID>>16;CAN_FilterInitStructure.CAN_FilterIdLow =ID;uint32_t Mask = (0x1FFFFF00u<<3)|0x4|0x2;CAN_FilterInitStructure.CAN_FilterMaskIdHigh = Mask>>16;CAN_FilterInitStructure.CAN_FilterMaskIdLow = Mask; CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //過濾器位寬:16位CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //過濾器模式:列表CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //配置過濾器關(guān)聯(lián),進FIFO0排隊還是進FIFO1排隊CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活過濾器開關(guān)CAN_FilterInit(&CAN_FilterInitStructure);
(5)只接收遙控幀
/*一共有14個過濾器,可配置多個*/CAN_FilterInitStructure.CAN_FilterNumber = 0;uint32_t ID = 0x2;CAN_FilterInitStructure.CAN_FilterIdHigh = ID>>16;CAN_FilterInitStructure.CAN_FilterIdLow =ID;uint32_t Mask = 0x2;CAN_FilterInitStructure.CAN_FilterMaskIdHigh = Mask>>16;CAN_FilterInitStructure.CAN_FilterMaskIdLow = Mask; CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //過濾器位寬:16位CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //過濾器模式:列表CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //配置過濾器關(guān)聯(lián),進FIFO0排隊還是進FIFO1排隊CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活過濾器開關(guān)CAN_FilterInit(&CAN_FilterInitStructure);