STM32F407单片机HAL库CAN2不能接收数据解决方法

  最近在使用stm32F407的片子调试can通信,直接在正点原子的代码上修改调试,调试can1的时候,基本没啥问题,收发都正常,使用查询模式和中断模式都可以。但是当修改到can2的时候,可以正常发送数据,但是中断函数始终进不去。折腾了一两个小时终于搞定了。下面将解决过程分享给大家。

  首先先上代码,这个代码是运行成功的代码,在后面我再详细介绍要注意的地方。

CAN_HandleTypeDef   g_canx_handler;     /* CANx句柄 */
CAN_TxHeaderTypeDef g_canx_txheader;    /* 发送参数句柄 */
CAN_RxHeaderTypeDef g_canx_rxheader;    /* 接收参数句柄 */
CAN_DATA_TypeDef    g_can_rx_data;      /* CAN接收数据句柄 */
CAN_DATA_TypeDef    g_can_tx_data;      /* CAN发送数据句柄 */

uint8_t can_init( uint32_t tsjw, uint32_t tbs2, uint32_t tbs1, uint16_t brp, uint32_t mode )
{
    g_canx_handler.Instance = CANx;                    /* 使用CAN1/CAN2 */
    g_canx_handler.Init.Prescaler = brp;                /* 分频系数(Fdiv)为brp+1 
    g_canx_handler.Init.Mode = mode;                    /* 模式设置 */
    g_canx_handler.Init.SyncJumpWidth = tsjw;           /* 重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ */
    g_canx_handler.Init.TimeSeg1 = tbs1;                /* tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ */
    g_canx_handler.Init.TimeSeg2 = tbs2;                /* tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ */
    g_canx_handler.Init.TimeTriggeredMode = DISABLE;    /* 非时间触发通信模式 */
    g_canx_handler.Init.AutoBusOff = DISABLE;           /* 软件自动离线管理 */
    g_canx_handler.Init.AutoWakeUp = DISABLE;           /* 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位) */
    //报文自动传送开启后,当数据发送失败时,can芯片会自动重发数据,直到数据发送成功,会造成程序假死状态。
    g_canx_handler.Init.AutoRetransmission = DISABLE;    /* 禁止报文自动传送 */
    g_canx_handler.Init.ReceiveFifoLocked = DISABLE;    /* 报文不锁锁定,FIFO装满后新的覆盖旧的,如果设置报文锁定后,FIFO装满后新的就会被丢弃*/
    g_canx_handler.Init.TransmitFifoPriority = DISABLE; /* 优先级由报文标识符决定 */
    if ( HAL_CAN_Init( &g_canx_handler ) != HAL_OK )
    {
        return 1;
    }    
    /* 使用中断接收 */
    __HAL_CAN_ENABLE_IT( &g_canx_handler, CAN_IT_RX_FIFO0_MSG_PENDING ); /* FIFO0消息挂号中断允许 */
    HAL_NVIC_EnableIRQ( CANx_RX0_IRQn );                        /* 使能CAN中断 */
    HAL_NVIC_SetPriority( CANx_RX0_IRQn, 7, 0 );                /* 抢占优先级7,子优先级0 */  

    CAN_FilterTypeDef sFilterConfig;
    sFilterConfig.FilterBank = FILTER_ADDR;                   /* 过滤器地址  当只使用CAN1时,此地址范围为0--13,,当使用CAN2时,CAN1也会工作,相当于CAN2就是从机 */
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
    sFilterConfig.FilterIdHigh = 0x0000;                      /* 32位ID */
    sFilterConfig.FilterIdLow = 0x0000;
    sFilterConfig.FilterMaskIdHigh = 0x0000;                  /* 32位MASK */
    sFilterConfig.FilterMaskIdLow = 0x0000;
    sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;    /* 过滤器0关联到FIFO0 */
    sFilterConfig.FilterActivation = CAN_FILTER_ENABLE;       /* 激活滤波器0 */
    sFilterConfig.SlaveStartFilterBank = FILTER_ADDR;         /* 从机滤波器起始地址,当只使用can1时,没有从机 此值无效*/

    /* 过滤器配置 */
    if ( HAL_CAN_ConfigFilter( &g_canx_handler, &sFilterConfig ) != HAL_OK )
    {
        return 2;
    }
    /* 启动CAN外围设备 */
    if ( HAL_CAN_Start( &g_canx_handler ) != HAL_OK )
    {
        return 3;
    }
    return 0;
}

