我正在使用多个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在库)。
我要尝试的一件事是在i2c总线上添加一个逻辑分析仪,看看消息是什么...但是随着“被黑客入侵”的库解决方案确实可以正常工作,我想还有其他事情发生了。
请注意,我有一个解决方法,它可以将所有库内容作为子ino文件直接包含在主草图中,虽然可以,但是当然不是很牢固。
背景信息:
为了使事情易于管理,我首先关注MCP23008 IC:我已经将LED连接到其中一个引脚,向其写入LOW将使LED点亮(当我将其吸收至3.3V时),而HIGH将把它关掉。到目前为止,如果我破解该库以支持我的特定i2c端口,那么到目前为止一切正常,
TwoWire myWire(&sercom2, 4, 3);
然后我开始:
myWire.begin();
pinPeripheral(4, PIO_SERCOM_ALT);
pinPeripheral(3, PIO_SERCOM_ALT);
在库的begin方法内。
然后我用myWire替换库中的所有Wire引用。
到目前为止,到目前为止,我还可以让LED从我的主要草图中闪烁。
所以这对于我的基本情况是有效的,但它是一个非常丑陋的解决方案:您可能再次要为MCP23008使用一些其他端口。就我而言,我想使用多个i2c设备,因为它们都使用myWire.begin();。在他们的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相关的问题。我现在通过在主项目的ino文件中添加MCP代码来创建一种变通方法,然后我可以使用依赖项注入而不会出现任何问题,虽然不是我想要的那么整洁,但现在可以使用...