如何在Arduino中使用CAN协议

描述

  如今,任何一辆普通汽车都包含大约 60 到 100 个传感器单元,用于传感和交换信息。随着汽车制造商不断通过自动驾驶、安全气囊系统、胎压监测、巡航控制系统等功能使他们的汽车更加智能,这个数字只会越来越高。与其他传感器不同,这些传感器处理关键信息,因此来自这些传感器的数据应使用标准汽车通信协议进行通信。例如,速度、油门位置等巡航控制系统数据是发送到电子控制单元 (ECU)的重要值为了决定汽车的加速水平,此处的错误通信或数据丢失可能导致严重故障。因此,与 UART、SPI或I2C等标准通信协议不同,设计人员使用更可靠的汽车通信协议,如 LIN、CAN、FlexRay 等。

  在本文中,我们将再次研究基础知识,最后我们还将使用 CAN 通信在两个 Arduino 之间交换数据。

  CAN简介

  CAN aka Controller Area Network是一种串行通信总线,专为工业和汽车应用而设计。它是一种基于消息的协议,用于多个设备之间的通信。当多个 CAN 设备如下图所示连接在一起时,连接形成一个网络,就像我们的中枢神经系统一样,允许任何设备与节点中的任何其他设备通话。

MCP2515

  CAN 网络将仅包含两条线CAN High 和 CAN Low,用于双向数据传输,如上所示。通常,CAN 的通信速度范围从 50 Kbps 到 1Mbps,距离范围可以从 1Mbps 的 40 米到 50kpbs 的 1000 米。

  CAN报文格式:

  在 CAN 通信中,数据以特定的消息格式在网络中传输。这种消息格式包含许多段,但两个主要段是标识符和有助于在 CAN 总线中发送和响应消息的数据。

  标识符或 CAN ID:标识符也称为 CAN ID 或也称为 PGN(参数组编号)。它用于识别 CAN 网络中存在的 CAN 设备。根据使用的 CAN 协议类型,标识符的长度为 11 位或 29 位。

  标准 CAN:0-2047(11 位)

  扩展 CAN:0-2 29 -1(29 位)

  数据:这是必须从一个设备发送到另一个设备的实际传感器/控制数据。大小数据的长度可以是 0 到 8 个字节。

  数据长度代码 (DLC):0 到 8 表示存在的数据字节数。

  CAN中使用的电线:

  CAN 协议由两条线组成,即CAN_H 和 CAN_L,用于发送和接收信息。两条线都充当差分线,这意味着 CAN 信号(0 或 1)由 CAN_L 和 CAN_H 之间的电位差表示。如果差为正且大于某个最小电压,则为 1,如果差为负,则为 0。

  通常使用双绞线电缆进行 CAN 通信。如图所示,CAN 网络的两端通常使用一个 120 欧姆的电阻,这是因为线路需要平衡并连接到相同的电位。

  CAN over SPI 和 I2C 的比较

  既然我们已经学会了如何使用SPI 和 Arduino和IIC 和 Arduino,让我们比较一下 SPI 和 I2C 和 CAN 的特性

MCP2515

  CAN 协议应用

  由于 CAN 协议的稳健性和可靠性,它们被用于汽车、工业机械、农业、医疗设备等行业。

  由于 CAN 中的布线复杂性降低,它们主要用于汽车等汽车应用。

  实施成本低,硬件组件价格也较低。

  易于添加和删除 CAN 总线设备。

  如何在 Arduino 中使用 CAN 协议

  由于 Arduino 不包含任何内置 CAN 端口,因此使用了一个名为MCP2515的 CAN 模块。该 CAN 模块通过 SPI 通信与 Arduino 连接。让我们详细了解 MCP2515 以及它如何与 Arduino 接口。

  MCP2515 CAN 模块:

MCP2515

  MCP2515 模块有一个 CAN 控制器 MCP2515,它是高速 CAN 收发器。MCP2515 与 MCU 之间通过 SPI 连接。因此,很容易与任何具有 SPI 接口的微控制器连接。

  对于想要学习 CAN Bus 的初学者来说,这个模块将是一个好的开始。此 CAN SPI 板非常适合工业自动化、家庭自动化和其他汽车嵌入式项目。

  MCP2515的特性和规格:

  使用高速CAN收发器TJA1050

  尺寸:40×28mm

  用于扩展多 CAN 总线接口的 SPI 控制

  8MHZ晶振

  120Ω终端电阻

  具有独立按键、LED指示灯、电源指示灯

  支持 1 Mb/s CAN 操作

  低电流待机操作

  最多可连接112个节点

  MCP2515 CAN 模块的引脚排列:

MCP2515

  在本教程中,让我们看看如何通过 CAN总线模块 MCP2515从 Arduino Nano 向 Arduino Uno 发送湿度和温度 (DHT11) 传感器数据。

  所需组件

  Arduino UNO

  阿杜诺纳米

  DHT11

  16x2 液晶显示器

  MCP2515 CAN 模块 – 2

  10k 电位器

  面包板

  连接电线

  MCP2515 Arduino电路图

MCP2515

  CAN Transmitter 端的连接:

MCP2515

  CAN接收器侧的电路连接:

MCP2515

MCP2515

  两个 MCP2515 CAN 模块之间的连接

MCP2515

  完成所有连接后,我的硬件如下所示

MCP2515

  为 CAN 通信编程 Arduino

  首先,我们必须在 Arduino IDE 中安装 CAN 库。通过使用以下库,将 MCP2515 CAN 模块与 Arduino 连接变得更加容易。

  在本教程中,编码分为两部分,一是CAN 发送器代码(Arduino Nano),另一部分是CAN 接收器代码(Arduino UNO),两者均可在本页底部找到。其说明如下。

  在编写发送和接收数据的程序之前,请确保您已经按照上述步骤安装了库,并且 CAN 模块 MCP2515 在您的程序中初始化如下。

  初始化 MCP2515 CAN 模块: 

要创建与 MCP2515 的连接,请执行以下步骤:

1.设置SPI CS连接的管脚号(默认10)

 

MCP2515 mcp2515(10);

 

2.设置波特率和振荡器频率

 

mcp2515.setBitrate(CAN_125KBPS, MCP_8MHZ);

 

可用波特率:

CAN_5KBPS、CAN_10KBPS、CAN_20KBPS、CAN_31K25BPS、CAN_33KBPS、CAN_40KBPS、CAN_50KBPS、CAN_80KBPS、CAN_83K3BPS、CAN_95KBPS、CAN_100KBPS、CAN_125KBPS、CAN_200KBPS、CAN_250KBPS、CAN_500KBPS、CAN_1000KBPS。

可用时钟速度:

MCP_20MHZ、MCP_16MHZ、MCP_8MHZ

3. 设置模式。

 

mcp2515.setNormalMode(); 
mcp2515.setLoopbackMode(); 
mcp2515.setListenOnlyMode();

 

CAN发射端代码说明(Arduino Nano)

在发射器部分,Arduino Nano 通过 SPI 引脚与 MCP2515 CAN 模块接口,DHT11 将温度和湿度数据发送到 CAN 总线。

首先包括所需的库,用于使用 SPI 通信的 SPI 库、用于使用 CAN 通信的 MCP2515 库和用于将 DHT 传感器与 Arduino 一起使用的 DHT 库。我们之前将 DHT11 与 Arduino 接口。

 

#include            
#include        
#include           

 

现在定义了与 Arduino Nano 的 A0 相连的 DHT11(OUT 引脚)的引脚名称

 

#define DHTPIN A0       

 

而且,DHTTYPE被定义为 DHT11。

 

#define DHTTYPE DHT11

 

用于存储 CAN 消息格式的canMsg结构数据类型。

 

结构 can_frame canMsg;

 

设置 SPI CS 连接的管脚号(默认为 10)

 

MCP2515 mcp2515(10);

 

