CAN总线基础

文章目录

    • CAN总线简介
    • CAN物理层
    • CAN总线结构
    • CAN时序
    • CAN的帧
    • 仲裁
    • STM32的CAN外设
    • STM32的bxCAN结构
    • STM32的CAN时序
    • CAN1给CAN2发送数据(实验)
      • Cube配置
      • 代码编写
    • 参考资料

CAN总线简介

  • 控制器局域网络,Controller Area Network
  • 异步半双工通信协议(没有时钟线,不能同时收发数据)
  • 总线制
    • CAN由一对差分对(两条线)CANH和CANL组成
    • CANH电压比CANL高视为逻辑0(显性电平),否则为逻辑1(隐性电平)
    • 多个设备可以连接到同一条CAN上
    • 总线上的设备通过ID号区分
    • ID号长度可以为11位(标准长度)或29位(拓展长度)
  • CAN2.0b标准(大部分设备支持)
    • 最大速率1Mbps
    • 一组数据称作一帧,分为数据帧和远程帧(不常用,不传输数据,告诉设备自己即将发送数据)
    • 数据帧最多传输8个字节的数据
    • 每一帧都有CRC校验
  • CAN FD标准(支持设备比较少)
    • 最大速率可达8Mbps
    • 数据帧最多可传输64字节数据

CAN物理层

  • 单片机一般使用类似串口的CAN RX和CAN TX接口驱动CAN
  • CAN RX/CAN TX需要经过CAN收发器(Transceiver)才能被转换成对应的差分电平
  • 一般CAN差分信号的电压为0-5V,也可以是0~3.3V(不建议,兼容性差)

CAN总线结构

  • CAN有两个120Ω终端电阻,位于CAN布线的两端(主控板集成了这个电阻)
  • 每个设备挂载在总线上形成数个支路

在这里插入图片描述

CAN时序

  • CAN每一位会被分成数个Time Quanta(Tq),一般在10个以上
  • CAN通信以Tq为基本时间单位
  • 每一位的时间长度可以调整以保证同步
  • 每一位被分成为3段:同步段,BS1和BS2
  • BS1和BS2中间会进行接收的采样(BS1,即Prop+Phase1;BS2,即Phase2)
  • CAN的实际工作频率会比通信频率更高
  • 保证了时钟不同步的情况下,通信保持同步
  • 设备可以通过插入和删去TQ来保证接收和发送同步

在这里插入图片描述

CAN的帧

  • ID:ID号,11位(标准长度)或29位(拓展长度)
    • 用于表示接收对象
  • RTR:区别远程帧与数据帧
    • 一般是数据帧,为0
  • IDE:区别ID号长度
    • 0,即为11位ID号;1,即29位ID号
  • DLC:数据长度(发送数据的字节长),大致为0~8
    • 在FDK中可以最高支持64位64个字节
  • CRC:校验部分
  • 连续的5个以上相同位中间会插入一个相反位(除结尾外)
    在这里插入图片描述

仲裁

  • 由于CAN定义2条信号线的电压差代表逻辑1或0,CAN的两条信号线实质等效于串口的一条线
  • 发送和接收不能同时进行
  • 当总线上连续3位的时间为1且没有正在传输的帧时,视为空闲状态,此时设备才能进行发送
  • 如果有2个或以上设备同时启动了发送则会进入仲裁(Arbitration)
    • 发送0更多的设备会赢得仲裁并继续发送
    • 由于CAN时序的规定,仲裁是很难进入的,只有2个设备在同一Tq内启动了发送才会进入仲裁
  • 仲裁保证了在半双工总线上不会产生冲突

STM32的CAN外设

  • FDCAN:仅限H系列和G系列(第三代STM32)支持
    • 支持FDCAN和CAN2.0b
  • bxCAN:标准CAN外设,仅支持CAN2.0b(最高速度只有1Mbps)
    • 一般有1~3个CAN
    • CAN1和CAN3是主CAN,CAN2是从CAN
    • CAN2仅在CAN1打开之后才能打开
    • 推荐CAN1和CAN2同时打开使用

STM32的bxCAN结构

  • 每个CAN具有3个发送邮箱和6个接收邮箱
  • 邮箱用于暂存数据(可以当成串口的缓冲区)
  • 接收邮箱分为2组FIFO,可触发不同的中断(向量不同)
  • 外设会自动管理这些邮箱的发送和接收队列
  • 2个CAN共享28个过滤器,用于过滤接收的ID号

