MCP2515调试心得

news2024/12/24 8:30:49

基于 STM32 芯片的 MCP2515 芯片调试心得

  • 1. MCP2515 芯片解析
    • 1.1 外部时钟源
    • 1.2 可采用连续传输提高效率
      • 发送数据时,使用 TX0 为例:
    • 1.3 关于 MASK 和 Filter 的注意事项
      • 1.3.1 Filter 的注意事项
      • 1.3.2 MASK 设置的一些问题
  • 2. STM32 硬件 SPI 问题

1. MCP2515 芯片解析

1.1 外部时钟源

  • 使用晶振
    外部晶振由 OSC1 和 OSC2 引脚输入
    在这里插入图片描述
    支持 8M 和 16M 两种时钟,详细参考下图:
    在这里插入图片描述
  • 使用外部时钟源
    在这里插入图片描述
    需要注意 OSC1 前面添加了一个反相器,猜测原因是为了提高外部时钟的驱动力。

在某次项目,使用 STM32 的 MCO 直接输出 8M 的 HSE 连接进 MCP2515 的 OSC1 出现无法驱动的情况,可见很需要加一个反相器

  • 第三种,方式不清楚没有使用过。
    在这里插入图片描述

1.2 可采用连续传输提高效率

网上大部分的 MCP2515 芯片驱动是发送一个字节,先发送该字节要写入的寄存器地址,这种方式在操作连续地址的多个寄存器时的效率很低。

发送数据时,使用 TX0 为例:

参阅 MCP2515 手册可知与 TX0 相关的寄存器地址是连续递增的,如下:

#define MCP2515_TXB0CTRL            0x30
#define MCP2515_TXB0SIDH            0x31
#define MCP2515_TXB0SIDL            0x32
#define MCP2515_TXB0EID8            0x33
#define MCP2515_TXB0EID0            0x34
#define MCP2515_TXB0DLC             0x35
#define MCP2515_TXB0D0              0x36
#define MCP2515_TXB0D1              0x37
#define MCP2515_TXB0D2              0x38
#define MCP2515_TXB0D3              0x39
#define MCP2515_TXB0D4              0x3A
#define MCP2515_TXB0D5              0x3B
#define MCP2515_TXB0D6              0x3C
#define MCP2515_TXB0D7              0x3D

其实 TXB0SIDH 到 MCP2515_TXB0D7 是可以通过只设置一次地址,连续传输将数据写入到 MCP2515 里。
具体操作的编码可以这样实现:

uint32_t _hdl_mcp2515_send(uint32_t mcp2515, uint32_t recv_id, uint8_t id_type, uint8_t *sendBuff, uint32_t count)
{
	uint32_t returnValue = 0;
    _id_reg idReg;
    _ctrl_status_t ctrlStatus;
    uint8_t instruction_load_buffer;
    uint8_t instruction_request_to_send;

    idReg.tempSIDH = 0;
    idReg.tempSIDL = 0;
    idReg.tempEID8 = 0;
    idReg.tempEID0 = 0;
  
    ctrlStatus.ctrl_status = _hdl_mcp2515_read_status_instruction(mcp2515);

    /* 目前 Transmission 查找并传送未 Pending 的缓冲器。 */
    if (ctrlStatus.TXB1REQ != 1)
    {
        instruction_load_buffer = MCP2515_LOAD_TXB1SIDH;
        instruction_request_to_send = MCP2515_RTS_TX1;
        returnValue = 1;
    }
    else if (ctrlStatus.TXB2REQ != 1)
    {
        instruction_load_buffer = MCP2515_LOAD_TXB2SIDH;
        instruction_request_to_send = MCP2515_RTS_TX2;
        returnValue = 1;
    } 
    else if (ctrlStatus.TXB0REQ != 1)
    {
        instruction_load_buffer = MCP2515_LOAD_TXB0SIDH;
        instruction_request_to_send = MCP2515_RTS_TX0;
        returnValue = 1;
    }

    if (returnValue)
    {
        /* 转换成符合 ID 类型 */
        _hdl_mcp2515_convert_can_id_to_register(recv_id, id_type, &idReg);
        /* Loading 要传送到 Tx Buffer */
        _hdl_mcp2515_load_tx_buffer(mcp2515, instruction_load_buffer, &idReg, sendBuff, count);
        /* 请求传送Tx Buffer的数据 */
        _hdl_mcp2515_request_to_send(mcp2515, instruction_request_to_send);
    }

    return returnValue;
}

  • _hdl_mcp2515_convert_can_id_to_register :将 can 的 id 转成对应的寄存器 id;
  • _hdl_mcp2515_load_tx_buffer :把数据填充到对应的长度寄存器 DLC 和 D0 ~ D7 寄存器;
  • _hdl_mcp2515_request_to_send: 发送命令将 TXnBuffer 中的数据发生出去;
    在这里插入图片描述
