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

文章目录

目的

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

CAN的一些基础介绍可以参考下面文章:
《CAN基础概念》https://blog.csdn.net/Naisu_kun/article/details/132814079

本文使用STM32F407作为主控芯片,CAN电路设计如下:

本文使用使用STM32CubeIDE进行开发。

关键配置与代码

轮询方式


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

c 复制代码
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_CAN1_Init();

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

  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 使用MASK模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 过滤器使用32bit模式
  sFilterConfig.FilterIdHigh = 0x0000;
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 经过过滤的数据放到FIFO0
  sFilterConfig.FilterBank = 0; // 使用第0组过滤器
  sFilterConfig.SlaveStartFilterBank = 14; // 从CAN过滤器其实地址
  sFilterConfig.FilterActivation = ENABLE; // 使能过滤器

  if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    // 过滤器设置失败
  }

  /**************** 以下为启动CAN外设 ****************/
  HAL_CAN_Start(&hcan1); // 启动CAN

  while (1)
  {

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

		if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) // 从接收队列中读取数据帧
		{
			CAN_TxHeaderTypeDef   TxHeader; // 用来保存发送数据帧头部信息
			uint8_t               TxData[8]; // 用来保存发送数据帧数据
			uint32_t              TxMailbox; // 用来保存发送数据帧所使用的邮箱号

			if(RxHeader.IDE == CAN_ID_STD) // 标准数据帧
			{
				TxHeader.StdId = RxHeader.StdId; // 标准帧ID
			}
			if(RxHeader.IDE == CAN_ID_EXT) // 扩展数据帧
			{
				TxHeader.ExtId = RxHeader.ExtId; // 扩展帧ID
			}
			TxHeader.IDE = RxHeader.IDE; // ID类型

			TxHeader.RTR = RxHeader.RTR; // CAN_RTR_DATA 数据帧;CAN_RTR_REMOTE 远程帧

			TxHeader.DLC = RxHeader.DLC; // 数据段字节数

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

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

			HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox); // 发送数据帧
		}
	}
  }
}

上面代码中的过滤器是接收数据才需要的。过滤器可以使只有ID匹配上的报文才接收到接收FIFO中。

过滤器最常使用MASK模式,该模式下与 FilterMaskId1 的bit位对应的 FilterId bit位的值必须完全匹配,比如下面示例:

  • FilterId = 0x00000233 FilterMaskId = 0xFFFFFFFF 只接收ID为 0x00000233 的报文;
  • FilterId = 0x00000233 FilterMaskId = 0xFFFFFF00 可以接收ID为 0x000002XX 的报文(X可以为任意值);
  • FilterId = 0x00000000 FilterMaskId = 0x00000000 可以接收ID为 0xXXXXXXXX 的报文(X可以为任意值);

需要注意的是就算不想过滤任何报文也需要设置过滤器,因为需要通过过滤器来指定接收的报文存放到哪个FIFO中。

中断方式

中断方式基础配置与上面相同,唯一的变化是使能相关中断:

(下面代码中实际只用到RX0中断)

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

c 复制代码
#include "main.h"

CAN_HandleTypeDef hcan1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_CAN1_Init(void);

/**************** 以下为重写中断回调函数 ****************/
// 注意下面的一些回调函数其实是在HAL库中已经若定义好的,我们只需要在保持函数类型不变的情况下重写实现其实际功能

// Fifo0收到消息回调
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
	if(hcan->Instance == CAN1) // 如果是来自CAN1的数据
	{
		CAN_RxHeaderTypeDef   RxHeader; // 用来保存接收到的数据帧头部信息
		uint8_t               RxData[8]; // 用来保存接收数据端数据

		if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) // 从接收队列中读取数据帧
		{
			CAN_TxHeaderTypeDef   TxHeader; // 用来保存发送数据帧头部信息
			uint8_t               TxData[8]; // 用来保存发送数据帧数据
			uint32_t              TxMailbox; // 用来保存发送数据帧所使用的邮箱号

			if(RxHeader.IDE == CAN_ID_STD) // 标准数据帧
			{
				TxHeader.StdId = RxHeader.StdId; // 标准帧ID
			}
			if(RxHeader.IDE == CAN_ID_EXT) // 扩展数据帧
			{
				TxHeader.ExtId = RxHeader.ExtId; // 扩展帧ID
			}
			TxHeader.IDE = RxHeader.IDE; // ID类型

			TxHeader.RTR = RxHeader.RTR; // CAN_RTR_DATA 数据帧;CAN_RTR_REMOTE 远程帧

			TxHeader.DLC = RxHeader.DLC; // 数据段字节数

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

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

			HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox); // 发送数据帧
		}
	}

}

// void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan) { } // Fifo0队满回调

// void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox0取消传输回调
// void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox0传输完成回调
// void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox1取消传输回调
// void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox1传输完成回调
// void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox2取消传输回调
// void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox2传输完成回调

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

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

  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 使用MASK模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 过滤器使用32bit模式
  sFilterConfig.FilterIdHigh = 0x0000;
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 经过过滤的数据放到FIFO0
  sFilterConfig.FilterBank = 0; // 使用第0组过滤器
  sFilterConfig.SlaveStartFilterBank = 14; // 从CAN过滤器其实地址
  sFilterConfig.FilterActivation = ENABLE; // 使能过滤器

  if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    // 过滤器设置失败
  }

  /**************** 以下为启动中断 ****************/
  HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // 使能FIFO0数据接收中断

  /**************** 以下为启动CAN外设 ****************/
  HAL_CAN_Start(&hcan1); // 启动CAN

  while (1)
  {
  }
}

收发测试

本示例演示结果可以通过各种CAN工具配合上位机软件进行测试:

示例链接

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

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

总结

STM32中使用CAN非常方便,进行配置生成代码后只需要设置过滤器,然后就可以收发数据了。

相关推荐
国科安芯11 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
茯苓gao13 小时前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
梁朝辉16 小时前
STM32H750VBT6中ADCINP与INN什么区别
stm32·单片机·嵌入式硬件
省四收割者17 小时前
一文详解信号完整性(1)
python·嵌入式硬件·数学建模·信息与通信·信号处理·智能硬件
崇山峻岭之间18 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious18 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
KaifuZeng1 天前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计1 天前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
kebidaixu1 天前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片详解
stm32
kebidaixu1 天前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片调试实录:四个关键问题与解决方案
stm32