STM32的CAN时序

  • CAN的频率计算公式如下

  • PSC决定了Tq的大小

  • 一般BS1约为BS2的2倍,且BS2不宜过低

  • 常用参数:对于STM32F407,主频168MHz时,APB1为42MHz,设置BS1=9,BS2=4,PSC=3,此时得到1MHz的CAN速率(常用配置)
    在这里插入图片描述
    在这里插入图片描述

CAN1给CAN2发送数据(实验)

  • 将CAN1和CAN2直线连在一起

  • c板的2个CAN都内置了终端电阻,可以直接互相连接

  • CAN总线是需要共地的,但是c板的CAN1没有引出接地线,这里可以不接;实际使用中必须接地(电调一般通过电源线接地)

  • 设置CAN速率为1Mbps(c板CAN总线接口最大支持1M传输速度)

Cube配置

打开Cube,一般情况下连接CAN需要GND接地,配置基础设置后

在connectivity下CAN1和CAN2都激活

然后根据芯片手册配置对应的引脚

配置好了之后就可以在CAN1下面配置时序

在Bit Timings Parameters中配置BS1,BS2 以及PSC

必须先设置BS1和BS2才能设置PSC

按照基本配置,BS1设为9,BS2设为4,PSC设为3

然后设置中断,一般CAN中的中断只会用到RX1和RX0,但我们也可以直接全部打开

再把按键外设打开,让它来触发发送

然后就可以生成代码

代码编写

在启动CAN之前要先配置过滤器

在main.c中USER CODE BEGIN 4/END 4之间先新建一个函数

/* USER CODE BEGIN 4 */
void CAN_Configfilter()
{
  
}
/* USER CODE END 4 */

在配置过滤器之前先定义一下过滤器的配置结构体

打开HAL库手册,找到CAN_FilterTypedef

可以看到在该结构体下面有如下的类型

在这里插入图片描述

这样我们可以先激活过滤器,即初始化结构体中的FilterActivation

在这里插入图片描述

sFiterconfig.FilterActivation = CAN_FILTER_ENABLE;//CAN_FILTER_ENABLE是一个宏定义,表示过滤器处于开启状态

然后设置过滤器编号,即FilterBank

在这里插入图片描述

可以看到编号从0到13或者0到27,我们这里选择0

sFiterconfig.FilterBank=0;

然后设置FilterFIFOAssignment,即代表中断给到RX0还是RX1,它有两个值
在这里插入图片描述

  sFiterconfig.FilterFIFOAssignment=CAN_FilterFIFO0;//CAN_FilterFIFO0是宏定义
//表示我们把这个过滤器给到了RX0

接下来设置FilterMode,在HAL库手册中可以看到有两种模式

在这里插入图片描述

我们可以选择常用模式CAN_FILTERMODE_IDMASK

  sFiterconfig.FilterMode=CAN_FILTERMODE_IDMASK;

还有一个FilterScale,同样有两种设置

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

我们这里配置为32bit

  sFiterconfig.FilterScale=CAN_FILTERSCALE_32BIT;

后面就可以设置filter里面的一些数据

FilterIdHigh,FilterIdLow,FilterMaskIdHigh以及FilterMaskIdLow

我们全部设置为0x0000

  sFiterconfig.FilterIdHigh=0x0000;
  sFiterconfig.FilterIdLow=0x0000;
  sFiterconfig.FilterMaskIdHigh=0x0000;
  sFiterconfig.FilterMaskIdLow=0x0000;

最后该结构体中还有一个SlaveStartFilterBank未设置

该参数是一个定值,一般设置为14

  sFilterconfig.SlaveStartFilterBank=14;

最终代码如下

/* USER CODE BEGIN 4 */
void CAN_Configfilter()
{
  CAN_FilterTypeDef sFiterconfig;
  sFiterconfig.FilterActivation = CAN_FILTER_ENABLE;
  sFiterconfig.FilterBank=0;
  sFiterconfig.FilterFIFOAssignment=CAN_FILTER_FIFO0;
  sFiterconfig.FilterMode=CAN_FILTERMODE_IDMASK;
  sFiterconfig.FilterScale=CAN_FILTERSCALE_32BIT;
  sFiterconfig.FilterIdHigh=0x0000;
  sFiterconfig.FilterIdLow=0x0000;
  sFiterconfig.FilterMaskIdHigh=0x0000;
  sFiterconfig.FilterMaskIdLow=0x0000;
  sFiterconfig.SlaveStartFilterBank=14;
}
/* USER CODE END 4 */

然后可以在HAL库手册中查找到如何使用该过滤器

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

由于是全英文的我们可以使用翻译网站来提供参考,翻译如下:

1.通过实现HAL_CAN_MspInit()来初始化CAN低层资源:
使用__HAL_RCC_CANx_CLK_ENABLE()使能CAN接口时钟
—配置CAN管脚
◦为CAN gpio启用时钟
◦配置CAN引脚作为备用功能open-drain
-如果使用中断(例如HAL_CAN_ActivateNotification())
◦配置CAN中断优先级使用HAL_NVIC_SetPriority()
◦启用CAN IRQ处理程序使用HAL_NVIC_EnableIRQ()
◦在CAN IRQ处理程序中,调用HAL_CAN_IRQHandler()
2. 使用HAL_CAN_Init()函数初始化CAN外围设备。该函数使用HAL_CAN_MspInit() for
低级的初始化。
3.使用以下配置功能配置接收过滤器:
- HAL_CAN_ConfigFilter ()
4. 使用HAL_CAN_Start()函数启动CAN模块。在这个级别上,节点在总线上是活动的:它接收
消息,并可以发送消息。
5. 为了管理消息传输,可以使用以下Tx控制函数:
- HAL_CAN_AddTxMessage()请求传输新消息。
- HAL_CAN_AbortTxRequest()终止挂起消息的传输。
- HAL_CAN_GetTxMailboxesFreeLevel()获取空闲的Tx邮箱的数量。
- HAL_CAN_IsTxMessagePending()用于检查Tx邮箱中是否有消息挂起。
- HAL_CAN_GetTxTimestamp()获取发送的Tx消息的时间戳,如果时间触发
已启用通信模式。
6. 当消息被接收到CAN Rx FIFOs中时,可以使用
HAL_CAN_GetRxMessage()函数。函数HAL_CAN_GetRxFifoFillLevel()允许知道如何
许多Rx消息存储在Rx Fifo中。
7. 调用HAL_CAN_Stop()函数将停止CAN模块。
8. 去初始化是通过HAL_CAN_DeInit()函数实现的。

我们可以看到前两条是系统的初始化,第三条是配置过滤器,第四条启动CAN

因此我们可以先配置过滤器

HAL_CAN_ConfigFilter(&hcan1,&sFiterconfig);//第一个参数是CAN的地址,第二个参数是配置的结构体

后面我们还需要设置CAN2的过滤器,不过在配置之前我们需要修改结构体中的一个参数

sFilterconfig.FilterBank=14;

然后配置CAN2的过滤器

 HAL_CAN_ConfigFilter(&hcan2,&sFiterconfig);

但是我们发现HAL_CAN_ConfigFilter()这个函数有返回值(int),该返回值可以反馈该配置是否成功

因此我们需要把该函数作为一个判断条件,判断是否配置成功

  if( HAL_CAN_ConfigFilter(&hcan1,&sFiterconfig)!=HAL_OK)
  {
    Error_Handler();//该函数是HAL库里面自带的一个用于处理错误的函数,该函数可以由我们来编写
  }//表示如果过滤器配置不成功,就会进行错误处理
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}//配置失败的情况下会把程序卡死

对CAN2进行同样的过滤器配置

如果不进行过滤器配置会导致收不到信息

配置完第三点的过滤器后,我们可以进行第四点启动CAN

先启动CAN1再启动CAN2

  if(HAL_CAN_Start(&hcan1)!=HAL_OK)
  {
    Error_Handler();
  }

  if(HAL_CAN_Start(&hcan2)!=HAL_OK)
  {
    Error_Handler();
  }

然后需要启动中断我们使用HAL_CAN_ActivateNotification()这个函数,该函数功能如下

在这里插入图片描述

它有两个参数即CAN_HandleTypeDef * hcan以及uint32_t ActiveITs

其中的ActiveITs是一个宏定义组

可以找到其中有如下宏定义

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

