【RoboMaster】終極CAN總線入門教程!【STM32】

同步博客地址從STM32開始的RoboMaster生活:進階篇 VII [CAN]

項目&教程倉庫-STM32-RoboMaster-


1.0 什麼是CAN?

1.1 背景 + 定義

Controller Area Network 控制器局域網絡 是ISO國際標準化的串行通信協議。在汽車產業中,出於對安全性、舒適性、方便性、低公害、低成本的要求,各種各樣的電子控制系統被開發了出來。由於這些系統之間通信所用的數據類型及對可靠性的要求不盡相同,由多條總線構成的情況很多,線束的數量也隨之增加。爲適應“減少線束的數量”、“通過多個LAN,進行大量數據的高速通信”的需要,1986 年德國電氣商博世公司開發出面向汽車的CAN 通信協議。此後,CAN 通過ISO11898及ISO11519進行了標準化,在歐洲已是汽車網絡的標準協議。

CAN 的高性能和可靠性已被認同,並被廣泛地應用於工業自動化、船舶、醫療設備、工業設備等方面。現場總線是當今自動化領域技術發展的熱點之一,被譽爲自動化領域的計算機局域網。它的出現爲分佈式控制系統實現各節點之間實時、可靠的數據通信提供了強有力的技術支持。近年來,它具有的高可靠性和良好的錯誤檢測能力受到重視,被廣泛應用於汽車計算機控制系統和環境溫度惡劣、電磁輻射強及振動大的工業環境。

1.2 意義 + 優勢

  1. 多主控制
    • 在總線空閒時,所有的單元都可開始發送消息(多主控制)
    • 最先訪問總線的單元可獲得發送權(CSMA/CA力式)
    • 多個單元同時開始發送時,發送高優先級 ID 消息的單元可獲得發送權
  2. 消息的發送
    • 在CAN協議中,所有的消息都以固定的格式發送。總線空閒時,所有與總線相連的單元都可開始發送新消息。兩個以上的單元同時開始發送消息時,根據標識符(Identifier以下稱爲ID)決定優先級。ID並不是表示發送的目的地址,而是表示訪問總線的消息的優先級。兩個以上的單元同時開始發送消息時,對各消息ID的每個位進行逐個仲裁比較。仲裁獲勝(被判定爲優先級最高)的單元可繼續發送消息。仲裁失利的單元則立刻停止發送而進行接收工作。
  3. 系統的柔軟性
    • 與總線相連的單元沒有類似於“地址”的信息。因此在總線上增加單元時,連接在總線上的其他單元的軟硬件及應用層都不需要改變。
  4. 通信速度
    • 根據整個網絡的規模,可設定適合的通信速度。在同一網絡中,所有單元必須設定成統一的通信速度。即使有一個單元的通信速度與其它的不一樣。此單元也會輸出錯誤信號,妨礙整個網絡的通信。不同網絡間則可以有不同的通信速度。
  5. 遠程數據請求
    • 可通過發送“遙控幀”,請求其他單元發送數據。
  6. 錯誤檢測功能
    • 所有的單元都可以檢測錯誤(錯誤檢測功能)
    • 檢測出錯誤的單元會立即同時通知其他所有單元(錯誤通知功能)
    • 正在發送消息的單元一旦檢測出錯誤,會強制結束當前的發送。強制結束髮送的單元會不斷反覆地重新發送此消息直到成功發送爲止(錯誤恢復功能)
  7. 故障封閉
    • CAN可以判斷出錯誤的類型是總線上暫時的數據錯誤(如外部噪聲等)還是持續的數據錯誤(如單元內部故障、驅動器故障、斷線等)。由此功能,當總線上發生持續數據錯誤時,可將引起此故障的單元從總線上隔離出去。
  8. 連接
    • CAN總線是可同時連接多個單元的總線。可連接的單元總數理論上是沒有限制的。但實際上可連接的單元數受總線上的時間延遲及電氣負載的限制。降低通信速度,可連接的單元數增加:提高通信速度,則可連接的單元數減少。

