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
}
@Dennis Hunink, 👍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, у вас не возникнет проблем с порядком байтов), либо сначала скопировать значения, которые вас интересуют, в байтовый буфер.
- Всегда 255-ответ в I2C между ATTiny85 (8 МГц) и Arduino Uno
- Что такое "tws_delay" и как его использовать? (с ATTiny85)
- Сколько читаемого текста может поместиться в ATTINY85? Возможность прокрутки буклета на 0,91-дюймовом OLED-дисплее
- Отправка и получение различных типов данных через I2C в Arduino
- Как работают функции вне цикла void?
- Как отображать переменные на 0,96-дюймовом OLED-дисплее с библиотекой u8glib?
- OVF в последовательном мониторе вместо данных
- ЖК-дисплей I2C отображает странные символы
Спасибо за ваш ответ. Я обновил 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