Arduino与Proteus仿真实例-两个Arduino之间I2C通信仿真

两个Arduino之间I2C通信仿真

1、I2C介绍

集成电路间 (I2C) 协议是一种旨在允许多个“外围”数字集成电路(“芯片”)与一个或多个“控制器”芯片进行通信的协议。它只需要两根信号线来交换信息。

在这里插入图片描述

  • SDA(串行数据):主机和从机发送和接收数据的线路。
  • SCL(串行时钟):传输时钟信号的线路。

与 SPI 一样,I2C 是同步的,因此位的输出通过主机和从机之间共享的时钟信号与位的采样同步。 时钟信号始终由主机控制。

2、I2C的数据组成

使用 I2C,数据以消息的形式传输。 消息被分解成数据帧。 每条消息都有一个地址帧,其中包含从站的二进制地址,以及一个或多个包含正在传输的数据的数据帧。 该消息还包括每个数据帧之间的开始和停止条件、读/写位和 ACK/NACK 位:

在这里插入图片描述

  • 启动条件:在 SCL 线从高电平切换到低电平之前,SDA 线从高电压电平切换到低电压电平。
  • 停止条件:SCL 线从低电平切换到高电平后,SDA 线从低电平切换到高电平。
  • 地址帧:每个从站唯一的 7 或 10 位序列,用于在主站想要与其通话时识别从站。
  • 读/写位:单个位指定主机是向从机发送数据(低电压电平)还是从它请求数据(高电压电平)。
  • ACK/NACK 位:消息中的每个帧后跟一个确认/不确认位。 如果成功接收到地址帧或数据帧,则从接收设备向发送方返回一个 ACK 位。

2)I2C寻址

I2C 没有像 SPI 那样的从机选择线,因此它需要另一种方式让从机知道正在向它发送数据,而不是另一个从机。 它通过寻址来做到这一点。 地址帧始终是新消息中起始位之后的第一帧。

主机将它想要与之通信的从机地址发送给与其相连的每个从机。 然后,每个从设备将主设备发送的地址与其自己的地址进行比较。 如果地址匹配,它会向主机发送一个低电压 ACK 位。 如果地址不匹配,从机什么也不做,SDA 线保持高电平。

3)读/写位

地址帧的末尾包含一个位,用于通知从机是要向其写入数据还是要从其接收数据。 如果主机要向从机发送数据,则读/写位为低电压电平。 如果主机向从机请求数据,则该位为高电压电平。

4)数据帧

在主机检测到来自从机的 ACK 位后,第一个数据帧准备好发送。

数据帧总是 8 位长,并以最高有效位在前发送。 每个数据帧后面紧跟着一个 ACK/NACK 位,以验证该帧是否已成功接收。 在发送下一个数据帧之前,主机或从机必须接收到 ACK 位(取决于谁在发送数据)。

发送完所有数据帧后,主设备可以向从设备发送停止条件以停止传输。 停止条件是在 SCL 线上从低到高转换之后 SDA 线上的电压从低到高转换,而 SCL 线保持高电平。

3、I2C通信过程

1)主机通过将 SDA 线从高电平切换到低电平,然后将 SCL 线从高电平切换到低电平,向每个连接的从机发送启动条件。

在这里插入图片描述

2)主机向每个从机发送它想要与之通信的从站的 7 位或 10 位地址以及读/写位:

在这里插入图片描述

3)每个从机将主机发送的地址与自己的地址进行比较。 如果地址匹配,从机通过将 SDA 线拉低一位来返回 ACK 位。 如果来自主机的地址与从机自己的地址不匹配,则从机将 SDA 线保持为高电平。

在这里插入图片描述

4)主机发送或接收数据帧:

在这里插入图片描述

5)传输完每个数据帧后,接收设备向发送方返回另一个 ACK 位以确认已成功接收该帧:

在这里插入图片描述

6)为了停止数据传输,主机通过在将 SDA 切换为高电平之前将 SCL 切换为高电平来向从机发送停止条件:

在这里插入图片描述

4、I2C连接模式

1)单主机多从机模式

由于 I2C 使用寻址,因此可以从单个主机控制多个从机。 对于 7 位地址,可以使用 128 (2^7) 个唯一地址。 使用 10 位地址并不常见,但可提供 1,024 (2^10) 个唯一地址。 要将多个从设备连接到单个主设备,请按如下方式连接它们,使用 4.7K 欧姆上拉电阻将 SDA 和 SCL 线连接到 Vcc:

在这里插入图片描述

2)多主机多从机模式

多个主机可以连接到一个从机或多个从机。 当两个主设备尝试通过 SDA 线同时发送或接收数据时,同一系统中的多个主设备就会出现问题。 为了解决这个问题,每个主设备在发送消息之前都需要检测 SDA 线是低还是高。 如果 SDA 线为低电平,则意味着另一个主机控制了总线,主机应等待发送消息。 如果 SDA 线为高电平,则传输消息是安全的。 要将多个主机连接到多个从机,请使用下图,使用 4.7K 欧姆上拉电阻将 SDA 和 SCL 线连接到 Vcc:

