I2C запрашивает более одного байта

Я настроил Arduino Uno в качестве ведущего устройства I2C для ведомого устройства ATtiny85, используя библиотеки Wire (Master) и TinyWireS (Slave).

Связь работает хорошо. Я смог поддерживать правильную работу сети в течение нескольких часов. Однако я не смог разрешить ведущему устройству запрашивать у ведомого устройства более одного байта.

Используя приведенный ниже код, ведущий запрашивает у ведомого 4 байта, но в моей последовательной консоли я получаю только фактическое значение для первых байтов. Следующие три байта считывают 255.

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


Master (Arduino Uno)

#include <Wire.h>

#define I2C_MASTER_ADDR 0x04
#define I2C_SLAVE_ADDR 0x05

int pollInterval = 700;//Milliseconds
unsigned long lastPoll = 0;


void setup() 
{
  Wire.begin(I2C_MASTER_ADDR);  // join i2c bus
  Serial.begin(115200); 
  Serial.println("Setup complete");
}

/*
 * The main loop
 */
int i = 0;

void loop() 
{

  //Writing to the slave
  if( (millis()-lastPoll) > pollInterval)
  {
    Wire.beginTransmission(I2C_SLAVE_ADDR);
    Wire.write(0x01);//Register to start at
    switch(i)
    {
      case 0:
        Wire.write(255);
        Wire.write(0);
        Wire.write(0);
        i++;
        break;
      case 1:
        Wire.write(0);
        Wire.write(255);
        Wire.write(0);
        i++;
        break;
      case 2:
        Wire.write(0);
        Wire.write(0);
        Wire.write(255);
        i = 0;
        break;
    }
    Wire.endTransmission();

    delay(1);//Dont let the slave panic

    //Set the register pointer back to 0x01, preparing for a read
    Wire.beginTransmission(I2C_SLAVE_ADDR);
    Wire.write(0x00);//Register to start at
    Wire.endTransmission();
    delay(1);//Dont let the slave panic

    //Get values from the three registers up from 0x01
    Wire.requestFrom(I2C_SLAVE_ADDR, 4);//Request N bytes
    while (Wire.available())
    {
      uint8_t next_byte = Wire.read();
      Serial.print(next_byte);Serial.print(" ");    
    }
    Serial.println("\n");

    lastPoll = millis();
  }//End if time to poll again

}//End loop

Рабыня - ATtiny85

#include <EEPROM.h>
#include <TinyWireS.h>
#include <Bounce2.h>
#include <WS2812.h>
#ifdef __AVR__ //Which will be true for ATtiny85
  #include <avr/power.h>
#endif

#define I2C_SLAVE_DEFAULT_ADDR 0x05
#define BUTTON_DEBOUNCE 5//Debounce milliseconds

#define NEOPIXEL_PIN  1
#define BUTTON_PIN    3

#define NUMPIXELS     1

/*
 * I2C Registers
 * 
 * Register map:
 * 0x00 - Button state
 * 0x01 - led value red
 * 0x02 - led value green
 * 0x03 - led value blue
 * 
 * Total size: 4
 */
const byte reg_size = 4;
volatile uint16_t i2c_regs[reg_size];

/*
 * Internal variables
 */
cRGB value;
volatile boolean led_needs_update = false;
volatile byte reg_position;

/*
 * Initialize instances/classes
 */
Bounce button = Bounce();
WS2812 led(NUMPIXELS);

void setup() 
{
  //Start I2C
  //uint8_t _device_addr = EEPROM_DATA::get_device_addr();
  TinyWireS.begin(I2C_SLAVE_DEFAULT_ADDR);
  TinyWireS.onReceive(i2cReceiveEvent);
  TinyWireS.onRequest(i2cRequestEvent);

  //Start Led
  led.setOutput(NEOPIXEL_PIN);
  value.b = 255; value.g = 0; value.r = 0;
  led.set_crgb_at(0, value); //Set value at LED found at index 0
  led.sync(); // Sends the value to the LED

  //Start Button
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  button.attach(BUTTON_PIN);
  button.interval(BUTTON_DEBOUNCE);

}

void loop() 
{
  button.update();

  if(led_needs_update)
  {
    led_update();
    led_needs_update = false;
  }

  if(button.fell())
  {
    i2c_regs[0x00] = true;
  }
  if(button.rose())
  {
    i2c_regs[0x00] = false;
  }

  // This needs to be here for the TinyWireS lib
  TinyWireS_stop_check();

}

/*
 * I2C Handelers
 */
void i2cReceiveEvent(uint8_t howMany)
{
    if (howMany < 1)
    {
        return;// Sanity-check
    }

    reg_position = TinyWireS.receive();
    howMany--;
    if (!howMany)
    {
        return;// This write was only to set the buffer for next read
    }

    while(howMany--)
    {
        //Store the recieved data in the currently selected register
        i2c_regs[reg_position] = TinyWireS.receive();

        //Proceed to the next register
        reg_position++;
        if (reg_position >= reg_size)
        {
            reg_position = 0;
        }
    }
    led_needs_update = true;
}//End i2cReceiveEvent()

void i2cRequestEvent()
{
    //Send the value on the current register position
    TinyWireS.send(i2c_regs[reg_position]);

    // Increment the reg position on each read, and loop back to zero
    reg_position++;
    if (reg_position >= reg_size)
    {
        reg_position = 0;
    } 
}//End i2cRequestEvent

/*
 * Helper functions
 */
void led_update()
{
  cRGB val;
  val.r = i2c_regs[0x01];
  val.g = i2c_regs[0x02];
  val.b = i2c_regs[0x03];
  led.set_crgb_at(0, val);
  led.sync(); // Sends the value to the LED
}

, 👍1


1 ответ


1

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

Предполагая, что ваша библиотека TinyWire работает как обычный провод Arduino, вам нужно использовать форму с двумя аргументами метода записи:

size_t TwoWire::write(const uint8_t *data, size_t quantity)

Так что ваш обработчик, вероятно, выглядел бы так

void i2cRequestEvent()
{
    TinyWireS.write(i2c_regs, sizeof(i2c_regs));
}

Однако вам также потребуется изменить свое определение на 8-разрядный тип, а не на 16-разрядный, который у вас неожиданно появился

volatile uint8_t i2c_regs[reg_size];

Если вам действительно нужно использовать 16-разрядный тип, вам нужно либо удвоить размер передачи (поскольку оба процессора являются AVR, у вас не возникнет проблем с порядком байтов), либо сначала скопировать значения, которые вас интересуют, в байтовый буфер.

,

Спасибо за ваш ответ. Я обновил uint16_t до uint8_t, моя ошибка. TinyWireS использует следующую команду записи, принимая только один аргумент: void USI_TWI_S::send(uint8_t data){ // отправьте его обратно мастеру usiTwiTransmitByte(data); } Но насколько я понимаю, это на самом деле нормальное соглашение I2C: проводная библиотека должна запрашивать несколько байтов по одному за раз. Подчиненный отвечает одним байтом на каждый запрос, а затем переходит к следующему регистру, так что следующий запрошенный байт поступает из следующего регистра., @Dennis Hunink

Он может предложить запись одного аргумента, но это не сработает для вас. Проверьте его исходный код и посмотрите, предлагает ли он также запись с двумя аргументами - обычная проводная библиотека предлагает и то, и другое. Если нет, то вам, вероятно, придется изменить библиотеку. И нет, соглашение i2c заключается в том, чтобы запрашивать несколько байтов одновременно, что вы и делаете. Однако если вы напишете начальный регистр в предыдущей записи, то, я думаю, вы могли бы делать отдельные однобайтовые запросы для каждого регистра по очереди., @Chris Stratton

Привет, Крис, еще раз спасибо. Я мог найти только один аргумент записи. С точки зрения производительности, будет ли выполнение нескольких однобайтовых запросов замедлять работу (по сравнению с запросом wire.request N байтов)? Просто выясняю свои варианты, основываясь на вашем ответе/комментарии., @Dennis Hunink

Да, несколько однобайтовых запросов будут медленнее. Кроме того, он может давать значения, которые не согласуются друг с другом, но исходят из разных точек данных. Вы должны быть в состоянии создать многобайтовую запись, сравнив источник обычной проводной библиотеки, в которой он есть, с вашим, который, по-видимому, этого не делает, и используя его для записи одного - то есть выяснить различия между ними для однобайтового случая, а затем применить их к многобайтовому случаю.байт-код., @Chris Stratton

Мастер использует "нормальную" библиотеку проводов, но TinyWireS совсем не нормальная. Мастер определяет, сколько байтов будет считано. Ведомое устройство может посылать байт или нет. Если ведомое устройство ничего не делает, то данные станут 255. Не могли бы вы попробовать requestEvent с четырьмя вызовами на TinyWireS.send с фиксированными номерами. Например, TinyWireS.send('A') и 'B', и 'C', и 'D', или с цифрами., @Jot