static void _hdl_mcp2515_convert_can_id_to_register(uint32_t tempPassedInID, uint8_t canIdType, _id_reg_t passedIdReg) 
{
    uint8_t wipSIDL = 0;

    if (canIdType == dEXTENDED_CAN_MSG_ID_2_0B) 
    {
        // EID0
        passedIdReg->tempEID0 = 0xFF & tempPassedInID;
        tempPassedInID = tempPassedInID >> 8;

        // EID8
        passedIdReg->tempEID8 = 0xFF & tempPassedInID;
        tempPassedInID = tempPassedInID >> 8;

        // SIDL
        wipSIDL = 0x03 & tempPassedInID;
        tempPassedInID = tempPassedInID >> 2;
        wipSIDL = ((0x07 & tempPassedInID) << 5) | wipSIDL;
        wipSIDL = wipSIDL | 0x08;
        passedIdReg->tempSIDL = 0xEB & wipSIDL;

        // SIDH
        tempPassedInID = tempPassedInID >> 3;
        passedIdReg->tempSIDH = 0xFF & tempPassedInID;
    }
    else
    {
        passedIdReg->tempEID8 = 0;
        passedIdReg->tempEID0 = 0;
        tempPassedInID = tempPassedInID << 5;
        passedIdReg->tempSIDL = 0xFF & tempPassedInID;
        tempPassedInID = tempPassedInID >> 8;
        passedIdReg->tempSIDH = 0xFF & tempPassedInID;
    }
}

void _hdl_mcp2515_load_tx_buffer(uint32_t mcp2515, uint8_t instruction, _id_reg_t id_reg, uint8_t *data, uint8_t length)
{
    uint8_t buff[length + sizeof(_id_reg) + 1]; // 不推荐这样写,后面会改这部分实现
    uint8_t idx = 0;

    buff[idx++] = instruction;

    memcpy(buff + idx, id_reg, sizeof(_id_reg));
    idx += sizeof(_id_reg);
    buff[idx++] = length;
    memcpy(buff + idx, data, length);
    hdl_mcp2515_transmit_hook(mcp2515, buff, sizeof(buff));
}

void _hdl_mcp2515_request_to_send(uint32_t mcp2515, uint8_t instruction)
{
    hdl_mcp2515_transmit_hook(mcp2515, &instruction, 1);
}

相关的寄存器定义如下:

typedef struct {
    uint8_t tempSIDH;
    uint8_t tempSIDL;
    uint8_t tempEID8;
    uint8_t tempEID0;
} _id_reg;

typedef union {
    struct {
        uint8_t RX0IF      : 1;
        uint8_t RX1IF      : 1;
        uint8_t TXB0REQ    : 1;
        uint8_t TX0IF      : 1;
        uint8_t TXB1REQ    : 1;
        uint8_t TX1IF      : 1;
        uint8_t TXB2REQ    : 1;
        uint8_t TX2IF      : 1;
    };
    uint8_t ctrl_status;  
} _ctrl_status_t;

1.3 关于 MASK 和 Filter 的注意事项

1.3.1 Filter 的注意事项

Filter 的设置有四个寄存器,如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
转成 C 的结构体:

typedef struct
{
    struct
    {
        uint8_t RXFnSIDH;
        struct
        {
            uint8_t EID16 : 1;
            uint8_t EID17 : 1;
            uint8_t reserve_1 : 1;
            uint8_t EXIDE : 1;
            uint8_t reserve_2 : 1;
            uint8_t SID0 : 1;
            uint8_t SID1 : 1;
            uint8_t SID2 : 1;
        } RXFnSIDL;
    } _sid_reg;
    struct 
    {
        uint8_t RXFxEID8;
        uint8_t RXFxEID0;
    } _eid_reg;
} _mcp2515_filter_reg;

