STM32 CAN使用记录:FDCAN基础通讯

2023-09-14 21:45:00

目的

CAN是非常常用的一种数据总线,被广泛用在各种车辆系统中。这篇文章将对STM32中FDCAN的使用做个示例。

CAN的一些基础介绍与使用可以参考下面文章:
《CAN基础概念》https://blog.csdn.net/Naisu_kun/article/details/132814079
《STM32 CAN使用记录:bxCAN基础通讯》https://blog.csdn.net/Naisu_kun/article/details/132830073

本文使用STM32H750作为主控芯片,PD0设置为FDCAN1_RXPD1设置为FDCAN1_TX 。本文使用使用STM32CubeIDE进行开发。

基础说明

STM32中FDCAN和传统的bxCAN的区别除了两者协议本身的区别,在STM32中这两个外设也有较大不同。不同点主要是FIFIO和Filter分布。bxCAN中FIFIO和Filter都是设定好一定组数的,我们是现成的拿来用;而FDCAN中提供了一定的内存,用户可以手动分配各个FIFIO和Filter的大小。
在这里插入图片描述

关于FDCAN的特征说明可以参考ST官方文档 《AN5348: Introduction to FDCAN peripherals for STM32 product classes》

关键配置与代码

轮询方式

在这里插入图片描述
在这里插入图片描述

除了默认生成的代码只需在 main.c 中手动添加一些代码即可:

#include "main.h"

FDCAN_HandleTypeDef hfdcan1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_FDCAN1_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_FDCAN1_Init();

  /**************** 以下为过滤器设置 ****************/
  FDCAN_FilterTypeDef sFilterConfig;

  // 下面这组设置只接受标准帧ID为0x666的消息
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 0;
  sFilterConfig.FilterType = FDCAN_FILTER_MASK;
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x666;
  sFilterConfig.FilterID2 = 0x7FF;
  sFilterConfig.RxBufferIndex = 0;
  HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig);

  // 下面这组设置只接受扩展ID为0x233和0x2233的消息
  sFilterConfig.IdType = FDCAN_EXTENDED_ID;
  sFilterConfig.FilterIndex = 0;
  sFilterConfig.FilterType = FDCAN_FILTER_MASK;
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x00002233;
  sFilterConfig.FilterID2 = 0x1FFFDFFF;
  sFilterConfig.RxBufferIndex = 0;
  HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig);

  // 默认情况下,如果未配置全局过滤器,则会接收所有不匹配的帧并将其重定向到RxFIFO0
  // 后面四个参数分别 拒绝未匹配的标准数据帧 拒绝未匹配的扩展数据帧 拒绝标准远程帧 拒绝扩展远程帧
  HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_REJECT_REMOTE, FDCAN_REJECT_REMOTE);

  /**************** 以下为启动CAN外设 ****************/
  HAL_FDCAN_Start(&hfdcan1);

  while (1)
  {
		/**************** 以下为接收消息并回发处理 ****************/
		if(HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) != 0) // 接收队列不为0,有数据可读
		{
			FDCAN_RxHeaderTypeDef   RxHeader; // 用来保存接收到的数据帧头部信息
			uint8_t                 RxData[64]; // 用来保存接收数据端数据

			if(HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) // 从接收队列中读取数据帧
			{
				FDCAN_TxHeaderTypeDef   TxHeader = {0}; // 用来保存发送数据帧头部信息
				uint8_t                 TxData[64]; // 用来保存发送数据帧数据

				TxHeader.Identifier = RxHeader.Identifier;
				TxHeader.IdType = RxHeader.IdType; // 标准-FDCAN_STANDARD_ID; 扩展-FDCAN_EXTENDED_ID
				TxHeader.TxFrameType = RxHeader.RxFrameType; // 数据帧-FDCAN_DATA_FRAME; 远程帧-FDCAN_REMOTE_FRAME
				TxHeader.DataLength = RxHeader.DataLength; // FDCAN_DLC_BYTES_xx
				                                           // xx = 0 1 2 3 4 5 6 7 8 12 16 20 24 32 48 64
				TxHeader.ErrorStateIndicator = RxHeader.ErrorStateIndicator; // FDCAN_ESI_ACTIVE FDCAN_ESI_PASSIVE
				TxHeader.BitRateSwitch = RxHeader.BitRateSwitch; // 波特率不可变-FDCAN_BRS_OFF; 波特率可变-FDCAN_BRS_ON
				TxHeader.FDFormat = RxHeader.FDFormat; // 经典CAN-FDCAN_CLASSIC_CAN; CANFD-FDCAN_FD_CAN
				// TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
				// TxHeader.MessageMarker = 0;

				for(int i=0; i<64; i++)
				{
					TxData[i] = RxData[i];
				}

				while(HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) == 0); // 等待有发送邮箱可用

				HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData); // 发送数据帧
			}
		}
  }
}

