CAN:代码篇
CAN2.0总线介绍-CSDN博客
一:.CAN总线单个设备环回测试
代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "exit.h"
#include "UART.h"
#include "OLED/OLED.h"
#include <stdio.h>
#include <stdarg.h>
#include "CAN/can.h"
uint8_t KeyNum;
uint32_t TxID = 0x555; // 发送ID (标准帧)
uint8_t TxLength = 8; // 发送数据长度
uint8_t TxData[] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}; // 发送数据
uint32_t RxID; // 接收ID
uint8_t RxLength; // 接收数据长度
uint8_t RxData[8]; // 接收数据缓冲区
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72MHz */
delay_init(72); /* 延时初始化 */
Uart_Init(115200);
OLED_Init();
CAN_Init(); // 修正函数名
printf("CAN通信测试启动...\r\n");
while (1)
{
// 每500ms发送一次数据
delay_ms(3000);
// 数据递增
TxData[0]++;
TxData[1]++;
TxData[2]++;
if (TxData[0] > 0xFF) TxData[0] = 0;
// 发送CAN消息(标准帧、数据帧)
if (CAN_AddTxMessage(TxID, TxLength, 1, TxData, 0) != HAL_OK) {
printf("CAN发送失败!\n");
} else {
printf("发送数据: ID=0x%03X\r\n", TxID);
for(uint8_t i=0;i<TxLength;i++)
{
printf("TXdata[%d]:=0x%03X\r\n",i, TxData[i]);
//十六进制格式输出
}
}
// 接收CAN消息
if (MyCAN_Receive(&RxID, &RxLength, RxData) == HAL_OK) {
if (RxLength > 0) {
printf("接收到数据: ID=0x%03X, 长度=%d\r\n",RxID, RxLength );
for(uint8_t i=0;i<RxLength;i++)
{
printf("RXdata[%d]:0x%03X\r\n",i, RxData[i]);
}
printf("----------------------------------\r\n");
}
}
}
}
#include "stm32f1xx_hal.h"
#include "delay.h"
// CAN句柄和过滤器全局变量
CAN_HandleTypeDef hcan1; // 修改变量名更规范
CAN_FilterTypeDef sFilterConfig;
/**
* @brief CAN初始化函数
* @param 无
* @retval 无
*/
void CAN_Init(void)
{
// 配置CAN1句柄
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 6; // 预分频值 6
hcan1.Init.Mode = CAN_MODE_LOOPBACK; // 环回模式(测试用)
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; // 重新同步跳跃宽度 1个时间单元
hcan1.Init.TimeSeg1 = CAN_BS1_5TQ; // 时间段1 5个时间单元
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ; // 时间段2 2个时间单元
hcan1.Init.TimeTriggeredMode=DISABLE;// TTCM时间触发通信模式
hcan1.Init.AutoBusOff= DISABLE; //ABOM 自动总线关闭恢复功能
hcan1.Init.AutoWakeUp=DISABLE; //AWUM--> 自动唤醒功能(AWUM)
hcan1.Init.AutoRetransmission= DISABLE; //NART 自动重传功能(NART)
hcan1.Init.ReceiveFifoLocked= DISABLE; //RFLM 接收 FIFO 锁定功能(RFLM
hcan1.Init.TransmitFifoPriority= DISABLE; //TXFP 发送 FIFO(邮箱) 优先级控制(TXFP)ENABLE:先请求先发送,DISABLE:ID号小的先发送
// 初始化CAN
if (HAL_CAN_Init(&hcan1) != HAL_OK) {
}
// 配置CAN过滤器(接收标准ID 0x555)
sFilterConfig.FilterBank = 0; // 过滤器组0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位过滤器
sFilterConfig.FilterIdHigh = 0x000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000; // 无掩码(精确匹配)
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 激活过滤器
sFilterConfig.SlaveStartFilterBank = 0;
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {
}
// 启动CAN
if (HAL_CAN_Start(&hcan1) != HAL_OK) {
}
}
/**
* @brief CAN底层初始化回调函数
* @param hcan: CAN句柄
* @retval 无
*/
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
if (hcan->Instance == CAN1) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能外设时钟 */
__HAL_RCC_CAN1_CLK_ENABLE(); // 使能CAN1时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
/* 配置CAN1 TX (PA12) 引脚 */
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;// 高速模式
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 配置CAN1 RX (PA11) 引脚 */
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉输入
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
/**
* @brief 发送CAN消息
* @param ID: 标识符
* @param Length: 数据长度(1-8)
* @param RTR: 1-数据帧, 0-远程帧
* @param data: 数据指针
* @param IDE: 1-扩展帧, 0-标准帧
* @retval HAL状态值
*/
HAL_StatusTypeDef CAN_AddTxMessage(uint32_t ID, uint8_t Length, uint8_t RTR, uint8_t *data, uint8_t IDE)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailbox;
// 检查输入参数
if (Length == 0 || Length > 8) {
return HAL_ERROR;
}
// 配置CAN消息头
if (IDE) {
TxHeader.IDE = CAN_ID_EXT; // 扩展帧
TxHeader.ExtId = ID;
TxHeader.StdId = 0; // 扩展帧时标准ID无效
} else {
TxHeader.IDE = CAN_ID_STD; // 标准帧
TxHeader.StdId = ID & 0x7FF; // 确保ID在11位范围内
TxHeader.ExtId = 0; // 标准帧时扩展ID无效
}
// 配置远程传输请求
TxHeader.RTR = (RTR == 1) ? CAN_RTR_DATA : CAN_RTR_REMOTE;
// 配置数据长度
TxHeader.DLC = Length;
TxHeader.TransmitGlobalTime = DISABLE;
// 等待发送邮箱可用
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0);
// 发送CAN消息并返回状态
return HAL_CAN_AddTxMessage(&hcan1, &TxHeader, data, &TxMailbox);
}
/**
* @brief 接收CAN消息
* @param ID: 接收ID指针
* @param Length: 接收长度指针
* @param Data: 接收数据指针
* @retval HAL状态值
*/
HAL_StatusTypeDef MyCAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *Data)
{
CAN_RxHeaderTypeDef RxHeader;
uint8_t rxData[8] = {0}; // 初始化接收缓冲区
// 检查FIFO0是否有消息
if (HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) == 0) {
return HAL_TIMEOUT;
}
// 接收CAN消息
if (HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, rxData) != HAL_OK) {
return HAL_ERROR;
}
// 处理ID
if (RxHeader.IDE == CAN_ID_STD) {
*ID = RxHeader.StdId; // 标准ID
} else {
*ID = RxHeader.ExtId; // 扩展ID
}
// 处理数据(仅数据帧)
if (RxHeader.RTR == CAN_RTR_DATA) {
*Length = RxHeader.DLC > 8 ? 8 : RxHeader.DLC;
for (uint8_t i = 0; i < *Length; i++) {
Data[i] = rxData[i];
}
} else {
*Length = 0; // 远程帧无数据
}
return HAL_OK;
}
1.关于函数结构体的解释
HAL_CAN_Init
typedef struct { uint32_t Prescaler; /* 波特率分频器 */ uint32_t Mode; /* 工作模式(正常、环回、静默等) */ uint32_t SyncJumpWidth; /* 重新同步跳跃宽度 */ uint32_t TimeSeg1; /* 时间段1 */ uint32_t TimeSeg2; /* 时间段2 */ FunctionalState TimeTriggeredMode; /* 时间触发模式 */ FunctionalState AutoBusOff; /* 自动总线关闭恢复 */ FunctionalState AutoWakeUp; /* 自动唤醒 */ FunctionalState AutoRetransmission; /* 自动重传 */ FunctionalState ReceiveFifoLocked; /* 接收FIFO锁定模式 */ FunctionalState TransmitFifoPriority; /* 发送FIFO优先级 */ } CAN_InitTypeDef;
- 波特率计算:
波特率 = APB1时钟 (CAN挂载的总线)/ (Prescaler × (TimeSeg1 + TimeSeg2 + 1))
例如:APB1=36MHz,Prescaler=6,TimeSeg1=5,TimeSeg2=2,则波特率为:36MHz / (6 × (5+2+1)) = 750kbps。- 工作模式:
CAN_MODE_NORMAL:正常模式,需外部物理总线。CAN_MODE_LOOPBACK:环回模式,用于自测试(发送数据直接进入接收 FIFO)。CAN_MODE_SILENT:静默模式,只接收不发送1.
TimeTriggeredMode
ENABLE:启用时间触发通信模式(TTCM)
- 数据传输按严格时序执行,每个节点同步在特定时间槽发送数据
- 适用于对时序要求极高的场景(如汽车电子、工业控制)
DISABLE:禁用 TTCM
- 采用标准异步通信,灵活性高但无严格时序保障
- 适用于普通数据传输(如传感器数据采集)
2.
AutoBusOff
ENABLE:启用自动总线关闭恢复(ABOM)
- 当总线错误计数器超过阈值时,自动进入总线关闭状态
- 检测到总线空闲后自动恢复通信
DISABLE:禁用 ABOM
- 总线错误时需软件手动干预恢复(如重启 CAN 控制器)
- 可能导致长时间通信中断
3.
AutoWakeUp
ENABLE:启用自动唤醒(AWUM)
- 当 CAN 控制器处于休眠模式时,检测到总线上的有效信号后自动唤醒
- 降低功耗,适用于电池供电设备
DISABLE:禁用 AWUM
- 需通过软件或外部中断手动唤醒控制器
- 功耗较高,但可避免误唤醒
4.
AutoRetransmission
ENABLE:启用自动重传(NART=0)
- 发送失败的数据帧会自动重传,直到成功或超过最大尝试次数
- 提高通信可靠性,适用于关键数据传输
DISABLE:禁用自动重传(NART=1)
- 发送失败后需软件手动处理重传逻辑
- 适用于对实时性要求高、允许少量丢包的场景
5.
ReceiveFifoLocked
ENABLE:启用接收 FIFO 锁定(RFLM)
- 当 FIFO 已满时,新数据不会覆盖旧数据,而是丢弃新数据
- 确保重要数据不丢失,但可能导致接收缓冲区溢出
DISABLE:禁用 RFLM
- 新数据会覆盖未读取的旧数据(先进先出)
- 适用于数据流处理,需及时读取数据
6.
TransmitFifoPriority
ENABLE:启用发送 FIFO 优先级控制(TXFP)
- 按发送请求的顺序处理邮箱(先请求先发送)
- 适用于需要公平调度的多任务场景
DISABLE:禁用 TXFP
- 按标识符(ID)优先级发送(ID 值越小优先级越高)
- 适用于需要保证关键消息优先发送的场景
HAL_CAN_ConfigFilter
typedef struct { uint32_t FilterBank; /* 过滤器组编号 */ uint32_t FilterMode; /* 过滤器模式(列表/掩码) */ uint32_t FilterScale; /* 过滤器位宽(16位/32位) */ uint32_t FilterIdHigh; /* 过滤器ID高16位 */ uint32_t FilterIdLow; /* 过滤器ID低16位 */ uint32_t FilterMaskIdHigh; /* 过滤器掩码高16位 */ uint32_t FilterMaskIdLow; /* 过滤器掩码低16位 */ uint32_t FilterFIFOAssignment; /* 关联的FIFO(FIFO0/FIFO1) */ FunctionalState FilterActivation; /* 启用/禁用过滤器 */ uint32_t SlaveStartFilterBank; /* 从CAN的起始过滤器组 */ } CAN_FilterTypeDef;uint32_t FilterBank; /* 过滤器组编号 */
功能:选择使用哪个过滤器组(编号范围取决于芯片型号,如 F1 系列为 0-13,F4 系列为 0-27)
int32_t FilterIdHigh; /*16位 */ uint32_t FilterIdLow; /* 过滤器ID低16位 */
- 存储规则:
- 标准 ID(11 位):左移 5 位后存入
FilterIdHigh的高 11 位,FilterIdLow置 0。- 扩展 ID(29 位):
- 高 16 位存入
FilterIdHigh。- 低 13 位存入
FilterIdLow的高 13 位。- 示例:
- 标准 ID
0x555→FilterIdHigh = (0x555 << 5) & 0xFFFF,FilterIdLow = 0(16位)。- 扩展 ID
0x12345678→FilterIdHigh = 0x1234,FilterIdLow = (0x5678 << 3) & 0xFFFF(32位)过滤器掩码
uint32_t FilterMaskIdHigh; /* 过滤器掩码高16位 */ uint32_t FilterMaskIdLow; /* 过滤器掩码低16位 */
- 掩码模式(
CAN_FILTERMODE_IDMASK):
- 掩码位为
1的位置表示需要与FilterId严格匹配。- 掩码位为
0的位置表示忽略该位,允许任意值。- 示例:
- FilterIdHigh = (0x100 << 5) & 0xFFFF; // ID设为0x100 FilterMaskIdHigh = (0x700 << 5) & 0xFFFF; // 掩码设为0x700(仅匹配高3位)
SlaveStartFilterBank的值取决于你的硬件配置和过滤器组的分配策略。以下是具体的设置建议:1. 单 CAN 控制器系统(如 STM32F103)
- 值:设为 0
- 原因:单 CAN 系统无需分配从 CAN 的过滤器组,所有过滤器组(如 0~13)均由主 CAN 使用。
运行
sFilterConfig.SlaveStartFilterBank = 0; // 单CAN系统设为02. 多 CAN 控制器系统(如 STM32F407)
假设系统有 CAN1(主) 和 CAN2(从),且总共有 28 个过滤器组(0~27):
方案一:均分过滤器组
- CAN1(主) 使用过滤器组 0~13
- CAN2(从) 使用过滤器组 14~27
可以这个样子使用(可以配置多个过滤器,有点ADC通道配置的意思)
// CAN1配置 CAN1.SlaveStartFilterBank = 0; // CAN1从组0开始 // CAN2配置 CAN2.SlaveStartFilterBank = 14; // CAN2从组14开始 void CAN_Init(void) { // 其他初始化代码不变... // 配置CAN过滤器(接收标准ID 0x555、0x666、0x777) sFilterConfig.FilterBank = 0; // 过滤器组0 sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式 sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; // 改为16位过滤尺度 sFilterConfig.FilterIdHigh = (0x555 << 5) & 0xFFFF; // 第一个ID: 0x555 sFilterConfig.FilterIdLow = (0x666 << 5) & 0xFFFF; // 第二个ID: 0x666 sFilterConfig.FilterMaskIdHigh = 0x0000; // 无掩码 sFilterConfig.FilterMaskIdLow = 0x0000; sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; sFilterConfig.FilterActivation = ENABLE; sFilterConfig.SlaveStartFilterBank = 0; if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) { // 错误处理 } // 如果需要更多ID,可以使用第二个过滤器组 sFilterConfig.FilterBank = 1; sFilterConfig.FilterIdHigh = (0x777 << 5) & 0xFFFF; // 第三个ID: 0x777 sFilterConfig.FilterIdLow = 0x0000; if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) { // 错误处理 } // 其他代码不变... }
CAN总线测试的时候为环回地址,没有问题可以设置为正常的模式(收发模式)
2.模式
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位过滤器
上面的采用32为的屏蔽模式。
sFilterConfig.FilterIdHig 和 sFilterConfig.FilterIdLow 组成32为的ID位
sFilterConfig.FilterMaskIdHigh 和 sFilterConfig.FilterMaskIdLow 组成32位的屏蔽位
![]()


