【STM32H7教程】第92章 STM32H7的FDCAN總線應用之雙FDCAN實現(支持經典CAN)


完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第92章       STM32H7的FDCAN總線應用之雙FDCAN實現(支持經典CAN)

本章節為大家講解STM32H7的帶兩個FDCAN控制器使用方法。CAN FD中的FD含義就是flexible data,靈活數據通信,且波特率可以和仲裁階段波特率不同。

92.1 初學者重要提示

92.2 FDCAN硬件接口設計

92.3 FDCAN基礎知識

92.4 FDCAN驅動代碼實現

92.5 雙FDCAN測試的接線盒跳線帽說明

92.6 開發板和H7-TOOL的FDCAN助手測試

92.7 實驗例程設計框架

92.8 實驗例程說明(MDK)

92.9 實驗例程說明(IAR)

92.10 總結

 

 

92.1 初學者重要提示

1、  FDCAN相關知識點可以看第90和91章。

2、  CAN菊花鏈組網時,在兩端接分別接120 Ω的終端電阻。

http://www.armbbs.cn/forum.php?mod=viewthread&tid=104793

3、  FDCAN控制器外接的PHY芯片輸出的是差分信號,組網接線時,注意是CANL接CANL,CANH接CANH。

4、  經典CAN每幀最大8字節,FDCAN每幀最大64字節。

5、  CAN不接外置PHY芯片,通信測試方法:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=104684

6、  關於CAN總線是否需要供地的問題:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=104704 

7、  CAN組網只有一個節點的情況下,接示波器看不到FDCAN數據幀波形。

8、  特別推薦瑞薩的CAN入門中英文手冊,做的非常好:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=14546

9、  帶隔離功能的FDCAN芯片搜集:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=90420

10、  除了本章提供的基於ST HAL庫實現的雙FDCAN通信,再提供個基於CMSIS-Driver的:

基於STM32H7的CMSIS-Driver驅動實現雙CAN FD和雙經典CAN兩種方式案例發布

http://www.armbbs.cn/forum.php?mod=viewthread&tid=105369

92.2 FDCAN硬件接口設計

STM32H7帶了兩個FDCAN控制器,然后外接物理層PHY芯片就可以使用了。FDCAN1和FDCAN2外接芯片原理圖如下:

 

 

使用的PHY芯片SN65HVD230即支持經典CAN,也支持FDCAN。PHY芯片輸出的是差分信號,邏輯0或者邏輯1的電平效果如下:http://www.armbbs.cn/forum.php?mod=viewthread&tid=30855

 

 

92.3 FDCAN基礎知識

FDCAN的基礎知識在第90已經做了詳細說,這里補充些本章要用到的知識點。

92.3.1 經典CAN和FDCAN的區別

CAN-FD的開發可以滿足需要更高帶寬的通信網絡需求。每幀最多具有64個字節的CAN-FD以及將比特率提高到最大的可能性,使數據階段要快8倍,在第二個仲裁階段要恢復到正常的比特率。通過以下方式確保數據傳輸的完整性:

(1)17級多項式對最大16字節的有效載荷進行CRC。

(2)21級多項式對16到64字節之間的有效載荷進行校驗。

標准幀和CAN FD的區別:

 

 

標識符后,CAN 2.0和CAN-FD具有不同的作用:

(1)CAN 2.0發送RTR位以精確確定幀類型:數據幀(RTR為主要)或遠程幀(RTR)是隱性的)。

(2)由於CAN-FD僅支持數據幀,因此始終發送占優勢的RRS(保留)。

 

IDE位保持在相同位置,並以相同的動作來區分基本格式(11位標識符)。請注意,在擴展格式的情況下,IDE位以顯性或隱性方式傳輸(29位標識符)。

 

與CAN 2.0相比,在CAN-FD幀中,在控制字段中添加了三個新位:

(1)擴展數據長度(EDL)位:隱性表示幀為CAN-FD,否則該位為顯性(稱為R0)在CAN 2.0幀中。

(2)比特率切換(BRS):指示是否啟用兩個比特率(例如,當數據階段位以不同的比特率傳輸到仲裁階段)。

(3)錯誤狀態指示器(ESI):指示節點處於錯誤活動模式還是錯誤被動模式。

 

控制字段的最后一部分是數據長度代碼(DLC),它具有相同的位置和相同的長度(4位),用於CAN 2.0和CAN-FD。 DLC功能在CAN-FD和CAN 2.0中相同,但CAN-FD有很小變化(下表中的詳細信息)。 CAN-FD擴展幀允許單個消息中發送64個數據字節,而CAN 2.0有效負載數據最多可以發送8個字節。

 

 