中断方式

在这里插入图片描述

除了默认生成的代码只需在 main.c 中手动添加一些代码即可:

#include "main.h"

FDCAN_HandleTypeDef hfdcan1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_FDCAN1_Init(void);

/**************** 以下为重写中断回调函数 ****************/
// Fifo0收到消息回调
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
	if (hfdcan == &hfdcan1) // 判断是hfdcan1的中断
	{
		if ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET) // 判断是FIFO0_NEW_MESSAGE回调
		{
			FDCAN_RxHeaderTypeDef   RxHeader; // 用来保存接收到的数据帧头部信息
			uint8_t                 RxData[64]; // 用来保存接收数据端数据

			if(HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) // 从接收队列中读取数据帧
			{
				FDCAN_TxHeaderTypeDef   TxHeader = {0}; // 用来保存发送数据帧头部信息
				uint8_t                 TxData[64]; // 用来保存发送数据帧数据

				TxHeader.Identifier = RxHeader.Identifier;
				TxHeader.IdType = RxHeader.IdType; // 标准-FDCAN_STANDARD_ID; 扩展-FDCAN_EXTENDED_ID
				TxHeader.TxFrameType = RxHeader.RxFrameType; // 数据帧-FDCAN_DATA_FRAME; 远程帧-FDCAN_REMOTE_FRAME
				TxHeader.DataLength = RxHeader.DataLength; // FDCAN_DLC_BYTES_xx
				                                           // xx = 0 1 2 3 4 5 6 7 8 12 16 20 24 32 48 64
				TxHeader.ErrorStateIndicator = RxHeader.ErrorStateIndicator; // FDCAN_ESI_ACTIVE FDCAN_ESI_PASSIVE
				TxHeader.BitRateSwitch = RxHeader.BitRateSwitch; // 波特率不可变-FDCAN_BRS_OFF; 波特率可变-FDCAN_BRS_ON
				TxHeader.FDFormat = RxHeader.FDFormat; // 经典CAN-FDCAN_CLASSIC_CAN; CANFD-FDCAN_FD_CAN
				// TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
				// TxHeader.MessageMarker = 0;

				for(int i=0; i<64; i++)
				{
					TxData[i] = RxData[i];
				}

				while(HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) == 0); // 等待有发送邮箱可用

				HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData); // 发送数据帧
			}
		}
	}
}

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_FDCAN1_Init();

  /**************** 以下为过滤器设置 ****************/
  FDCAN_FilterTypeDef sFilterConfig;

  // 下面这组设置只接受标准帧ID为0x666的消息
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 0;
  sFilterConfig.FilterType = FDCAN_FILTER_MASK;
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x666;
  sFilterConfig.FilterID2 = 0x7FF;
  sFilterConfig.RxBufferIndex = 0;
  HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig);

  // 下面这组设置只接受扩展ID为0x233和0x2233的消息
  sFilterConfig.IdType = FDCAN_EXTENDED_ID;
  sFilterConfig.FilterIndex = 0;
  sFilterConfig.FilterType = FDCAN_FILTER_MASK;
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x00002233;
  sFilterConfig.FilterID2 = 0x1FFFDFFF;
  sFilterConfig.RxBufferIndex = 0;
  HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig);

  // 默认情况下,如果未配置全局过滤器,则会接收所有不匹配的帧并将其重定向到RxFIFO0
  // 后面四个参数分别 拒绝未匹配的标准数据帧 拒绝未匹配的扩展数据帧 拒绝标准远程帧 拒绝扩展远程帧
  HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_REJECT_REMOTE, FDCAN_REJECT_REMOTE);

  /**************** 以下为启动中断 ****************/
  HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0); // 使能FIFO0数据接收中断

  /**************** 以下为启动CAN外设 ****************/
  HAL_FDCAN_Start(&hfdcan1);

  while (1)
  {
  }
}

收发测试

本示例演示结果可以通过各种CAN工具配合上位机软件进行测试:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

示例链接

仓库地址: https://github.com/NaisuXu/STM32_MCU_Examples

本文中的示例位于仓库中 FDCAN_RxTxPoll_H750FDCAN_RxTxIT_H750

总结

STM32中使用FDCAN并不复杂,进行配置生成代码后只需要设置过滤器,然后就可以收发数据了。

更多推荐

使用 rtty 进行远程 Linux 维护和调试