其中

  • CAN_IT_TX_MAILBOX_EMPTY表示发送的邮箱变为空,即发送完成
  • CAN_IT_RX_FIFO0_MSG_PENDING代表在RX0的中断里面收到了数据
  • CAN_IT_RX_FIFO1_MSG_PENDING代表在RX1的中断里面收到了数据
  if(HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING|CAN_IT_RX_FIFO1_MSG_PENDING)!=HAL_OK)
  {
    Error_Handler();
  }

    if(HAL_CAN_ActivateNotification(&hcan2,CAN_IT_RX_FIFO0_MSG_PENDING|CAN_IT_RX_FIFO1_MSG_PENDING)!=HAL_OK)
  {
    Error_Handler();
  }

这样就完成了CAN初始化的函数编写,完整代码如下

/* USER CODE BEGIN 4 */
//定义can过滤器
void CAN_Configfilter()
{
  CAN_FilterTypeDef sFiterconfig;
  sFiterconfig.FilterActivation = CAN_FILTER_ENABLE;
  sFiterconfig.FilterBank=0;
  sFiterconfig.FilterFIFOAssignment=CAN_FILTER_FIFO0;
  sFiterconfig.FilterMode=CAN_FILTERMODE_IDMASK;
  sFiterconfig.FilterScale=CAN_FILTERSCALE_32BIT;
  sFiterconfig.FilterIdHigh=0x0000;
  sFiterconfig.FilterIdLow=0x0000;
  sFiterconfig.FilterMaskIdHigh=0x0000;
  sFiterconfig.FilterMaskIdLow=0x0000;
  sFiterconfig.SlaveStartFilterBank=14;
//启动can1过滤器
  if( HAL_CAN_ConfigFilter(&hcan1,&sFiterconfig)!=HAL_OK)
  {
    Error_Handler();
  }
 //更改过滤器编号
  sFilterconfig.FilterBank=14;
//启动can2过滤器
  if( HAL_CAN_ConfigFilter(&hcan2,&sFiterconfig)!=HAL_OK)
  {
    Error_Handler();
  }
//启动can1
  if(HAL_CAN_Start(&hcan1)!=HAL_OK)
  {
    Error_Handler();
  }
//启动can2
  if(HAL_CAN_Start(&hcan2)!=HAL_OK)
  {
    Error_Handler();
  }
//启动can1的rx0和rx1中断  
if(HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING|CAN_IT_RX_FIFO1_MSG_PENDING)!=HAL_OK)
  {
    Error_Handler();
  }

//启动can2的rx0和rx1中断
    if(HAL_CAN_ActivateNotification(&hcan2,CAN_IT_RX_FIFO0_MSG_PENDING|CAN_IT_RX_FIFO1_MSG_PENDING)!=HAL_OK)
  {
    Error_Handler();
  }

}
/* USER CODE END 4 */

编写好了之后要先进行该函数的声明

/* USER CODE BEGIN PFP */
void CAN_Configfilter();//声明can过滤器函数
/* USER CODE END PFP */

然后可以在USER CODE BEGIN 2/END 2之间调用

  /* USER CODE BEGIN 2 */
  void CAN_Configfilter();//调用can过滤器函数
  /* USER CODE END 2 */

这样就完成了CAN的启动

然后就可以创建要发送数据的数组以及接受数据的数组

  /*USER CODE BEGIN PV*/
  uitnt8_t txDateBuffer[8],rxDateBuffer[8]
  /*USER CODE END PV*/

CAN有两个header的问题,因此我们需要给两个header的结构体

  /*USER CODE BEGIN PV*/
  uitnt8_t txDateBuffer[8],rxDateBuffer[8];
  CAN_TxHeaderTypeDef txHeader;//主要存储了我们要发送的ID号,帧的类型(数据帧还是远程帧),ID号长度(11位还是29位)等信息
  CAN_RxHeaderTypeDef rxHeader;
  /*USER CODE END PV*/

我们使用了key按键来触发,因此需要编写相应的代码

在c中使用布尔变量我们需要添加对应的头文件,

/*USER CODE BEGIN Includes*/
#include"stdbool.h"
/*USER CODE END Includes*/

这样就可以使用布尔的函数
在之前编写的USER CODE BEGIN PV/END PV中间加入bool类型的pa0Pressed
修改后代码如下

  /*USER CODE BEGIN PV*/
  uitnt8_t txDateBuffer[8],rxDateBuffer[8];
  CAN_TxHeaderTypeDef txHeader;//主要存储了我们要发送的ID号,帧的类型(数据帧还是远程帧),ID号长度(11位还是29位)等信息
  CAN_RxHeaderTypeDef rxHeader;
  bool pa0Pressed = false;//通过这个布尔以及读取的gpio的电平来判断gpio的上升沿和下降沿
  /*USER CODE END PV*/