RTR(Remote Transmission Request):远程请求位,区分数据帧和遥控帧。数据帧必须为显性0,遥控帧必须为隐性1。
二:CAN总线3个节点测试
can收发器供电为5V,过长的杜邦线可能会导致CAN在普通模式下无法正常通信(环回模式通过)
代码部分和一:.CAN总线单个设备环回测试 一样
不同设备,不能发送相同ID相同格式的帧,因为这样会号致仲裁冲突
三:标准格式扩展格式数据帧遥控帧
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "exit.h"
#include "UART.h"
#include "OLED/OLED.h"
#include <stdio.h>
#include <stdarg.h>
#include "CAN/can.h"
CAN_TxHeaderTypeDef TAraay[]={
//StdId(标准格式) ExtId(扩展格式) IDE(标准帧) RTR(数据帧) DLC(字节) FilterMatchIndex
{0x555,0x12345678,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x666,0x14785236,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x777,0x87654321,CAN_ID_STD,CAN_RTR_REMOTE,0,DISABLE},
{0x888,0x09876543,CAN_ID_EXT,CAN_RTR_REMOTE,0,DISABLE},
};
uint8_t KeyNum;
uint8_t arrayLength = sizeof(TAraay) / sizeof(TAraay[0]);
uint16_t TXNum=0;
uint8_t TxData[] = {0x11,0x22,0x33,0x44}; // 发送数据
CAN_RxHeaderTypeDef RxHeaderArry;
uint8_t RxData[8]; // 接收数据缓冲区
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72MHz */
delay_init(72); /* 延时初始化 */
Uart_Init(115200);
KEY_Init();
OLED_Init();
CAN_Init(); // 修正函数名
printf("CAN通信测试启动...\r\n");
while (1)
{
// 每500ms发送一次数据
delay_ms(2000);
// 数据递增
if(KEY_Scan())
{
// 发送CAN消息(标准帧、数据帧)
if (CAN_AddTxMessage(&TAraay[TXNum],TxData) != HAL_OK) {
printf("CAN发送失败!\n");
} else {
printf("发送数据: ID=0x%02X\r\n",TAraay[TXNum++].StdId);
for(uint8_t i=0;i<sizeof(TxData);i++)
{
printf("TXdata[%d]:=0x%02X\r\n",i, TxData[i]);
//十六进制格式输出
}
TxData[0]++;
TxData[1]++;
TxData[2]++;
TxData[3]++;
if(TXNum>=arrayLength) TXNum=0;
}
}
// 接收CAN消息
if (MyCAN_Receive(&RxHeaderArry,RxData) == HAL_OK) {
if (RxHeaderArry.DLC > 0) {
if(RxHeaderArry.IDE)
{
//扩展格式
printf("接收到数据: ID=0x%08X, 长度=%d\r\n",RxHeaderArry.ExtId,RxHeaderArry.DLC);
}
else
{
printf("接收到数据: ID=0x%03X, 长度=%d\r\n",RxHeaderArry.StdId,RxHeaderArry.DLC);
}
for(uint8_t i=0;i<RxHeaderArry.DLC;i++)
{
printf("RXdata[%d]:0x%08X\r\n",i, RxData[i]);
}
printf("----------------------------------\r\n");
}
}
}
}
#include "stm32f1xx_hal.h"
#include "delay.h"
// CAN句柄和过滤器全局变量
CAN_HandleTypeDef hcan1; // 修改变量名更规范
CAN_FilterTypeDef sFilterConfig;
/**
* @brief CAN初始化函数
* @param 无
* @retval 无
*/
void CAN_Init(void)
{
// 配置CAN1句柄
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 6; // 预分频值 6
hcan1.Init.Mode = CAN_MODE_LOOPBACK; // 环回模式(测试用)
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; // 重新同步跳跃宽度 1个时间单元
hcan1.Init.TimeSeg1 = CAN_BS1_5TQ; // 时间段1 5个时间单元
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ; // 时间段2 2个时间单元
hcan1.Init.TimeTriggeredMode=DISABLE;// TTCM时间触发通信模式
hcan1.Init.AutoBusOff= DISABLE; //ABOM 自动总线关闭恢复功能
hcan1.Init.AutoWakeUp=DISABLE; //AWUM--> 自动唤醒功能(AWUM)
hcan1.Init.AutoRetransmission= DISABLE; //NART 自动重传功能(NART)
hcan1.Init.ReceiveFifoLocked= DISABLE; //RFLM 接收 FIFO 锁定功能(RFLM
hcan1.Init.TransmitFifoPriority= DISABLE; //TXFP 发送 FIFO(邮箱) 优先级控制(TXFP)ENABLE:先请求先发送,DISABLE:ID号小的先发送
// 初始化CAN
if (HAL_CAN_Init(&hcan1) != HAL_OK) {
}
// 配置CAN过滤器(接收标准ID 0x555)
sFilterConfig.FilterBank = 0; // 过滤器组0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位过滤器
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000; // 无掩码(精确匹配)
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 激活过滤器
sFilterConfig.SlaveStartFilterBank = 0;
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {
}
// 启动CAN
if (HAL_CAN_Start(&hcan1) != HAL_OK) {
}
}
/**
* @brief CAN底层初始化回调函数
* @param hcan: CAN句柄
* @retval 无
*/
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
if (hcan->Instance == CAN1) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能外设时钟 */
__HAL_RCC_CAN1_CLK_ENABLE(); // 使能CAN1时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
/* 配置CAN1 TX (PA12) 引脚 */
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;// 高速模式
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 配置CAN1 RX (PA11) 引脚 */
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉输入
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
/**
* @brief 发送CAN消息
* @param ID: 标识符
* @param Length: 数据长度(1-8)
* @param RTR: 1-数据帧, 0-远程帧
* @param data: 数据指针
* @param IDE: 1-扩展帧, 0-标准帧
* @retval HAL状态值
*/
HAL_StatusTypeDef CAN_AddTxMessage(CAN_TxHeaderTypeDef *TxHeader,uint8_t *data)
{
uint32_t TxMailbox;
// 等待发送邮箱可用
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0);
// 发送CAN消息并返回状态
return HAL_CAN_AddTxMessage(&hcan1, TxHeader, data, &TxMailbox);
}
/**
* @brief 接收CAN消息
* @param ID: 接收ID指针
* @param Length: 接收长度指针
* @param Data: 接收数据指针
* @retval HAL状态值
*/
HAL_StatusTypeDef MyCAN_Receive(CAN_RxHeaderTypeDef *RxHeader,uint8_t *RXdata)
{
uint8_t rxData[8] = {0}; // 初始化接收缓冲区
// 检查FIFO0是否有消息
if (HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) == 0) {
return HAL_TIMEOUT;
}
// 接收CAN消息
if (HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, RxHeader, rxData) != HAL_OK) {
return HAL_ERROR;
}
for(uint8_t i=0;i<RxHeader->DLC;i++)
{
RXdata[i]=rxData[i];
}
return HAL_OK;
}
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位过滤器
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000; // 无掩码(精确匹配)
sFilterConfig.FilterMaskIdLow = 0x0000;
发送的数据都可以通过具体的参考上面的
FilterMatchIndex
{0x555,0x12345678,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE}, 标准格式数据帧 发送4字节
{0x666,0x14785236,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE}, 扩展格式 数据帧 发送4字节
{0x777,0x87654321,CAN_ID_STD,CAN_RTR_REMOTE,0,DISABLE}, 标准格式 遥控帧 0字节
{0x888,0x09876543,CAN_ID_EXT,CAN_RTR_REMOTE,0,DISABLE}, 扩展格式 遥控帧 0字节
};
四:16位列表模式
实现:

RTR(Remote Transmission Request):远程请求位,区分数据帧和遥控帧。数据帧必须为显性0,遥控帧必须为隐性1。
IDE(ldentifier Extension):扩展标志位,区分标准格式和扩展格式。标准格式固定为显性0,扩展格式为隐性1。
其他部分都一样,只能通过ID为0x234,0x345,0x456 数据帧(RTR),标准格式(IDE)这2为默认为0。
FR1和FR2寄存器各存放两个16位ID
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; // 32位过滤器
sFilterConfig.FilterIdHigh = (0x234<<5);
sFilterConfig.FilterIdLow = (0x345<<5);
sFilterConfig.FilterMaskIdHigh = (0x567<<5); // 无掩码(精确匹配)
sFilterConfig.FilterMaskIdLow = 0x0000;
在修改为:
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; // 32位过滤器
sFilterConfig.FilterIdHigh = (0x234<<5|0x18);
sFilterConfig.FilterIdLow = (0x345<<5);
sFilterConfig.FilterMaskIdHigh = (0x567<<5); // 无掩码(精确匹配)
sFilterConfig.FilterMaskIdLow = 0x0000;
ID为234的只能通过扩展ID的遥控帧
五.16为屏蔽模式

以下代码可以实现上面的表格:
屏蔽为的RTR或者IDE为1表示对应的ID必须匹配。
FR1低16位存FilterIdLow(ID),高16位存FilterMaskIdLow(掩码);FR2低16位存FilterIdHigh(ID),高16位存FilterMaskIdHigh(掩码)
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; // 32位过滤器
sFilterConfig.FilterIdHigh = (0x200<<5);
sFilterConfig.FilterMaskIdHigh = (0xF00<<5); // 无掩码(精确匹配)
sFilterConfig.FilterIdLow = (0x320<<5);
sFilterConfig.FilterMaskIdLow = (0xFF0<<5);
发送的数据:
CAN_TxHeaderTypeDef TAraay[]={
//StdId(标准格式) ExtId(扩展格式) IDE(标准帧) RTR(数据帧) DLC(字节) FilterMatchIndex
{0x100,0x12345678,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x101,0x14785236,CAN_ID_EXT,CAN_RTR_REMOTE,4,DISABLE},
{0x1FE,0x87654321,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x1FF,0x09876543,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x200,0x12345678,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x201,0x14785236,CAN_ID_STD,CAN_RTR_REMOTE,4,DISABLE},
{0x2FE,0x87654321,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x2FF,0x09876543,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x310,0x12345678,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x311,0x14785236,CAN_ID_EXT,CAN_RTR_REMOTE,4,DISABLE},
{0x31E,0x87654321,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x31F,0x09876543,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x320,0x12345678,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x321,0x14785236,CAN_ID_STD,CAN_RTR_REMOTE,4,DISABLE},
{0x32E,0x87654321,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x32F,0x09876543,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
};只有:
{0x200,0x12345678,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x201,0x14785236,CAN_ID_STD,CAN_RTR_REMOTE,4,DISABLE},
{0x2FE,0x87654321,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x2FF,0x09876543,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x320,0x12345678,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x321,0x14785236,CAN_ID_STD,CAN_RTR_REMOTE,4,DISABLE},
{0x32E,0x87654321,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},
{0x32F,0x09876543,CAN_ID_STD,CAN_RTR_DATA,4,DISABLE},的数据可以通过
ID的RTR(0),IDE(0)。默认为0 遥控帧和标准格式。
因为屏蔽位RTR IDE 选择位0(无所谓),所以可以接收到遥控帧。
注意16位的不能让扩展帧过去(扩展帧的ID位29位)
改:
sFilterConfig.FilterIdHigh = (0x200<<5);
sFilterConfig.FilterMaskIdHigh = (0xF00<<5)|0x18; // 无掩码(精确匹配)
屏蔽位RTR IDE置1 表示ID对应的位必须匹配。RTR=0(数据帧) 遥控帧会被过滤掉。
计算ID和掩码:

六:32位列表

CAN_FxR1和CAN_FxR2各存一个完整的32位ID
sFilterConfig.FilterBank = 0; // 过滤器组0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位过滤器
//标准格式 数据帧
uint32_t ID=0x100<<21;
sFilterConfig.FilterIdHigh = ID>>16;
sFilterConfig.FilterIdLow = ID&0xFFFF;
uint32_t ID2=(0x14785236u<<3)|0x6; //扩展格式 遥控帧
sFilterConfig.FilterMaskIdHigh = ID2>>16; // 无掩码(精确匹配)
sFilterConfig.FilterMaskIdLow = ID2;
七:32位屏蔽
CAN_FxR1(FilterIdHigh +FilterIdLow)存ID,CAN_FxR2(FilterMaskIdLow+FilterMaskIdHigh)存屏蔽码
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位过滤器
//标准格式
uint32_t ID=0x12300000<<3;
sFilterConfig.FilterIdHigh = ID>>16;
sFilterConfig.FilterIdLow = ID&0xFFFF;
uint32_t Mask=(0x12300000<<3);
sFilterConfig.FilterMaskIdHigh = Mask>>16; // 无掩码(精确匹配)
sFilterConfig.FilterMaskIdLow = Mask;
CAN_TxHeaderTypeDef TAraay[]={
//StdId(标准格式) ExtId(扩展格式) IDE(标准帧) RTR(数据帧) DLC(字节) FilterMatchIndex
{0x100,0x12345678,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x101,0x14785236,CAN_ID_EXT,CAN_RTR_REMOTE,4,DISABLE},
{0x1FE,0x87654321,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x1FF,0x09876543,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x100,0x12388878,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x101,0x12380236,CAN_ID_EXT,CAN_RTR_REMOTE,4,DISABLE},
{0x1FE,0x12300000,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x1FF,0x09876543,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
};只有扩展ID高位位123xx xxxx x--->表示无所谓可以通过
uint32_t Mask=(0x12300000<<3); 表示高位的123必须一样,后面的无所谓。0--->表示无所谓 所以数据帧和遥控帧都可以通过
总结
FilterIdHigh=CAN_FxR1[31:16](过滤器寄存器 1 的高 16 位)FilterIdLow=CAN_FxR1[15:0](过滤器寄存器 1 的低 16 位)FilterMaskIdHigh=CAN_FxR2[31:16](过滤器寄存器 2 的高 16 位)FilterMaskIdLow=CAN_FxR2[15:0](过滤器寄存器 2 的低 16 位)FilterFIFOAssignment:指定匹配的报文存到哪个接收 FIFO(选 FIFO0/FIFO1)
八:中断接收
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "exit.h"
#include "UART.h"
#include "OLED/OLED.h"
#include <stdio.h>
#include <stdarg.h>
#include "CAN/can.h"
CAN_TxHeaderTypeDef TAraay[]={
//StdId(标准格式) ExtId(扩展格式) IDE(标准帧) RTR(数据帧) DLC(字节) FilterMatchIndex
{0x100,0x12345678,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x100,0x12388878,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x101,0x12380236,CAN_ID_EXT,CAN_RTR_REMOTE,4,DISABLE},
{0x101,0x14785236,CAN_ID_EXT,CAN_RTR_REMOTE,4,DISABLE},
{0x1FE,0x87654321,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x1FF,0x09876543,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x1FE,0x12300000,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
{0x1FF,0x09876543,CAN_ID_EXT,CAN_RTR_DATA,4,DISABLE},
};
uint8_t KeyNum;
uint8_t arrayLength = sizeof(TAraay) / sizeof(TAraay[0]);
uint16_t TXNum=0;
uint8_t TxData[] = {0x11,0x22,0x33,0x44}; // 发送数据
extern uint8_t CAN1_FIFO_IRQHandler;
CAN_RxHeaderTypeDef RxHeaderArry;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72MHz */
delay_init(72); /* 延时初始化 */
Uart_Init(115200);
KEY_Init();
OLED_Init();
CAN_Init(); // 修正函数名
printf("CAN通信测试启动...\r\n");
while (1)
{
CAN_AddTxMessage(&TAraay[TXNum],TxData);
TXNum>=arrayLength ? TXNum=0 : TXNum++;
delay_ms(1000);
}
}
#include "stm32f1xx_hal.h"
#include "delay.h"
#include <stdio.h>
#include <stdarg.h>
// CAN句柄和过滤器全局变量
CAN_HandleTypeDef hcan1; // 修改变量名更规范
CAN_FilterTypeDef sFilterConfig;
uint8_t CAN1_FIFO_IRQHandler=0;
/**
* @brief CAN初始化函数
* @param 无
* @retval 无
*/
void CAN_Init(void)
{
// 配置CAN1句柄
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 6; // 预分频值 6
hcan1.Init.Mode = CAN_MODE_LOOPBACK; // 环回模式(测试用)
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; // 重新同步跳跃宽度 1个时间单元
hcan1.Init.TimeSeg1 = CAN_BS1_5TQ; // 时间段1 5个时间单元
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ; // 时间段2 2个时间单元
hcan1.Init.TimeTriggeredMode=DISABLE;// TTCM时间触发通信模式
hcan1.Init.AutoBusOff= DISABLE; //ABOM 自动总线关闭恢复功能
hcan1.Init.AutoWakeUp=DISABLE; //AWUM--> 自动唤醒功能(AWUM)
hcan1.Init.AutoRetransmission= DISABLE; //NART 自动重传功能(NART)
hcan1.Init.ReceiveFifoLocked= DISABLE; //RFLM 接收 FIFO 锁定功能(RFLM
hcan1.Init.TransmitFifoPriority= DISABLE; //TXFP 发送 FIFO(邮箱) 优先级控制(TXFP)ENABLE:先请求先发送,DISABLE:ID号小的先发送
// 初始化CAN
if (HAL_CAN_Init(&hcan1) != HAL_OK) {
}
// 配置CAN过滤器(接收标准ID 0x555)
sFilterConfig.FilterBank = 0; // 过滤器组0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位过滤器
//标准格式
uint32_t ID=0x12300000<<3;
sFilterConfig.FilterIdHigh = ID>>16;
sFilterConfig.FilterIdLow = ID&0xFFFF;
uint32_t Mask=(0x12300000<<3);
sFilterConfig.FilterMaskIdHigh = Mask>>16; // 无掩码(精确匹配)
sFilterConfig.FilterMaskIdLow = Mask;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 激活过滤器
sFilterConfig.SlaveStartFilterBank = 0;
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {
}
// 启动CAN
if (HAL_CAN_Start(&hcan1) != HAL_OK) {
}
//FOFO 0 fofo接收到一个数据就触发中断
__HAL_CAN_ENABLE_IT(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
}
/**
* @brief CAN底层初始化回调函数
* @param hcan: CAN句柄
* @retval 无
*/
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
if (hcan->Instance == CAN1) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能外设时钟 */
__HAL_RCC_CAN1_CLK_ENABLE(); // 使能CAN1时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 1, 2);
HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
/* 配置CAN1 TX (PA12) 引脚 */
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;// 高速模式
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 配置CAN1 RX (PA11) 引脚 */
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉输入
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
/**
* @brief 发送CAN消息
* @param ID: 标识符
* @param Length: 数据长度(1-8)
* @param RTR: 1-数据帧, 0-远程帧
* @param data: 数据指针
* @param IDE: 1-扩展帧, 0-标准帧
* @retval HAL状态值
*/
HAL_StatusTypeDef CAN_AddTxMessage(CAN_TxHeaderTypeDef *TxHeader,uint8_t *data)
{
uint32_t TxMailbox;
// 等待发送邮箱可用
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0);
// 发送CAN消息并返回状态
return HAL_CAN_AddTxMessage(&hcan1, TxHeader, data, &TxMailbox);
}
/**
* @brief 接收CAN消息
* @param ID: 接收ID指针
* @param Length: 接收长度指针
* @param Data: 接收数据指针
* @retval HAL状态值
*/
HAL_StatusTypeDef MyCAN_Receive(CAN_RxHeaderTypeDef *RxHeader,uint8_t *RXdata)
{
uint8_t rxData[8] = {0}; // 初始化接收缓冲区
// 检查FIFO0是否有消息
if (HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) == 0) {
return HAL_TIMEOUT;
}
// 接收CAN消息
if (HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, RxHeader, rxData) != HAL_OK) {
return HAL_ERROR;
}
for(uint8_t i=0;i<RxHeader->DLC;i++)
{
RXdata[i]=rxData[i];
}
return HAL_OK;
}
/**
* @brief CAN1 RX0中断处理函数
* @param 无
* @retval 无
*/
void CAN1_RX0_IRQHandler(void)
{
HAL_CAN_IRQHandler(&hcan1);
}
// 接收数据缓冲区
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
CAN_RxHeaderTypeDef RxHeaderArry;
uint8_t RxData[8];
if(hcan->Instance==CAN1)
{
if (MyCAN_Receive(&RxHeaderArry,RxData) == HAL_OK) {
if (RxHeaderArry.DLC > 0) {
if(RxHeaderArry.IDE)
{
//扩展格式
printf("接收到数据: ID=0x%08X, 长度=%d\r\n",RxHeaderArry.ExtId,RxHeaderArry.DLC);
}
else
{
printf("接收到数据: ID=0x%03X, 长度=%d\r\n",RxHeaderArry.StdId,RxHeaderArry.DLC);
}
for(uint8_t i=0;i<RxHeaderArry.DLC;i++)
{
printf("RXdata[%d]:0x%08X\r\n",i, RxData[i]);
}
printf("----------------------------------\r\n");
}
}
}
}
更多推荐





所有评论(0)