2.0 CAN原理

2.1 Differential signal 差分信號

ISO11898-2.jpg

從上圖我們可以看到,與圖中下部分的普通信號不同,並不是低電平表示0高電平表示1,而是當兩根數據線在同一時刻,如果有電壓差表示0無電壓差表示1。

相對於單信號線傳輸的方式,使用差分信號傳輸具有如下優點:

  • 抗干擾能力強:當外界存在噪聲干擾時,幾乎會同時耦合到兩條信號線上,而接收端只關心兩個信號的差值,所以外界的共模噪聲可以被完全抵消。
  • 有效抑制電磁干擾:同樣的道理,由於兩根信號的極性相反,他們對外輻射的電磁場可以相互抵消,耦合的越緊密,泄放到外界的電磁能量越少。
  • 時序定位精確:由於差分信號的開關變化是位於兩個信號的交點,而不像普通單端信號依靠高低兩個閾值電壓判斷,因而受工藝,溫度的影響小,能降低時序上的誤差,同時也更適合於低幅度信號的電路

由於差分信號線具有這些優點,所以在USB協議,485 協議,以太網協議,及 CAN 協議的物理層中,都使用了差分信號傳輸。

CAN node.png

雖然CAN是通過差分信號傳輸數據,但是MCU不是,所以需要CAN Transceiver來把MCU的普通信號轉換成差分信號。STM32並不自己提供該功能,所以需要外接CAN Transceiver。

2.2 Time Quantum 時間量子

  • 數據的每一Bit位,都分爲4段
    • 同步段 ( SS / Sync )
    • 傳播時間段 ( PTS / Prop )
    • 相位緩衝段 1 ( PBS1 / Phase 1 )
    • 相位緩衝段 2 ( PBS2 / Phase 2 )

CAN_Bit_Timing2.jpg

  • 而這每一段又由稱爲Time Quantum 時間量子的最小時間單位組成

2.3 Data Frame 數據幀

在CAN標準中,分爲標準格式和擴展格式,而幀也分爲5種

  • 數據幀
  • 遙控幀
  • 錯誤幀
  • 過載幀
  • 間隔幀

我們這裏主要解析一下數據幀

CAN-Bus-frame_in_base_format_without_stuffbits_1.jpg

每個數據幀總共包含7個段

  1. Start of Frame 幀起始:表示數據幀開始的段
  2. Arbitration Field 仲裁段:表示該幀優先級的段
  3. Control 控制段:表示數據的字節數及保留位的段
  4. Data 數據段:數據的內容,一幀可發送 0~8 個字節的數據
  5. CRC Field CRC段:檢查幀的傳輸錯誤的段
  6. ACK Field ACK段:表示確認正常接收的段
  7. End of Frame 幀結束:表示數據幀結束的段

2.4 Arbitration 仲裁

arbitration.jpg

在CAN中,仲裁是爲了解決當同一時刻有多個Node在試圖發送信息的時候,讓優先權最大的那個Node先發。在CAN中,1是隱性電平,0是顯性電平,在ID中顯性電平越靠前越多,也就是ID越小,優先權越大。在仲裁的時候,比如有上圖中的3個Node同時發送數據,當他們同時發送幀ID的時候,CAN的特性之一就會起作用,隱性電平會被顯性電平覆蓋,也就是只要在同一時刻,CAN上既有Node發送隱性電平又有Node發送顯性電平,那CAN上就只有顯性電平,反過來說,只有當CAN上所有Node都只發送隱性電平的時候,CAN上纔是隱性電平。這就是仲裁機制背後的物理機制。

