CAN
Introduction
控制器區域網路 (Controller Area Network, 簡稱 CAN 或 CANbus)是一種網路,其特點是允許網路上的多個微控制器或設備直接互相通訊,網路上不需要主機(Host)控制通訊,並且提供高安全等級及有效率的即時控制。更具備了偵錯和優先權判別的機制,網路訊息的傳輸變的更為可靠而有效率。訊息的ID並不是定義在節點,而是定義在訊息上,所以只要在軟體上修正就能夠輕易增加或移除節點,增加了在升級網路時的便利性。實作上又因為只需雙線溝通的特性,也降低線路複雜易造成錯誤的發生機會。總而言之,CANbus具有高擴充性 、高可靠度且低成本等特性。
Features
Network Topology(CANbus網路架構)
CANbus是一種匯流排網路,他的匯流排是由兩條線路所實現,當兩條線路電位差小的時候為1,電位差大的時候為0。
MESSAGE TRANSFER(CAN通訊的資料格式)
CAN2.0有兩種版本,CAN2.0A(Standard),CAN2.0B(Extended)。
1.DATA FRAME(資料通訊格式):
資料通訊格式主要用於傳送資料,主要分成5個部分。
Start of Frame(SOF):送出1位元dominant(0),用來同步。
Arbitration Field:表示節點的優先權,用來判別優先權。若多個節點同時傳送,在仲裁欄位逐一比對優先權,位元為dominant(0)可優先傳送。長度有兩種11bits(Standard identifier)和29bits(Extended Identifier)。
RTR 則為優先判斷與資料接收與否的識別,RTR=dominant(0),表資料要傳出,RTR=recessive(1),表要接收資料。
- Control Field:資料的長度。
IDE: 標準格式(Standard identifier)為dominant(0);延伸格式(Extended Identifier)為 recessive(1)。
R0:保留
- Data Field:0-8bytes的資料。
資料長度0-8 bytes
- CRC Field:錯誤檢查。
- ACK Field:接收正確,接收端送出1位元dominant(0)。
傳送端送出一個recessive bit(1)當接收端正確收到一個訊息,則在ACK Slot中回傳一個為dominant(0)位元,告知傳送端。
- End of Frame(EOF):通訊格式結束的欄位。
每一個資料都是由7個連續的recessive(1)位元作為結束。
2.REMOTE FRAME(遠端通訊格式):
遠端通訊格式用來請求遠端節點傳送資料。
遠端通訊格式基本上就是資料長度為0的資料通訊格式,指示設定上略有不同。當A節點需要B節點送出資料時,可藉由送出一個遠端通訊格式來完成,將B節點的ID寫入仲裁欄位,在RTR填入recessive(1)。
3.ERROR FRAME(錯誤通訊格式):
當接收到的訊息有錯誤時,節點會發送錯誤通訊格式通知其他節點。
- CAN有5種錯誤檢查來確保收發資料的正確性,在偵測到錯誤的當下決定要在資料的哪個位元回傳錯誤訊息(一定是在Data資料不完整的狀態下)。
- 在節點的狀態方面共有3種(主動,被動,關閉)。通訊格式上,則有主動和被動之分,差別在於Error Flag的值不同。主動是6個dominant(0),被動是6個recessive(1)。
4.OVERLOAD FRAME(過載通訊格式):
通知其他節點延遲傳送資料的格式
節點處理資料中,對於下一筆資料需要延遲時使用,通常接在遠端通訊格式或資料通訊格式後面。
5.INTERFRAME SPACING(通訊格式間隔):
與前一個資料間隔的格式
- 有3bit的intermission和一直處於recessive(1)的Bus Idle
- 除了過載通訊格式和錯誤通訊格式之外,訊息之間至少都要有intermission
CAN的錯誤處理
Error Detection(資料偵錯機制)
- BIT ERROR
發送資料中的節點會比較發送的資料和bus中的電位高低變化是否一致,如果不一致則直在位傳完的訊息後方接著傳送Error Frame。這種偵錯機制僅限於SOF,control,data,CRC field。
- STUFF ERROR
bit stuffing,就是接收端和傳送端約定連續傳幾個相同的bits就要傳送一個相反的bit,用來和訊息中的某些delimiter 做區隔,避免錯亂。在CAN當中是規定某些資料格式中最多只能有5個連續相同的bit,下一個bit就會自動安插相反的bit,並且在接收端會做de-stuffed的動作來把這個bit清除來還原資料。
CAN只在Data Frame中的SOF, ARBITRATION FIELD, CONTROL FIELD,DATA FIELD 和 CRC SEQUENCE這些非固定格式的部分有bit stuffing 的機制。所以當訊息中的這些部分連續偵測到6個相同的bit的時候,就會從下一個bit開始傳送Error Frame。
- CRC ERROR
CRC是一種checksum機制。主要用來檢測數據傳輸後可能出現的錯誤。生成的數字在傳輸之前計算出來並且附加到數據後面,然後接收方進行檢驗確定數據是否發生變化。
CRC的計算方式是假設M(x)是原始的message polynomial,K(x)是n階的generator polynomial,那麼CRC就是 M(x)*x^n對K(x)做模二除法(modulo 2 division:二進位多項式除法,並且用xor運算取代減法運算)的餘式R(x)。所以各個polynomial之間有以下關係:
M(x)x^n = Q(x)K(x) + R(x)
在接收端就是在確認K(x)能不能整除(M(x)*x^n - R(x)),如果可以就表示資料正確,反之代表資料錯誤。
ex:假設資料位元(message polynomial)為10101010,generator polynomial為x4+x2+x^1+1 (10111)
1100為此資料在以x4+x2+x^1+1為generator polynomial之下的CRC。
在CAN當中M(x)是下圖中的Fields所產生的message polynomial,K(x)則是x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1。
- FORM ERROR
CRC,ACK Field 都有delimiter,甚至EOF本身就是Data Frame的delimiter,這些bit都預設為recessive(1),如果在這些bit上讀到dominant(0),那從下一個bit開始就傳送Error Frame。
- ACKNOWLEDGMENT ERROR
傳送端會傳出2個bit的recessive(1)為ACK Field,前者為ACK Slot後者為ACK delimiter。當傳送端在ACK Slot送出recessive(1)的時候,接收端要回傳dominant(0),告知傳送端。如果沒有那就在ACK delimiter後面傳送error frame。
FAULT CONFINEMENT(節點錯誤機制)
CAN的每個節點都有計數器來記錄錯誤的歷史(Transmit Error Counter(TEC) & Receive Error Counter (REC)。評估節點的方式則是根據不同的錯誤形式有不同的權重去計算出狀態數值,並且當節點反應正常的時候還會有下修機制避免因為長時間執行造成的錯誤。當這個評估的數值大於127這個門檻後,就會判定節點從Error active state轉為Error passive state。當節點變成Error passive state的時候,錯誤訊息格式內的flag則是從Active-error flag(6 recessives)改成Passive-error flag(6 dominants),錯誤提醒的部分則是從Error active state的主動全部節點的提醒,改成只在該錯誤節點記錄錯誤。當數值大於255,就判定此節點失效並且關閉此節點的所有行為。當失效節點偵測到128次連續11個recessive(0)bit的時候,重新出始化節點所有的TEC和REC數值歸零,以Error active state的狀態重新回到bus當中。
CAN in STM32F429 Board
Main operating modes
主要的操作模式主要是在Sleep,Initialization,Normal mode 之間轉換,轉換的方式就是去改變CAN_MSR寄存器上面的INAK和SLAK bit。
- Initialization mode
Initialization mode,主要是用來初始化點的bxCAN設定。當INRQ bit被拉起來的時候,CAN就進入Initialization mode,在這過程當中主要是要設定CAN_MCR(mode設定)和CAN_BTR(baud rate設定)。在初始化的過程當中會停止節點對CANbus的任何傳送或接收的動作。(Filter 設定可以在Initialization mode之外完成)
- Sleep mode (low power)
Sleep mode主要是為了省電而設計的,藉由拉起CAN_MCR裡的SLEEP bit來開啟,但是除了由拉低SLEEP bit來關閉之外,只要bus上有傳送訊息Sleep mode也會自動關閉。
- Normal mode
最主要的工作模式。藉由拉低INAK和SLAK bit進入。
Test modes
Test modes主要是用來測試node狀態, 藉由設定CAN_BTR寄存器的SILM和LBKM bits來選擇。
- Silent mode
就是把node設定成只進不出的狀態。就連接收成功的ACK bit也傳不出去。
- Loop back mode
就是把node設定成只出不進的狀態。並且把自己傳出的message存回Rx mailbox之中。
- Combined mode
自己的Tx直接進入自己的Rx,通常用來檢測node本身。
Transmission handling
用bxCAN傳送一筆資料的時候首先要做的事有3個,選擇空的mailbox,輸入傳送資料的ID和設定資料長度。然後再去拉起CAN_TIxR寄存器的TXRQ bit,對mailbox下達傳送指令。
- Nonautomatic retransmission mode
在正常的設定之下,當傳送失敗的時候,bxCAN會不斷的嘗試重新傳送。但是如果設定成這個模式,bxCAN不管有沒有傳送成功都只會傳送一次。
- Abort
在bxCAN傳送的過程如果收到中止命令,還未傳送的資料就會直接中止,如果是在傳送中就會等資料傳完,如果失敗在進入中止狀態。
- Transmit mailbox states
mailbox處理主要分成3個階段,PENDING,SCHEDULED和TRANSMIT。當傳送的資料進入mailbox處理的時候,會先進入PENDING的階段找出priority最高的message,然後進入SCHEDULED階段等待CANbus回到idle的狀態。一旦CANbus回到idle的狀態,就會把message傳出去。這時候如果傳送失敗,資料就會被送回來SCHEDULED階段並且和PENDING的階段的message比較priority。如果message然有最高的priority,則留在SCHEDULED階段等待,否則就換最高priority的message到SCHEDULED階段等待。
- Transmit priority
傳送資料不止一筆的時候,bxCAN會排定傳送順序。排定的方法有兩種,一種是依照順序,一種是依照ID。號碼小的優先傳送,如果ID一樣的時候mailbox號碼小的優先。
Reception handling
在bxCAN的接收端,有三階段的FIFO來為接收緩衝。
- Receive FIFO states
當bxCAN接收到資料,就會直接被丟到第一階段的FIFO等待,等CPU來處理,此時是PENDING第一階段,FMP=0x01。如果還來不及處理就有新的資料進來,當下這筆資料就會被移到第二階段的FIFO去等待,新進的資料則停在第一階段的FIFO,此時是PENDING第二階段,FMP=0x10。當CPU有空的時候就先從高段的FIFO開始處理。依此類推…。總共有三個階段。
- Overrun
Overrun處理有兩種模式。第一種是有開啟FIFO lock。當三階段的FIFO都滿了,但CPU還沒處理。接下來的資料都會遺失。此時CAN_RFR寄存器的FOVR bit會被拉起。第二種是沒有開啟FIFO lock,接下來的資料則會直接覆蓋FIFO的最後一筆資料。
Identifier filtering
由於CANbus在傳送資料的過程是類似廣播的方式,所以在node數量眾多的時候,單一node會收到所有node所傳出的各種資料。但是如果這個node的功能比較單一的話,它其實可以不需要接收每個node的資訊,只要接收跟它有相關的就好了,所以在接收端就需要filter來過濾資料,減輕node負擔。在stmf4的設計中總共有28組寄存器來當作filter使用。filter則有2種mode可以提供選擇。
- Mask mode
在Mask mode之下,只要ID中特定的幾個bit符合filter的狀態,此筆資料就會被接收。通常用在接收某個類別的訊息用。
- Identifier list mode
在Mask mode之下,只要ID中全部的bit都符合filter的狀態,此筆資料才會被接收。通常用在需要接收特定節點資料的時後使用。
- Filter bank
- Filter match index
Filter match index是一個可以更進一步減輕處理負擔的機制。當filter開啟的時候,接收的封包會在接收的時候加入此筆資料是從哪個filter所傳進來的,這樣可以在資料處理的時候節省很多辨識ID的工作。
- Filtering mechanism
以下是一個filter操作流程的範例。當接收到一筆訊息的時候,會先在filter bank當中尋找是否有符合的資料,順序是先看list在看mask。在進入FIFO之前,先加入filter match index,然後再進入FIFO排隊等待處理。
Bit Timming
ex:假設APB clock為45Mhz如何得到1Mhz的baud rate?
選擇9為prescaler, tBS1 = 2 tq, tBS2 = 2 tq
tq = 1/(45Mhz/9) = 0.2 us
Baud rate = 1/Bittime = 1/((1+2+2)*tq) = 1Mhz
Demo
我們利用3塊STM32F429-DISCO板子裡面的L3GD20陀螺儀來量測個別板子的旋轉,然後除了用LCD顯示出來之外,同時傳送bxCAN封包告知另外的版子現在的旋轉情形。在STM32F429-DISCO上面只有CAN controller並沒有CAN transceiver 所以外接一個MCP2551才能在STM32F429-DISCO上實現CANbus網路。為了軟體維護的便利性,我們直接在硬體上做了設定ID的電路,所以可以用一樣的軟體設定不同板子的ID。
Initialization:
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* CAN GPIOs configuration **************************************************/
/* Enable GPIO clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* Connect CAN pins to AF9 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_CAN2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_CAN2);
/* Configure CAN RX and TX pins */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(CAN_GPIO_PORT, &GPIO_InitStructure);
/* CAN configuration ********************************************************/
/* Enable CAN clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
/* CAN register init */
CAN_DeInit(CAN2);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = DISABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
/* CAN Baudrate = 1 MBps 45/(9*(2+2+1))*/
CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
CAN_InitStructure.CAN_Prescaler = 9;
CAN_Init(CAN2, &CAN_InitStructure);
/* CAN filter init */
CAN_FilterInitStructure.CAN_FilterNumber = 14;
/* USE_CAN2 */
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
/* Enable FIFO 0 message pending Interrupt */
CAN_ITConfig(CAN2, CAN_IT_FMP0, ENABLE);
Transmition package:
/* Transmit Structure preparation */
TxMessage.ExtId = (uint32_t)ID | 0x1FFF0000;
TxMessage.RTR = CAN_RTR_DATA;
TxMessage.IDE = CAN_ID_EXT;
TxMessage.DLC = 4;
TxMessage.Data[0] = p[0];
TxMessage.Data[1] = p[1];
TxMessage.Data[2] = p[2];
TxMessage.Data[3] = p[3];
CAN_Transmit(CAN2, &TxMessage);
Receive package handler
void CAN2_RX0_IRQHandler(void)
{
if(CAN_GetITStatus(CAN2,CAN_IT_FMP0)==SET){
CAN_Receive(CAN2, CAN_FIFO0, &RxMessage);
CAN_ClearITPendingBit(CAN2,CAN_IT_FMP0);
}
}
Interrupt Setting:
void CAN2_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = CAN2_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
source code: stm32f429_Demo_CAN