此外,DHT 类的对象 dht 与 Arduino Nano 和 DHT 类型为 DHT11 的 DHT 引脚被初始化。

 

DHT dht(DHTPIN, DHTTYPE);     

 

接下来在 void setup() 中:

使用以下语句开始 SPI 通信

 

SPI.开始();               

 

然后使用以下语句开始接收来自 DHT11 传感器的温度和湿度值。                       

 

dht.begin();               

 

接下来,使用以下命令重置 MCP2515

 

mcp2515.reset();

 

现在 MCP2515 的速度设置为 500KBPS 和 8MHZ 作为时钟

 

mcp2515.setBitrate(CAN_500KBPS,MCP_8MHZ);

 

并且 MCP2525 设置为正常模式

 

mcp2515.setNormalMode();

 

在无效循环()中:

以下语句获取湿度和温度值并存储在整数变量 h 和 t 中。

 

int h = dht.readHumidity();       
int t = dht.readTemperature();    

 

接下来,CAN ID 为 0x036(根据选择),DLC 为 8,我们将 h 和 t 数据分配给data[0]  和data[1]   ,并将所有数据保留为 0。

 

canMsg.can_id = 0x036;           
canMsg.can_dlc = 8;               
canMsg.data[0] = h; //更新[0]中的湿度值
canMsg.data[1] = t; //更新[1]中的温度值
canMsg.data[2] = 0x00; //全部用 0 休息
canMsg.data[3] = 0x00; 
canMsg.data[4] = 0x00; 
canMsg.data[5] = 0x00; 
canMsg.data[6] = 0x00; 
canMsg.data[7] = 0x00;

 

毕竟,要将消息发送到 CAN BUS,我们使用以下语句。

 

  mcp2515.sendMessage(&canMsg);     

 

所以现在温度和湿度数据作为消息发送到 CAN 总线。

CAN接收端代码说明(Arduino UNO)

在接收器部分,Arduino UNO 与 MCP2515 和16x2 LCD 显示器连接。在这里,Arduino UNO 从 CAN 总线接收温度和湿度,并在 LCD 中显示接收到的数据。

首先包括所需的库,用于使用 SPI 通信的 SPI 库、用于使用 CAN 通信的 MCP2515 库和用于将 16x2 LCD 与 Arduino 一起使用的 LiquidCrsytal 库。

 

#include            
#include     

 

接下来定义用于连接 Arduino UNO 的 LCD 引脚。

 

const int rs = 3, en = 4, d4 = 5, d5 = 6, d6 = 7, d7 = 8;  
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);  

 

声明了一个结构数据类型用于存储CAN 消息格式。

 

结构 can_frame canMsg;

 

设置 SPI CS 连接的管脚号(默认为 10)

 

MCP2515 mcp2515(10);                  

 

在 void setup() 中:

首先将 LCD 设置为 16x2 模式并显示欢迎信息。

 

  lcd.开始(16,2);                   
lcd.setCursor(0,0);                
  lcd.print("电路文摘"); 
  lcd.setCursor(0,1); 
  lcd.print("CAN ARDUINO"); 
  延迟(3000);
  lcd.clear();

 

使用以下语句开始 SPI 通信。

 

 SPI.开始();                         

 

接下来,使用以下命令重置 MCP2515。

 

mcp2515.reset();

 

现在将 MCP2515 设置为 500KBPS 的速度和 8MHZ 作为时钟。

 

mcp2515.setBitrate(CAN_500KBPS,MCP_8MHZ);

 

MCP2525 设置为正常模式。

 

mcp2515.setNormalMode();

 

接下来在 void loop() 中:

以下语句用于接收来自 CAN 总线的消息。如果收到消息,则进入if条件。

 

if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK)

 

在if条件下,数据被接收并存储在 c anMsg中,具有湿度值的数据 [0] 和具有温度值的数据 [1]。这两个值都存储在整数 x 和 y 中。

 

     int x = canMsg.data[0];         
     int y = canMsg.data[1];       

 