然後接下來,我們繼續談談仲裁的過程中到底發生了什麼,繼續上圖的例子,在時刻5的時候,Node 2是隱性電平 ( 1 ) ,而其他Node是顯性電平 ( 0 ) ,所以整個CAN上也是顯性電平 ( 0 ) ,然後Node 2自己一對比自己的輸入輸出不同,很自覺得知道自己優先權比其他Node低,然後停止發送數據,轉爲監聽模式,Node 1和Node 3繼續仲裁。在時刻2的時候,Node 1是隱性電平 ( 1 ) ,而Node 3是顯性電平 ( 0 ) ,所以Node 1也轉爲監聽模式,最後當整個ID發送完,仲裁完成後,只有Node 3發現自己沒事,就知道自己是優先權最大的那個,接下來就繼續發送數據幀,而其他Node就暫停發送數據,一直監聽Node 3的數據,直到Node 3發完數據,其他Node再繼續仲裁下去,決定接下來的發送權。

3.0 STM32的CAN配置原理

3.1 圖解CAN內部實現機構

https://i.loli.net/2020/03/30/Wu1C8gAadiDZv79.jpg

從圖上我們可以看到,CAN 1與CAN 2爲主從關係,CAN 1爲Master ( 主 ) ,CAN 2爲Slave ( 從 ) 。CAN 1和CAN 2有各自的3個發送郵箱,2個FIFO Buffer緩衝,6個接收郵箱。但是,28個Filter 過濾器卻是共用的,我們可以規定,哪些Filter給那個CAN用,甚至可以在程序運行的時候調控。

Transmit Mailbox.png

Receive Mailbox.png

上面倆圖是發送郵箱和接收郵箱的具體的流程和機制。具體的細節就實在太多了,本文畢竟不是參考手冊,想更加深入的看官,可以去ST官方的Reference Manual查閱具體細節。

3.2 Filter 過濾器

Filter 過濾器就很簡單了,就是過濾接收到的每一幀的ID,如果符合特定條件,那就將該幀放入接收FIFO中。

Filter 過濾器有兩種模式:

  1. Identifier List Mode 標識符列表模式:它把要接收報文的 ID 列成一個表,要求報文 ID 與列表中的某一個標識符完全相同纔可以接收,可以理解爲白名單管理
  2. Mask Mode 掩碼模式:它把可接收報文 ID 的某幾位作爲列表,這幾位被稱爲掩碼,可以把它理解成關鍵字搜索,只要掩碼(關鍵字)相同,就符合要求,報文就會被保存到接收FIFO

CAN Mask.png

每種模式又有對應的16 Bit位,32 Bit位模式,具體的使用方法和區別,本文不深入討論,這部分內容再出個完整的教程都足夠了,所以這部分留給客官去看Reference Manuel來理解。

3.3 如何通過Bit Rate 比特率來選擇Time Quantum 時間量子

CAN for STM32.png

該圖生成於CAN Bit Time Calculation,可以看出在iRM2018的時鐘樹配置下,42MHz的CAN總線,最推薦的是黃色的那條,是穩定和高速的交際範圍,越往上越快,但是越不穩定,越往下越穩定,但是越慢。因爲C620電調和M3508電機接收的比特率是1Mbps,所以我們只能選擇最上面的那個最快的那組參數。

3.4 特殊功能選項

  • Automatic Bus-Off Management 自動離線管理:可以在出錯時離線後適時自動恢復,不需要軟件干預
  • Automatic Retransmission 自動重傳:使用本功能時,會一直髮送報文直到成功爲止

3.5 RoboMaster Assistant

在用串口轉接器後,可以在PWM接口連接上PC的RoboMaster Assitant來調試電機,重要的是可以調整電機的CAN數據發送頻率,適當調低可以降低CPU的接收壓力,把計算資源留給其他更加重要的功能。

3.6 終端電阻

在C620電調的側翼,有個開關,用於是否開啓終端電阻。這個電阻是一定要開的,因爲CAN總線的任何終端,任何Node 節點,都需要在兩根線中間接120 Ω120\ Ω的電阻。

4.0 CAN的用法