rtty是一个用于在终端上进行远程连接和数据传输的工具。它提供了一种简单的方式来与远程设备进行通信,使得在不同主机之间传输数据变得更加方便。安装rtty是一个可执行程序,可以在Linux、macOS和Windows等平台上使用。Linux/macOS在终端中执行以下命令,使用curl下载rtty可执行文件:curl-L

Golang代码漏洞扫描工具介绍——trivy

Golang代码漏洞扫描工具介绍——trivyGolang作为一款近年来最火热的服务端语言之一,深受广大程序员的喜爱,笔者最近也在用,特别是高并发的场景下,golang易用性的优势十分明显,但笔者这次想要介绍的并不是golang本身,而且golang代码的漏洞扫描工具,毕竟作为服务端的程序,安全性一直是一个不同忽视的地

新势力在智能化路上,正抢了Tier 1的生意

作者|&nbsp;Amy编辑|&nbsp;德新上半年的汽车行业价格内卷,下半年则一下资本涌入,风起云涌。先是蔚来拿到了11亿美元来自中东的投资,紧接着7月大众以7亿美元投资小鹏汽车,8月哪吒完成70亿元Crossover轮投资。传闻中,还有大众捷达与Stelliantis两大集团接洽零跑汽车,秘密洽谈投资收购以及潜在的

.NET超简单轻量级的HTTP请求组件Flurl

简介Flurl是一个用于构建基于HTTP请求的C#代码的库。它的主要目的是简化和优雅地处理网络请求(只用很少的代码完成请求)。Flurl提供了一种简单的方法来构建GET、POST、PUT等类型的请求,以及处理响应和异常。它还提供了一些高级功能,如链式调用、缓存请求结果、自动重定向等。本文将介绍Flurl的GET、POS

提升服务质量,群狼调研物业客户满意度调查来帮忙

在当今商业环境中,物业企业的持续发展离不开客户满意度调查这一重要环节。为何重视物业客户满意度调查?客户满意度是评估企业服务质量的重要指标,对于物业企业而言更是至关重要。通过调查客户的满意度,企业能够深入了解客户的需求和期望,发现问题所在,并做出相应的改进和调整,从而提升客户体验、增强客户忠诚度,进而促进企业的持续健康发

conan入门(二十七):因profile [env]字段废弃导致的boost/1.81.0 在aarch64-linux-gnu下交叉编译失败

今天在尝试用conan1.60.0使用aarch64-linux-gnu编译器交叉编译boost/1.81.0时报错了:conaninstallboost/1.81.0@-pr:haarch64-linux-gnu.jinja-pr:bdefault--buildboost输出如下:Configuration(prof

Hyper-V 虚拟机CentOS配置网络(三)

总目录https://preparedata.blog.csdn.net/article/details/132877836文章目录总目录一、创建交换机二、共享网络给交换机三、虚拟机配置交换机四、配置CentOS网络一、创建交换机二、共享网络给交换机打开物理宿主机的控制面板的网络链接中心,此时已经多了一个上一步配置好虚

通过Vue-cli搭建spa项目

🏅我是默,一个在CSDN分享笔记的博主。📚📚🌟在这里,我要推荐给大家我的专栏《Vue》。🎯🎯🚀无论你是编程小白,还是有一定基础的程序员,这个专栏都能满足你的需求。我会用最简单易懂的语言,带你走进Vue的世界,让你从零开始,一步步成为JAVA大师。🚀🏆🌈让我们在Vue的世界里畅游吧!🌈🎁如果感觉还

css前端面试题(三)

文章目录1、可继承属性和不可继承属性字体系列属性文本系列属性元素可见性列表布局属性光标属性2、link和@import的区别3、css优化4、CSS预处理器/后处理器是什么?为什么要使用它们?5、单行、多行文本溢出隐藏6、实现一个扇形7、实现一个自适应的正方形8、为什么现在的设备分辨率差异很大,2.5k屏幕都已经出现,

计算机网络 | I/O模型、网络模型(OSI七层及TCP/IP四层)

欢迎关注博主Mindtechnist或加入【LinuxC/C++/Python社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。I/O模型、网络模型I/O模型阻塞I/O非阻塞I/OI/

C—数据的储存(下)

文章目录前言🌟一、练习一下🌏1.例一🌏2.例二🌏3.例三🌏4.例四🌟二、浮点型在内存中的储存🌏1.浮点数🌏2.浮点数存储💫(1).二进制浮点数💫(2).浮点数的存储规定💫(3).浮点数的取出规定🌏3.例题😽总结前言👧个人主页:@小沈熬夜秃头中୧⍤⃝❅😚小编介绍:欢迎来到我的乱七八糟小星球�

热文推荐