Указатель C ++ опасен? Как мне успешно создать более универсальную библиотеку i2c / TWI для Arduino? - PullRequest
0 голосов
/ 11 января 2019

Я работаю над проектом, в котором я использую несколько микросхем / датчиков i2c. Adafruit имеет очень хорошие базовые библиотеки для отдельных микросхем, и это очень удобно (экономит часы на копании таблиц данных). Но их библиотеки негибки. Я пытался облегчить это, применяя инъекцию зависимостей.

Таким образом, решение состоит в том, чтобы использовать экземпляр 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...

}

Программа не дает сбоя (я использую индикатор сердцебиения, а последовательный выход продолжает работать, шина i2c также остается отзывчивой (начало / конец), но индикатор подключенного к mcp23008 не реагирует в версии инъекции зависимостей TwoWire библиотека).

Я попытаюсь добавить логический анализатор к шине i2c и посмотреть, какое сообщение приходит ... Но поскольку решение «взломанной» библиотеки работает без проблем, я думаю, что-то еще происходит.

Примечание. У меня есть обходной путь, который включает все библиотечные материалы непосредственно в основной эскиз в виде вложенных ino-файлов, которые работают, но, конечно, не очень твердо.

Справочная информация:

Чтобы сделать все управляемым, я сначала сфокусировался на микросхеме MCP23008: я подключил светодиод к одному из выводов, и, записав НИЗКИЙ, он включится (как я опустил до 3,3 В), и ВЫСОКИЙ выключи. Пока все хорошо, если я взломаю библиотеку для поддержки моего конкретного порта i2c, и это будет работать без проблем:

TwoWire myWire(&sercom2, 4, 3);

Затем я начинаю:

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

внутри метода begin библиотеки.

Затем я заменяю все ссылки на провода в библиотеке на myWire.

Опять же, пока этот момент пока хорош, я могу позволить светодиоду мигать на моем основном наброске и т. Д.

Так что это работает для моего основного случая, но это очень уродливое решение: у вас снова может быть какой-то другой порт, который вы хотите использовать для своего MCP23008. И в моем случае я хочу использовать более одного устройства i2c, и как все они делают myWire.begin (); в их начальном методе шина i2c зависает и т. д.

И, как я сказал ранее: на самом деле не нужно, чтобы сама библиотека выполняла работу myWire.begin (и, если необходимо, дополнительные вызовы pinPeripheral и т. Д.): Кто-то хочет, чтобы основной эскиз делал это.

Натан Сейдл из SparkFun Electronics написал прекрасную статью о негибкости проблемы библиотек 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 (); для того, чтобы запутать проводной канал, но даже тогда он не запутается.

Обновление 2:

Я действительно смог подтвердить, что впрыск работает с другими периферийными устройствами i2c, включая акселерометр / магнитометр FXOS8700. Так что, возможно, это просто связано с MCP, или я где-то допустил небольшую ошибку. Может быть, пришло время углубиться в таблицу данных MCP.

Обновление 3: Также отлично работает для FXAS21002C, поэтому я думаю, что это проблема, связанная с MCP. На данный момент я создал обходной путь, добавив код MCP в файл ino в своем основном проекте, после чего я мог бы использовать внедрение зависимостей без каких-либо проблем, не так аккуратно, как хотелось бы, но пока работоспособно ...

...