通過增加有效載荷數據的數據字段來改善網絡帶寬,因為需要更少的包處理。 同時,通過為CRC添加更多位來增強消息完整性:

(1)如果有效載荷數據最多為16個字節,則CRC以17位編碼。

(2)如果有效載荷數據大於20(16)個字節,則CRC以21位編碼。

 

另外,為了確保CAN-FD幀的魯棒性,填充位機制支持CRC字段。下表總結了CAN-FD和CAN 2.0之間的主要區別。 提供的主要功能與CAN 2.0相比,CAN FD的改進之處在於數據有效負載的增加和速度的提高由CAN-FD中可用的BRS,EDL和ESI位來確保。

 

 

下表可幫助用戶簡化將STM32設備中的CAN 2.0協議升級到CAN-FD協議的過程。該表還指定了FDCAN的改進。與傳統的BxCAN(基本擴展CAN)相比,FDCAN具有許多優勢,包括更快的數據傳輸速度。速率和數據字節數的擴展,減少了幀開銷。 總線負載也可以減少。 傳輸和接收中消息數量的增加要求RAM存儲器的改進:

 

 

鑒於BxCAN的兼容性,BxCAN開發人員可以輕松地遷移到FDCAN,因為FDCAN可以無需對整個系統設計進行修訂即可實施。 FDCAN包含所有BxCAN功能,並滿足新應用程序的要求。

92.3.2 STM32H7的FDCAN1和FDCAN2的區別

僅FDCAN1支持TTCAN時間觸發通信,而FDCAN2不支持。

92.3.3 FDCAN支持的最高速度

經典CAN是1Mbps,CAN FD最高2Mbps,CAN FD-SiC是5-8Mbps,CAN XL是10Mbps。

 

 

92.3.4 FDCAN的主時鍾選擇

FDCAN1和FDCAN2支持三種時鍾源HSE,PLL1Q和PLL2Q,我們這里選擇的PLL2Q輸出20MHz。

  

92.3.5 FDCAN仲裁階段和通信階段波特率配置(重要)

CAN FD中的FD含義就是flexible data,靈活數據通信,且波特率可以和仲裁階段波特率不同。看下面的圖,意思就是紅色部分可以是一個波特率,藍色部分也可以是一個波特率。

 

 

波特率計算公式,看如下的位時間特性圖:

 

 

1bit的CAN FD數據需要時間由Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2組成。

 

  •   仲裁階段波特率對應的HAL庫配置如下:
/* CAN時鍾分配設置,一般設置為1即可,全部由PLL配置好,tq = NominalPrescaler x (1/ fdcan_ker_ck) */
hfdcan2.Init.NominalPrescaler = 0x1;  

/* 特別注意這里的Seg1,這里是兩個參數之和,對應位時間特性圖的 Pro_Seg + Phase_Seg1 */   
hfdcan2.Init.NominalTimeSeg1 = 0x1F; 

/* 對應位時間特性圖的 Phase_Seg2 */       
hfdcan2.Init.NominalTimeSeg2 = 0x8;     
       
/* 用於動態調節  Phase_Seg1和 Phase_Seg1,所以不可以比Phase_Seg1和 Phase_Seg1大 */
hfdcan2.Init.NominalSyncJumpWidth = 0x8;  

其中:

Sync_Seg是固定值 = 1 
Pro_Seg + Phase_Seg1 = DataTimeSeg1
hase_Seg2 = DataTimeSeg2

FDCAN時鍾是20MHz時,仲裁階段的波特率就是

FDCAN Freq / (Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2)

= 20MHz / (1+0x1F + 8)

= 0.5Mbps

 

  •   數據階段波特率對應的HAL庫配置如下:
/* CAN時鍾分配設置,一般設置為1即可,全部由PLL配置好,tq = NominalPrescaler x (1/ fdcan_ker_ck) */
hfdcan2.Init.DataPrescaler = 0x1;                

/* 特別注意這里的Seg1,這里是兩個參數之和,對應位時間特性圖的 Pro_Seg + Phase_Seg1 */
hfdcan2.Init.DataTimeSeg1 = 0x5; 

/* 對應位時間特性圖的 Phase_Seg2 */
hfdcan2.Init.DataTimeSeg2 = 0x4; 

/* 用於動態調節  Phase_Seg1和 Phase_Seg1,所以不可以比Phase_Seg1和 Phase_Seg1大 */             
hfdcan2.Init.DataSyncJumpWidth = 0x4; 

