Arduino MCP2515 CAN总线接口教程

在这个项目中,我们将学习MCP2515 CAN控制器模块,如何接口MCP2515 CAN总线控制器与Arduino,最后如何使两个Arduino板之间的通信与两个MCP2515 CAN控制器和CAN协议。

Arduino MCP2515可以总线接口图像

介绍

简单CAN控制区域网是一种总线标准,它允许微控制器及其外围设备不需要主机或计算机就可以进行通信。

由Robert Bosch GmbH开发,可以协议是Main用于汽车之间的用于控制单元和其组件之间的通信。

例如,发动机控制单元是汽车中主要的控制装置。该单元连接到许多传感器和执行器,如空气流量,压力,温度,阀门控制,电机空气控制等。这些模块与控制单元之间通过CAN总线进行通信。

为了对CAN总线、CAN控制器等重要方面有一点了解,MCP2515 CAN总线控制器模块是很有帮助的。

还读:spi通信基础知识

MCP2515 CAN总线控制模块简介

MCP2515 CAN总线控制器是一个简单的模块,支持CAN协议版本2.0B,可以用于1Mbps的通信。为了建立一个完整的通信系统,您将需要两个CAN总线模块。

项目中使用的模块如下图所示。

Arduino MCP2515 CAN总线接口模块

MCP2515是一个独立的CAN控制器,集成了SPI接口,用于与微控制器通信。

在TJA1050 IC上,它作为MCP2515 CAN控制器IC和物理CAN总线之间的接口。

下图显示了典型MCP2515模块上的组件和引脚。

Arduino MCP2515 CAN总线接口MCP2515可以模块组件

MCP2515 CAN总线模块的原理图

在查看模块的原理图之前,您需要了解关于ICS i.E.MCP2515和TJA1050的几件事。

MCP2515 IC是主控制器,内部由三个主要子组件组成:CAN模块,控制逻辑和SPI块。

CAN模块负责在CAN总线上传输和接收消息。控制逻辑通过接口所有块来处理MCP2515的设置和操作。SPI块负责SPI通信接口。

来看看TJA1050 IC,因为它作为MCP2515 CAN控制器和物理CAN总线之间的接口,这个IC负责从控制器获取数据并将其中继到总线。

下图是MCP2515 CAN模块的原理图,显示了MCP2515 IC和TJA1050 IC在模块上是如何连接的。

Arduino MCP2515 CAN总线接口原理图

MCP2515与Arduino接口电路图

以下图像显示了与Arduino的接口MCP2515 Can模块的电路图,以及两个Arduino通过CAN协议之间的可能通信。

Arduino MCP2515 CAN总线接口电路图

如果MCP2515模块的引脚不清楚,下面的图像可能有用。

Arduino MCP2515 CAN总线接口

组件的要求

  • arduino uno x 2 [在这里买]
  • MCP2515 x 2
  • USB连接线x 2
  • 连接电线

电路设计

如前所述,CAN控制器IC促进SPI通信协议与任何微控制器接口。将MCP2515模块的SPI引脚SCK, MOSI (SI), MISO (SO), CS与Arduino对应的SPI引脚连接(见电路图)。

Arduino MCP2515 CAN总线接口电路设计

做两个这样的连接:一对作为发射器,另一对作为接收器。现在这个发射器和接收器之间的通信,连接每个MCP2515模块的CANH和CANL引脚。

代码

在进入代码之前,需要下载MCP2515模块的库。有很多图书馆,但我用过特别的一个。

下载并将提取的内容放在Arduino的库目录中。

由于通信涉及到发送模块和接收模块,所以代码又分为发送代码和接收代码。

发射机的代码

接收方代码

工作

这个项目的工作非常简单,所有的工作都由库(SPI和CAN)完成。由于CAN是基于消息的通信,您需要在0到8字节之间发送消息。

在本项目中,发射机发送的信息为1 1 2 3 0 5 6 7。该信息通过CAN总线传输,接收机接收到该信息并显示在其串行监视器上。

此外,0TH.和图4TH.接收机分别提取上述序列中的1位和0位,并将Arduino 2引脚连接的LED开关ON和OFF。

应用程序

如前所述,CAN在汽车领域得到了广泛的应用。其中一些应用包括:

  • 电子换档系统
  • 自动化主界面(如工业)
  • 医疗设备
  • 机器人
  • 汽车启动/停止汽车发动机

