STM32 CAN 外设:
bxCAN:
FDCAN:
GD32 CAN 外设:
bxCAN:
FDCAN:
bxCAN 和 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参与,大大提升了效率。
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 中断:
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 外设配置
标准库
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的顺序决定优先级波特率计算公式:
过滤器配置:
// 配置过滤器
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));
}