其中:

Sync_Seg是固定值 = 1 
Pro_Seg + Phase_Seg1 = DataTimeSeg1
hase_Seg2 = DataTimeSeg2

FDCAN時鍾是20MHz時,數據階段的波特率就是

CAN FD Freq / (Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2)

= 20MHz / (1+5+ 4)

= 2Mbps

 

STM32H7在400MHz情況下,PLL配置輸出20MHz的CAN FD時鍾(大家可以使用STM32CubeMX來配置的):

/* 選擇PLL2Q作為FDCANx時鍾 */
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
PeriphClkInitStruct.PLL2.PLL2M = 5;
PeriphClkInitStruct.PLL2.PLL2N = 80;
PeriphClkInitStruct.PLL2.PLL2P = 2;
PeriphClkInitStruct.PLL2.PLL2Q = 20;
PeriphClkInitStruct.PLL2.PLL2R = 2;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_2;
PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

 

92.3.6 FDCAN的采樣點位置推薦設置為85% - 90%

FDCAN每個bit的時間組成如下:

 

 

為了實現更好的穩定性,在滿足指定波特率的情況下,讓采樣點的位置滿足85%到90%是推薦的方式。主要用到HAL庫的如下兩個成員:

hfdcan2.Init.NominalTimeSeg1     
hfdcan2.Init.NominalTimeSeg2   

SyncSeg是固定1,也就是:

(SyncSeg + NominalTimeSeg1)/ (SyncSeg + NominalTimeSeg1 + NominalTimeSeg2)滿足這個條件。

92.3.7 FDCAN的2560字RAM空間分配問題

關於FDCAN的2560字RAM空間在本教程第90章的第5小節有詳細說明。本章配套程序將前1280字分配給FDCAN1,后1280字分配給FDCAN2。

92.3.8 FDCAN過濾器常用的范圍過濾器和經典的位屏蔽過濾器

關於FDCAN的過濾器類型在本教程第90章的第6小節有詳細說明,我們這里重點了解范圍過濾器和經典位屏蔽過濾器。

  •   范圍過濾器

范圍過濾器比較簡單,也比較好理解,對應的HAL庫配置代碼如下:

sFilterConfig2.IdType = FDCAN_STANDARD_ID;  
sFilterConfig2.FilterIndex = 0;
sFilterConfig2.FilterType = FDCAN_FILTER_RANGE;          
sFilterConfig2.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; 
sFilterConfig2.FilterID1 = 0x111;                      
sFilterConfig2.FilterID2 = 0x555;                         
HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig2);     

FilterType類型配置為FDCAN_FILTER_RANGE表示范圍過濾器。

FilterID1 = 0x111和FilterID2 = 0x555表示僅接收 ≥0x111且 ≤ 0x555的ID。

  •   經典的位屏蔽過濾

對應的HAL庫配置代碼如下:

sFilterConfig2.IdType = FDCAN_STANDARD_ID;            
sFilterConfig2.FilterIndex = 0;                           
sFilterConfig2.FilterType = FDCAN_FILTER_MASK;          
sFilterConfig2.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;  
sFilterConfig2.FilterID1 = 0x222;                       
sFilterConfig2.FilterID2 = 0x7FF;         
HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig2); 

FilterType類型配置為FDCAN_FILTER_MASK表示經典的位屏蔽過濾。

FilterID1 = filter  表示ID。

FilterID2 = mask  表示ID屏蔽位,mask每個bit含義:

0: 表示FilterID1相應bit不關心,該位不用於比較。

1: 表示FilterID1相應bit必須匹配,即接收到的ID位必須與FilterID1的相應位一致。

 

我們這里FilterID1 = 0x222,FilterID2 = 0x7FF 表示僅接收ID為0x222的FDCAN幀。

92.4 FDCAN驅動代碼實現

這里以FDCAN1為例進行說明,FDCAN2的驅動程序在本章配套例子里面也提供了。

92.4.1 FDCAN配置

代碼里面對每個成員都進行了詳細注釋說明:

/*
*********************************************************************************************************
*    函 數 名: bsp_InitCan1
*    功能說明: 初始CAN1
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_InitCan1(void)
{     
    /*                    位時間特性配置
        Bit time parameter         | Nominal      |  Data
        ---------------------------|--------------|----------------
        fdcan_ker_ck               | 20 MHz       | 20 MHz
        Time_quantum (tq)          | 50 ns        | 50 ns
        Synchronization_segment    | 1 tq         | 1 tq
        Propagation_segment        | 23 tq        | 1 tq
        Phase_segment_1            | 8 tq         | 4 tq
        Phase_segment_2            | 8 tq         | 4 tq
        Synchronization_Jump_width | 8 tq         | 4 tq
        Bit_length                 | 40 tq = 2us  | 10 tq = 0.5us
        Bit_rate                   | 0.5 MBit/s   | 2 MBit/s
    */
    hfdcan1.Instance = FDCAN1;                     /* 配置FDCAN1 */             
    hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS; /* 配置使用FDCAN可變波特率 */  
    hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;         /* 配置使用正常模式 */ 
    hfdcan1.Init.AutoRetransmission = ENABLE;      /*使能自動重發 */ 
    hfdcan1.Init.TransmitPause = DISABLE;          /* 配置禁止傳輸暫停特性 */
    hfdcan1.Init.ProtocolException = ENABLE;       /* 協議異常處理使能 */
    
    /* 
        配置仲裁階段波特率 
        CAN時鍾20MHz時,仲裁階段的波特率就是
        CAN FD Freq / (Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2) = 20MHz / (1+0x1F + 8) = 0.5Mbps    
        
        其中Sync_Seg是固定值 = 1 , Pro_Seg + Phase_Seg1 = NominalTimeSeg1, Phase_Seg2 = NominalTimeSeg2
    */
/* CAN時鍾分配設置,一般設置為1即可,全部由PLL配置好,tq = NominalPrescaler x (1/ fdcan_ker_ck) */
    hfdcan1.Init.NominalPrescaler = 0x01; 

    /* 用於動態調節  Phase_Seg1和 Phase_Seg1,所以不可以比Phase_Seg1和 Phase_Seg1大 */
    hfdcan1.Init.NominalSyncJumpWidth = 0x08; 

/* 特別注意這里的Seg1,這里是兩個參數之和,對應位時間特性圖的 Pro_Seg + Phase_Seg1 */
    hfdcan1.Init.NominalTimeSeg1 = 0x1F;      

/* 對應位時間特性圖的 Phase_Seg2 */
    hfdcan1.Init.NominalTimeSeg2 = 0x08;     


    /* 
        配置數據階段波特率 
        CAN時鍾20MHz時,數據階段的波特率就是
        CAN FD Freq / (Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2) = 20MHz / (1+5+ 4) = 2Mbps
        
        其中Sync_Seg是固定值 = 1 , Pro_Seg + Phase_Seg1 = DataTimeSeg1, Phase_Seg2 = DataTimeSeg2
    */
/* CAN時鍾分配設置,一般設置為1即可,全部由PLL配置好,tq = NominalPrescaler x (1/ fdcan_ker_ck),
范圍1-32 */
    hfdcan1.Init.DataPrescaler = 0x01; 

/* 用於動態調節  Phase_Seg1和 Phase_Seg1,所以不可以比Phase_Seg1和 Phase_Seg1大,范圍1-16 */
    hfdcan1.Init.DataSyncJumpWidth = 0x04;  

/* 特別注意這里的Seg1,這里是兩個參數之和,對應位時間特性圖的 Pro_Seg + Phase_Seg1,范圍 */
    hfdcan1.Init.DataTimeSeg1 = 0x05; 

/* 對應位時間特性圖的 Phase_Seg2 */        
    hfdcan1.Init.DataTimeSeg2 = 0x04;           
    
    
    hfdcan1.Init.MessageRAMOffset = 0;      /* CAN1和CAN2共享2560個字, 這里CAN1分配前1280字 */
    
    
    hfdcan1.Init.StdFiltersNbr = 1;                     /* 設置標准ID過濾器個數,范圍0-128 */       
    hfdcan1.Init.ExtFiltersNbr = 0;                     /* 設置擴展ID過濾器個數,范圍0-64 */   
    hfdcan1.Init.RxFifo0ElmtsNbr = 2;                   /* 設置Rx FIFO0的元素個數,范圍0-64 */  
    /* 設置Rx FIFO0中每個元素大小,支持8,12,16,20,24,32,48或者64字節 */   
hfdcan1.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_8; 
    hfdcan1.Init.RxFifo1ElmtsNbr = 0;                   /* 設置Rx FIFO1的元素個數,范圍0-64 */

/* 設置Rx FIFO1中每個元素大小,支持8,12,16,20,24,32,48或者64字節 */    
    hfdcan1.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_8; 
    hfdcan1.Init.RxBuffersNbr = 0;                      /* 設置Rx Buffer個數,范圍0-64 */
    