HAL_StatusTypeDef HAL_CAN_ActivateNotification(CAN_HandleTypeDef *hcan, uint32_t ActiveITs);
  • 參數
    • hcan:指向CAN配置結構體
    • ActiveITs:表明哪個中斷會被啓動,開啓改中斷的消息提示
  • 返回值
    • HAL_StatusTypeDef:如果開啓成功,返回HAL_OK;如果失敗,返回HAL_ERROR

HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan);
  • 參數
    • hcan:指向CAN配置結構體
  • 返回值
    • HAL_StatusTypeDef:如果開啓成功,返回HAL_OK;如果失敗,返回HAL_ERROR

HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig);
  • 參數
    • hcan:指向CAN配置結構體
    • sFilterConfig:指向Filter過濾器配置結構體
  • 返回值
    • HAL_StatusTypeDef:如果開啓成功,返回HAL_OK;如果失敗,返回HAL_ERROR

HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox);
  • 參數
    • hcan:指向CAN配置結構體
    • pHeader:指向發送數據的配置結構體
    • aData[]:指向需要發送的數據
    • pTxMailbox:該函數會返回用於儲存發送數據的發送郵箱編號到該變量
  • 返回值
    • HAL_StatusTypeDef:如果添加成功,返回HAL_OK;如果失敗,返回HAL_ERROR

uint32_t HAL_CAN_IsTxMessagePending(CAN_HandleTypeDef *hcan, uint32_t TxMailboxes);
  • 參數
    • hcan:指向CAN配置結構體
    • TxMailboxes:指向發送數據的發送郵箱
  • 返回值
    • uint32_t:如果有發送數據正在等待發送,返回1;如果沒有,返回0

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);
  • 在寫代碼的時候,在main.c中創建HAL_CAN_RxFifo0MsgPendingCallback函數
  • 在該函數中填寫在FIFO 0中已經沒有正在等待接收的數據的時候開啓中斷,需要執行的代碼

HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]);
  • 參數
    • hcan:指向CAN配置結構體
    • RxFifo:指向負責接收的FIFO
    • pHeader:指向接收數據的配置結構體
    • aData[]:指向需要接收的數據
  • 返回值
    • HAL_StatusTypeDef:如果接收成功,返回HAL_OK;如果失敗,返回HAL_ERROR

5.0 練習項目

5.1 項目簡介

  • CAN電機控制與讀取:通過CAN控制電機,並且在按下按鈕時通過CAN讀取電機運行數據

5.2 項目代碼

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct{
	uint8_t     motor_id;
	int16_t     angle;
	int16_t     current_get;
	int16_t     speed_rpm;
	uint8_t     temperature;
}   motor_3508_t;
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define CAN1_DEVICE_NUM     4
#define FIRST_GROUP_ID      0x200
#define MOTOR_SPEED_MAX     16384
#define CAN_DATA_SIZE       8
#define CAN1_RX_ID_START    0x201
#define MOTOR_ID            2
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
CAN_HandleTypeDef hcan1;

UART_HandleTypeDef huart7;