在while(1)中补齐代码

if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET && !pa0Pressed)
{
	pa0Pressed=true;//按键的上升沿
}
else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_SET && !pa0Pressed)
{
	pa0Pressed=false;//按键的下降沿
}
HAL_Delay(100);
//这个布尔变量pa0Pressed,在我们按下按键时会从SET变为RESET
//这样就进行了一个边沿的判定

然后在上升沿判断里进行一个发送数据的操作

设置txHeader结构体的初始化

可以在HAL库里面看到该结构体

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

我们可以把txHeader里面的StdId设置为0x200,但为了调试我们后面可能会修改这个数值

因此我们可以再设置一个uint16_t txId,让它等于0x200,让txHeader.StdId等于该数据

配置IDE可以看到需要CAN_identifier_type

在这里插入图片描述

可以配置成两种CAN_ID_STD和CAN_ID_EXT

这里我们配置标准的这个

然后是RTR,即设置为远程帧还是标准帧,需要CAN_remote_transmission_request

同样是两种

在这里插入图片描述

这里我们使用数据帧

最后配置DLC,即数据长度,这里我们给8位

  txHeader.StdId=txId;
  txHeader.IDE=CAN_ID_STD;
  txHeader.RTR=CAN_RTR_DATA;
  txHeader.DLC=8;

这样我们就配置好了txHeader

然后我们还是让它传送一个系统运行时间

  *((uint32_t*)(txDateBuffer))=HAL_GetTick();
  //这里我们把txDateBuffer(原来是uint8_t类型)强制类型转换成一个uint32_t类型

然后我们执行发送函数

HAL_CAN_AddTxMessage(&hcan1,&txHeader,txDateBuffer,)
/*发送需要的Header,以及数据txDateBuffer
最后的参数是一个ptxMailbox,STM32的发送邮箱有三个,ptxMailbox会把实际发送的这个邮箱丢进这个指针指向的这个地址
该参数的函数有两个返回值,第一个返回值是hal ok即表示发送是否完成,第二个返回值表示用了哪个Mailbox发送,我们可以直接写一个NULL即往0的地址去写数据,可能会出现问题
因此我们可以再写一个变量,让它把数据写到这个地址里*/
  HAL_CAN_AddTxMessage(&hcan1,&txHeader,txDateBuffer,&TXmailbox);
  //这样在使用这个函数时,可以发送数据,同时把该函数的返回值丢进TXmailbox这个地址里

我们还需要判断该Mailbox是否被占用

  if(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1)!=0)
  {
    HAL_CAN_AddTxMessage(&hcan1,&txHeader,txDateBuffer,&TXmailbox);
  }

至此我们就完成了发送的函数

我们的接收是中断接收,因此需要编写中断的回调函数

RX0和RX1对应两个不同的中断向量,因此对应两个不同的中断函数

void HAL_can_RxFifo0MsgPendingCallback(CAN_HanderTypeDef*hcan)//接收数据的回调函数
{

}//RX0
void HAL_can_RxFifo1MsgPendingCallback(CAN_HanderTypeDef*hcan)//接收数据的回调函数
{

}//RX1

在回调函数中要先确认是CAN2接收数据

然后将接收到的数据存储到数据缓冲区中

我们需要用到HAL_CAN_GetRxMessage这个函数
在这里插入图片描述

    HAL_CAN_GetRxMessage(&hcan2,CAN_RX_FIFo0,&rxDateBuffer);

但是还会存在有两个接收中断的问题,因此我们可以给他们分配两个不同的Header,和两个不同的Buffer

这样就需要我们把原来的rx Header与rxDateBuffer改为两个
这样代码修改如下

/* USER CODE BEGIN PV */
  uint8_t txDateBuffer[8], rxDateBuffer0[8],rxDateBuffer1[8];
  CAN_TxHeaderTypeDef txHeader;//主要存储了我们要发送的ID号,帧的类型(数据帧还是远程帧),ID号长度(11位还是29位)等信息
  CAN_RxHeaderTypeDef rxHeader0,rxHeader1;
  bool pa0Pressed = false;//通过这个布尔以及读取的gpio的电平来判断gpio的上升沿和下降沿
  uint16_t txId=0x200;//方便调试
  uint32_t TXmailbox;
/* USER CODE END PV */