/* 設置Rx Buffer中每個元素大小,支持8,12,16,20,24,32,48或者64字節 */
hfdcan1.Init.RxBufferSize = 0;                         


    hfdcan1.Init.TxEventsNbr = 0;          /* 設置Tx Event FIFO中元素個數,范圍0-32 */    
    hfdcan1.Init.TxBuffersNbr = 0;         /* 設置Tx Buffer中元素個數,范圍0-32 */
    hfdcan1.Init.TxFifoQueueElmtsNbr = 2; /* 設置用於Tx FIFO/Queue的Tx Buffers個數。范圍0到32 */
    hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; /* 設置FIFO模式或者QUEUE隊列模式 */

/* 設置Tx Element中的數據域大小,支持8,12,16,20,24,32,48或者64字節 */
    hfdcan1.Init.TxElmtSize = FDCAN_DATA_BYTES_8;          
    HAL_FDCAN_Init(&hfdcan1);


    /* 
        配置過濾器, 過濾器主要用於接收,這里采樣屏蔽位模式。
        FilterID1 = filter
        FilterID2 = mask
        
        FilterID2的mask每個bit含義
        0: 不關心,該位不用於比較;
        1: 必須匹配,接收到的ID必須與濾波器對應的ID位相一致。
        
        舉例說明:
        FilterID1 = 0x111
        FilterID2 = 0x7FF 
        表示僅接收ID為0x111的FDCAN幀。
        
    */
    sFilterConfig1.IdType = FDCAN_STANDARD_ID;              /* 設置標准ID或者擴展ID */
    /* 用於過濾索引,如果是標准ID,范圍0到127。如果是擴展ID,范圍0到64 */
sFilterConfig1.FilterIndex = 0;                           
    sFilterConfig1.FilterType = FDCAN_FILTER_MASK;          /* 過濾器采樣屏蔽位模式 */
    sFilterConfig1.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;  /* 如果過濾匹配,將數據保存到Rx FIFO 0 */
    sFilterConfig1.FilterID1 = 0x111;                       /* 屏蔽位模式下,FilterID1是消息ID */
    sFilterConfig1.FilterID2 = 0x7FF;                     /* 屏蔽位模式下,FilterID2是消息屏蔽位 */
    HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig1);      /* 配置過濾器 */
    
    /* 設置Rx FIFO0的wartermark為1 */
    HAL_FDCAN_ConfigFifoWatermark(&hfdcan1, FDCAN_CFG_RX_FIFO0, 1);

    /* 激活RX FIFO0的watermark通知中斷,位開啟Tx Buffer中斷*/
    HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_WATERMARK, 0);

    /* 啟動FDCAN */
    HAL_FDCAN_Start(&hfdcan1);    
}

 

92.4.2 FDCAN的GPIO,NVIC等配置

主要配置了FDCAN的GPIO,GPIO時鍾,FDCAN時鍾和NVIC:

/*
*********************************************************************************************************
*    函 數 名: HAL_FDCAN_MspInit
*    功能說明: 配置CAN gpio
*    形    參: hfdcan
*    返 回 值: 無
*********************************************************************************************************
*/
void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* hfdcan)
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

    if (hfdcan == &hfdcan1)
    {
        /*##-1- 使能外設這個GPIO時鍾 #################################*/
        FDCAN1_TX_GPIO_CLK_ENABLE();
        FDCAN1_RX_GPIO_CLK_ENABLE();

        /* 選擇PLL2Q作為FDCANx時鍾 */
        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
        PeriphClkInitStruct.PLL2.PLL2M = 5;
        PeriphClkInitStruct.PLL2.PLL2N = 80;
        PeriphClkInitStruct.PLL2.PLL2P = 2;
        PeriphClkInitStruct.PLL2.PLL2Q = 20;
        PeriphClkInitStruct.PLL2.PLL2R = 2;
        PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_2;
        PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
        PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
        PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL2;
        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);
        }

        __HAL_RCC_FDCAN_CLK_ENABLE();

        GPIO_InitStruct.Pin       = FDCAN1_TX_PIN;
        GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull      = GPIO_PULLUP;
        GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = FDCAN1_TX_AF;
        HAL_GPIO_Init(FDCAN1_TX_GPIO_PORT, &GPIO_InitStruct);

        GPIO_InitStruct.Pin       = FDCAN1_RX_PIN;
        GPIO_InitStruct.Alternate = FDCAN1_RX_AF;
        HAL_GPIO_Init(FDCAN1_RX_GPIO_PORT, &GPIO_InitStruct);

        HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 2, 0);
        HAL_NVIC_SetPriority(FDCAN1_IT1_IRQn, 2, 0);
        HAL_NVIC_SetPriority(FDCAN_CAL_IRQn, 2, 0);
        HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn);
        HAL_NVIC_EnableIRQ(FDCAN1_IT1_IRQn);
        HAL_NVIC_EnableIRQ(FDCAN_CAL_IRQn);
    }
}

 

