Как отправить несколько байтов с помощью Wire без копирования

Когда мне нужно отправить через Wire несколько байтов, например, длинное целое число, я преобразую его в массив байтов и указываю длину

long int i;
Wire.write((byte*)&i, 4);

Но если я хочу отправить байты из более чем одной переменной, мне нужно создать буфер

byte i;
byte j;
byte message[2] = {i, j};
Wire.write(message, 2);

Если я хочу отправить их без копирования данных, я могу поместить их в структуру

struct message {
  byte i;
  byte j;
};
Wire.write((byte*)&message, 2);

Но это потребует от меня рефакторинга существующего кода.

Есть ли какое-нибудь волшебство c, которое я могу использовать для отправки байтов из разных мест моего кода по Wire без их копирования в буфер и без рефакторинга кода?

, 👍0


4 ответа


Лучший ответ:

4

Нет, эта функция перезаписи Wire.write() предназначена для буферов, т. е. связанных пространств в памяти, которые можно покрыть, просто увеличив буфер. Это не относится к отдельным переменным.

Но возникает вопрос, почему вы вообще хотите сделать это одним вызовом Wire.write()? Эта функция не осуществляет связь по линиям I2C, она просто записывает данные во внутренний буфер библиотеки. Данные фактически отправляются в Wire.endTransmission() или в прерываниях onRequest в фоновом режиме (в зависимости от того, где у вас есть эти операторы записи). Таким образом, у вас уже есть буфер и вам не нужен еще один. Просто вызовите Wire.write() несколько раз, по одному разу для каждой переменной. Это приведет к последовательному накоплению данных во внутреннем буфере.

,

Что ж, мое устройство работает в ведомом режиме, и мой Wire.write() вызывается (возможно, не напрямую, а по линии) из обработчика onRequest. В своих экспериментах я обнаружил, что как только я вызываю Wire.write(), я не могу вызвать его снова, иначе я получаю мусор. Можно ли использовать множественную запись в моем случае?, @user88434

Я докопался до сути! Но это было слишком долго для комментария, поэтому я добавил ответ. Еще раз спасибо!, @user88434


1
// Автор: Ник Гэммон
// май 2012 г.

template <typename T> unsigned int I2C_writeAnything (const T& value)
  {
  Wire.write((byte *) &value, sizeof (value));
  return sizeof (value);
  }  // конец I2C_writeAnything

template <typename T> unsigned int I2C_readAnything(T& value)
  {
    byte * p = (byte*) &value;
    unsigned int i;
    for (i = 0; i < sizeof value; i++)
          *p++ = Wire.read();
    return i;
  }  // конец I2C_readAnything

Тогда в теле кода вы можете:

I2C_writeAnything(message);

Точно так же ваш другой конец может I2C_readAnything.

Необходим небольшой рефакторинг, изменение вызова функции.

,

Я думаю, что в этом коде могут быть проблемы с заполнением структуры или несоответствием порядков байтов, если вам не повезло, но, возможно, в большинстве случаев это работает., @Emil


1

Нет, нельзя.

C не гарантирует каких-либо свойств размещения переменных. Даже если вы записываете одно определение переменной сразу после другого, обеим переменным могут быть назначены адреса, между которыми находится другая память.

Итак, если вам нужен "пакет" отправленный через Wire, который содержит значения нескольких переменных, вам необходимо заполнить буфер этими значениями.

Несмотря на то, что C поддерживает порядок членов структуры, имейте в виду, что в зависимости от целевой архитектуры между членами могут быть промежутки для выравнивания. Однако некоторые компиляторы имеют языковые расширения для решения этой проблемы.

,

1

Как сказал @chrisl, существует внутренний буфер, поэтому лучшее, что я могу сделать, это просто использовать несколько вызовов Wire.write, но в моем случае это не сработало. Я получил только последнее записанное значение или пустые данные.

Я просмотрел исходный код библиотеки Wire и обнаружил, что версия в github (на момент написания этой статьи) позволяет использовать множественную запись путем добавления в буфер:

// устанавливаем длину и копируем данные в буфер tx
for(i = 0; i < length; ++i){
  twi_txBuffer[twi_txBufferLength+i] = data[i];
}
twi_txBufferLength += length;

Но тот, который я установил локально, выглядит так:

// устанавливаем длину и копируем данные в буфер tx
twi_txBufferLength = length;
for(i = 0; i < length; ++i){
  twi_txBuffer[i] = data[i];
}

Это многое объясняет.

Если у кого-то еще есть эта проблема, вам просто нужно обновить ее.

Мне даже было лень обновляться, поэтому я просто заменил функцию. Наверное, не лучшее решение, но оно работает!

Спасибо!

,

исправлено 6 лет назад, @Juraj

@Juraj, да, мне стало любопытно, и я тоже проверил. Наверное, кто-то еще был таким же ленивым, как я, и не обновлялся! Я не могу вспомнить, откуда я взял свой arduino sdk, но это было около 2 лет назад, так что определенно было много времени для обновлений, лол., @user88434