typedef _mcp2515_filter_reg* _mcp2515_filter_reg_t;
  • 标准 ID 和扩展 ID 是根据 EXIDE 位来控制,这想必没问题。
  • 但是注意上方红色方框的话,如果采用的扩展 ID,那么 RXFxEID8 : RXFxEID0 对应的是 ID 号的低 16 位,剩余的位,按低到高依次填充进 EID16、EID17、SID0、SID1、SID2、RXFnSIDH 寄存器里面。

对应的代码实现:

void _hdl_mcp2515_add_filter(uint32_t mcp2515, hdl_mcp2515_filter_t filter, uint32_t id, HDL_MCP2515_BOOL isExt)
{
    _mcp2515_filter_reg filter_reg = {0};
    uint8_t filter_addr;


    switch (filter)
    {
        case HDL_MCP2515_RX_BUFF1_FILTER_1: filter_addr = MCP2515_RXF0SIDH; break;
        case HDL_MCP2515_RX_BUFF1_FILTER_2: filter_addr = MCP2515_RXF1SIDH; break;
        case HDL_MCP2515_RX_BUFF2_FILTER_1: filter_addr = MCP2515_RXF2SIDH; break;
        case HDL_MCP2515_RX_BUFF2_FILTER_2: filter_addr = MCP2515_RXF3SIDH; break;
        case HDL_MCP2515_RX_BUFF2_FILTER_3: filter_addr = MCP2515_RXF4SIDH; break;
        case HDL_MCP2515_RX_BUFF2_FILTER_4: filter_addr = MCP2515_RXF5SIDH; break;
        default: 
            break;
    }

    if (isExt == HDL_MCP2515_TRUE)
    {
        filter_reg._sid_reg.RXFnSIDL.EXIDE = 0x01;

        filter_reg._eid_reg.RXFxEID0 = id & 0xFF;
        filter_reg._eid_reg.RXFxEID8 = (id >> 8) & 0xFF;
        filter_reg._sid_reg.RXFnSIDL.EID16 = (id >> 16) & 0x01;
        filter_reg._sid_reg.RXFnSIDL.EID17 = (id >> 17) & 0x01;

        filter_reg._sid_reg.RXFnSIDL.SID0 = (id >> 18) & 0x01;
        filter_reg._sid_reg.RXFnSIDL.SID1 = (id >> 19) & 0x01;
        filter_reg._sid_reg.RXFnSIDL.SID2 = (id >> 20) & 0x01;

        filter_reg._sid_reg.RXFnSIDH = (id >> 21) & 0xFF;

        _hdl_mcp2515_write_sequence(mcp2515, filter_addr, (uint8_t*)&filter_reg, sizeof(filter_reg));
    }
    else
    {
        filter_reg._sid_reg.RXFnSIDL.EXIDE = 0x00;
        
        filter_reg._sid_reg.RXFnSIDL.SID0 = id & 0x01;
        filter_reg._sid_reg.RXFnSIDL.SID1 = (id >> 1) & 0x01;
        filter_reg._sid_reg.RXFnSIDL.SID2 = (id >> 2) & 0x01;
		filter_reg._sid_reg.RXFnSIDH = (id >> 3) & 0xFF;

        _hdl_mcp2515_write_sequence(mcp2515, filter_addr, (uint8_t*)&filter_reg, sizeof(filter_reg._sid_reg));
    }
}

Filter 寄存器的设置也可以采用连续传输的方式实现。

1.3.2 MASK 设置的一些问题

  • MASK 寄存器的设置与 Filter 非常相似,除了没有 EXIDE 位之外;
  • 设置 MASK 后,不推荐标准 ID 和 扩展 ID 混用,在 ID 过滤上的计算很复杂,具体说来:
    – 设置 MASK 为扩展 ID 位时,标准 ID 过滤采用 RXMnSIDH、RXMnSIDL 里面的位;
    – 设置 MASK 为标准 ID 位时,RXMnEID8 和 RXMnEID0 会默认为 0,此时扩展 ID 也是参考 RXMnSIDH、RXMnSIDL 的位;