92.4.3 FDCAN的數據發送

FDCAN每幀數據最大64字節:

/*
*********************************************************************************************************
*    函 數 名: can1_SendPacket
*    功能說明: 發送一包數據
*    形    參:_DataBuf 數據緩沖區
*              _Len 數據長度, 支持8,12,16,20,24,32,48或者64字節
*    返 回 值: 無
*********************************************************************************************************
*/
void can1_SendPacket(uint8_t *_DataBuf, uint8_t _Len)
{        
    FDCAN_TxHeaderTypeDef TxHeader = {0};
    
    /* 配置發送參數 */
    TxHeader.Identifier = 0x222;                      /* 設置接收幀消息的ID */
    TxHeader.IdType = FDCAN_STANDARD_ID;              /* 標准ID */
    TxHeader.TxFrameType = FDCAN_DATA_FRAME;         /* 數據幀 */
    TxHeader.DataLength = (uint32_t)_Len << 16;      /* 發送數據長度 */
    TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; /* 設置錯誤狀態指示 */
    TxHeader.BitRateSwitch = FDCAN_BRS_ON;           /* 開啟可變波特率 */
    TxHeader.FDFormat = FDCAN_FD_CAN;                /* FDCAN格式 */
    TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;/* 用於發送事件FIFO控制, 不存儲 */
    TxHeader.MessageMarker = 0;     /* 用於復制到TX EVENT FIFO的消息Maker來識別消息狀態,范圍0到0xFF */
    
    /* 添加數據到TX FIFO */
    HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, _DataBuf);
}

這里特別注意兩點:

  •   發送的數據幅值給DataLength,需要右移16bit。
  •   要發送的數據會被復制到TX FIFO硬件緩存中。

92.4.4 FDCAN的中斷服務程序處理

這里FDCAN中斷主要用於數據接收:

/*
*********************************************************************************************************
*    函 數 名: FDCAN1_IT0_IRQHandler
*    功能說明: CAN中斷服務程序
*    形    參: hfdcan
*    返 回 值: 無
*********************************************************************************************************
*/
void FDCAN1_IT0_IRQHandler(void)
{
    HAL_FDCAN_IRQHandler(&hfdcan1);
}

void FDCAN1_IT1_IRQHandler(void)
{
    HAL_FDCAN_IRQHandler(&hfdcan1);
}

void FDCAN_CAL_IRQHandler(void)
{
    HAL_FDCAN_IRQHandler(&hfdcan1);
}
/*
*********************************************************************************************************
*    函 數 名: HAL_FDCAN_RxFifo0Callback
*    功能說明: CAN中斷服務程序-回調函數
*    形    參: hfdcan
*    返 回 值: 無
*********************************************************************************************************
*/
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
    if (hfdcan == &hfdcan1)
    {
        if ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_WATERMARK) != RESET)
        {
            /* 從RX FIFO0讀取數據 */
            HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &g_Can1RxHeader, g_Can1RxData);

            /* 激活Rx FIFO0 watermark notification */
            HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_RX_FIFO0_WATERMARK, 0);
            
            if (g_Can1RxHeader.Identifier == 0x111 && g_Can1RxHeader.IdType == FDCAN_STANDARD_ID)
            {
                bsp_PutMsg(MSG_CAN1_RX, 0);    /* 發消息收到數據包,結果在g_Can1RxHeader, g_Can1RxData */
            }
        }
    }
}

 

92.5 雙FDCAN測試的接線和跳線帽說明

大家可以直接使用板載的兩個FDCAN進行通信測試。跳線帽設置如下:

 

 

雙CAN FD接線:

 

 

92.6 開發板和H7-TOOL的FDCAN助手測試

H7-TOOL的CAN/CANFD助手支持以太網,USB和WiFi三種通信方式。大家可以用使用本章配套的雙FDCAN例子與H7-TOOL的FDCAN助手進行通信。

92.6.1 接線說明

