MCP2515调试心得

基于 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) {// EID0passedIdReg->tempEID0 = 0xFF & tempPassedInID;tempPassedInID = tempPassedInID >> 8;// EID8passedIdReg->tempEID8 = 0xFF & tempPassedInID;tempPassedInID = tempPassedInID >> 8;// SIDLwipSIDL = 0x03 & tempPassedInID;tempPassedInID = tempPassedInID >> 2;wipSIDL = ((0x07 & tempPassedInID) << 5) | wipSIDL;wipSIDL = wipSIDL | 0x08;passedIdReg->tempSIDL = 0xEB & wipSIDL;// SIDHtempPassedInID = 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.hqwc.cn/news/102283.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# OpenVino Yolov8 Detect 目标检测

效果 项目 代码 using OpenCvSharp; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using static System.Net.Mime.MediaT…

《TCP/IP网络编程》阅读笔记--域名及网络地址

目录 1--域名系统 2--域名与 IP 地址的转换 2-1--利用域名来获取 IP 地址 2-2--利用 IP 地址获取域名 3--代码实例 3-1--gethostbyname() 3-2--gethostbyaddr() 1--域名系统 域名系统&#xff08;Domain Name System&#xff0c;DNS&#xff09;是对 IP 地址和域名进行相…

深入了解 Axios 的 put 请求:使用技巧与最佳实践

在前端开发中&#xff0c;我们经常需要与后端服务器进行数据交互。其中&#xff0c;PUT 请求是一种常用的方法&#xff0c;用于向服务器发送更新或修改数据的请求。通过发送 PUT 请求&#xff0c;我们可以更新服务器上的资源状态。 Axios 是一个流行的 JavaScript 库&#xff0…

RK3399如何在Loader模式下拉高GPIO

目录 一、RK GPIO计算方法 1.1 GPIO2_A6计算它的num值 二、在烧录的Loader模式拉高GPIO 2.1 如何找到GPIOA2_A6的寄存器2.2 设置GPIO上面的映射的地址2.3 设置GPIO上面的映射的地址代码2.4 Loader模式串口log 一、RK GPIO计算方法 1.1 GPIO2_A6计算它的num值 K3399 有 5 组…

ubuntu 扩展内存挂载

一般新建虚拟机时&#xff0c;系统默认的空间是20G&#xff0c;但是当我们搭建一些环境之后&#xff0c;需要解压一些稍微大点的源码时内存可能不够用了&#xff0c;这时我们需要扩展内存。 一、硬盘扩展 首先&#xff0c;关闭虚拟机&#xff0c;在虚拟机设置中将硬盘容量扩展…

RabbitMQ: topic 结构

生产者 package com.qf.mq2302.topic;import com.qf.mq2302.utils.MQUtils; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection;public class Pubisher {public static final String EXCHANGE_NAME"mypubilisher";public static void ma…

【Springcloud】Actuator服务监控

【Springcloud】Actuator服务监控 【一】基本介绍【二】如何使用【三】端点分类【四】整合Admin-Ui【五】客户端配置【六】集成Nacos【七】登录认证【八】实时日志【九】动态日志【十】自定义通知 【一】基本介绍 &#xff08;1&#xff09;什么是服务监控 监视当前系统应用状…

pdf怎么转换成dwg格式?简单转换方法分享

当我们需要在CAD中编辑PDF文件中的向量图形时&#xff0c;将PDF转换成DWG格式是一个非常好的选择。因为PDF是一种非常流行的文档格式&#xff0c;很多时候我们会接收到PDF文件&#xff0c;但是PDF文件中的向量图形无法直接在CAD中编辑。而将PDF转换成DWG格式后&#xff0c;就可…

shell入门运算符操作、条件判断

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

PHP8函数包含文件-PHP8知识详解

在php中&#xff0c;可以使用以下函数来包含其他文件&#xff1a;include()、include_once()、require()、require_once()。 1、include(): 包含并运行指定文件中的代码。如果文件不存在或包含过程中出现错误&#xff0c;将发出警告。 <?php include filename.php; ?>…

2023年高教社杯数学建模国赛C题详细版思路

C 题 蔬菜类商品的自动定价与补货决策 2023年国赛如期而至&#xff0c;为了方便大家尽快确定选题&#xff0c;这里将对C题进行解题思路说明&#xff0c;以分析C题的主要难点、出题思路以及选择之后可能遇到的难点进行说明&#xff0c;方便大家尽快找到C题的解题思路。 难度排…

uni-app 折叠自定义

uni-app的uni-collapse折叠组件样式修改 下面是修改后的样式 <uni-collapse accordion class"ze" v-model"isCollapse" click"toggleCollapse"><!-- 因为list默认带一条分隔线&#xff0c;所以使用 titleBorder"none" 取消…