Arduino作为具有多个i2c地址的从属

时间:2016-01-09 08:47:22

标签: arduino i2c atmega

我想使用Arduino作为i2c奴隶。但我要求Arduino通过向多个i2c地址注册来充当多个设备。

这可能不是人们通常会做的事情,但这是我做这件事的原因:

我想使用Arduino作为Spektrum遥测的遥测传感器。遥测接收器有一些i2c插头,连接到多个传感器(电流0x02,电压0x03,空速0x11等),每个传感器都有一个遥测接收器所需的固定i2c地址。

我想使用一个 Arduino作为所有这些设备,通过注册所有上述地址,并适当地回应读数。

我可以使用每个传感器一个Arduino,这看起来很傻,因为我可以用一个Arduino pro-mini执行所有这些读数。

我知道您可以使用

注册Arduino
Wire.begin(0x02); 

但是我需要类似的东西(伪代码)

Wire.begin(0x02, 0x03, 0x11);

当收到请求时,我需要知道Arduino被查询的地址。

例如(伪代码)

void receiveEvent(byte address, int bytesReceived){
  if(address == 0x02){
    // Current reading
  }
  else if(address == 0x03){
    // Voltage reading
  }
  else if(address == 0x11){
    // Airspeed reading
  }
}

任何建议都将受到赞赏。

5 个答案:

答案 0 :(得分:7)

由于Wire.begin()仅允许传递单个从地址,因此无法通过使用Wire库使Arduino监听多个从地址。

即使是大多数Arduinos所基于的Atmel ATmega微控制器,其硬件2线串行接口(TWI)也可通过其2线地址寄存器TWAR设置为单个7位地址。然而,可以通过使用TWI地址掩码寄存器TWAMR掩蔽一个或多个地址位来解决这个限制,如在文献中(稍微简要地)记载的那样。这个ATmega datasheet部分22.9.6:

  

TWAMR可以加载7位Salve(sic!)地址掩码。 TWAMR中的每个位都可以屏蔽(禁用)TWI地址寄存器(TWAR)中的相应地址位。如果屏蔽位设置为1,则地址匹配逻辑忽略输入地址位与TWAR中相应位之间的比较。

因此我们首先必须根据我们想要响应的所有I2C地址设置掩码位,方法是对它们进行“或”运算并向右移动以匹配TWAMR寄存器布局(TWAMR保持掩码在bit7:1中,bit0未使用):

TWAMR = (sensor1_addr | sensor2_addr | sensor3_addr) << 1;

此处的主要问题是找出查询的特定I2C地址(我们只知道它与地址掩码匹配)。 如果我正确解释第22.5.3节,说明

  

TWDR包含要传输的地址或数据字节,或接收的地址或数据字节。

我们应该能够从TWDR寄存器中检索未屏蔽的I2C地址。

ATmega TWI操作是基于中断的,更具体地说,它利用单个中断向量来处理由TWSR状态寄存器中的状态代码指示的过多TWI事件。 在TWI中断服务程序中,我们必须

  1. 确保我们进入ISR的原因是因为我们已经被查询过了。这可以通过检查TWSR状态代码0xA8已收到自己的SLA + R
  2. 来完成
  3. 通过检查TWDR中总线上的最后一个字节,根据实际查询的I2C地址,确定将哪些传感器数据发送回主站。
  4. ISR的这一部分可能看起来像这样(未经测试):

    if (TWSR == 0xA8) { // read request has been received
      byte i2c_addr = TWDR >> 1; // retrieve address from last byte on the bus
      switch (i2c_addr) {
        case sensor1_addr:
          // send sensor 1 reading
          break;
        case sensor2_addr:
          // send sensor 2 reading
          break;
        case sensor3_addr:
          // send sensor 3 reading
          break;
        default:
          // I2C address does not match any of our sensors', ignore.
          break;
      }
    }
    

    感谢您提出这个有趣的问题!

答案 1 :(得分:4)

我真的很喜欢vega8的答案,但我还想提一下,如果您的I2C主设备不会以非常快的速度计时,那么使用基于软件的I2C实现也是可行的,并为您提供自由你想要的。

如果粗略计算显示在TWI ISR中花费的时间太长而中断可能开始重叠,您可能需要考虑这种方法。

答案 2 :(得分:1)

void setup() 
{
    Wire.begin(0x11 | 0x12);       // Adr 11 and 12 are used for Alt and Speed by Spectrum DX
    Wire.onRequest(requestEvent);  // register callback function
    TWAMR = (0x11 | 0x12) << 1;    // set filter for given adr
}

答案 3 :(得分:1)

void requestEvent() {
    int adr = TWDR >> 1;  // move 1 bit to align I2C adr
    if (adr == 0x12)      // check for altitude request at adr 12
        Wire.write(tmpSpektrumDataAlt, 16); // send buffer
    if (adr == 0x11)      // check for speed request at adr 11
        Wire.write(tmpSpektrumDataSpd, 16); // send buffer
}

这适用于带有遥测模块的Spectrum DX8。 Spectrum界面在Sectrums主页上公开。技术文件。

答案 4 :(得分:0)

I2C总线上可能有其他器件,TWAMR应设置尽可能少的位。所以我认为计算掩码的更好方法是:

AddrOr =  Addr1 | Addr2 | Addr3 | Addr4 ... 
AddrAnd = Addr1 & Addr2 & Addr3 & Addr4 ...
TWAMR = (AddrOr ^ AddrAnd) << 1

,而TWAR可以设置为AddrOr或AddrAnd 通过这种方式,我们可以将地址冲突的可能性限制在最低限度