0

我正在做一个使用多个 i2c IC/传感器的项目。Adafruit 为各个 IC 提供了非常好的基本库,这非常方便(节省了数小时挖掘数据表的时间)。但是他们的库是不灵活的。我试图通过应用依赖注入来缓解这种情况。

所以解决方案是使用 TwoWire 实例并将其作为对(在我的情况下)MCP23008 库(依赖注入)的引用传递,并让该库使用相同的 TwoWire 实例来执行所有 i2c 调用。

为了使主题更具可读性——但仍然提供我已经测试过的内容和原因的见解——我将首先提到这个问题,然后在底部提供更多背景信息。

// part from the modified library for the MCP23008 
// i2c port expander by Limor Fried

MyLib_MCP23008.cpp

// I added a constructor
MyLib_MCP23008::MyLib_MCP23008(TwoWire *w)
{
    // Constructors
    this->_wire = w;
}

// convenience begin with default address
void MyLib_MCP23008::begin() {
    begin(MCP23008_ADDRESS); // defined in .h as 0x20
}

void MyLib_MCP23008::begin(uint8_t addr) {
    if (addr > 7) {
        addr = 7;
    }
    this->i2caddr = addr;
    // set defaults!
    // _wire->begin(); don't do this here, expect the dependent TwoWire to do this...
    // there is a condition for ARDUINO >= 100 else in the original...
    _wire->beginTransmission(MCP23008_ADDRESS | i2caddr);
    _wire->write((byte)MCP23008_IODIR);
    _wire->write((byte)0xFF);  // all inputs
    _wire->write((byte)0x00);
    _wire->write((byte)0x00);
    _wire->write((byte)0x00);
    _wire->write((byte)0x00);
    _wire->write((byte)0x00);
    _wire->write((byte)0x00);
    _wire->write((byte)0x00);
    _wire->write((byte)0x00);
    _wire->write((byte)0x00);
    _wire->endTransmission();
}

我也改变了所有的“电线”。在其他方法中引用“_wire->”。

MyLib_MCP23008.h:

// I left out the defines for better reading

class MyLib_MCP23008 {
    public:
        MyLib_MCP23008(TwoWire *w = &Wire);
        void begin(uint8_t addr);
        void begin(void);

        void pinMode(uint8_t p, uint8_t d);
        void digitalWrite(uint8_t p, uint8_t d);
        void pullUp(uint8_t p, uint8_t d);
        uint8_t digitalRead(uint8_t p);
        uint8_t readGPIO(void);
        void writeGPIO(uint8_t);

    private:
        uint8_t i2caddr;
        uint8_t read8(uint8_t addr);
        void write8(uint8_t addr, uint8_t data);
        TwoWire *_wire;
};

在我的测试草图(mcp23008_test.ino)中:

TwoWire myWire(&sercom2, 4, 3);
MyLib_MCP23008 mcp23008(&myWire);
const int mcp_address = 0x20;

void setup()
{
    myWire.begin();
    pinPeripheral(4, PIO_SERCOM_ALT);
    pinPeripheral(3, PIO_SERCOM_ALT);

    // if I can find mcp23008: 
    myWire.beginTransmission(mcp_address);
    if (myWire.endTransmission() == 0) {
        mcp23008.begin();
    }

    // irrelevant setup stuff for pinMode / Serial etc.

    mcp23008.pinMode(mcpLED, OUTPUT);
}

void loop() {

    mcpLEDState = !mcpLEDState;
    mcp23008.digitalWrite(mcpLED, !mcpLEDState); // i2c LED alas nothing...
    digitalWrite(hbLED, mcpLEDState); // heartbeat: works fine

    // after this I scan the i2c wire 
    // using begin/endTransmission calls
    // and output it to Serial
    // result: works fine...

}

程序不会崩溃(我使用心跳 LED 并且串行输出继续工作,i2c 总线也保持响应(开始/结束)但连接到 mcp23008 的 LED 在库的 TwoWire 依赖注入版本中没有反应)。

我将尝试的一件事是在 i2c 总线上添加一个逻辑分析仪,看看会出现什么消息……但是由于“被黑”库解决方案确实可以正常工作,我猜想还有其他事情发生。

请注意,我有一个解决方法,即将所有库内容作为子 ino 文件直接包含在主草图中,这可行,但当然不是很可靠。

背景资料:

为了使事情易于管理,我首先关注 MCP23008 IC:我已将 LED 连接到其中一个引脚,向其写入低电平将使 LED 亮起(当我将其降至 3.3V 时),高电平将其关闭. 到目前为止一切顺利,如果我破解库以支持我的特定 i2c 端口并且这可以正常工作:

TwoWire myWire(&sercom2, 4, 3);

然后开始我做:

myWire.begin();
pinPeripheral(4, PIO_SERCOM_ALT);
pinPeripheral(3, PIO_SERCOM_ALT);

在库的 begin 方法中。

然后,我将库中的所有 Wire 引用替换为 myWire。

到目前为止,到目前为止一切都很好,我可以让 LED 从我的主草图等上闪烁。

所以这适用于我的基本情况,但它是一个非常丑陋的解决方案:您可能又想为 MCP23008 使用一些不同的端口。在我的情况下,我想使用多个 i2c 设备,因为它们都使用 myWire.begin(); 在他们的开始方法中,i2c 总线冻结等。

正如我之前所说:人们并不真正希望库本身做 myWire.begin 的东西(如果需要额外的 pinPeripheral 调用等):人们希望主草图这样做。

SparkFun Electronics 的 Nathan Seidle 写了一些关于 Arduino 库不灵活问题的好文章:https ://www.sparkfun.com/news/2194

但是他的解决方案和他基于他的解决方案的解决方案对我不起作用:我可以在总线上看到 MCP23008,但在使用修改后的库时它对我的命令没有反应。

我还查看了https://github.com/arduino/ArduinoCore-samd/blob/master/libraries/Wire/Wire.cpp/.h,因为它与我尝试完成的工作几乎相同,但比在 Wire等级。我没有看到我的代码与该示例有任何偏差,但查看自己的代码当然总是一个弱点。

更新

这种简约的实现确实有效:我使用了标准的 Wire | 主作家作为一个板上的基本草图和标准电线 | 另一块板上的从接收器。

我换了主人:

#include <Wire.h>
#include "GenericWireTest.h"

TwoWire myWire(&sercom3, 0, 1);
GenericWireTest genericWire(&myWire);

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  genericWire.begin();
  Serial.begin(115200);
}

byte x = 0;

void loop()
{
  genericWire.sendMessage(x);
  Serial.print("Wrote: x = ");
  Serial.println(x);
  x++;
  delay(500);
}

CPP 文件:

#include "Wire.h"
#include "GenericWireTest.h"

GenericWireTest::GenericWireTest(TwoWire *w)
{
  this->_wire = w;
}

void GenericWireTest::sendMessage(int x) {
  _wire->beginTransmission(4); // transmit to device #4
  _wire->write("x is ");        // sends five bytes
  _wire->write(x);              // sends one byte
  _wire->endTransmission();    // stop transmitting
}

void GenericWireTest::begin(uint8_t addr) {
  // nothing to do in this case...
  begin();
}

void GenericWireTest::begin(void) {
  _wire->begin();
}

H 文件:

#include "Wire.h"

class GenericWireTest {
    public:
        GenericWireTest(TwoWire *w = &Wire);
        void begin(uint8_t addr);
        void begin(void);

        void sendMessage(int x);

    private:
        uint8_t i2caddr;
        TwoWire *_wire;
};

这按预期工作......注意:我添加了 _wire->begin(); 为了可能混淆Wire通道,但即使那样它也不会混淆。

更新 2:

我实际上能够确认注入可以与其他 i2c 外围设备一起使用,包括 FXOS8700 加速度计/磁力计。所以它可能只是与 MCP 有关,或者我在某个地方犯了一个小错误。也许是时候深入研究 MCP 的数据表了。

更新 3: 也适用于 FXAS21002C,所以我猜这是一些与 MCP 相关的问题。我现在通过将 MCP 代码添加到我的主项目的 ino 文件中创建了一个解决方法,然后我可以毫无问题地使用依赖注入,不像我想要的那样整洁,但现在可行......

4

0 回答 0