RXBn 接收标准 ID 或扩展 ID 还是同时接收,可以配置 RXM<1:0> 来设置:
在这里插入图片描述

2. STM32 硬件 SPI 问题

采用 STM32 硬件 SPI 时注意使用软件去控制 NSS 的输出,硬件控制操作 MCP2515 芯片会有问题。本人使用的是 STM32F407VG 和 STM32F429 去操作时都会出现这个问题。

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

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

相关文章

外滩大会发布银行数字化5大趋势:随身银行、AI风控、数字员工、边缘物联与云原生

通用人工智能风起云涌&#xff0c;金融行业将如何应对&#xff1f; 9月8日&#xff0c;由中国银行业协会指导&#xff0c;网商银行承办的外滩大会银行业数字化论坛上&#xff0c;IDC中国副总裁兼首席分析师武连峰发布了《银行数字科技五大趋势》&#xff1a;随身银行、AI风控、…

ULN2003 芯片

芯片介绍&#xff1a; ULN2003 是高耐压、大电流达林顿陈列&#xff0c;由七个硅 NPN 达林顿管组成。 达林顿管并联可以承受更大的电流。 此电路主要应用于继电器驱动器&#xff0c;字锤驱动器&#xff0c;灯驱动器&#xff0c;显示驱动器&#xff08;LED 气 体放电&#…

Apache Tomcat 漏洞复现

文章目录 Apache Tomcat 漏洞复现1. Tomcat7 弱密码和后端 Getshell 漏洞1.1 漏洞描述1.2 漏洞复现1.3 漏洞利用1.3.1 jsp小马1.3.2 jsp大马 2. Aapache Tomcat AJP任意文件读取/包含漏洞2.1 漏洞描述2.1 漏洞复现2.2 漏洞利用工具 3. 通过 PUT 方法的 Tomcat 任意写入文件漏洞…

10元/月?中国电信推出手机直连卫星功能,华为联合开启卫星之旅

2021年9月8日&#xff0c;华为Mate 60 Pro 系列手机首次推出“卫星语音通话”功能。此功能需与运营商合作&#xff0c;而中国电信率先推出了“手机直连卫星”服务。 中国电信的用户可以在自己的普通手机卡套餐基础上&#xff0c;加装直连卫星服务。此项服务的价格如下&#xff…

时序数据库 TimescaleDB 基础概念

时序数据在许多领域中具有广泛的应用&#xff0c;例如金融市场分析、气象预测、交通流量监测、生产过程监控等&#xff0c;时序数据通常是大规模的、高维度的、需要实时计算和分析&#xff0c;针对时序数据的特点与其所带来的挑战&#xff0c;针对时序数据处理所面临的挑战&…

1000元订金?华为折叠屏手机MateX5今日开始预订,售价尚未公布

华为最新款折叠屏手机Mate X5今日在华为商城开始预订&#xff0c;吸引了众多消费者的关注。预订时需交纳1000元的订金&#xff0c;而具体售价尚未公布。据华为商城配置表显示&#xff0c;Mate X5预计将搭载Mate 60系列同款麒麟9000S处理器&#xff0c;或可能搭载麒麟9100处理器…

vue3:4、组合式API-setup选项

setup每次都要return&#xff0c;好麻烦。怎么解决&#xff1f; 使用 <script setup> 语法糖&#xff08;底层帮你return了&#xff09; 写法如下

在线实时监测离子风机的功能

离子风机是一种能够通过释放大量负离子来净化空气并提供清新环境的设备。要实现联网实时在线监测离子风机&#xff0c;可以考虑以下几个步骤&#xff1a; 1. 设备接入互联网&#xff1a;离子风机需要具备网络连接功能&#xff0c;可以通过无线网络或者以太网接入路由器&#x…

优思学院|质量工程师和QA区别在哪?质量工程师有什么发展策略?

质量管理內容相当复杂&#xff0c;从供应商中选择SQE&#xff0c;入料检验的IQC&#xff0c;制程管控的IPQC&#xff0c;站在客户的立场&#xff0c;保证出货质量OQC&#xff0c;所以一般来说QC/QA人员必须管理从材料到出货的所有质量项目。 而质量工程师&#xff08;QE&#…

2023国赛数学建模C题模型代码

