CAN 是 Controller Area Network 的縮寫(如下稱爲 CAN),是 ISO 國際標準化的串行通訊
協議。在當前的汽車產業中,出於對安全性、溫馨性、方便性、低公害、低成本的要求,各類
各樣的電子控制系統被開發了出來。因爲這些系統之間通訊所用的數據類型及對可靠性的要求
不盡相同,由多條總線構成的狀況不少,線束的數量也隨之增長。爲適應「減小線束的數量」、
「經過多個 LAN,進行大量數據的高速通訊」的須要, 1986 年德國電氣商博世公司開發出面
向汽車的 CAN 通訊協議。此後, CAN 經過 ISO11898 及 ISO11519 進行了標準化,如今在歐
洲已經是汽車網絡的標準協議。
如今, CAN 的高性能和可靠性已被認同,並被普遍地應用於工業自動化、船舶、醫療設
備、工業設備等方面。現場總線是當今自動化領域技術發展的熱點之一,被譽爲自動化領域的
計算機局域網。它的出現爲分佈式控制系統實現各節點之間實時、可靠的數據通訊提供了強有
力的技術支持。
CAN 控制器根據兩根線上的電位差來判斷總線電平。總線電平分爲顯性電平和隱性電平,
兩者必居其一。發送方經過使總線電平發生變化,將消息發送給接收方。 安全
基本配置跳過,直接講CAN的配置,只是收發的話,配好CAN的時鐘加上開個接收中斷就好了。網絡
CAN的時鐘配置是掛載在APB1的時鐘上的,可根據這一點來計算CAN總線的波特率,好比本例程的APB1分佈式
是45MHz,則CAN總線的波特率爲 :45M/((9+5+1)*6)M=500KMz。函數
要注意的是阿波羅F429的CAN數據線的GPIO口是在PA11和PA12,但Cube自動生成的不是這兩個GPIO口。oop
記得改一下GPIO口複用,軟件要和硬件要匹配。性能
Cube生成的代碼以下:測試
/* Includes ------------------------------------------------------------------*/ #include "can.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ CAN_HandleTypeDef hcan1; /* CAN1 init function */ void MX_CAN1_Init(void) { hcan1.Instance = CAN1; hcan1.Init.Prescaler = 6; hcan1.Init.Mode = CAN_MODE_NORMAL; hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan1.Init.TimeSeg1 = CAN_BS1_9TQ; hcan1.Init.TimeSeg2 = CAN_BS2_5TQ; hcan1.Init.TimeTriggeredMode = DISABLE; hcan1.Init.AutoBusOff = DISABLE; hcan1.Init.AutoWakeUp = DISABLE; hcan1.Init.AutoRetransmission = DISABLE; hcan1.Init.ReceiveFifoLocked = DISABLE; hcan1.Init.TransmitFifoPriority = DISABLE; if (HAL_CAN_Init(&hcan1) != HAL_OK) { Error_Handler(); } } void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(canHandle->Instance==CAN1) { /* USER CODE BEGIN CAN1_MspInit 0 */ /* USER CODE END CAN1_MspInit 0 */ /* CAN1 clock enable */ __HAL_RCC_CAN1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**CAN1 GPIO Configuration PA11 ------> CAN1_RX PA12 ------> CAN1_TX */ GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_CAN1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* CAN1 interrupt Init */ HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn); /* USER CODE BEGIN CAN1_MspInit 1 */ /* USER CODE END CAN1_MspInit 1 */ } } void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle) { if(canHandle->Instance==CAN1) { /* USER CODE BEGIN CAN1_MspDeInit 0 */ /* USER CODE END CAN1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_CAN1_CLK_DISABLE(); /**CAN1 GPIO Configuration PA11 ------> CAN1_RX PA12 ------> CAN1_TX */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12); /* CAN1 interrupt Deinit */ HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn); /* USER CODE BEGIN CAN1_MspDeInit 1 */ /* USER CODE END CAN1_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */
除了Cube的配置外,咱們還要配置一個CAN_FilterTypeDef(CAN1濾波器)和一個CAN_TxHeaderTypeDef(CAN1發送消息句柄),而後ui
就能夠啓動CAN,使能CAN中斷,至於CAN_RxHeaderTypeDef(CAN1接收消息句柄)只用定義不用配置。spa
CAN_TxHeaderTypeDef hCAN1_TxHeader; //CAN1發送消息
CAN_RxHeaderTypeDef hCAN1_RxHeader; //CAN1接收消息
CAN_FilterTypeDef hCAN1_Filter; //CAN1濾波器指針
/******************************************************************************* * Function Name : vApp_CAN_TxHeader_Init * Description : 初始化發送幀頭句柄 * Input : pHeader 發送幀頭指針 StdId 標識符 ExtId 擴展標識符 IDE 0:標準幀 1:拓展幀 RTR 0:數據幀 1:遠程幀 DLC 數據長度 * Output : None * Return : None ****************************************************************************** */ void vApp_CAN_TxHeader_Init(CAN_TxHeaderTypeDef * pHeader, uint32_t StdId, uint32_t ExtId, uint32_t IDE, uint32_t RTR, uint32_t DLC) { pHeader->StdId = StdId; //11位 標準標識符 pHeader->ExtId = ExtId; //29位 擴展標識符 pHeader->IDE = IDE; //1位 0:標準幀 1:拓展幀 pHeader->RTR = RTR; //1位 0:數據幀 1:遠程幀 pHeader->DLC = DLC; //4位 發送的數據的長度 pHeader->TransmitGlobalTime = ENABLE; } /******************************************************************************* * Function Name : vApp_CAN_Filter_Init * Description : 初始化濾波器 * Input : pFilter 濾波器句柄,初始化所有值 IdHigh, IdLow, MaskIdHigh, MaskIdLow, FIFOAssignment, Bank, Mode, Scale, Activation, SlaveStartFilterBank * Output : None * Return : None ****************************************************************************** */ void vApp_CAN_Filter_Init(CAN_FilterTypeDef * pFilter, uint32_t IdHigh, uint32_t IdLow, uint32_t MaskIdHigh, uint32_t MaskIdLow, uint32_t FIFOAssignment, uint32_t Bank, uint32_t Mode, uint32_t Scale, uint32_t Activation, uint32_t SlaveStartFilterBank) { pFilter->FilterIdHigh = 0; pFilter->FilterIdLow = 0; pFilter->FilterMaskIdHigh = 0; pFilter->FilterMaskIdLow = 0; pFilter->FilterFIFOAssignment = CAN_FILTER_FIFO0; pFilter->FilterBank = 0; pFilter->FilterMode = CAN_FILTERMODE_IDMASK; pFilter->FilterScale = CAN_FILTERSCALE_32BIT; pFilter->FilterActivation = ENABLE; pFilter->SlaveStartFilterBank = 0; }
/*******************************************************************************
* Function Name : vApp_User_CAN_Configuration
* Description : 初始化CAN(用戶修改)
* Input : None
* Output : None
* Return : None
****************************************************************************** */
void vApp_User_CAN_Configuration(void)
{
/*----------------- CAN初始化配置 --------------------------*/
vApp_CAN_Configuration(&hCAN1_TxHeader, &hCAN1_Filter,
/* TxHeader 句柄配置 */
/* StdId ExtId IDE RTR DLC */
0x12, 0, CAN_ID_STD, CAN_RTR_DATA, 8,
/* Filter 句柄配置 */
/* IdHigh IdLow MaskIdHigh MaskIdLow FIFOAssignment Bank Mode Scale Activation SlaveStartFilterBank */
0, 0, 0, 0, CAN_FILTER_FIFO0, 0, CAN_FILTERMODE_IDMASK, CAN_FILTERSCALE_32BIT, ENABLE, 0);
}
/******************************************************************************* * Function Name : vApp_CAN_Configuration * Description : CAN初始化配置,配置發送幀頭,配置濾波器 * Input : (...) * Output : None * Return : None ****************************************************************************** */ void vApp_CAN_Configuration(CAN_TxHeaderTypeDef * pTxHeader, CAN_FilterTypeDef * pFilter, uint32_t StdId, uint32_t ExtId, uint32_t IDE, uint32_t RTR, uint32_t DLC, uint32_t IdHigh, uint32_t IdLow, uint32_t MaskIdHigh, uint32_t MaskIdLow, uint32_t FIFOAssignment, uint32_t Bank, uint32_t Mode, uint32_t Scale, uint32_t Activation, uint32_t SlaveStartFilterBank) { /*-1- 初始化TxHeader句柄 ----------------------------------------*/ vApp_CAN_TxHeader_Init(pTxHeader, StdId, ExtId, IDE, RTR, DLC); /*-2- 初始化濾波器句柄 ------------------------------------------*/ vApp_CAN_Filter_Init(pFilter, IdHigh, IdLow, MaskIdHigh, MaskIdLow, FIFOAssignment, Bank, Mode, Scale, Activation, SlaveStartFilterBank); HAL_CAN_ConfigFilter(&hcan1, pFilter); /*-3- 啓動CAN ---------------------------------------------------*/ while(HAL_CAN_Start(&hcan1) != HAL_OK ) { printf("\nCAN_Start Failed!!"); HAL_Delay(100); } printf("\nCAN_Start Success!!"); /*-4- 使能中斷通知 ----------------------------------------------*/ HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); }
發送的只用調用HAL庫的HAL_CAN_AddTxMessage函數就好了
/*******************************************************************************
* Function Name : vApp_User_CAN1_TxMessage
* Description : 使用CAN1發送數據
* Input : None
* Output : None
* Return : None
****************************************************************************** */
void vApp_User_CAN1_TxMessage(uint8_t aTxData[], uint8_t DLC)
{
vApp_CAN_TxMessage(&hcan1, &hCAN1_TxHeader, aTxData, DLC);
}
/******************************************************************************* * Function Name : vApp_CAN_TxMessage * Description : 郵箱發送數據 * Input : hcan pTxHeader 發送幀頭 aData 數據段 DLC 數據段長度 * Output : None * Return : None ****************************************************************************** */ void vApp_CAN_TxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef * pTxHeader, uint8_t aData[], uint8_t DLC) { uint32_t Tx_MailBox; /*-1- 配置數據段長度 ----------------------------------------*/ pTxHeader->DLC = DLC; /*-2- 發送aData ---------------------------------------------*/ while(HAL_CAN_AddTxMessage(hcan, pTxHeader, aData, &Tx_MailBox) != HAL_OK) { printf("TxMsg Failed!!"); HAL_Delay(100); } printf("\nSend Tx Message Success!!Tx_Mail:%d", Tx_MailBox); }
接收是用中斷回調函數void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)來接收信息,這個是個HAL庫裏的弱函數,要本身重寫。
/******************************************************************************* * Function Name : HAL_CAN_RxFifo0MsgPendingCallback * Description : 消息接收回調函數 * Input : hcan * Output : None * Return : None ****************************************************************************** */ void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { uint8_t aRxData[8], i; if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &hCAN1_RxHeader, aRxData) == HAL_OK) { printf("\nGet Rx Message Success!!\nData:"); for(i=0; i<8; i++) printf("%d", aRxData[i]); } }
本次測試用的CAN分析儀是周立功的CANalyst-II,上位機是CANTest,下面介紹一下怎麼用。根據上面計算的波特率,設置爲500K,而後點最後的一欄
單片機主函數程序以下:
int main(void) { /* USER CODE BEGIN 1 */ uint8_t key; uint8_t TxData[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_CAN1_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ vApp_User_CAN_Configuration(); // KEY_Init(); //初始化按鍵 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { vApp_User_CAN1_TxMessage(TxData, 8); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
測試結果以下:
發送:
接收: