首页

Arduino MCP2515 CAN总线接口教程

在本项目中,我们将了解MCP2515 CAN控制器模块,如何将MCP2515 CAN总线控制器与Arduino连接,最后如何借助两个MCP2515 CAN控制器和CAN协议实现两个Arduino板之间的通信。

Arduino MCP2515 CAN总线接口图像

介绍

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

(adsense1)

CAN协议由罗伯特·博世公司开发,主要用于汽车控制单元及其组件之间的通信。

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

为了了解更多一点关于CAN总线、CAN控制器等重要方面的知识,MCP2515 CAN总线控制器模块非常有帮助。

还读:spi通信基础知识

MCP2515 CAN总线控制模块简介

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

(adsense2)

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

Arduino MCP2515 CAN总线接口MCP2515 CAN模块

该模块基于MCP2515 CAN控制器IC和TJA1050 CAN收发器IC, MCP2515是一个独立的CAN控制器,并集成了用于与微控制器通信的SPI接口。

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

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

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

MCP2515 CAN总线模块原理图

在查看模块的原理图之前,您需要了解关于MCP2515和TJA1050这两个ic的一些事情。

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

CAN模块负责CAN总线上的消息的发送和接收。Control Logic通过连接所有模块来处理MCP2515的设置和操作。SPI块负责SPI通信接口。

对于TJA1050 IC,由于它作为MCP2515 CAN控制器和物理CAN总线之间的接口,该IC负责从控制器获取数据并将其中继到总线上。

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

Arduino MCP2515 CAN总线接口MCP2515示意图

MCP2515与Arduino接口电路图

下图为MCP2515 CAN模块与Arduino接口的电路图,以及两个Arduino之间可能通过CAN协议进行的通信。

Arduino MCP2515 CAN总线接口电路图

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

Arduino MCP2515 CAN总线接口MCP2515 CAN模块引脚

组件的要求

  • Arduino UNO x 2 [在这里买
  • 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的libraries目录下。

由于通信涉及到一个发送模块和一个接收模块,因此代码也分为发送代码和接收代码。

发射机的代码

接收方代码

工作

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

在这个项目中,发射机发送的消息是1 1 2 3 0 5 6 7。此消息通过CAN总线传输,接收方接收到此消息并在其串行监视器上显示。

另外,0th和图4th接收端分别提取上述序列中的1、0位,并对Arduino引脚2所连接的LED进行ON、OFF操作。

应用程序

正如介绍中提到的,CAN在汽车领域的应用非常广泛。其中一些应用包括:

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

17的反应

  1. 谢谢你提供的信息。让2个Arduinos+MCP相互交谈是有趣而简单的。当您连接到车辆的OBDII端口时,就会出现挑战。根据我的经验,事情会变得复杂得多:

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

    -即使是使用标准CAN帧的车辆,当你发送PID请求例如发动机的rpm时,似乎车辆没有收到数据。

    有什么指导吗?

    谢谢,
    戴夫

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

  2. 嘿!你举的例子看起来很不错。我只是想知道我是否可以同时编写一个Arduino作为发射器和接收器。

    谢谢! !
    宝拉

    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. 如果我需要发送消息,然后收到一个答案,重新包装答案到不同的包,并发送回来-如何在一个简单的草图中做到这一点?

  4. 我在一个大学项目中,我们建造了一辆电动摩托车,通信是用CAN进行的。我已经使用了你的项目的一半,试图读取总线CAN,但它甚至不能通过开始。可以循环。从我在网上看到的,这可能是由于晶体是一个8oooKhz,图书馆是为16000Khz。你能帮我吗?

    1. 也许对你来说太晚了,但其他人可能会发现这很有用。函数mcp2515_configRate接受晶体频率作为第二个参数.....对于8MHz,只需通过MCP_8MHz

      /*********************************************************************************************************
      **函数名:mcp2515_configRate
      **描述:设置波特率
      *********************************************************************************************************/
      mcp2515_can::mcp2515_configRate(const字节canSpeed, const字节clock) {
      字节集,cfg1, cfg2, cfg3;
      Set = 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;
      打破;

      默认值:
      Set = 0;
      打破;

      打破;

      case (MCP_8MHz):
      切换(canSpeed) {
      case (CAN_5KBPS):
      cfg1 = MCP_8MHz_5kBPS_CFG1;
      cfg2 = MCP_8MHz_5kBPS_CFG2;
      cfg3 = MCP_8MHz_5kBPS_CFG3;
      打破;

      case (CAN_10KBPS):
      cfg1 = MCP_8MHz_10kBPS_CFG1;
      cfg2 = MCP_8MHz_10kBPS_CFG2;
      cfg3 = MCP_8MHz_10kBPS_CFG3;
      打破;

      case (CAN_20KBPS):
      cfg1 = MCP_8MHz_20kBPS_CFG1;
      cfg2 = MCP_8MHz_20kBPS_CFG2;
      cfg3 = MCP_8MHz_20kBPS_CFG3;
      打破;

      case (CAN_31K25BPS):
      cfg1 = MCP_8MHz_31k25BPS_CFG1;
      cfg2 = MCP_8MHz_31k25BPS_CFG2;
      cfg3 = MCP_8MHz_31k25BPS_CFG3;
      打破;

      case (CAN_40KBPS):
      cfg1 = MCP_8MHz_40kBPS_CFG1;
      cfg2 = MCP_8MHz_40kBPS_CFG2;
      cfg3 = MCP_8MHz_40kBPS_CFG3;
      打破;

      case (CAN_50KBPS):
      cfg1 = MCP_8MHz_50kBPS_CFG1;
      cfg2 = MCP_8MHz_50kBPS_CFG2;
      cfg3 = MCP_8MHz_50kBPS_CFG3;
      打破;

      case (CAN_80KBPS):
      cfg1 = MCP_8MHz_80kBPS_CFG1;
      cfg2 = MCP_8MHz_80kBPS_CFG2;
      cfg3 = MCP_8MHz_80kBPS_CFG3;
      打破;

      case (CAN_100KBPS):
      cfg1 = MCP_8MHz_100kBPS_CFG1;
      cfg2 = MCP_8MHz_100kBPS_CFG2;
      cfg3 = MCP_8MHz_100kBPS_CFG3;
      打破;

      case (CAN_125KBPS):
      cfg1 = MCP_8MHz_125kBPS_CFG1;
      cfg2 = MCP_8MHz_125kBPS_CFG2;
      cfg3 = MCP_8MHz_125kBPS_CFG3;
      打破;

      case (CAN_200KBPS):
      cfg1 = MCP_8MHz_200kBPS_CFG1;
      cfg2 = MCP_8MHz_200kBPS_CFG2;
      cfg3 = MCP_8MHz_200kBPS_CFG3;
      打破;

      case (CAN_250KBPS):
      cfg1 = MCP_8MHz_250kBPS_CFG1;
      cfg2 = MCP_8MHz_250kBPS_CFG2;
      cfg3 = MCP_8MHz_250kBPS_CFG3;
      打破;

      case (CAN_500KBPS):
      cfg1 = MCP_8MHz_500kBPS_CFG1;
      cfg2 = MCP_8MHz_500kBPS_CFG2;
      cfg3 = MCP_8MHz_500kBPS_CFG3;
      打破;

      case (CAN_1000KBPS):
      cfg1 = MCP_8MHz_1000kBPS_CFG1;
      cfg2 = MCP_8MHz_1000kBPS_CFG2;
      cfg3 = MCP_8MHz_1000kBPS_CFG3;
      打破;

      默认值:
      Set = 0;
      打破;

      打破;

      默认值:
      Set = 0;
      打破;

      If (set) {
      mcp2515_setRegister (MCP_CNF1 cfg1);
      mcp2515_setRegister (MCP_CNF2 cfg2);
      mcp2515_setRegister (MCP_CNF3 cfg3);
      返回MCP2515_OK;
      }其他{
      返回MCP2515_FAIL;

  5. 假设我想通过4个CAN模块连接4个Arduino。
    那么,将CAN消息直接发送到一个特定的CAN接收器的首选方式是什么?

    1. 有地址的不是arduino,而是应用程序。当您的特定arduino运行具有修复Id的“应用程序”时,您可以解决这个问题。

      所以你需要根据你要发送的信息来设计你的应用程序。例如:打开灯,ID为0x45,关闭灯ID为0x46。打开喇叭,0x10 -如果没有喇叭关闭,每10毫秒发送一次。在汽车中,你通常每X毫秒发送一个信息到前灯上,如果信息没有发送,灯就会关闭。这取决于应用程序的安全功能。

  6. 你好,
    我已经复制了发射机和接收机代码的确切代码,并下载了上面提到的相关库。
    但是,在编译错误消息“不能将变量CAN声明为抽象类型MCP_CAN”时,

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

      1. 我在例题中找到了解。

        # include
        # include“mcp2518fd_can.h”

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

        现在起作用了。

        1. 你能分享一下代码吗?事实上,我正在做类似的事情。
          我试图从电池通信CAN信息。我只需要有代码的接收端。但这行不通。它只是每次都显示相同的值。
          你能帮我一下吗?

  7. 嗨,谁能帮我一下,让我知道我需要做什么修改才能让它与arduino mega一起工作?
    我试过使用相同的别针和相同的代码,但一直收到信息
    CAN总线初始化失败
    谢谢,
    汤姆

留下回复

你的电邮地址将不会公布。必填字段已标记

bob多特蒙德官方下载Electronicshub图标
<\/i>","library":""}}" data-widget_type="nav-menu.default">
Baidu
map