C题代码全部都完成了&#xff0c;可以看文末名片 我们先看C题的一个背景 在生鲜商超中,蔬菜类商品保鲜期短,且品相会随销售时间增加而变差。商超需要根据历史销售和需求每天进行补货。由于蔬菜品种众多、产地不同,补货时间在凌晨,商家须在不明确具体单品和价格的情况下进行补…

读书笔记:多Transformer的双向编码器表示法(Bert)-1

多Transformer的双向编码器表示法 Bidirectional Encoder Representations from Transformers&#xff0c;即Bert&#xff1b; 本笔记主要是对谷歌Bert架构的入门学习&#xff1a; 介绍Transformer架构&#xff0c;理解编码器和解码器的工作原理&#xff1b;掌握Bert模型架构…

Tableau自学四部曲_Part4:BI仪表盘搭建

文章目录 一、数据可视化原则1. 区分用户2. 主次分明、详略得当3. 真实准确4. 符合大众认知和审美习惯5. 适度原则6. 五秒原则6. 恰到好处的说明7. 少即是多8. 可视化案例 二、BI仪表盘搭建1. 仪表盘搭建原则2. 明确仪表盘主题3. 仪表盘主题拆解4. 开发设计工作表5. 构思仪表盘…

LQR 控制器

LQR&#xff08;Linear Quadratic Regulator&#xff09;控制器 LQR&#xff08;Linear Quadratic Regulator&#xff09;是一种经典的线性控制器设计方法&#xff0c;用于设计线性时不变系统的状态反馈控制器&#xff0c;以最小化系统性能指标&#xff0c;通常是二次代价函数…

软件测试/测试开发丨ChatGPT:带你进入智能对话的新时代

简介 人工智能时代来临 我们正处于AI的iPhone时刻。——黄仁勋&#xff08;英伟达CEO&#xff09; ChatGPT 好得有点可怕了&#xff0c;我们距离危险的强人工智能不远了。——马斯克&#xff08;Tesla/SpaceX/Twitter CEO&#xff09; 以上的内容说明我们现在正处于一个技术大…

Docker镜像解析获取Dockerfile文件

01、概述 当涉及到容器镜像的安全时&#xff0c;特别是在出现镜像投毒引发的安全事件时&#xff0c;追溯镜像的来源和解析Dockerfile文件是应急事件处理的关键步骤。在这篇博客中&#xff0c;我们将探讨如何从镜像解析获取Dockerfile文件&#xff0c;这对容器安全至关重要。 02…

2023 年高教社杯全国大学生数学建模竞赛题D 题 圈养湖羊的空间利用率

2023 年全国大学生数学建模竞赛题D 题 圈养湖羊的空间利用率思路详解Python源码 昨天已经将E题第一二问的详解和思路源码都写了出来&#xff0c;大家如果想从E题下手的话推荐参考本人文章&#xff0c;个人认为E题在建模上是优于D题的&#xff0c;毕竟有给出数据而且有明确的建…

基于springboot+vue+mysql的新能源充电系统--前后端分离(内含源码+报告+部署教程)

新能源充电系统管理系统按照操作主体分为管理员和用户。管理员的功能包括反馈管理、客服聊天管理、充电桩管理、充电桩预约管理、字典管理、新能源公告管理、用户管理、管理员管理等&#xff0c;可以管理报修。用户的功能包括管理部门以及部门岗位信息&#xff0c;管理充电桩信…

2023年下半年高项考试学习计划

之前总结 2023年上半年的考试&#xff0c;对于我自己&#xff0c;就是虎头蛇尾&#xff0c;也谈不上太过自信&#xff0c;好好学习了一段时间之后&#xff0c;也就是不再发博文&#xff0c;截止到2022年11月的时候&#xff0c;自己就算是放弃了&#xff0c;没有再主动学习。 结…

华为云云耀云服务器L实例评测|老用户回归的初印象

华为云云耀云服务器L实例评测&#xff5c;老用户回归的初印象 前言一、新面孔1. 云耀云服务器2. 服务器特色 二、上手感官体验1. 性价比感受2. 推荐宝塔面板3. CloudShell登录4. 安全性 总结 前言 其实笔者接触华为云已经很久了&#xff0c;第一次使用的云服务器就是华为云。当…

【Linux成长史】Linux基本指令大全

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集 数据库专栏 初阶数据结构 &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如…