void HAL_CAN_MspInit( CAN_HandleTypeDef* hcan )
{
    if ( CANx == hcan->Instance )       /* 如果地址为CANx 的地址 */
    {        
        CAN_RX_GPIO_CLK_ENABLE();       /* CAN_RX脚时钟使能 */
        CAN_TX_GPIO_CLK_ENABLE();       /* CAN_TX脚时钟使能 */     
        CANx_CLK_ENABLE();              /* 使能CAN时钟 */        

        GPIO_InitTypeDef gpio_init_struct;
        gpio_init_struct.Pin = CAN_TX_GPIO_PIN;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_PULLUP;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        gpio_init_struct.Alternate = GPIO_AF9_CANx;
        HAL_GPIO_Init( CAN_TX_GPIO_PORT, &gpio_init_struct ); /* CAN_TX脚 模式设置 */
        gpio_init_struct.Pin = CAN_RX_GPIO_PIN;       
        HAL_GPIO_Init( CAN_RX_GPIO_PORT, &gpio_init_struct ); /* CAN_RX脚 模式设置 */
    }
}
void CANx_RX0_IRQHandler( void )
{
    HAL_CAN_IRQHandler( &g_canx_handler );      /* 调用HAL库 CAN 中断入口函数*/
}
void  HAL_CAN_RxFifo0MsgPendingCallback( CAN_HandleTypeDef* hcan )
{
    HAL_CAN_GetRxMessage( hcan, CAN_RX_FIFO0, &g_canx_rxheader, g_can_rx_data.buf );  /* 读取数据 */
}

  下面是头文件

//#define USE_CAN1        1               /* 使用can1口,如果要使用can2,将此宏定义屏蔽 */

#ifdef USE_CAN1
/* CAN1 引脚 定义 */
#define CAN_RX_GPIO_PORT                GPIOD
#define CAN_RX_GPIO_PIN                 GPIO_PIN_0
#define CAN_RX_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)     /* PD口时钟使能 */

#define CAN_TX_GPIO_PORT                GPIOD
#define CAN_TX_GPIO_PIN                 GPIO_PIN_1
#define CAN_TX_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)     /* PD口时钟使能 */

#define CANx                            CAN1
#define CANx_RX0_IRQn                   CAN1_RX0_IRQn
#define CANx_CLK_ENABLE()               __HAL_RCC_CAN1_CLK_ENABLE()
#define CANx_RX0_IRQHandler             CAN1_RX0_IRQHandler
#define GPIO_AF9_CANx                   GPIO_AF9_CAN1

#define FILTER_ADDR                     0                                               /* 滤波器地址 */

#else

/* CAN2 引脚 定义 */
#define CAN_RX_GPIO_PORT                GPIOB
#define CAN_RX_GPIO_PIN                 GPIO_PIN_5
#define CAN_RX_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)     /* PB口时钟使能 */

#define CAN_TX_GPIO_PORT                GPIOB
#define CAN_TX_GPIO_PIN                 GPIO_PIN_6
#define CAN_TX_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)     /* PB口时钟使能 */

#define CANx                            CAN2
#define CANx_RX0_IRQn                   CAN2_RX0_IRQn
#define CANx_CLK_ENABLE()               __HAL_RCC_CAN1_CLK_ENABLE(); __HAL_RCC_CAN2_CLK_ENABLE()
#define CANx_RX0_IRQHandler             CAN2_RX0_IRQHandler
#define GPIO_AF9_CANx                   GPIO_AF9_CAN2

