没志青年
发布于 2025-06-24 / 45 阅读
0

32单片机 - CAN 通信

STM32 CAN 外设:

  • bxCAN:

  • FDCAN:

GD32 CAN 外设:

  • bxCAN:

  • FDCAN:

bxCAN 和 FDCAN 区别:

bxCAN(Basic Extended CAN)

FDCAN(CAN)

系列

F1

G4

支持版本

经典 CAN 2.0 A/B

  • 经典 CAN 2.0 A/B

  • CAN FD (灵活数据速率 CAN)

数据长度

固定 8 字节

  • CAN 2.0 A/B:8 字节

  • CAN FD:最大 64 字节

接收FIFO

2 个, 各 3 个消息

2 个, 各 3 个消息

发送

3 个发送邮箱

一个 FIFO,3 个消息

速度

1 Mbps

  • CAN 2.0 A/B:1Mbps

  • CAN FD:5 Mbps 以上

过滤

......

FDCAN 新增了很多功能,更加强大。

STM32F103、STM32F407 是没有 CANFD

CAN 网络拓扑结构

控制器和收发器的引脚不用交叉连接

CAN 的终端电阻问题,不能多啥的。

CAN 控制器组成结构

虽然有 bxCAN1 和 bxCAN2 两个外设,但只有 bxCAN1 是真正的 CAN 总线,bxCAN2 作为辅助,是接在 bxCAN1 的总线上的。

也就是说只有一个 CAN 总线。

CAN1:主 bxCAN,用于管理 bxCAN 与 512 字节 SRAM 存储器之间的通信。

CAN2:从 bxCAN,无法直接访问 SRAM 存储器。

所以 bxCAN2 的作用是什么?

CAN 外设设置 邮箱 和 FIFO 机制,是为了解决 CPU 和CAN外设的速度不一致问题,缓冲作用。

过滤器即筛选器,筛选需要的报文,存入FIFO,不需要的直接忽略。

设计 FIFO,是为了当 CAN 接收数据速度大于 CPU 处理速度时,防止太拥挤导致丢失很多数据。

STM32G474:

位时序

过滤器配置

过滤器的作用:硬件上直接过滤,完全不需要CPU参与,大大提升了效率。

模式

适用报文格式

组成

说明

16 位列表模式

标准报文

16 位掩码模式

标准报文

拓展报文

拓展报文

14 个过滤器组,默认都是关闭的,需要用多少过滤器组就配置多少。

每个过滤器组可设置独自的工作模式,有至少一个匹配到了就放入 FIFO,若一个都没匹配到就丢弃该报文。

所有过滤器组由硬件并行匹配,若报文 ID 通过了多个过滤器组,硬件会选择组号最小的。

bxCAN 有两种滤波模式:

(1)列表模式,即白名单机制

设置固定的 ID,接收报文的 ID 和它相等则通过,放入 FIFO。

缺点:只能匹配有限个报文 ID。

列表模式掩码必须全部为 1

(2)掩码模式

使用 C 语言中的掩码,进行 & 运算。

一句话:掩码=1的位,必须和预设ID一样;掩码=0的位,随便是什么都行。

list:1100 11xx 000
	mask:1111 1100 111


就是mask=0的位,list随便设置就行了,所以设置can_filter.filter_list_high = (0x << 5)的时候,有多种值。

就是这个流程:

if (接收的报文 & Mask) == (FilterID & Mask) {
    // 接收
} else {
    // 丢弃
}

因此需要设置用于对比的ID掩码

掩码值哪一位为 1,就表示这一位要和匹配 ID 相等,所有位都相等则通过。

之所以叫“组”,是因为一个过滤器有两个 32 位寄存器,这两个寄存器有四种组合:

  • 16 位列表模式:适用于标准报文,标准 ID 有 11 位。两个 32 位寄存器拆分为 4 个 16 位域,因此可设置 4 个白名单 ID

  • 16 位掩码模式:适用于标准报文。一个寄存器划分为 2 个 16 位域,第一个 16 位域存放用来对比的 ID,第二个 16 位域存放掩码值。有两个寄存器,因此能设置两条规则

  • 32 位列表模式:适用于拓展报文,拓展 ID 有29位。因此可设置 2 个白名单 ID

  • 32 位掩码模式:适用于拓展报文。第一个寄存器存放用来对比的 ID,第二个寄存器存放掩码。因此只能设置一条规则

32 位的模式标准报文也能使用,但要注意左移的位数。

一个过滤器组所验证的必须是统一的帧类型,不能标准报文和拓展报文混合过滤。

标准报文过滤:

// HAL



// 标准

拓展报文过滤:

// HAL



// 标准

STM32 CAN过滤器配置详解_stm32can过滤器详解-CSDN博客

混合过滤:?

