• MCP2515调试心得


    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    其实 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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • _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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    相关的寄存器定义如下:

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 标准 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));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    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 去操作时都会出现这个问题。

  • 相关阅读:
    信息安全实验四:Ip包监视程序实现
    C++特性——auto关键字、范围for、指针空值nullptr
    C++——多态
    【数据结构笔记09】数据结构之稀疏矩阵的三元组和十字链表法具体代码实现(C语言)
    【阿里云】域名解析 & Tomcat绑定域名
    JSqlParser 解析 sql
    论文阅读之《Quasi-Unsupervised Color Constancy 》
    如何进行一篇论文的阅读
    【磁盘】为什么文件大小很小,占用空间很大?
    【DataRoom】- 基于VUE的开源的大屏可视化设计器
  • 原文地址:https://blog.csdn.net/Bin_Watson/article/details/132752808