#define FILTER_ADDR                     14                                               /* 滤波器地址 */
#endif

  这里通过一个宏定义来控制使用can1还是can2.
在这里插入图片描述
  为了方便代码的编写,在头文件中将can1和can2配置时需要改动的地方都用宏定义来表示,这样切换can1和can2的时候,程序中的代码就不需要改动了。

  can1和can2最大的区别首先就是 IO口不一样,can1使用的是PD0、PD1,can2使用的是PB5,、PB6,接下来不一样的就是中断源和中断函数入口。下面就就是can的时钟,这里要注意一个就是使用can1的时候,开启can1的时钟就行,但是使用can2的时候,也必须开启can1的时钟。大多数can2调试不通的原因就是这个。
在这里插入图片描述
  在can的控制器中,存储访问控制器是由can1控制的,当使用can2的时候,can2要访问存储访问控制器时,必须通过can1才能访问,所以使用can2的时候,can1为主机,can2为从机。所以使用can2时,时钟使能的宏定义是将can1和can2的的时钟都开启的。

#define CANx_CLK_ENABLE()  __HAL_RCC_CAN1_CLK_ENABLE(); __HAL_RCC_CAN2_CLK_ENABLE()

  正常情况下,使用can2时,只需要开启can1的时钟,就能使用了,但是实际测试的时候发现,can2只能发送数据,不能接收数据。最后查找了找了半天原因才发现是滤波器地址选择引起的。
在这里插入图片描述
  这个是正点原子默认的过滤器配置值。关于这两个值的含义可以直接在代码中看官方的解释。
在这里插入图片描述
  通过注释大概可以知道当使用一个can时FilterBank的值范围是0–13,当使用双路时它的范围是0–27。当使用一个can时 SlaveStartFilterBank的值是没意义的,当使用双路时它的值是0–27。

  看了这个解释之后其实还是不知道具体啥意思,是怎么用的。通过我实际测试之后,我对这两个值的理解如下:

  1.当只使用can1时,FilterBank的值必须在0到13之间,一旦这个值大于13,那么can1就不能进入接收中断。 SlaveStartFilterBank这个值在使用can1时,系统内部是忽略这个值的。不管设置多少就可以,无所谓。

  2.当使用can2时,由于can2读取数据需要依赖can1,所以要使用can2,就必须要使用can1,那么此时就是两路can,此时FilterBank的值用来设置过滤器的地址,这个值从0到27都可以。接下来 SlaveStartFilterBank的值就很关键了,这个值的含义是,从机滤波器的起始地址,那么当使用can2时,can2就是从机,那么这个值就是can2滤波器的起始地址,而上面FilterBank这个值是要使用的滤波器地址。 这里要注意的是,SlaveStartFilterBank的值一定要小于等于FilterBank的值。也就是滤波器起始地址一定要比要使用的滤波器地址小。

  如果按照上面代码中的设置,那么滤波器的地址设置为0,滤波器的起始地址设置为14,那么can2从滤波器起始地址14开始查找数据,它永远也找不到0号滤波器,所以就接收不到数据。

  就是因为这两个值的原因,导致使用can2的时候,一直收不到数据。为了写代码时方便一点,这里直接将这两个值设置为一样的。滤波器起始地址和要使用的滤波器地址一样,这样不管怎么设置都不会错。
在这里插入图片描述
  在头文件中直接用宏定义设置这个值,由于使用can1的时候,值不能超过13,所以can1的值直接设置为0。当使用can2的时候,直接将值设置为14。

  这个滤波器的值设置好之后,can2使用起来就正常了。希望这个小小的坑大家都不要踩。为了一个地址值,折腾了好几个小时。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/25537.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WPF 热重载失效了

关于 热重载官方说明: WPF 和 UWP 应用的 XAML 热重载是什么Introducing the .NET Hot Reload experience for editing code at runtime 热重载简单来说,就是点击运行程序后,修改 XAML 代码,应用程序会实时的显示你的修改 为…

Python入门(十三)函数(一)

函数(一) 1.函数概述2.函数定义2.1向函数传递信息2.2实参和形参 作者:xiou 1.函数概述 函数是带名字的代码块,用于完成具体的工作。要执行函数定义的特定任务,可调用该函数。需要在程序中多次执行同一项任务时&#…

中间件SOME/IP简述

SOME/IP SOME/IP 不是广义上的中间件,严格的来讲它是一种通信协议,但中间件这个概念太模糊了,所以我们也一般称 SOME/IP 为通信中间件。 SOME/IP 全称是 Scalable service-Oriented MiddlewarE over IP。也就是基于 IP 协议的面向服务的可扩…

使用Python绘制M2货币供应率曲线

M2广义货币供应量:流通于银行体系之外的现金加上企业存款、居民储蓄存款以及其他存款,它包括了一切可能成为现实购买力的货币形式,通常反映的是社会总需求变化和未来通胀的压力状态。近年来,很多国家都把M2作为货币供应量的调控目…

ChatGPT国内免费使用方法【国内免费使用地址】

当下人工智能技术的快速发展,聊天机器人成为了越来越多人们日常生活和工作中的必备工具。如何在国内免费使用ChatGPT聊天机器人,成为了热门话题。本文将为你详细介绍ChatGPT国内免费使用方法,让你轻松拥有聊天机器人助手,提高工作…

Vue3 小兔鲜:Pinia入门

Vue3 小兔鲜:Pinia入门 Date: May 11, 2023 Sum: Pinia概念、实现counter、getters、异步action、storeToRefs保持响应式解构 什么是Pinia Pinia 是 Vue 的专属状态管理库,可以实现跨组件或页面共享状态,是 vuex 状态管理工具的替代品&…

论文学习笔记:Swin Transformer: Hierarchical Vision Transformer using Shifted Windows

论文阅读:Swin Transformer: Hierarchical Vision Transformer using Shifted Windows 今天学习的论文是 ICCV 2021 的 best paper,Swin Transformer,可以说是 transformer 在 CV 领域的一篇里程碑式的工作。文章的标题是一种基于移动窗口的…

C++异步调用方法

C之future和promise future和promise的作用是在不同线程之间传递数据。使用指针也可以完成数据的传递,但是指针非常危险,因为互斥量不能阻止指针的访问;而且指针的方式传递的数据是固定的,如果更改数据类型,那么还需要…

代码随想录算法训练营第四十六天|139.单词拆分、关于多重背包,你该了解这些!、背包问题总结篇!

文章目录 一、139.单词拆分二、关于多重背包&#xff0c;你该了解这些&#xff01;三、背包问题总结篇&#xff01;总结 一、139.单词拆分 public boolean wordBreak(String s, List<String> wordDict) {//完全背包问题&#xff0c;因为可以重复&#xff0c;背包正序排列…

ROS:ROS是什么

目录 一、ROS简介二、ROS可以做些什么三、ROS特征四、ROS特点4.1点对点设计4.2不依赖编程语言4.3精简与集成4.4便于测试4.5开源4.6强大的库与社区 五、ROS的发展六、ROS架构6.1OS层6.2中间层6.3应用层 七、通信机制八、计算图8.1节点&#xff08;Node&#xff09;8.2节点管理器…

FastReport.Net FastReport.Core 2023.2.15 Crack

快速报告.NET .NET 7 的报告和文档创建库 FastReport.Net & FastReport.Core适用于 .NET 7、.NET Core、Blazor、ASP.NET、MVC 和 Windows 窗体的全功能报告库。它可以在 Microsoft Visual Studio 2022 和 JetBrains Rider 中使用。 快速报告.NET 利用 .NET 7、.NET Core、…

从零开始学习JVM(六)-直接内存和执行引擎

1 直接内存介绍 直接内存不是虚拟机运行时数据区的一部分&#xff0c;也不是《Java虚拟机规范》中定义的内存区域。直接内存是在Java堆外的、直接向系统申请的内存空间。直接内存来源于NIO&#xff0c;通过存在堆中的DirectByteBuffer操作Native内存。通常访问直接内存的速度会…

在 Linux 中启动时自动启动 Docker 容器的 2 种方法

Docker 是一种流行的容器化平台&#xff0c;允许开发人员将应用程序及其依赖项打包成一个独立的容器&#xff0c;以便在不同环境中运行。在 Linux 系统中&#xff0c;我们可以通过配置来实现在系统启动时自动启动 Docker 容器。本文将详细介绍两种方法&#xff0c;以便您了解如…

《深入理解计算机系统(CSAPP)》第9章虚拟内存 - 学习笔记

写在前面的话&#xff1a;此系列文章为笔者学习CSAPP时的个人笔记&#xff0c;分享出来与大家学习交流&#xff0c;目录大体与《深入理解计算机系统》书本一致。因是初次预习时写的笔记&#xff0c;在复习回看时发现部分内容存在一些小问题&#xff0c;因时间紧张来不及再次整理…

数据库基础——3.SQL概述及规范

这篇文章我们来讲一下SQL概述和使用规范 目录 1.SQL概述 1.1SQL背景 1.2 SQL语言排行榜 1.3 SQL分类 2.SQL规则与规范 2.1基本规则 2.2 SQL大小写规范 &#xff08;建议遵守&#xff09; 2.3 注 释 2.4 命名规则&#xff08;暂时了解&#xff09; 2.5 数据导入指令 1…

Linux 实操篇-网络配置

Linux 实操篇-网络配置 Linux 网络配置原理图 查看网络IP 和网关 查看虚拟网络编辑器和修改IP 地址 查看网关 查看windows 环境的中VMnet8 网络配置(ipconfig 指令) 查看linux 的网络配置ifconfig ping 测试主机之间网络连通性 基本语法 ping 目的主机&#xff08;功能描述…

交换机安全功能介绍

今天海翎光电的小编来给大家聊聊以太网交换机安全功能。 交换机作为局域网中最常见的设备&#xff0c;在安全上面临着重大威胁&#xff0c;这些威胁有的是针对交换机管理上的漏洞&#xff0c;攻击者试图控制交换机。有的针对的是交换机的功能&#xff0c;攻击者试图扰乱交换机的…

使用curl命令传输数据

文章目录 一、curl命令二、举例和注意事项Reference 一、curl命令 curl是传输数据的命令行工具&#xff0c;可以通过命令行发送HTTP请求和接收HTTP响应。它的名字是“client for URLs”&#xff0c;意为URL的客户端&#xff0c;表示该工具主要用于处理URL相关的任务。curl可以…

vue实现深拷贝的方法

在 vue中&#xff0c;深拷贝是一个很有用的功能&#xff0c;在不改变原来对象状态的情况下&#xff0c;进行对象的复制。 但要实现深拷贝&#xff0c;需要两个对象具有相同的属性。如果两个对象不同&#xff0c;深拷贝也不能实现。 1.我们将变量A的属性赋给变量B&#xff0c;但…

软件测试之自动化测试【webdriver API】

目录 一、webdriver API 1.元素的定位 2.操作测试对象 3.添加等待 3.1 sleep 强制等待 3.2 隐式等待 3.3 显式等待 4.打印信息 5.浏览器的操作 5.1 浏览器的前进和后退 5.2 浏览器滚动条操作 5.3 浏览器最大化及设置浏览器宽、高 6.键盘按键 7. 鼠标事件 8.定位…