基于stm32 f407vet6芯片 使用hal庫開發 can
簡單講解一下can的基礎使用
CubeMX配置
這里打開CAN1 并且設置好波特率和NVIC相關的配置
波特率使用波特率計算器軟件
使用采樣率最高的這段 填入
得到波特率1M bit/s
然后編寫代碼
環形緩沖區
#include "driver_buffer.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"RingBuffer USERRxDataBuffer1; //定義用戶緩沖區
RingBuffer USERRxDataBuffer2;/* 初始化環形緩沖區 */
int Driver_Buffer_Init(ptRingBuffer buffer, uint16_t size)
{if (buffer == NULL || size == 0)return -1; /* 判斷合法性 */if (buffer->fifo == NULL) /* 判斷是否為內存大小空*/{buffer->fifo = (uint8_t *)malloc(size); /* 動態分配內存 */if (buffer->fifo == NULL){//printf("Malloc %d bytes failed.\r\n", size);return -1;}}buffer->pw = buffer->pr = 0;buffer->buf_size = size;return 0;
}/* 環形緩沖區寫一個字節 */
int Driver_Buffer_Write(ptRingBuffer buffer, const uint8_t data)
{if (buffer == NULL || buffer->fifo == NULL)return -1; /* 判斷合法性 */int i = (buffer->pw + 1) % buffer->buf_size;if (i != buffer->pr) /* 判斷是否寫滿 */{buffer->fifo[buffer->pw] = data; /* */buffer->pw = i; /* 重置寫指針 */return 0;}return -1;
}/* 環形緩沖區寫多個字節 */
int Driver_Buffer_WriteBytes(ptRingBuffer buffer, const uint8_t *data_stream, uint8_t len)
{int i;if (buffer == NULL || buffer->fifo == NULL)return -1; /* 判斷合法性 */if (data_stream == NULL || len == 0)return -1;for (i = 0; i < len; i++){if (Driver_Buffer_Write(buffer, data_stream[i]) != 0)break;}return i;
}/* 環形緩沖區 讀一個字節 */
int Driver_Buffer_Read(ptRingBuffer buffer, uint8_t *data)
{if (buffer == NULL || buffer->fifo == NULL)return -1; /* 判斷合法性 */if (data == NULL)return -1;if (buffer->pr == buffer->pw)return -1; /* 滿 */*data = buffer->fifo[buffer->pr];buffer->pr = (buffer->pr + 1) % buffer->buf_size; /* 自增 */return 0;
}/* 環形緩沖區 讀多個字節 */
int Driver_Buffer_ReadBytes(ptRingBuffer buffer, uint8_t *data_stream, uint8_t len)
{int i = 0;if (buffer == NULL || buffer->fifo == NULL)return -1; /* 判斷合法性 */if (data_stream == NULL || len == 0)return -1;for (i = 0; i < len; i++){if (Driver_Buffer_Read(buffer, &data_stream[i]) != 0)break;}return i;
}/* 清空環形緩沖區 */
int Driver_Buffer_Clean(ptRingBuffer buffer)
{if (buffer == NULL || buffer->fifo == NULL)return -1; /* 判斷合法性 */memset(buffer->fifo, 0, buffer->buf_size); /* 清空 */buffer->pr = buffer->pw = 0; /* 歸零 */return 0;
}
/*** @brief 更新數據到數組* @param buffer* @param data_stream* @return 返回更新的數據長度*/
int Driver_Buffer_RefreshData(ptRingBuffer buffer, uint8_t *data_stream)
{uint16_t len = 0;if (buffer->pw == buffer->buf_size)buffer->pw = 0;while (buffer->pw != buffer->pr){data_stream[len++] = buffer->fifo[buffer->pr];buffer->fifo[buffer->pr] = 0;buffer->pr++;if (buffer->pr >= buffer->buf_size)buffer->pr = 0;if (len >= buffer->buf_size)break;}return len;
}
#ifndef __DRIVER_BUFFER_H
#define __DRIVER_BUFFER_H#include "stdint.h"
typedef struct{uint8_t *fifo;uint16_t pw; /*寫地址*/uint16_t pr;uint16_t buf_size;}RingBuffer,*ptRingBuffer;extern RingBuffer USERRxDataBuffer1;
extern RingBuffer USERRxDataBuffer2;int Driver_Buffer_Init(ptRingBuffer buffer, uint16_t size);int Driver_Buffer_Write(ptRingBuffer buffer, const uint8_t data);int Driver_Buffer_WriteBytes(ptRingBuffer buffer, const uint8_t *data_stream, uint8_t len);int Driver_Buffer_Read(ptRingBuffer buffer, uint8_t *data);int Driver_Buffer_ReadBytes(ptRingBuffer buffer, uint8_t *data_stream, uint8_t len);int Driver_Buffer_Clean(ptRingBuffer buffer);int Driver_Buffer_RefreshData(ptRingBuffer buffer, uint8_t *data_stream);#endif
#include "driver_can.h"
#include "can.h"
#include "driver_buffer.h"#include <stdio.h> //使用printf函數
#include <stdint.h>HAL_StatusTypeDef CAN_Init(void)
{HAL_StatusTypeDef result = HAL_OK;Driver_Buffer_Init(&USERRxDataBuffer1, 256); /* 開辟環形緩沖區空間 */result = CAN_Filter_Config_Scale32_IdMask(&hcan1, 0x100, 0x100); // 過濾器設置return result;
}HAL_StatusTypeDef CAN_Filter_Config_Scale32_IdMask(CAN_HandleTypeDef *hcan, uint32_t id, uint32_t mask)
{HAL_CAN_Stop(hcan); // 停止canHAL_StatusTypeDef result = HAL_OK;CAN_FilterTypeDef sFilterConfig;/* 配置過濾器參數 */sFilterConfig.FilterBank = 0; // 過濾器組編號(0-27)sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 掩碼模式(或列表模式)sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位位寬// ID分解(右移13位取高16位,左移3位處理IDE/RTR位)sFilterConfig.FilterIdHigh = (id >> 13) & 0xFFFF;sFilterConfig.FilterIdLow = (id << 3) & 0xFFF8| CAN_ID_EXT|CAN_RTR_DATA; /*只接受拓展幀 和數據幀*/// 掩碼設置為0xFFFF0000(匹配前16位)sFilterConfig.FilterMaskIdHigh = (mask >> 13) & 0xFFFF;sFilterConfig.FilterMaskIdLow = (mask << 3) & 0xFFF8| CAN_ID_EXT|CAN_RTR_DATA; /*只接受拓展幀 和數據幀*/sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 接收FIFO選擇sFilterConfig.FilterActivation = ENABLE; // 啟用過濾器sFilterConfig.SlaveStartFilterBank = 14; // 雙CAN時從過濾器組起始編號 單can無意義/* 應用過濾器配置 */if (HAL_CAN_ConfigFilter(hcan, &sFilterConfig) != HAL_OK){Error_Handler();}result = HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // FIFO0消息掛起中斷result = HAL_CAN_Start(hcan); // 啟動CAN外設return result;
}/* 32位列表模式 */
HAL_StatusTypeDef CAN_Filter_Config_Scale32_IdList(CAN_HandleTypeDef *hcan, uint32_t id1, uint32_t id2)
{HAL_CAN_Stop(hcan); // 停止canHAL_StatusTypeDef result = HAL_OK;CAN_FilterTypeDef sFilterConfig;/* 基礎參數配置 */sFilterConfig.FilterBank = 0; // 使用過濾器組0(CAN1默認組0-13)sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式,需精確匹配IDsFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位位寬,支持標準幀和擴展幀sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 接收報文存入FIFO0sFilterConfig.FilterActivation = ENABLE; // 啟用過濾器sFilterConfig.SlaveStartFilterBank =14; // CAN2使用過濾器組14-27/* 設置兩個目標擴展ID(0x18F60000和0x18F60001) */uint32_t target_id1 = id1 | CAN_ID_EXT; // 擴展幀需設置IDE位uint32_t target_id2 = id2 | CAN_ID_EXT;// 擴展ID分解:高16位右移13位,低16位左移3位(IDE/RTR位對齊)sFilterConfig.FilterIdHigh = (target_id1 >> 13) & 0xFFFF;sFilterConfig.FilterIdLow = (target_id1 << 3) & 0xFFF8| CAN_ID_EXT|CAN_RTR_DATA; /*只接受拓展幀 和數據幀*/sFilterConfig.FilterMaskIdHigh = (target_id2 >> 13) & 0xFFFF; // 第二個ID的高16位sFilterConfig.FilterMaskIdLow = (target_id2 << 3) & 0xFFF8| CAN_ID_EXT|CAN_RTR_DATA; /*只接受拓展幀 和數據幀*//* 應用過濾器配置 */if (HAL_CAN_ConfigFilter(hcan, &sFilterConfig) != HAL_OK){Error_Handler();}HAL_CAN_Start(hcan); HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // FIFO0消息掛起中斷// 啟動CAN外設return result;
}HAL_StatusTypeDef CAN_Filter_Config_Scale16_IdMask(CAN_HandleTypeDef *hcan, uint16_t id, uint16_t mask)
{HAL_CAN_Stop(hcan); // 停止canHAL_StatusTypeDef result = HAL_OK;CAN_FilterTypeDef sFilterConfig;/* 配置過濾器參數 */sFilterConfig.FilterBank = 0; // 過濾器組編號(0-27)sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 掩碼模式(或列表模式)sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; // 32位位寬sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 接收報文存入FIFO0sFilterConfig.FilterActivation = ENABLE; // 啟用過濾器sFilterConfig.SlaveStartFilterBank = 0; // CAN2使用過濾器組14-27// 標準幀ID左移5位對齊(STID[10:0]占高11位)sFilterConfig.FilterIdHigh = (id << 5); // ID高16位寄存器(實際存儲前11位)sFilterConfig.FilterIdLow = 0x0000; // ID低16位寄存器(未使用)sFilterConfig.FilterMaskIdHigh = (mask << 5); // 掩碼高16位寄存器sFilterConfig.FilterMaskIdLow = 0x0000; // 掩碼低16位寄存器(未使用)/* 應用過濾器配置 */if (HAL_CAN_ConfigFilter(hcan, &sFilterConfig) != HAL_OK){Error_Handler();}// 啟動CAN外設HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // FIFO0消息掛起中斷HAL_CAN_Start(hcan); return result;
}/* 16位列表模式 */
HAL_StatusTypeDef CAN_Filter_Config_Scale16_IdList(CAN_HandleTypeDef *hcan, uint16_t id1, uint16_t id2, uint16_t id3, uint16_t id4)
{HAL_CAN_Stop(hcan); // 停止canHAL_StatusTypeDef result = HAL_OK;CAN_FilterTypeDef sFilterConfig;/* 基礎參數配置 */sFilterConfig.FilterBank = 0; // 使用過濾器組0(CAN1默認組0-13)sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式,需精確匹配IDsFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 接收報文存入FIFO0sFilterConfig.FilterActivation = ENABLE; // 啟用過濾器sFilterConfig.SlaveStartFilterBank = 0; // CAN2使用過濾器組14-27sFilterConfig.FilterIdHigh = id1 << 5; // 標準ID 0x100sFilterConfig.FilterIdLow = id2 << 5; // 標準ID 0x101sFilterConfig.FilterMaskIdHigh = id3 << 5; // 標準ID 0x102sFilterConfig.FilterMaskIdLow = id4 << 5; // 標準ID 0x103/* 應用過濾器配置 */if (HAL_CAN_ConfigFilter(hcan, &sFilterConfig) != HAL_OK){Error_Handler();}HAL_CAN_Start(hcan); // 啟動CAN外設HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // FIFO0消息掛起中斷return result;
}/** @brief 發送一幀CAN數據* @param hcan: CAN句柄指針* @param id: 報文標識符(標準ID或擴展ID)* @param data: 數據緩沖區指針(最大8字節)* @param len: 數據長度(0-8)* @param isExtId: 是否為擴展ID(1=擴展幀,0=標準幀)* @retval HAL狀態:HAL_OK=成功,其他=失敗*/
HAL_StatusTypeDef CAN_Send_Frame(CAN_HandleTypeDef *hcan, uint32_t id, uint8_t *data, uint8_t len, uint8_t isExtId)
{CAN_TxHeaderTypeDef txHeader;uint32_t mailbox;HAL_StatusTypeDef result = HAL_OK;/* 校驗參數合法性 */if (len > 8 || data == NULL)return HAL_ERROR;/* 配置報文頭部 */if (isExtId){txHeader.ExtId = id; // 擴展IDtxHeader.IDE = CAN_ID_EXT; // 擴展幀標識}else{txHeader.StdId = id; // 標準IDtxHeader.IDE = CAN_ID_STD; // 標準幀標識}txHeader.RTR = CAN_RTR_DATA; // 數據幀(非遠程請求)txHeader.DLC = len; // 數據長度碼txHeader.TransmitGlobalTime = DISABLE; // 不記錄全局時間戳/* 提交發送請求 */result = HAL_CAN_AddTxMessage(hcan, &txHeader, data, &mailbox);while (HAL_CAN_GetTxMailboxesFreeLevel(hcan) != 3) // 3 個郵箱都為空; /*等待發送完成 防止丟包*/return result;
}/* CAN發送多幀數據 */
void CAN_Send_Data(CAN_HandleTypeDef *hcan, uint32_t id, uint8_t *buf, uint32_t len, uint8_t isExtId)
{uint32_t transmission_times = 0; /* 發送次數 */uint32_t remian_bytes = 0; /* 剩余字節 */uint32_t frame_length = 0; /* 幀長度 */frame_length = 8; /* 標準can最大8 */transmission_times = len / frame_length;remian_bytes = len % frame_length;int i = 0;while (i < transmission_times){CAN_Send_Frame(hcan, id, buf + i * frame_length, frame_length, isExtId);i++;}if (remian_bytes > 0) // 尾幀{CAN_Send_Frame(hcan, id, buf + transmission_times * frame_length, remian_bytes, isExtId);}
}/* CAN 接收數據 */
uint32_t CAN_Receive_Message(CAN_HandleTypeDef *hcan, uint32_t RxFifo, uint8_t *buf)
{CAN_RxHeaderTypeDef RxHeader;if (HAL_CAN_GetRxFifoFillLevel(hcan, RxFifo) == 0) // 檢查郵箱{return 0; // 沒有數據}HAL_CAN_GetRxMessage(hcan, RxFifo, &RxHeader, buf);return RxHeader.DLC; // 返回接收長度
}/*** @brief 接收FIFO0的回調函數 (中斷方式)* @param hcan*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{uint8_t can_rec_data[8];uint32_t len = 0;len = CAN_Receive_Message(hcan, CAN_RX_FIFO0, can_rec_data);Driver_Buffer_WriteBytes(&USERRxDataBuffer1, can_rec_data, len); /*將收到的數據寫到緩沖區*/char buffer[10] = "";char *ptr = buffer;for (size_t i = 0; i < 8; i++){ptr += sprintf(ptr, "%02X ", can_rec_data[i]); // 將元素轉為十六進制字符串}//usb_printf("%s\n", buffer); // 一次性輸出整個字符串
}//void MX_CAN_Init(void)
//{
// CAN_FilterTypeDef sFilterConfig;
//
// /*CANμ¥?3???ˉ*/
//
// hCAN.Instance = CANx; /* CAN?? */
// hCAN.Init.Prescaler = 3; /* BTR-BRP 2¨??·?μ? ?¨?áˊ±???μ?±????42/(1+10+3)/3=1Mbps */
// hCAN.Init.Mode = CAN_MODE_NORMAL; /* ?3£1¤??? */
// hCAN.Init.SyncJumpWidth = CAN_SJW_1TQ; /* BTR-SJW ???2????2?????? */
// hCAN.Init.TimeSeg1 = CAN_BS1_10TQ; /* BTR-TS1 ???1 ??á??????? */
// hCAN.Init.TimeSeg2 = CAN_BS2_3TQ; /* BTR-TS1 ???2 ??á??????? */
// hCAN.Init.TimeTriggeredMode = DISABLE; /* MCR-TTCM 1??±??·¢?????? */
// hCAN.Init.AutoBusOff = ENABLE; /* MCR-ABOM ??ˉà?1?
// hCAN.Init.AutoWakeUp = ENABLE; /* MCR-AWUM ????ˉ??Б?? */
// hCAN.Init.AutoRetransmission = DISABLE; /* MCR-NART ???±¨???ˉ?′? DISABLE-??ˉ?′? */
// hCAN.Init.ReceiveFifoLocked = DISABLE; /* MCR-RFLM ???IFO ??¨?? DISABLE-?3???±¨????ǔ-?±¨? */
// hCAN.Init.TransmitFifoPriority = DISABLE; /* MCR-TXFP ·¢?FIFO?ψ?? DISABLE-?ψ??????±¨?±?·? */
// HAL_CAN_Init(&hCAN);
//
// /*CAN1y??3???ˉ*/
// sFilterConfig.FilterBank = 0; /* 1y???0 */
// sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; /* 1¤??±?·??±???? */
// sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; /* 1y??λ??μ¥??32λ?£*/
// sFilterConfig.FilterIdHigh = (((uint32_t)0x1314<<3)&0xFFFF0000)>>16;; /* ?1y?μ?D??? */
// sFilterConfig.FilterIdLow = (((uint32_t)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF;; /* ?1y?μ?Dμ?? */
// sFilterConfig.FilterMaskIdHigh = 0xFFFF; /* 1y????6λ?λ±??? */
// sFilterConfig.FilterMaskIdLow = 0xFFFF; /* 1y??μ?6λ?λ±??? */
// sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; /* 1y??±?1?aμ?FIFO 0 */
// sFilterConfig.FilterActivation = ENABLE; /* ??1y?? */
// sFilterConfig.SlaveStartFilterBank = 0; /* ?à′??′?y??μ??憷±? */
//
// HAL_CAN_ConfigFilter(&hCAN, &sFilterConfig);
//
//
//}
#ifndef __DRIVER_CAN_H__
#define __DRIVER_CAN_H__#include "main.h"HAL_StatusTypeDef CAN_Init(void);HAL_StatusTypeDef CAN_Filter_Config_Scale32_IdMask(CAN_HandleTypeDef *hcan, uint32_t id, uint32_t mask);HAL_StatusTypeDef CAN_Filter_Config_Scale32_IdList(CAN_HandleTypeDef *hcan, uint32_t id1, uint32_t id2);HAL_StatusTypeDef CAN_Filter_Config_Scale16_IdMask(CAN_HandleTypeDef *hcan, uint16_t id, uint16_t mask);HAL_StatusTypeDef CAN_Filter_Config_Scale16_IdList(CAN_HandleTypeDef *hcan, uint16_t id1, uint16_t id2, uint16_t id3, uint16_t id4);HAL_StatusTypeDef CAN_Send_Frame(CAN_HandleTypeDef *hcan, uint32_t id, uint8_t *data, uint8_t len, uint8_t isExtId);void CAN_Send_Data(CAN_HandleTypeDef *hcan, uint32_t id, uint8_t *buf, uint32_t len, uint8_t isExtId);uint32_t CAN_Receive_Message(CAN_HandleTypeDef *hcan, uint32_t RxFifo, uint8_t *buf);#endif
提供了32位掩碼和列表 16位掩碼和列表的過濾器
編寫應用層代碼
void APP_User_Task(void)
{for(int i =0 ;i<8;i++){buf[i] = i;}/*發送丟包測試*/ for(int i =0 ;i<1024;i++){tx_Pathping[i] = i%256;}CAN_Send_Data(&hcan1,0x100,tx_Pathping,1024,1);for(;;){/*接收丟包測試*/if(Driver_Buffer_RefreshData(&USERRxDataBuffer1,tx_buf)){tx_buf[7] = 0xa5;CAN_Send_Data(&hcan1,0x5A,tx_buf,8,1);}}
}
實驗效果
一開機就輸出了128幀數據 可以看到數據都是連續的沒有丟包
然后發送1024幀數據 可以看到也沒有丟包
發送的數據接收到環形緩沖區 然后讀出數據打印
此工程代碼已經在STM32F407VET6 和STM32F103CBT6芯片上都驗證過了 沒有太大問題