这样RX0和RX1这两个中断向量接收到的数据就会进入不同的缓存中

这样代码编写就完成了,可以进行编译与烧录了

全部代码如下:

/* USER CODE BEGIN PV */
  uint8_t txDateBuffer[8], rxDateBuffer0[8],rxDateBuffer1[8];
  CAN_TxHeaderTypeDef txHeader;//主要存储了我们要发送的ID号,帧的类型(数据帧还是远程帧),ID号长度(11位还是29位)等信息
  CAN_RxHeaderTypeDef rxHeader0,rxHeader1;
  bool pa0Pressed = false;//通过这个布尔以及读取的gpio的电平来判断gpio的上升沿和下降沿
  uint16_t txId=0x200;//方便调试
  uint32_t TXmailbox;
/* USER CODE END PV */
/* USER CODE BEGIN PFP */
void CAN_Configfilter();
/* USER CODE END PFP */
  /* USER CODE BEGIN 2 */
  void CAN_Configfilter();
  /* USER CODE END 2 */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET && !pa0Pressed)
{
	pa0Pressed=true;//按键的上升沿
  txHeader.StdId=txId;
  txHeader.IDE=CAN_ID_STD;
  txHeader.RTR=CAN_RTR_DATA;
  txHeader.DLC=8;
  *((uint32_t*)(txDateBuffer)) = HAL_GetTick();
  if(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1)!=0)
  {
    HAL_CAN_AddTxMessage(&hcan1,&txHeader,txDateBuffer,&TXmailbox);
  }
}
else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_SET && !pa0Pressed)
{
	pa0Pressed=false;//按键的下降沿
}
HAL_Delay(100);
//这个布尔变量pa0Pressed,在我们按下按键时会从SET变为RESET
  }
  /* USER CODE END 3 */
/* USER CODE BEGIN 4 */
void CAN_Configfilter()
{
  CAN_FilterTypeDef sFiterconfig;
  sFiterconfig.FilterActivation = CAN_FILTER_ENABLE;
  sFiterconfig.FilterBank=0;
  sFiterconfig.FilterFIFOAssignment=CAN_FILTER_FIFO0;
  sFiterconfig.FilterMode=CAN_FILTERMODE_IDMASK;
  sFiterconfig.FilterScale=CAN_FILTERSCALE_32BIT;
  sFiterconfig.FilterIdHigh=0x0000;
  sFiterconfig.FilterIdLow=0x0000;
  sFiterconfig.FilterMaskIdHigh=0x0000;
  sFiterconfig.FilterMaskIdLow=0x0000;
  sFiterconfig.SlaveStartFilterBank=14;

  if( HAL_CAN_ConfigFilter(&hcan1,&sFiterconfig)!=HAL_OK)
  {
    Error_Handler();
  }
 
  sFiterconfig.FilterBank=14;

  if( HAL_CAN_ConfigFilter(&hcan2,&sFiterconfig)!=HAL_OK)
  {
    Error_Handler();
  }

  if(HAL_CAN_Start(&hcan1)!=HAL_OK)
  {
    Error_Handler();
  }

  if(HAL_CAN_Start(&hcan2)!=HAL_OK)
  {
    Error_Handler();
  }

  if(HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_RX_FIFO1_MSG_PENDING)!=HAL_OK)
  {
    Error_Handler();
  }

    if(HAL_CAN_ActivateNotification(&hcan2,CAN_IT_RX_FIFO0_MSG_PENDING|CAN_IT_RX_FIFO1_MSG_PENDING)!=HAL_OK)
  {
    Error_Handler();
  }

}
void HAL_can_RxFifo0MsgPendingCallback(CAN_HandleTypeDef*hcan)//接收数据的回调函数
{
  if(hcan==&hcan2)
  {
    HAL_CAN_GetRxMessage(&hcan2,CAN_RX_FIFO0,&rxHeader0,rxDateBuffer0);
  }
}//RX0
void HAL_can_RxFifo1MsgPendingCallback(CAN_HandleTypeDef*hcan)//接收数据的回调函数
{
    if(hcan==&hcan2)
  {
    HAL_CAN_GetRxMessage(&hcan2,CAN_RX_FIFO1,&rxHeader1,rxDateBuffer1);
  }
}//RX1
/* USER CODE END 4 */

参考资料

CH4.1 CAN 第1讲 CAN总线基础【南工骁鹰嵌入式软件培训】

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

书阁下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值