/* USER CODE BEGIN PV */
int16_t speedFlag=0;
uint8_t can1_rx_buffer[CAN1_DEVICE_NUM][CAN_DATA_SIZE];
motor_3508_t motorPrintTest={.motor_id=MOTOR_ID};
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_CAN1_Init(void);
static void MX_UART7_Init(void);
/* USER CODE BEGIN PFP */
void can_filter_enable(CAN_HandleTypeDef* hcan);
void can_filter_disable(CAN_HandleTypeDef* hcan);
void can_transmit(CAN_HandleTypeDef* hcan, uint16_t id, int16_t msg1, int16_t msg2, int16_t msg3, int16_t msg4);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* 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_UART7_Init();
  /* USER CODE BEGIN 2 */
  can_filter_disable(&hcan1);
  HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
  HAL_CAN_Start(&hcan1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  	while(speedFlag <= MOTOR_SPEED_MAX/12){
  		can_transmit(&hcan1,FIRST_GROUP_ID,speedFlag,speedFlag,speedFlag,speedFlag);
	    speedFlag++;
  		HAL_Delay(1);
  	}
  	while(speedFlag >= -MOTOR_SPEED_MAX/12){
  		can_transmit(&hcan1,FIRST_GROUP_ID,speedFlag,speedFlag,speedFlag,speedFlag);
	    speedFlag--;
	    HAL_Delay(1);
  	}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 6;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief CAN1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_CAN1_Init(void)
{

  /* USER CODE BEGIN CAN1_Init 0 */

  /* USER CODE END CAN1_Init 0 */

  /* USER CODE BEGIN CAN1_Init 1 */

  /* USER CODE END CAN1_Init 1 */
  hcan1.Instance = CAN1;
  hcan1.Init.Prescaler = 3;
  hcan1.Init.Mode = CAN_MODE_NORMAL;
  hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan1.Init.TimeSeg1 = CAN_BS1_11TQ;
  hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
  hcan1.Init.TimeTriggeredMode = DISABLE;
  hcan1.Init.AutoBusOff = ENABLE;
  hcan1.Init.AutoWakeUp = DISABLE;
  hcan1.Init.AutoRetransmission = ENABLE;
  hcan1.Init.ReceiveFifoLocked = DISABLE;
  hcan1.Init.TransmitFifoPriority = DISABLE;
  if (HAL_CAN_Init(&hcan1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN CAN1_Init 2 */

  /* USER CODE END CAN1_Init 2 */

}

/**
  * @brief UART7 Initialization Function
  * @param None
  * @retval None
  */
static void MX_UART7_Init(void)
{

  /* USER CODE BEGIN UART7_Init 0 */

  /* USER CODE END UART7_Init 0 */

  /* USER CODE BEGIN UART7_Init 1 */

  /* USER CODE END UART7_Init 1 */
  huart7.Instance = UART7;
  huart7.Init.BaudRate = 115200;
  huart7.Init.WordLength = UART_WORDLENGTH_8B;
  huart7.Init.StopBits = UART_STOPBITS_1;
  huart7.Init.Parity = UART_PARITY_NONE;
  huart7.Init.Mode = UART_MODE_TX_RX;
  huart7.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart7.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart7) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN UART7_Init 2 */

  /* USER CODE END UART7_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();

  /*Configure GPIO pin : Button_Pin */
  GPIO_InitStruct.Pin = Button_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(Button_GPIO_Port, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI2_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(EXTI2_IRQn);

}

/* USER CODE BEGIN 4 */
void can_filter_enable(CAN_HandleTypeDef* hcan){
	CAN_FilterTypeDef CAN_FilterConfigStructure;

	CAN_FilterConfigStructure.FilterIdHigh = 0x0000;
	CAN_FilterConfigStructure.FilterIdLow = 0x0000;
	CAN_FilterConfigStructure.FilterMaskIdHigh = 0x0000;
	CAN_FilterConfigStructure.FilterMaskIdLow = 0x0000;
	CAN_FilterConfigStructure.FilterFIFOAssignment = CAN_FILTER_FIFO0;
	CAN_FilterConfigStructure.FilterMode = CAN_FILTERMODE_IDMASK;
	CAN_FilterConfigStructure.FilterScale = CAN_FILTERSCALE_32BIT;
	CAN_FilterConfigStructure.FilterActivation = ENABLE;
	CAN_FilterConfigStructure.SlaveStartFilterBank = 27;

	CAN_FilterConfigStructure.FilterBank = 0;

	HAL_CAN_ConfigFilter(hcan, &CAN_FilterConfigStructure);
}

void can_filter_disable(CAN_HandleTypeDef* hcan){
	CAN_FilterTypeDef CAN_FilterConfigStructure;

	CAN_FilterConfigStructure.FilterIdHigh = 0x0000;
	CAN_FilterConfigStructure.FilterIdLow = 0x0000;
	CAN_FilterConfigStructure.FilterMaskIdHigh = 0x0000;
	CAN_FilterConfigStructure.FilterMaskIdLow = 0x0000;
	CAN_FilterConfigStructure.FilterFIFOAssignment = CAN_FILTER_FIFO0;
	CAN_FilterConfigStructure.FilterMode = CAN_FILTERMODE_IDMASK;
	CAN_FilterConfigStructure.FilterScale = CAN_FILTERSCALE_32BIT;
	CAN_FilterConfigStructure.FilterActivation = DISABLE;
	CAN_FilterConfigStructure.SlaveStartFilterBank = 27;

	CAN_FilterConfigStructure.FilterBank = 0;

	HAL_CAN_ConfigFilter(hcan, &CAN_FilterConfigStructure);
}

void can_transmit(CAN_HandleTypeDef* hcan, uint16_t id, int16_t msg1, int16_t msg2, int16_t msg3, int16_t msg4){
	CAN_TxHeaderTypeDef tx_header;
	uint8_t             data[8];
	uint32_t            pTxMailbox;

	tx_header.StdId = id;
	tx_header.IDE   = CAN_ID_STD;
	tx_header.RTR   = CAN_RTR_DATA;
	tx_header.DLC   = CAN_DATA_SIZE;
	tx_header.TransmitGlobalTime = DISABLE;
	data[0] = msg1 >> 8;
	data[1] = msg1;
	data[2] = msg2 >> 8;
	data[3] = msg2;
	data[4] = msg3 >> 8;
	data[5] = msg3;
	data[6] = msg4 >> 8;
	data[7] = msg4;

	if (HAL_CAN_AddTxMessage(hcan, &tx_header, data, &pTxMailbox) == HAL_OK){
		while (HAL_CAN_IsTxMessagePending(hcan, pTxMailbox));
	}
}

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan){
	CAN_RxHeaderTypeDef rx_header;
	rx_header.StdId = (CAN_RI0R_STID & hcan->Instance->sFIFOMailBox[CAN_RX_FIFO0].RIR) >> CAN_TI0R_STID_Pos;
	uint8_t idx = rx_header.StdId - CAN1_RX_ID_START;
	HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, can1_rx_buffer[idx]);
}

void can1_read(uint8_t id, uint8_t buf[CAN_DATA_SIZE]){
	memcpy(buf, can1_rx_buffer[--id], CAN_DATA_SIZE);
}

void get_motor_data(motor_3508_t *motor, uint8_t buf[CAN_DATA_SIZE]){
	motor->angle        = (int16_t)(buf[0] << 8 | buf[1]);
	motor->speed_rpm    = (int16_t)(buf[2] << 8 | buf[3]);
	motor->current_get  = (int16_t)(buf[4] << 8 | buf[5]);
	motor->temperature  = (uint8_t)buf[6];
}

void print_motor_data(motor_3508_t *motor, char *msg){
	sprintf(msg, "======== 3508 at CAN bus ========\r\n"\
	             "ID           %d\r\n"\
	             "Angle        %d\r\n"\
	             "Current      %d\r\n"\
	             "Speed        %d\r\n"\
	             "Temperature  %u\r\n"\
	             "=================================\r\n\r\n"\
	             , motor->motor_id, motor->angle, motor->current_get, motor->speed_rpm, motor->temperature);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
	if(GPIO_Pin == Button_Pin){
		can_filter_enable(&hcan1);
		uint8_t motorStatus[CAN_DATA_SIZE];
		char motorMsg[250];
		can1_read(MOTOR_ID,motorStatus);
		get_motor_data(&motorPrintTest, motorStatus);
		print_motor_data(&motorPrintTest, motorMsg);
		HAL_UART_Transmit(&huart7, (uint8_t*)motorMsg, strlen(motorMsg), HAL_MAX_DELAY);
		can_filter_disable(&hcan1);
	}
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

5.3 效果展示

http://player.bilibili.com/player.html?aid=242558868&bvid=BV1he411x7DN&cid=170233216&page=1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章