通过传递类方法使用Wire.onRequest吗?

时间:2019-02-16 10:05:34

标签: c++ pointers arduino

我有一个可以在Arduino UNO上工作的Arduino草图,我正试图让Uno通过i2c连接与树莓派进行通信。 问题是使用 wire.h 库,其中的方法Wire.onRequest在我这样使用时可以正常工作。

#include <Wire.h>
#define COMM_DELAY 50
#define SLAVE_ADDRESS 0x04

int current_rule = 0;

void initI2c() {
  // initialize i2c as slave
  Wire.begin(SLAVE_ADDRESS);

  // define callbacks for i2c communication
  Wire.onReceive(receiveData);
}

// callback for received data
void receiveData(int byteCount) {
  while (Wire.available()) {
    current_rule = Wire.read();
  }
}

但是当我尝试使用类方法获得此确切结果时,出现错误: invalid use of non-static member function (将Wire.onRequest(this->receiveData)行标记为红色)

就像这样:


void (*funptr)();
typedef void (*Callback)(byte);
class Comm{
public:
  int callback_list_size = 0;
  bool option_debug;
  byte option_address;
  int option_comm_delay;
  void(*callback_list[256]);
  byte *rules;

  // function for receiving data. raspberry -> arduino
  // Whenever the master sends new data, this method will call the appropriate callback.
  void receiveData()
  {
    byte data;
    Serial.println("[INFO] Received new data from master");
    while (Wire.available())
    {
      data = Wire.read();
    }
    for (int i = 0; i < callback_list_size; i++)
    {
      if (rules[i] == data){
        funptr = callback_list[i];
        funptr();
        }
    }
  }

  // function for sending data. Called when raspberry request data. arduino -> raspberry
  // Whenever the master requests data, this method will be called. For now we don't need this but anyway.
  void sendData(int s)
  {
    if (option_debug)
      Serial.println("[INFO] Master requests data!");
  }

  /* Constructor that takes 3 parameters at max. Only the adress is mandatory others are optional and will be filled with default values
     :address - adress of slave(arduino) - Example 0x04
     :delay - a delay is needed because I2C clock is quite slow compared to the CPU clock - 50
     :debug - for debug purposes if true debug info will be sent to Serial interface - true/false 
  */
  Comm(byte address, int delay = 50, bool debug = false)
  {
    option_address = address;
    option_comm_delay = delay;
    option_debug = debug;
    if (debug)
      Serial.println("[INFO] Comm Object Created!");
  }

  // Function needs to be called to initialize the communication channel.
  void initI2c()
  {
    Wire.begin(option_address);
    Wire.onReceive(this->sendData);
    Wire.onRequest(this->receiveData);
    if (option_debug)
      Serial.println("[INFO] I2C channel initialized");
  }

  // Function to add new callback for a rule.
  // This function returns id of passed callback
  int addCallback(Callback func, byte rule)
  {
    callback_list_size++;
    // Enlarge rules array to keep 1 more byte
    byte *temp = new byte[callback_list_size];       // create new bigger array.
    for (int i = 0; i + 1 < callback_list_size; i++) // reason fo i+1 is if callback_list_size is 1 than this is the first initializition so we don't need any copying.
    {
      temp[i] = rules[i]; // copy rules to newer array.
    }
    delete[] rules; // free old array memory.
    rules = temp;   // now rules points to new array.
    callback_list[callback_list_size - 1] = &func;
    rules[callback_list_size - 1] = rule;
    return callback_list_size;
  }
};

Comm *i2c_comm;
void loop()
{
}
void setup()
{
  Serial.begin(9600);
  initI2C();
}


void initI2C()
{
  i2c_comm = new Comm(0x04, 50, true);
  i2c_comm->initI2c();

  //Callback Definitions
  i2c_comm->addCallback(&rule_1, 0x01);
      i2c_comm->addCallback(&rule_2, 0x02);
          i2c_comm->addCallback(&rule_3, 0x03);
              i2c_comm->addCallback(&rule_4, 0x04);
}

我还尝试将receiveData方法设为静态。 但是在这种情况下,我会出现如下错误: invalid use of member Com::callback_list_size in static member function

这对我来说很有意义,因为静态方法不会知道我在说哪个callback_list_size。

所以我对如何处理这样的问题感到很困惑?

1 个答案:

答案 0 :(得分:0)

您快到了。一般来说,在C ++中,您需要为回调函数传递静态类方法。

将方法更改为静态后收到的错误是预期的,因为您正在尝试访问 Comm 类的实例的成员,而该操作无法在一种没有“ this”的静态方法。

这里是要考虑的许多技术之一,但是请阅读SO帖子Using a C++ class member function as a C callback function

无论如何,这里的方法是利用指向实例的静态指针

class Comm {
private:
    static Comm* pSingletonInstance;

    static void OnReceiveHandler() {
        if (pSingletonInstance)
            pSingletonInstance->receiveData();
    }
    static void OnSendHandler(int s) {
        if (pSingletonInstance)
            pSingletonInstance->sendData(s);
    }
    void initI2c() {
        Comm::pSingletonInstance = this; // Assign the static singleton used in the static handlers.
        Wire.onReceive(Comm::OnSendHandler);
        Wire.onRequest(Comm::OnReceiveHandler);
        Wire.begin(option_address);
    }
}
// static initializer for the static member.
Comm* Comm::pSingletonInstance = 0;

同样有许多方法可以解决此问题,但以上方法很简单,很可能适合您的项目。如果需要管理Comm的多个实例,则必须做一些完全不同的事情。

祝你好运!