H7-TOOL和STM32H7的FDCAN2進行通信,注意是CANH接CANH,  CANL接CANL

 

 

92.6.2 啟動H7-TOOL上位機

打開最新版的H7-TOOL上位機,使用USB,以太網或者WiFi方式均支持。選擇左側的CAN助手 -> 啟動CAN助手:

 

 

參數介紹:

(1)幀類型 :0 - 經典CAN,最大收發8字節

              1 - CAN FD ,   仲裁段和數據點波特率相同,最大收發64字節

              2 - CAN FD 雙波特率,仲裁段和數據點波特率不同,最大收發64字節

(2)仲裁段和數據段波特率 :除了提供常用的波特率,還提供了用戶可自定義配置模式,需要用戶選擇如下選項,這樣就可以配置右側的“CAN波特率高級配置”

 

 

我們這里選擇CAN FD雙波特率,仲裁段設置為500Kbps,數據段設置為2Mbps,最大數據設置為8字節, CAN解碼器設置為none_decoder.lua

 

 

92.6.3 H7-TOOL的FDCAN接收數據測試

第2步設置完畢參數后,按下幾次STM32H7板子的 K1, 可以看到H7-TOOL上位機接收到了數據:

 

 

92.6.4 H7-TOOL的FDCAN發送數據測試

如果有多種發送格式,用戶可以在快捷面板里面發送,這個面板也支持用戶加載專門的配置文件,不用每次都設置。

 

 

如下的配置,點擊發送,可以控制STM32H7板子的LED1點亮:

 

如下的配置,點擊發送,可以控制STM32H7板子的LED1熄滅:

 

 

92.7 實驗例程設計框架

通過程序設計框架,讓大家先對配套例程有一個全面的認識,然后再理解細節,本次實驗例程的設計框架如下:

 

 

  第1階段,上電啟動階段:

  • 這部分在第14章進行了詳細說明。

  第2階段,進入main函數:

  • 第1步,硬件初始化,主要是MPU,Cache,HAL庫,系統時鍾,滴答定時器,LED和串口。
  • 第2步,FDCAN應用程序設計部分。

92.8 實驗例程說明(MDK)

配套例子:

V7-069_雙CAN FD之間通信

實驗目的:

  1. 學習雙CAN FD的實現。

實驗內容:

  1. K1按鍵按下,CAN2發送消息給CAN1,蜂鳴器鳴響4次。
  2. K2按鍵按下,CAN1發送消息給CAN2,點亮LED1。
  3. K2按鍵按下,CAN1發送消息給CAN2,熄滅LED1。

注意事項:

  1. 接線方式是CAN1的CANL1和CAN2的CANL2連接,CAN1的CANH2和CAN2的CANH2連接,具體接線看本工程Doc文件中的截圖。
  2. 啟用CAN1,需要將V7主板上的J12跳線帽短接PA11,J13跳線帽短接PA12。
  3. 啟用CNA2,硬件無需跳線,要禁止使用以太網功能(有引腳復用)。

 

 

 

上電后串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1

 

 

程序設計:

  系統棧大小分配:

 

 

  RAM空間用的AXI SRAM:

 

 

  硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鍾:
       - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。
       - 設置NVIV優先級分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鍾到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
}

  MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區和FMC的擴展IO區。

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

#if 0
       /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

 #else
     /* 當前是采用下面的配置 */
    /* 配置AXI SRAM的MPU屬性為NORMAL, NO Read allocate,NO Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
#endif
    /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

  主功能:

主程序實現如下操作:

  •   上電啟動了一個軟件定時器,每100ms翻轉一次LED2。
  •   K1按鍵按下,CAN2發送消息給CAN1,蜂鳴器鳴響4次。
  •   K2按鍵按下,CAN1發送消息給CAN2,點亮LED1。
  •   K2按鍵按下,CAN1發送消息給CAN2,熄滅LED1。
/*
*********************************************************************************************************
*    函 數 名: DemoCANFD
*    功能說明: CAN測試
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoCANFD(void)
{
    uint8_t ucKeyCode;        /* 按鍵代碼 */

    can_Init();     /* 初始化CAN */
    
    bsp_StartAutoTimer(0, 500);    /* 啟動1個500ms的自動重裝的定時器 */
    
    while (1)
    {
        {
            MSG_T msg;

            if (bsp_GetMsg(&msg))
            {
                switch (msg.MsgCode)
                {
                    case MSG_CAN1_RX:        /* 接收到CAN設備的應答 */
                        printf("CAN1接收到消息\r\n");
                    can1_Analyze();
                        break;    
                    
                    case MSG_CAN2_RX:       /* 接收到CAN設備的應答 */
                         printf("CAN2接收到消息\r\n");
                        can2_Analyze();
                        break;                    
                }
            }
        }
        
        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {            
            /* 每隔500ms 進來一次 */  
            bsp_LedToggle(2);
        }
        
        /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下 */
                    printf("CAN2發送消息給CAN1,蜂鳴器鳴響4次\r\n");
                    can_BeepCtrl(1, 4);
                    break;

                case KEY_DOWN_K2:            /* K2鍵按下 */
                    printf("CAN1發送消息給CAN2,點亮LED1\r\n");
                    can_LedOn(1, 1);
                    break;


                case KEY_DOWN_K3:        /* K3鍵按下 */
                    printf("CAN1發送消息給CAN2,熄滅LED1\r\n");
                    can_LedOff(1, 1);
                    break;

                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        
        }
    }
}

 