15反应

  1. 感谢您的信息。获得2个Arduinos + MCP彼此交谈是有趣和简单的。连接到车辆的Obdii端口时会发生挑战。那是根据我的经验,事情变得更加复杂:

    -车辆可以使用标准的can帧或扩展帧(我不确定Seeed库是否适用于扩展的can帧)。

    -即使是使用标准CAN帧的车辆,当你发送PID请求例如引擎的转速,似乎车辆没有收到数据。

    任何指引吗?

    谢谢,
    戴夫

    1. 如果您还没有找到答案,那么大多数较新的汽车都有防火墙,因此您不能从OBD端口获得某些数据,除非您实际使用OBD协议来获得DTC pid。例如,与ECU直接连接的老式汽车应该可以工作。

  2. 嘿!你举的这个例子似乎很不错。我只是想知道我能不能把一个Arduino同时编码成发射器和接收器。

    谢谢! !
    Paul.

    1. 嗨。我追求的是类似于宝拉的东西。在我的情况下,我感兴趣的是从一个Arduino运行两个CAN模块-在双总线车(HSCAN=500kbps, MSCAN=125kbps)或作为一个隔离的CAN桥接-例如,当一个电液动力转向泵从一个不同的模型,需要广播的CAN翻译工作,但一定不能看到新车辆的原始信息。虽然SPI适用于多个模块,但我想我很难找到一个同时支持多个CAN接口的库。

      路加福音

      1. 可能你已经找到了这个,但如果你需要它:

        你可以像这样创建2个不同的对象来连接到2个MC2515 shiles:

        mcp_can can_one(spicspin1);
        MCP_CAN CAN_TWO (spiCSPin2);

        例如,你需要两个CS引脚
        spiCSPin1 = 10
        spiCSPin2 = 9
        您将这些连接到每个CAN模块上的相应CS引脚
        而另一个SPI引脚(11,12,13)连接到两个模块
        SPI是一个总线,所以每个从设备只需要一个额外的引脚。

        所以,你可以用不同的速度初始化这两个模块的CANbus。等等,你可以分别读和写。

        如果你依赖中断引脚来通知你收到的消息,你可能需要额外的引脚。在这种情况下,您可以使用引脚2和3。否则,您可以依赖这两个模块的频繁轮询。然而,也要考虑到编程是更复杂的2模块和MCU可能超载

  3. 你好,

    我的项目像这样,然而我使用35tem。传感器。但我不会写代码。ı怎么写?


  4. 如果我需要发送消息,然后接收一个答案,将答案重新打包到不同的数据包并将其发送回-如何在一个单一的草图?

  5. 我在一个大学项目中,在我们建造了一个电流摩托车,可以用罐头进行通信。我使用了一半的项目来试图阅读公共汽车可以,但它甚至无法通过Begin.Can循环。从我在网上看到的东西,它可能是由于晶体是8oookhz,图书馆是为16000khz制作的。你能帮助我吗?

    1. 对你来说可能为时已晚,但其他人可能会发现这种有用。功能MCP2515_CONFIGRETE接受晶体频率作为第二个参数...... 8MHz只通过MCP_8MHz

      / *********************************************************************************************************
      **函数名称:MCP2515_CONFIGRATE
      **说明:设置波特率
      *********************************************************************************************************/
      mcp2515_can::mcp2515_configRate(const byte canSpeed, const byte clock) {
      字节集,CFG1,CFG2,CFG3;
      = 1;
      开关(时钟){
      案例(MCP_16MHz):
      开关(canSpeed) {
      案例(CAN_5KBPS):
      cfg1 = MCP_16MHz_5kBPS_CFG1;
      cfg2 = MCP_16MHz_5kBPS_CFG2;
      cfg3 = mcp_16mhz_5kbps_cfg3;
      休息;

      案例(CAN_10KBPS):
      cfg1 = mcp_16mhz_10kbps_cfg1;
      cfg2 = MCP_16MHz_10kBPS_CFG2;
      cfg3 = MCP_16MHz_10kBPS_CFG3;
      休息;

      案例(CAN_20KBPS):
      cfg1 = MCP_16MHz_20kBPS_CFG1;
      cfg2 = MCP_16MHz_20kBPS_CFG2;
      cfg3 = mcp_16mhz_20kbps_cfg3;
      休息;

      案例(CAN_25KBPS):
      cfg1 = mcp_16mhz_25kbps_cfg1;
      cfg2 = mcp_16mhz_25kbps_cfg2;
      cfg3 = mcp_16mhz_25kbps_cfg3;
      休息;

      案例(CAN_31K25BPS):
      cfg1 = mcp_16mhz_31k25bps_cfg1;
      cfg2 = MCP_16MHz_31k25BPS_CFG2;
      cfg3 = MCP_16MHz_31k25BPS_CFG3;
      休息;

      案例(CAN_33KBPS):
      cfg1 = MCP_16MHz_33kBPS_CFG1;
      cfg2 = mcp_16mhz_33kbps_cfg2;
      cfg3 = mcp_16mhz_33kbps_cfg3;
      休息;

      案例(CAN_40KBPS):
      cfg1 = MCP_16MHz_40kBPS_CFG1;
      cfg2 = mcp_16mhz_40kbps_cfg2;
      cfg3 = mcp_16mhz_40kbps_cfg3;
      休息;

      案例(CAN_50KBPS):
      cfg1 = MCP_16MHz_50kBPS_CFG1;
      cfg2 = MCP_16MHz_50kBPS_CFG2;
      cfg3 = MCP_16MHz_50kBPS_CFG3;
      休息;

      案例(CAN_80KBPS):
      cfg1 = mcp_16mhz_80kbps_cfg1;
      cfg2 = MCP_16MHz_80kBPS_CFG2;
      cfg3 = MCP_16MHz_80kBPS_CFG3;
      休息;

      案例(CAN_83K3BPS):
      cfg1 = mcp_16mhz_83k3bps_cfg1;
      cfg2 = MCP_16MHz_83k3BPS_CFG2;
      cfg3 = MCP_16MHz_83k3BPS_CFG3;
      休息;

      案例(CAN_95KBPS):
      cfg1 = MCP_16MHz_95kBPS_CFG1;
      cfg2 = mcp_16mhz_95kbps_cfg2;
      cfg3 = MCP_16MHz_95kBPS_CFG3;
      休息;

      案例(CAN_100KBPS):
      cfg1 = MCP_16MHz_100kBPS_CFG1;
      cfg2 = mcp_16mhz_100kbps_cfg2;
      cfg3 = mcp_16mhz_100kbps_cfg3;
      休息;

      案例(CAN_125KBPS):
      cfg1 = MCP_16MHz_125kBPS_CFG1;
      cfg2 = MCP_16MHz_125kBPS_CFG2;
      cfg3 = MCP_16MHz_125kBPS_CFG3;
      休息;

      案例(CAN_200KBPS):
      cfg1 = mcp_16mhz_200kbps_cfg1;
      cfg2 = mcp_16mhz_200kbps_cfg2;
      cfg3 = mcp_16mhz_200kbps_cfg3;
      休息;

      案例(CAN_250KBPS):
      cfg1 = MCP_16MHz_250kBPS_CFG1;
      cfg2 = mcp_16mhz_250kbps_cfg2;
      cfg3 = mcp_16mhz_250kbps_cfg3;
      休息;

      案例(CAN_500KBPS):
      cfg1 = MCP_16MHz_500kBPS_CFG1;
      cfg2 = mcp_16mhz_500kbps_cfg2;
      cfg3 = mcp_16mhz_500kbps_cfg3;
      休息;

      案例(CAN_666KBPS):
      cfg1 = mcp_16mhz_666kbps_cfg1;
      cfg2 = MCP_16MHz_666kBPS_CFG2;
      cfg3 = MCP_16MHz_666kBPS_CFG3;
      休息;

      案例(CAN_1000KBPS):
      cfg1 = MCP_16MHz_1000kBPS_CFG1;
      cfg2 = MCP_16MHz_1000kBPS_CFG2;
      cfg3 = mcp_16mhz_1000kbps_cfg3;
      休息;

      默认:
      设置= 0;
      休息;
      }
      休息;

      案例(MCP_8MHz):
      开关(canSpeed) {
      案例(CAN_5KBPS):
      cfg1 = MCP_8MHz_5kBPS_CFG1;
      cfg2 = mcp_8mhz_5kbps_cfg2;
      cfg3 = MCP_8MHz_5kBPS_CFG3;
      休息;

      案例(CAN_10KBPS):
      cfg1 = MCP_8MHz_10kBPS_CFG1;
      cfg2 = mcp_8mhz_10kbps_cfg2;
      cfg3 = MCP_8MHz_10kBPS_CFG3;
      休息;

      案例(CAN_20KBPS):
      cfg1 = mcp_8mhz_20kbps_cfg1;
      cfg2 = MCP_8MHz_20kBPS_CFG2;
      cfg3 = mcp_8mhz_20kbps_cfg3;
      休息;

      案例(CAN_31K25BPS):
      cfg1 = MCP_8MHz_31k25BPS_CFG1;
      cfg2 = mcp_8mhz_31k25bps_cfg2;
      cfg3 = mcp_8mhz_31k25bps_cfg3;
      休息;

      案例(CAN_40KBPS):
      cfg1 = MCP_8MHz_40kBPS_CFG1;
      cfg2 = MCP_8MHz_40kBPS_CFG2;
      cfg3 = mcp_8mhz_40kbps_cfg3;
      休息;

      案例(CAN_50KBPS):
      cfg1 = MCP_8MHz_50kBPS_CFG1;
      cfg2 = MCP_8MHz_50kBPS_CFG2;
      cfg3 = MCP_8MHz_50kBPS_CFG3;
      休息;

      案例(CAN_80KBPS):
      cfg1 = MCP_8MHz_80kBPS_CFG1;
      cfg2 = MCP_8MHz_80kBPS_CFG2;
      cfg3 = mcp_8mhz_80kbps_cfg3;
      休息;

      案例(CAN_100KBPS):
      cfg1 = MCP_8MHz_100kBPS_CFG1;
      cfg2 = MCP_8MHz_100kBPS_CFG2;
      cfg3 = MCP_8MHz_100kBPS_CFG3;
      休息;

      案例(CAN_125KBPS):
      cfg1 = mcp_8mhz_125kbps_cfg1;
      cfg2 = MCP_8MHz_125kBPS_CFG2;
      cfg3 = mcp_8mhz_125kbps_cfg3;
      休息;

      案例(CAN_200KBPS):
      cfg1 = MCP_8MHz_200kBPS_CFG1;
      cfg2 = MCP_8MHz_200kBPS_CFG2;
      cfg3 = MCP_8MHz_200kBPS_CFG3;
      休息;

      案例(CAN_250KBPS):
      cfg1 = MCP_8MHz_250kBPS_CFG1;
      cfg2 = MCP_8MHz_250kBPS_CFG2;
      cfg3 = mcp_8mhz_250kbps_cfg3;
      休息;

      案例(CAN_500KBPS):
      cfg1 = MCP_8MHz_500kBPS_CFG1;
      cfg2 = MCP_8MHz_500kBPS_CFG2;
      cfg3 = MCP_8MHz_500kBPS_CFG3;
      休息;

      案例(CAN_1000KBPS):
      cfg1 = mcp_8mhz_1000kbps_cfg1;
      cfg2 = MCP_8MHz_1000kBPS_CFG2;
      cfg3 = MCP_8MHz_1000kBPS_CFG3;
      休息;

      默认:
      设置= 0;
      休息;
      }
      休息;

      默认:
      设置= 0;
      休息;
      }

      如果(集){
      mcp2515_setRegister (MCP_CNF1 cfg1);
      MCP2515_SetRegister(MCP_CNF2,CFG2);
      MCP2515_SetRegister(MCP_CNF3,CFG3);
      返回MCP2515_OK;
      } 别的 {
      返回MCP2515_FAIL;
      }
      }

  6. 假设我想通过4个CAN模块连接4个Arduino。
    那么发送一个可以直接到1个特定的CAN接收器发送可以信息的首选方法是什么?

    1. 它不是具有地址的Arduino,它是应用程序。当您的特定Arduino使用修复ID运行“应用程序”时,可以解决此问题。

      因此,您需要设计有关您想要发送的消息的应用程序。例如:打开灯ID 0x45,关闭灯ID 0x46。打开喇叭,0x10 -发送每10ms如果没有喇叭关闭。在汽车中,通常你发送每X毫秒一条信息来点亮前灯,如果信息没有发送,灯就会熄灭。这取决于应用程序的安全功能。

  7. 你好,
    我已经复制了准确的发射器和接收器代码,并下载了上述的相关库。
    然而,在编译错误消息时“不能声明变量CAN为抽象类型MCP_CAN”

    有人能帮我解决这个错误吗

      1. 我在例子中找到了答案。

        # include
        # include“mcp2518fd_can.h”

        const int SPI_CS_PIN = 9;
        mcp2515_can可以(SPI_CS_PIN);//设置CS引脚

        现在工作。

留下一个回复

您的电子邮件地址将不会被公布。必需的地方已做标记*