标准报文:

中断

stm32 中断:

FIFO 中断

触发时机

CAN_IT_FMP<>

消息挂号中断

有新报文进入 FIFO 时触发

CAN_IT_FF<>

溢出中断

有新报文进入 FIFO 但发现 FIFO 已满时触发

CAN_IT_FOV<>

覆盖中断

当新报文已经覆盖了旧报文后触发,覆盖的是最旧的报文

gd32 中断

在中断中接收报文:

void CAN1_RX0_IRQHandler(void) {
  CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
  // ......
}

HAL 库回调函数:

接收数据

两个接收 FIFO 是平级的,没有优先级。

报文经过了过滤器才能进入 FIFO,并且过滤器可指定进入哪个 FIFO

过滤器默认是关闭的。

FIFO 满的时候策略:

  • 直接丢弃

  • 替换掉末尾的报文

这个结构体是报文的一些头部信息:

typedef struct
{
  uint32_t StdId;  
  uint32_t ExtId;   
  uint32_t IDE;    
  uint32_t RTR;     
  uint32_t DLC;  // 数据长度
  uint32_t Timestamp; 
  uint32_t FilterMatchIndex; 
} CAN_RxHeaderTypeDef;

发送数据

邮箱就是缓冲区,发送一条数据,就是填充一个邮箱。

当有多个邮箱待发送时,可设置优先发送策略:先请求先发送、按ID号优先级发送。

(1)选择一个空邮箱

(2)写入报文

(3)请求发送

发送策略:

  • 先请求先发送

  • 按照ID发送

CAN 外设配置

STM32之CAN通信 - Sakura_Ji - 博客园

标准库

CAN 工作模式:

#define CAN_Mode_Normal             ((uint8_t)0x00)  // 正常通信的模式
#define CAN_Mode_LoopBack           ((uint8_t)0x01)  // 本地环回模式,用于测试,相当于电脑的localhost,不会发到总线上
#define CAN_Mode_Silent             ((uint8_t)0x02)  // 静默模式:监听总线而不发送
#define CAN_Mode_Silent_LoopBack    ((uint8_t)0x03)  // 静默环回模式:不理解

CAN_InitStructure.CAN_TTCM = DISABLE; // 非时间触发通信模式
  CAN_InitStructure.CAN_ABOM = DISABLE; // 软件自动离线管理
  CAN_InitStructure.CAN_AWUM =      DISABLE; // 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
  CAN_InitStructure.CAN_NART = ENABLE;  // 禁止报文自动传送,即报文只发送一次
  CAN_InitStructure.CAN_RFLM = DISABLE; // DISABLE:报文满了自动覆盖,ENABLE:无操作,需要手动清理
  CAN_InitStructure.CAN_TXFP = DISABLE; // DISABLE:报文ID决定优先级,ENABLE:进入FIFO的顺序决定优先级

波特率计算公式:

波特率=Fpclk1((1+BS1+BS2)Prescaler)波特率 = \frac{ Fpclk1} { ((1 + BS1 + BS2) * Prescaler)}

过滤器配置:

 // 配置过滤器
 CAN_FilterInitTypeDef CAN_FilterInitStructure;
 CAN_FilterInitStructure.CAN_FilterNumber = 0;      // 过滤器组 0

 // 32 位掩码模式
 CAN_FilterInitStructure.CAN_FilterMode =      CAN_FilterMode_IdMask;
 CAN_FilterInitStructure.CAN_FilterScale =      CAN_FilterScale_32bit;

 // 用于对比的ID
 CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
 CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;

 // 掩码 
 CAN_FilterInitStructure.CAN_FilterMaskIdHigh =      0x0000; // 过滤器屏蔽标识符的高16位值
 CAN_FilterInitStructure.CAN_FilterMaskIdLow =      0x0000; // 过滤器屏蔽标识符的低16位值

 CAN_FilterInitStructure.CAN_FilterFIFOAssignment =      CAN_Filter_FIFO0; // 设定了指向过滤器的FIFO为0

 CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // 过滤器使能
 CAN_FilterInit(&CAN_FilterInitStructure);              // 滤波器初始化

发送报文:例如

uint8_t AX_CAN_SendPacket(uint32_t id, uint32_t len, uint8_t *pbuf) {
  uint8_t i;

  // 封装数据
  TxMessage.StdId = id;         // 标准标识符
  TxMessage.IDE = CAN_ID_STD;   // 使用标准标识符
  TxMessage.RTR = CAN_RTR_DATA; // 为数据帧
  TxMessage.DLC = len;          // 数据长度,单位为字节

  for (i = 0; i < len; i++) {
    TxMessage.Data[i] = *(pbuf + i);
  }

  // 发送数据
  return (CAN_Transmit(CAN1, &TxMessage));
}