92.9 實驗例程說明(IAR)

配套例子:

V7-069_雙CAN FD之間通信

實驗目的:

  1. 學習雙CAN FD的實現。

實驗內容:

  1. K1按鍵按下,CAN2發送消息給CAN1,蜂鳴器鳴響4次。
  2. K2按鍵按下,CAN1發送消息給CAN2,點亮LED1。
  3. K2按鍵按下,CAN1發送消息給CAN2,熄滅LED1。

注意事項:

  1. 接線方式是CAN1的CANL1和CAN2的CANL2連接,CAN1的CANH2和CAN2的CANH2連接,具體接線看本工程Doc文件中的截圖。
  2. 啟用CAN1,需要將V7主板上的J12跳線帽短接PA11,J13跳線帽短接PA12。
  3. 啟用CNA2,硬件無需跳線,要禁止使用以太網功能(有引腳復用)。

 

 

上電后串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1

 

 

程序設計:

  系統棧大小分配:

 

 

  RAM空間用的AXI SRAM:

 

 

  硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鍾:
       - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。
       - 設置NVIV優先級分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鍾到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
}

  MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區和FMC的擴展IO區。

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

#if 0
       /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

 #else
     /* 當前是采用下面的配置 */
    /* 配置AXI SRAM的MPU屬性為NORMAL, NO Read allocate,NO Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
#endif
    /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 主功能:

主程序實現如下操作:

  •   上電啟動了一個軟件定時器,每100ms翻轉一次LED2。
  •   K1按鍵按下,CAN2發送消息給CAN1,蜂鳴器鳴響4次。
  •   K2按鍵按下,CAN1發送消息給CAN2,點亮LED1。
  •   K2按鍵按下,CAN1發送消息給CAN2,熄滅LED1。
/*
*********************************************************************************************************
*    函 數 名: DemoCANFD
*    功能說明: CAN測試
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoCANFD(void)
{
    uint8_t ucKeyCode;        /* 按鍵代碼 */

    can_Init();     /* 初始化CAN */
    
    bsp_StartAutoTimer(0, 500);    /* 啟動1個500ms的自動重裝的定時器 */
    
    while (1)
    {
        {
            MSG_T msg;

            if (bsp_GetMsg(&msg))
            {
                switch (msg.MsgCode)
                {
                    case MSG_CAN1_RX:        /* 接收到CAN設備的應答 */
                        printf("CAN1接收到消息\r\n");
                    can1_Analyze();
                        break;    
                    
                    case MSG_CAN2_RX:       /* 接收到CAN設備的應答 */
                         printf("CAN2接收到消息\r\n");
                        can2_Analyze();
                        break;                    
                }
            }
        }
        
        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {            
            /* 每隔500ms 進來一次 */  
            bsp_LedToggle(2);
        }
        
        /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下 */
                    printf("CAN2發送消息給CAN1,蜂鳴器鳴響4次\r\n");
                    can_BeepCtrl(1, 4);
                    break;

                case KEY_DOWN_K2:            /* K2鍵按下 */
                    printf("CAN1發送消息給CAN2,點亮LED1\r\n");
                    can_LedOn(1, 1);
                    break;


                case KEY_DOWN_K3:        /* K3鍵按下 */
                    printf("CAN1發送消息給CAN2,熄滅LED1\r\n");
                    can_LedOff(1, 1);
                    break;

                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        
        }
    }
}

 

92.10   總結

本章節就為大家講解這么多,FDCAN涉及到的知識點非常多,建議先將例子熟練運用,然后再深入了解。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM