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

news2024/11/29 6:35:50

  最近在使用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使用起来就正常了。希望这个小小的坑大家都不要踩。为了一个地址值,折腾了好几个小时。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

@Transactional注解作用,不生效的场景,事务回滚

目录 一、Transactional注解二、注解失效问题1、Transactional 应用在非 public 修饰的方法上2、Transactional 注解属性 rollbackFor 设置错误3、同一个类中方法调用,导致Transactional失效4、捕获异常 三、Transactional回滚1、Transactional2、Transactional(rol…

HUD(抬头显示)的方案介绍

目录 一、基于DLP3030-Q1的HUD电路设计 二、DLP3030-Q1的介绍 三、DLP3030-Q1工作原理 四、DLPC120-Q1DMD 显示控制器 五、TMS320F2802332 位 MCU 六、 HUD显示实例 HUD主板实例 七、HUD的软件环境 一、基于DLP3030-Q1的HUD电路设计 本设计采用了DLP3030-Q1 芯片组&…

H3C IPSec IKE野蛮模式

这里使用H3C模拟器。 H3C IPSec IKE野蛮模式,又称为IKE Main Mode,主要是在第一阶段(Phase 1)的过程中提供身份保护。它主要用于VPN隧道建立过程中的密钥交换。以下是配置步骤: 创建IKE提案: system-view…

vite源码分析之dev

最近研究socket, 所以就顺便看了一下vite源码, vite的热更新就是根据socket实现的, 所以正好记录一下. 前端任何脚手架的入口,肯定是在package.json文件中,当我们输入script命令时, 会经历什么样的步骤呢? 接下来我们一起来探索一下~~~ 入口-package.json 看下面就是一个普…

【C++】string介绍

String 前言为什么学习string类?string类的常用接口说明string类对象的常见构造析构函数赋值运算符重载[ ] 重载size和length迭代器字符串追加关于容量的函数insert和erasefindreplacec_strrfindfind_first_offind_first_not_offind_last_ofsubstrgetlineto_string …

linux+onenet可视化(图形化步骤)

文章目录 一、ONENET项目搭建1.1 ONENET注册1.2 创建产品与设备1.3 添加数据流 二、可视化配置 OneNET是由中国移动打造的PaaS物联网开放平台。平台能够帮助开发者轻松实现设备接入与设备连接,快速完成产品开发部署,为智能硬件、智能家居产品提供完善的物…

孤儿僵尸守护进程

孤儿僵尸守护进程 1. 孤儿进程:2. 僵尸进程:3. 守护进程:(重点) 1. 孤儿进程: 父进程退出,还没退出的子进程就变成了孤儿进程 不要怕,还有爷爷进程init: 孤儿进程将被init进程所收养,并由init进程对它们完成状态收集…

认识HTTP协议---2

hi,大家好,今天继续为大家带来HTTP协议相关的知识 认识请求报头 👀1.header 👀2.Content-Type,Content-Length 👀3.User-Agent 👀4.Referer 👀5.Cookie机制 小复习 进入正题之前我们先回忆一下之前的知识 http报…

牛客小白月赛73

A&#xff1a;最小的数字 A-最小的数字_牛客小白月赛73 (nowcoder.com) #include<bits/stdc.h> #define endl \n #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define int long long using namespace std; int n, m, k, A, B, N, M, K; const int ma…

22种transforms数据预处理方法

来源&#xff1a;投稿 作者&#xff1a;阿克西 编辑&#xff1a;学姐 建议搭配视频学习↓ 视频链接&#xff1a;https://ai.deepshare.net/detail/p_5df0ad9a09d37_qYqVmt85/6 1.数据增强&#xff08;data augmentation&#xff09; 数据增强又称为数据增广&#xff0c;数据…

docker安装单机nacos、rocketmq、reids、xxl-job、minio、elasticsearch、kibana、gogs、nginx

目录在右侧中部 启动容器报错 直接删除那个name后边的就可以 安装nacos 首先需要拉取对应的镜像文件&#xff1a;docker pull nacos/nacos-server 挂载目录&#xff1a; mkdir -p /mydata/nacos/logs/ #新建logs目录mkdir -p /mydata/nacos/init.d/ …

最小二乘估计心得

基本思想 存在一组观察值 ( x i , y i ) (x_i, y_i) (xi​,yi​)&#xff0c;其中 y i y_i yi​和 x i x_i xi​之间满足一定的线性关系&#xff0c;如 y a 0 f 0 ( x ) a 1 f 1 ( x ) . . . a m − 1 f m − 1 ( x ) y a_0 f_0(x) a_1 f_1(x) ... a_{m-1} f_{m-1}(x…

改进YOLOv5/YOLOv8:结合华为诺亚VanillaNet Block模块:深度学习中极简主义的力量

YOLOv5结合华为诺亚VanillaNet Block模块 介绍核心代码加入YOLOv5yaml文件:运行结果论文: VanillaNet: the Power of Minimalism in Deep Learning 代码: https://link.zhihu.com/?target=https%3A//github.com/huawei-noah/VanillaNet 介绍 基础模型的核心是 "更多…

Terra-Luna归零一年后:信任重建、加密未来路在何方?

本月既是Terra-Luna归零的一周年&#xff0c;也是FTX崩溃的第六个月&#xff0c;而这两个事件分别代表着2022年加密市场连环爆的开始与高潮&#xff0c;引发了加密行业15年历史上最可怕的生存危机。 尽管今年市场行情有所回暖&#xff0c;比特币开年至今涨幅70%&#xff0c;以太…

【办公类-19-04】办公中的思考——幼儿姓名笔画数统计(单字、全名字)

结果展示--笔画数统计 背景需求 中6班幼儿都能够书写学号&#xff08;基本没有镜像了&#xff09;&#xff0c;于是我开始尝试让幼儿熟悉自己的“姓氏”&#xff0c;并书写姓氏&#xff08;单字&#xff09;。4位幼儿能够默写全名&#xff0c;3位幼儿可以默写名字中的某一个字…

3*5差值结构参照1分类迭代次数比较

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入有3个节点&#xff0c;训练集AB各由5张二值化的图片组成&#xff0c;让A中有6个1&#xff0c;B中全是1&#xff0c;排列组合A &#xff0c;统计迭代次数并排序。共收集到了181个不同的迭代次数&#xff0c;其中有4个 A-B 迭…

创建web后端程序(servlet程序搭建)

目录 一、Servlet概述 二、创建servlet程序 1.创建类继承HttpServlet 2.重写HttpServlet类中 service、destroy、init方法 3.重新启动服务器 一、Servlet概述 Server Applet的简称&#xff0c;用Java编写的服务器端的程序。它运行在web服务器中&#xff0c;web服务器负责…

路径规划算法:基于回溯搜索优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于回溯搜索优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于回溯搜索优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…

Android逆向学习之Frida逆向与抓包实战学习笔记(持续更新中)

学习资料:《安卓Frida逆向与抓包实战》陈佳林/著 文章目录 基础环境第三章 Frida逆向入门之Java层hook3.1 frida基础3.1.3frida基础知识frida存在两种操作模式frida操作App的方式有两种 3.1.4Frida IDE配置 3.2 frida脚本入门3.2.1 frida脚本的概念3.2.2 Java层hook基础1.hook…

续---初识HTML!!!(结束篇)

续———初识HTML&#xff01;&#xff01;&#xff01; 一、表单 1.表单的作用 关于表单的作用&#xff1a;表单最大的作用就是收集用户的信息&#xff0c;用户填写表单&#xff0c;点击提交数据给服务器。 2.如何画出表单&#xff1f; 在HTML中我们采用form标签来画一个…