收到数值后,温度和湿度值使用以下语句显示在 16x2 LCD 显示屏上。

 

 lcd.setCursor(0,0);          
lcd.print("湿度:");     
lcd.print(x); 
lcd.setCursor(0,1); 
      lcd.print("温度:"); 
      lcd.print(y); 
      延迟(1000);
      lcd.clear();

 

  Arduino中CAN通信的工作

  硬件准备好后,将 CAN 发送器和 CAN 接收器的程序(完整程序如下)上传到各自的 Arduino 板上。通电后,您应该注意到 DHT11 读取的温度值将通过 CAN 通信发送到另一个 Arduino,并显示在第二个Arduino 的 LCD 上,如下图所示。我还使用交流遥控器检查液晶显示屏上显示的温度是否接近实际室温。

MCP2515
CAN 发射器代码(Arduino Nano):


#include //使用SPI通信的库

#include //使用CAN通信的库

#include //使用DHT传感器的库


#define DHTPIN A0

#define DHTTYPE DHT11


结构 can_frame canMsg;

MCP2515 mcp2515(10);


DHT dht(DHTPIN, DHTTYPE); //使用STM32的DHT引脚和DHT类型为DHT11的DHT类初始化对象dht


void setup()

{

while (!Serial);

序列号.开始(9600);



SPI.开始();//开始 SPI 通信

dht.begin(); //开始读取温湿度传感器值



mcp2515.reset();

mcp2515.setBitrate(CAN_500KBPS,MCP_8MHZ); //将 CAN 设置为速度 500KBPS 和时钟 8MHz

mcp2515.setNormalMode();

}


void loop()

{

int h = dht.readHumidity(); //获取湿度值

int t = dht.readTemperature(); //获取温度值


canMsg.can_id = 0x036; //CAN id 为 0x036

canMsg.can_dlc = 8; //CAN数据长度为8

canMsg.data[0] = h; //更新[0]中的湿度值

canMsg.data[1] = t; //更新[1]中的温度值

canMsg.data[2] = 0x00; //全部用 0 休息

canMsg.data[3] = 0x00;

canMsg.data[4] = 0x00;

canMsg.data[5] = 0x00;

canMsg.data[6] = 0x00;

canMsg.data[7] = 0x00;

mcp2515.sendMessage(&canMsg); //发送CAN报文

delay(1000);

}






CAN接收器代码(Arduino UNO):


#include //使用SPI通信的库

#include //使用CAN通信的库

#include //使用LCD显示的库


const int rs = 3, en = 4, d4 = 5, d5 = 6, d6 = 7, d7 = 8;


LiquidCrystal lcd(rs, en, d4, d5, d6, d7); //定义LCD显示引脚RS,E,D4,D5,D6,D7


结构 can_frame canMsg;

MCP2515 mcp2515(10); // SPI CS 引脚 10



void setup() {

lcd.begin(16,2); //设置 LCD 为 16x2 类型

lcd.setCursor(0,0); //显示欢迎信息

lcd.print("CIRCUIT DIGEST");

lcd.setCursor(0,1);

lcd.print("CAN ARDUINO");

延迟(3000);

lcd.clear();



SPI.开始();//开始SPI通信



Serial.begin(9600); //以 9600 波特率开始串行通信



mcp2515.reset();

mcp2515.setBitrate(CAN_500KBPS,MCP_8MHZ); //将 CAN 设置为 500KBPS 和 8MHz 时钟

mcp2515.setNormalMode(); //将CAN设置为正常模式

}


void loop()

{

if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) // 接收数据(轮询读取)

{

int x = canMsg.data[0];

int y = canMsg.data[1];



lcd.setCursor(0,0); //显示在 16x2 LCD 上接收到的温度和湿度值

lcd.print("Humidity : ");

lcd.print(x);

lcd.setCursor(0,1);

lcd.print("温度:");

lcd.print(y);

延迟(1000);

lcd.clear();

}

}

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
评论(0)
发评论
jf_72821860 04-17
0 回复 举报
作者大大,求原程序 收起回复

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分