在这里插入图片描述

5、仿真电路原理图

在这里插入图片描述

6、仿真代码实现

1)主机代码

#include <Wire.h>

#define SLAVE_ADDRESS 0x12 // 从机地址
#define DATA_SIZE 8 // 数据缓冲大小

#define CMD_LED_OFF 0 // LED关命令
#define CMD_LED_ON 1 // LED开命令

void setup() {
  // 启动为主机模式
  Wire.begin(); 
  Serial.begin(9600);
}
void loop() {
  // 向从机发送数据
  Wire.beginTransmission(SLAVE_ADDRESS);
  Wire.write(CMD_LED_OFF);             
  Wire.endTransmission();  
  
  Serial.println("send led off cmd");
  // 向从机请求数据
  Wire.beginTransmission(SLAVE_ADDRESS);
  Wire.requestFrom(SLAVE_ADDRESS, DATA_SIZE);
  if (Wire.available()) {
    Serial.print("Data returned: ");
    while (Wire.available()) Serial.print((char) Wire.read());
    Serial.println();
  }
  Wire.endTransmission();
  
  delay(500);
  Wire.beginTransmission(SLAVE_ADDRESS);
  Wire.write(CMD_LED_ON);
  Wire.endTransmission();   
  Serial.println("send led on cmd");
  
  // 向从机发送数据
  Wire.beginTransmission(SLAVE_ADDRESS);
  Wire.requestFrom(SLAVE_ADDRESS, DATA_SIZE);
  if (Wire.available()) {
    Serial.print("Data returned: ");
    while (Wire.available()) Serial.print((char) Wire.read());
    Serial.println();
  }
  Wire.endTransmission();
  
  delay(500);
}

2)从机代码

#include <Wire.h>
int cmd_dat = 0;
int ledPin = 7; // LED引脚
#define SLAVE_ADDRESS 0x12 // 从机地址
#define CMD_LED_OFF 0 // LED关命令
#define CMD_LED_ON 1 // LED开命令

void setup() {
  // 设置LED引脚为输出
  pinMode (ledPin , OUTPUT);
  // 开始I2C总线,设置为从机模式
  Wire.begin(SLAVE_ADDRESS); 
  // 设置数据接收回调函数
  Wire.onReceive(receiveEvent);
  // 设置数据请求回调函数
  Wire.onRequest(requestEvent);
  // 初始化串口
  Serial.begin(9600);
}
void receiveEvent(int bytes) {
  cmd_dat = Wire.read();    // 读取数据
}

void requestEvent()
{
  switch (cmd_dat) {
    case CMD_LED_OFF:
      Serial.println("request led off");
      Wire.write("led off",SLAVE_ADDRESS);
      break;
    case CMD_LED_ON:
     Wire.write("led on",SLAVE_ADDRESS);
      Serial.println("request led off");
      break;
    default:
      Wire.write("nothing",SLAVE_ADDRESS);
  }
}

void loop() {
  if (cmd_dat == CMD_LED_OFF) {
    Serial.println("led off");
    digitalWrite(ledPin,LOW);
    cmd_dat = -1;
  }
  if (cmd_dat == CMD_LED_ON) {
    Serial.println("led on");
    digitalWrite(ledPin,HIGH);
    cmd_dat = -1;
  }
  
}

7、仿真结果

在这里插入图片描述

ArduinoProteus仿真是通过硬件描述语言(HDL)将Arduino项目的软件逻辑映射到虚拟 Proteus 环境中,以便进行模拟测试。这里我们以 OLED 显示屏 SSD1306 为例,它通常通过SPI(串行外设接口)与Arduino通信。 首先,你需要准备的硬件有: 1. Arduino开发板 2. SSD1306型号的OLED显示屏 3. SPI连接线 步骤如下: 1. **安装库**:在Arduino IDE中,下载并安装SSD1306库,这会提供一组函数来控制屏幕显示,如初始化、写像素等。 ```arduino #include <Wire.h> #include <Adafruit_SSD1306.h> // SSD1306的尺寸,例如128x64 Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64); ``` 2. **设置OLED参数**:根据实际的屏幕尺寸调整相应的宽度和高度。 3. **初始化显示屏**: ```arduino void setup() { if (!display.begin(SSD1306_I2C)) { // 使用I2C通信,默认地址0x3c Serial.println("Display initialization failed!"); return; } // 设置工作模式 display.clearDisplay(); delay(2000); // 等待一段时间让初始化完成 } ``` 4. **编写SPI数据传输**:在处理数据发送时,可以使用`shiftOut()`函数来模拟SPI通信。注意,在 Proteus 中,需要配置正确的寄存器和信号线连接。 5. **在Proteus仿真**: - 在Proteus环境中创建一个新的项目,并导入你的Arduino sketch文件。 - 配置硬件连接,特别是SPI引脚,确保它们与Arduino的实际接线对应。 - 运行仿真,你可以看到屏幕上的内容与Arduino程序同步显示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

视觉与物联智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值