1、FlexCAN 簡介
FlexCAN 控制器局域網模塊是符合 ISO 11898-1 標準和 CAN 2.0B 規范的通信控制器,支持 CAN 總線協議。FlexCAN 模塊框圖如 圖1 所示。FlexCAN 模塊包括 CAN 收發器(CAN Transceiver)、協議引擎(Protocol Engine)、控制接口(Controller Host Interface)、總線接口單元(Bus Interface Unit),以及多個支持獨立收發功能的信息緩沖區(Message Buffer)。其中 CAN 收發器負責將收發引腳的 TTL 信號轉換為 CAN 總線的電平信號,協議引擎負責處理對信息緩沖區的讀寫請求,控制接口負責傳輸幀的發送仲裁以及接收匹配,總線接口單元處理 FlexCAN 和內部總線的交互,信息緩沖區用于存儲 FlexCAN 的傳輸幀。
圖 1 MM32F0140 FlexCAN 模塊框圖
MM32F0140 的 FlexCAN 模塊支持標準幀和擴展幀兩種幀格式,支持數據幀和遠程請求幀兩種幀類型,其中數據幀的最大有效數據長度可達8比特。FlexCAN 模塊支持最高可達 1Mps 的可編程比特率,支持對前 16 個信息緩沖區的中斷,支持局部和全局的接收幀過濾機制,支持可選擇的 FIFO 接收功能。
FlexCAN 模塊支持三種工作模式:回環模式、只聽模式和正常工作模式。因為在回環模式下,FlexCAN 的傳輸幀并不會通過其收發引腳發送到 CAN 總線,所以通常用于測試單塊芯片的 FlexCAN 模塊是否工作正常。只聽模式下,FlexCAN 模塊將只接收總線上的幀,而不能發送幀,并且也無法發送接收應答。正常工作模式下,FlexCAN 模塊既可發送幀,也可接收幀。
2、FlexCAN 幀格式
FlexCAN 幀傳輸過程
圖 2 CAN 幀發送流程
圖2 是 CAN 幀的發送流程,首先是幀起始位 SOF,SOF 值規定為 1,標識此幀的開始。在 CAN 總線協議中約定,值 0 為顯性位,值 1 為隱性位。FlexCAN 采取多主機、基于優先級的總線傳輸方式,總線幀的收發順序在 Arbitration field 仲裁段確定。仲裁成功的節點會繼續發送幀,仲裁失敗的節點會轉為接收狀態。如果不設置使用幀的優先級進行仲裁,則使用幀的 ID 號進行仲裁比較,越小優先級越高。結合 圖2 CAN 幀發送流程 和 圖3 CAN 標準數據幀格式可知,Arbitration field 仲裁段存放 CAN 報文的 ID 號以及 RTR 標識,RTR 用于表示幀類型,在下文講述 FlexCAN 使用信息緩沖區發送報文時,會進一步解釋其含義。Control filed 控制段在 MM32F0140 的 FlexCAN 中,主要涉及兩個部分,IDE 字段和 DLE 字段。IDE 字段用于標識幀格式,為數據幀還是遠程幀。DLC 字段用于標識此幀的有效字節數,DLC 字段值將影響后續的 Data field 字段長度。FDF 字段表示此幀是否為 CAN FD 幀,在MM32F0140 的 FlexCAN 模塊中暫時未使用到。Data field 有效傳輸數據負載段,如果此幀為遠程幀,將不攜帶任何數據,所以 DLC 字段對應為 0;如果此幀為數據幀,此段長度由 DLC 字段控制。CRC field 循環冗余碼段,用于在發送和接收流程中檢查此幀是否出現比特錯誤。CRC 后,為 ACK field 檢測段,發送方會發送隱性位 1,然后在此段期間回讀總線上信號。如果讀到為顯性位 0,則說明有其他 CAN 接收器接收到此幀,發送方發送成功。最后是 EOF 結束位,標識發送結束。
圖 3 CAN 標準數據幀格式
以下是 MM32F0140 FlexCAN 在上述 CAN 發送流程要求下,使用信息緩沖區寄存器進行 CAN 幀的收發的詳細操作流程。信息緩沖區寄存器的結構如圖 4 所示。
圖 4 FlexCAN 信息緩沖區結構圖
FlexCAN 發送幀
FlexCAN 發送幀時,首先將幀相關信息寫入預計使用的信息緩沖區寄存器中。如果此幀的 ID 號可用 11 個比特位進行表示,則將此幀的 ID 號填入 ID 段,并將 IDE 位置 0,標識為標準幀。如果此幀的 ID 號超過 11 位,則將剩余低位部分填入 ID(Extended) 段,并將 IDE 位置 1,標識為擴展幀。FlexCAN 的幀 ID 長度不能超過 29 位。
然后根據此幀的幀類型,如果是攜帶數據的數據幀,則將 SRR 替代遠程請求位和 RTR 遠程請求位都設置為 0,并將對應需要發送的數據寫入 DataByte 區,將數據長度寫入 DLC 字段;如果是不攜帶數據的遠程請求幀,則將上述的 SRR 位和 RTR 位都設置為 1。
如果 MCR[LPRIOEN] 位被設置為 1,則在發送前還需要將此幀的優先級寫入 PRIO 位,PRIO 位值越小,優先級越高。
當上述各字段都填充完畢后,將 CODE 值 12 填入 CODE 區,此 CODE 值表示將要發送此幀,發送完畢后 CODE 值變為 8,表示發送完畢。回讀 TIME STAMP 段,可以得到發送成功時的時間戳。
FlexCAN 接收幀
FlexCAN 接收幀前,需要設置此接收信息緩沖區的 ID 號、幀格式 IDE 和幀類型 SRR 和 RTR,參考上述發送過程的設置。然后將 CODE 區值設置為值 4,表示等待接收。如果需要幀過濾,還需要設置局部幀過濾器 RXIMR 和全局幀過濾器 RXMGMASK、RX14MASK和RX15MASK。當 CAN 總線上有其他 FlexCAN 模塊發送的幀時,信息緩沖區會將總線上的幀 ID 與自己的 ID 段以及掩碼值進行匹配,接收幀,此時 TIME STAMP 段的值為接收時的時間戳。
在傳輸中會常使用到的 CODE 值如下表 1 所示。
表格 1 信息緩沖區 CODE 值含義
FlexCAN 功能測試流程
首先使用 FlexCAN 的回環模式測試開發板上的 FlexCAN 是否工作正常。回環測試成功以后可以進行 FlexCAN 正常工作模式的測試。如果使用的是兩塊開發板或使用 CAN 分析儀進行測試,需要注意總線兩端必須連接 120Ω 終端電阻。在配置 FlexCAN 過程中,要保證收發方的波特率都配置成相同值。測試 FlexCAN 正常工作模式下收發的實驗過程中,要確保至少有一個接收節點是正常工作模式,而非只聽模式。
3、FlexCAN 配置
配置時鐘
首先需要使能 FlexCAN 時鐘,根據所使用的外設對 RCC 的 RCC_APB1ENR 寄存器進行賦值,將對應外設位置 1 即可使能時鐘,詳細外設如圖 5 所示。
圖 5 MM32F0140 APB1總線外設
使能并初始化模塊
使能 FlexCAN 模塊,通過配置 FLEXCAN_MCR[MDIS] 為 0,可以使能 FlexCAN 模塊。使能后,FlexCAN 模塊會自動進入凍結模式。在凍結模式下,設置 FLEXCAN_MCR_SOFTRST 為 1,軟件重置 FlexCAN 模塊寄存器。然后將所有的信息緩沖區的獨立掩碼寄存器和全局掩碼寄存器的掩碼值都設置為 1,表示接收信息緩沖區將只接收 ID 號和緩沖區提前配置的 ID 號完全相同的報文,同時將每個信息緩沖區寄存器也全部清 0。
配置工作模式
如果 FlexCAN 需要進入正常工作模式,僅需在使能 FlexCAN、初始化信息緩沖區后退出凍結模式即可進入,即將 FLEXCAN_MCR[FRZ] 設置為 0。因為退出凍結模式需要一定時間,通過輪詢 FLEXCAN_MCR[FRZACK] 是否等于 1 判斷是否退出成功。如果 FlexCAN 需要配置為只讀模式,則需要將 FLEXCAN_CTRL1[LOM] 置為 1;如果 FlexCAN 需要配置為回環模式,則需要將 FLEXCAN_CTRL1[LPB] 設置為 1。值得注意的是,當配置為回環模式時,FlexCAN 的 FLEXCAN_MCR[SRXDIS] 不能被設置為 1,否則 FlexCAN 將無法收到回環報文。
配置波特率
首先通過設置 FLEXCAN_CTRL1[CLKSRC] 指定 FlexCAN 協議引擎的時鐘源,設置為 0 時使用振蕩器時鐘,設置為 1 時使用外設時鐘。可詳見圖 6 FlexCAN 模塊協議引擎時鐘框圖。
圖 6 FlexCAN模塊協議引擎時鐘框圖
對 FlexCAN 引擎時鐘頻率進行可編程分頻后得到 FlexCAN 串行時鐘頻率,FlexCAN 串行時鐘頻率的倒數被稱為時間份額,用于衡量 FlexCAN 傳輸過程中各個階段所需的時間長度,詳見圖 7 1 bit time 下 FlexCAN 傳輸段。
圖 7 1 bit time下FlexCAN傳輸段
SYNC_SEG 表示使用 1 個時間份額用于同步;Time Segment 1 段包括 PROPSEG 段和 PSEG1 段,前者用于補償實際網絡傳輸延時,后者和 PSEG2 都將用于補償在 FlexCAN 傳輸過程中潛在的邊緣相位誤差。上述三項可以分別通過 FLEXCAN_CTRL1[PROPSEG]、FLEXCAN_CTRL1[PSEG1] 和 FLEXCAN_CTRL1[PSEG2] 進行設置。下圖 8 是 Bosch CAN 2.0B 標準中對于 PSEG1 和PSEG2 的設置表。
圖 8 Bosch CAN 2.0B 標準中 PSEG1 和 PSEG2 的建議設置
在上述定義下,假定此時引擎時鐘源的時鐘頻率為 ClockFreqHz,PROPSEG 中指定值為 PropSegLen,PSEG1 和PSEG2 中指定值分別為 PhaSegLen1 和 PhaSegLen2,所需波特率為 BaudRate 時,我們可以得到預期的分頻數值 Div 為:
得到的 Div 值通過配置 FLEXCAN_CTRL1[PreDiv] 實現。這里尤其要注意各項和 ClockFreqHz 之間是否滿足整除關系。
配置發送幀
首先需要將發送幀的內容填入等待發送的 i 號信息緩沖區的寄存器 FLEXCAN_MB[i] 的 CS、ID、WORD0 和WORD1 寄存器。然后將發送 CODE 值 (12) 填入 FLEXCAN_MB[i].CS[CODE] 中,即可發送。此時,如果將 FLEXCAN_ IMASK1 中對應中斷位設置為 1,則發送成功后將會喚起中斷。
配置接收幀格式
接收匹配需要設置對應的 i 號信息緩沖區 FLEXCAN_MB[i].CS[CODE] 值為 4,表示當前信息緩沖區接收為空,可以接收信息并安全存儲,配置 FLEXCAN_MB[i].ID 為預期接收的幀 ID 號。設置 FLEXCAN_MB[i].CS[IDE] 配置接收擴展幀或標準幀,接收擴展幀則配置為 1,接收標準幀則配置為 0。設置 FLEXCAN_MB[i].CS[RTR] 配置接收遠程幀或數據幀,接收遠程幀則此位配置為 1,否則配置為 0。
如果需要對幀進行過濾接收,則需額外考慮配置全局幀過濾器或局部幀過濾器。配置 FLEXCAN_MCR[IRMQ] 為 1 則采用局部幀過濾,配置為 0 則采用全局幀過濾。當使用局部幀過濾方法時,掩碼需要通過 FLEXCAN_RXIMRN[i] 寄存器配置,全局則通過 FLEXCAN_RXMGMASK、FLEXCAN_RX14MASK 和FLEXCAN_RX15MASK 寄存器進行配置。
4、FlexCAN 實驗
在 SDK 中已有支持的 pokt-f0140 開發板上,在 driver example 下的 flexcan_loopback 樣例中,使用 FlexCAN 模塊進行回環測試,演示 FlexCAN 的初始化設置、接收和發送設置以及中斷處理。
初始化外設時鐘
FlexCAN 模塊在 APB1 總線上,因此對 RCC_ APB1ENR 寄存器的 FLEXCAN 對應位設置為 1。
RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_FLEXCAN, true);
初始化FlexCAN
使用外設時鐘作為 FlexCAN 的時鐘源,則 FlexCAN 的時鐘頻率為 pokt-f0140 上 APB1 總線時鐘頻率,為48MHz。此時將傳輸階段的時鐘配置 PhaseSegLen1,PhaseSegLen2,PropSegLen 分別配置為 2, 1, 1;將波特率APP_FLEXCAN_XFER_BAUDRATE 設置為 1MHz。則根據先前所述的波特率計算公式,可以滿足整除關系。在實際調整波特率,需要考慮傳輸段的時鐘設置是否滿足整除關系。
/* Setup the flexcan module. */ void app_flexcan_init(void) { /* Set the baudrate and bit time. */ FLEXCAN_TimConf_Type flexcan_tim_conf; flexcan_tim_conf.EnableExtendedTime = false;/* No need to use extended time setting register. */ flexcan_tim_conf.PhaSegLen1 = 2u; flexcan_tim_conf.PhaSegLen2 = 1u; flexcan_tim_conf.PropSegLen = 1u; /* Setup flexcan. */ FLEXCAN_Init_Type flexcan_init; flexcan_init.MaxXferNum = APP_FLEXCAN_XFER_MaxNum; /* The max mb number to be used. */ flexcan_init.ClockSource = FLEXCAN_ClockSource_Periph; /* Use peripheral clock. */ flexcan_init.BaudRate = APP_FLEXCAN_XFER_BAUDRATE; /* Set baudrate. */ flexcan_init.ClockFreqHz = CLOCK_APB1_FREQ; /* Set clock frequency. */ flexcan_init.SelfWakeUp = FLEXCAN_SelfWakeUp_BypassFilter; /* Use unfiltered signal to wake up flexcan. */ flexcan_init.WorkMode = FLEXCAN_WorkMode_LoopBack; /* Normal workmode, can receive and transport. */ flexcan_init.Mask = FLEXCAN_Mask_Global; /* Use global mask for filtering. */ flexcan_init.EnableSelfReception = true; /* Must receive mb frame sent by self. */ flexcan_init.EnableTimerSync = true; /* Every tx or rx done, refresh the timer to start from zero. */ flexcan_init.TimConf = flexcan_tim_conf; /* Set timing sychronization. */ /* Enable FlexCAN. */ FLEXCAN_Init(BOARD_FLEXCAN_PORT, flexcan_init); }
配置接收信息緩沖區
配置接收 MB 的接收幀類型為標準數據幀,ID 為 APP_FLEXCAN_XFER_ID。通過配置接收 MB 的 CODE 區,將其配置為接收為空的狀態。
/* Set rx mb. */ FLEXCAN_RxMbConf_Type flexcan_mb_conf; flexcan_mb_conf.Id = APP_FLEXCAN_XFER_ID; /* Id for receiving. */ flexcan_mb_conf.MbType = FLEXCAN_MbType_Data; /* Only receive standard data frame. */ flexcan_mb_conf.MbFormat = FLEXCAN_MbFormat_Standard; FLEXCAN_SetRxMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_CH, flexcan_mb_conf); /* Set for receiving frames. */ FLEXCAN_SetMbCode(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_CH, FLEXCAN_MbCode_RxEmpty);
配置發送信息緩沖區
將發送 MB 的寄存器清空。通過設置該 MB 的 CODE 區,將該 MB 設置為發送空閑。
/* Reset tx mb. */ FLEXCAN_ResetMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH); FLEXCAN_SetMbCode(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH, FLEXCAN_MbCode_TxInactive);
配置接收中斷
使能 FlexCAN 的接收中斷,設置 NVIC。
/* Enable intterupts for rx mb. */ FLEXCAN_EnableMbInterrupts(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_INT, true); NVIC_EnableIRQ(BOARD_FLEXCAN_IRQn);
中斷處理函數
中斷處理函數檢查接收中斷,設置接收全局標志位為真。
/* Interrupt request handler. */ void BOARD_FLEXCAN_IRQHandler(void) { /* Check mb status. If received. */ if (0u!= (FLEXCAN_GetMbStatus(BOARD_FLEXCAN_PORT) BOARD_FLEXCAN_RX_MB_STATUS) ) { /* Clear flexcan mb interrupt flag. */ FLEXCAN_ClearMbStatus(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_STATUS); /* Update the flag. */ app_flexcan_rx_flag = true; } }
發送數據
設置標準數據幀的 ID 號,幀類型和幀格式以及優先級;然后填入數據;設置數據長度。將上述配置寫入 MB 相關寄存器,然后修改 MB 的 CODE 區進行發送。
/* Send a message frame. */ void app_flexcan_tx(uint8_t * tx_buf) { /* Prepare sending mb frame. */ FLEXCAN_Mb_Type mb; mb.ID = APP_FLEXCAN_XFER_ID; /* Indicated ID number. */ mb.TYPE = FLEXCAN_MbType_Data; /* Data frame type. */ mb.FORMAT = FLEXCAN_MbFormat_Standard; /* Standard frame format. */ mb.PRIORITY = APP_FLEXCAN_XFER_PRIORITY; /* The priority of the frame mb. */ /* Set the information. */ mb.BYTE0 = tx_buf[0]; mb.BYTE1 = tx_buf[1]; mb.BYTE2 = tx_buf[2]; mb.BYTE3 = tx_buf[3]; mb.BYTE4 = tx_buf[4]; mb.BYTE5 = tx_buf[5]; mb.BYTE6 = tx_buf[6]; mb.BYTE7 = tx_buf[7]; /* Set the workload size. */ mb.LENGTH = APP_FLEXCAN_XFER_BUF_LEN; /* Send. */ FLEXCAN_WriteTxMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH, mb); FLEXCAN_SetMbCode(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH, FLEXCAN_MbCode_TxDataOrRemote); /* Write code to send. */ }
讀取數據
讀取對應的 MB 寄存器,將幀數據段內容解析出來。
/* Receive a message frame. */ void app_flexcan_read(uint8_t *rx_buf) { /* Read the info from mb and reconstruct for understanding. */ FLEXCAN_ReadRxMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_CH, app_flexcan_rx_mb); rx_buf[0] = app_flexcan_rx_mb.BYTE0; rx_buf[1] = app_flexcan_rx_mb.BYTE1; rx_buf[2] = app_flexcan_rx_mb.BYTE2; rx_buf[3] = app_flexcan_rx_mb.BYTE3; rx_buf[4] = app_flexcan_rx_mb.BYTE4; rx_buf[5] = app_flexcan_rx_mb.BYTE5; rx_buf[6] = app_flexcan_rx_mb.BYTE6; rx_buf[7] = app_flexcan_rx_mb.BYTE7; }
main() 函數
主函數會在鍵入之后,準備好數據并通過發送 MB 發送;等待中斷處理函數將全局標志位設置為真后,解析接收MB 收到的幀,并打印。然后再等待下一次鍵入。
int main(void) { BOARD_Init(); printf("rnflexcan_loopback example.rn"); /* Setup the flexcan module.*/ app_flexcan_init(); printf("press any key to send loop back frame with id %u.rn", (unsigned)APP_FLEXCAN_XFER_ID); while (1) { getchar(); /* Send a message through flexcan. */ for (uint8_t i = 0u; i < APP_FLEXCAN_XFER_BUF_LEN; i++) { app_flexcan_tx_buf[i] = ( app_flexcan_tx_buf[i] + i) % 256u; } app_flexcan_tx(app_flexcan_tx_buf); printf("app_flexcan_tx() done.rn"); /* Wait for reception. */ while (!app_flexcan_rx_flag) /* This flag will be on when the Rx interrupt is asserted. */ { } app_flexcan_rx_flag = false; /* Read the message. */ app_flexcan_read(app_flexcan_rx_buf); /* Send frame. */ printf("app_flexcan_read(): "); for (uint8_t i = 0u; i < APP_FLEXCAN_XFER_BUF_LEN; i++) { printf("%u ", (unsigned)app_flexcan_rx_buf[i]); } printf("rnrn"); } }
實驗結果
圖 9 實驗結果
來源:靈動MM32MCU
免責聲明:本文為轉載文章,轉載此文目的在于傳遞更多信息,版權歸原作者所有。本文所用視頻、圖片、文字如涉及作品版權問題,請聯系小編進行處理
審核編輯 黃宇
-
控制器
+關注
關注
112文章
16332瀏覽量
177806 -
CAN
+關注
關注
57文章
2744瀏覽量
463615 -
局域網
+關注
關注
5文章
751瀏覽量
46277 -
總線
+關注
關注
10文章
2878瀏覽量
88051
發布評論請先 登錄
相關推薦
評論