Multiple Wire.write() не работает для Arduino Nano I2C

Код для мастера

#include <Wire.h>

void setup() 
{
  Wire.begin();

  Serial.begin(9600); 
}

void loop()    
{      
    Wire.beginTransmission(9);
     
    Wire.write('c');   
    Serial.println('c');             
    Wire.write('a');   
    Serial.println('a');             
    Wire.endTransmission();
}

В моем случае Wire.write('c') выполняет задачу, которая назначена 'c' в slave board. Затем его печать "c" в последовательном мониторе, как и предполагалось. Но, Wire.write('a') линия игнорируется, и ничего не происходит на невольничьей плате. Но опять же, это печать " а " в последовательном мониторе в соответствии с кодом. Почему этим Wire.write('a') пренебрегают? Обе платы-Arduino Nano.

Код для ведомого устройства

#include <Wire.h>
#define RED 8
#define GREEN 10
int x = 0;

void setup() {
  pinMode (RED, OUTPUT);
  pinMode (GREEN, OUTPUT);
  Wire.begin(9);
  Wire.onReceive(receiveEvent);
}

void receiveEvent(int bytes) {
  x = Wire.read();    // read one character from the I2C
}

void loop() {
    if (x == 'a') {
        digitalWrite(RED, HIGH);
        digitalWrite(GREEN, LOW);
    }
    if (x == 'b') {
        digitalWrite(RED, LOW);
        digitalWrite(GREEN, HIGH);
    }
    if (x == 'c') {
        digitalWrite(RED, HIGH);
        digitalWrite(GREEN, HIGH);
    }
    if (x == 'd') {
        digitalWrite(RED, LOW);
        digitalWrite(GREEN, LOW);
    }
}

, 👍1

Обсуждение

Что такое ваша подчиненная плата? Это еще один Arduino? Если да, вам также нужно показать нам код для этого. Возможно, вы неправильно понимаете, как работает библиотека "Провод". Wire.write() ничего не отправляет по линиям I2C. Он только помещает данные во внутренний буфер библиотеки. Передача затем выполняется с помощью " Wire.endTransmission(), который отправляет содержимое внутреннего буфера в виде одной передачи I2C. Я предполагаю, что ваш подчиненный читает только первый байт каждой передачи, таким образом, игнорируя второй байт (a"), @chrisl

Как вы для вас ответите, я также добавил рабский код... пожалуйста, посмотрите его.. еще раз спасибо, @Akshay

"передача затем выполняется по проводу.endTransmission()" эта строка произвела на меня впечатление, я изменил свой мастер-код, и теперь он работает идеально.. спасибо, чувак, @Akshay

Убедитесь, что вы отметили принятый ответ и проголосовали за него, так как он содержит то, что вы сочли полезным., @timemage


1 ответ


7

Важная информация: Wire.write() ничего не отправляет по линиям I2C. Он просто помещает данные во внутренний буфер библиотеки. Затем фактическая передача осуществляется по проводу.endTransmission().

I2C-это упакованный протокол передачи. Это означает, что передача осуществляется в ограниченных пакетах данных. В вашем мастер-коде вы вызываете Wire.write() два раза перед Wire.endTransmission(). Это означает, что вы отправляете 1 пакет с 2 байтами данных в нем.

Теперь перейдем к вашему подчиненному коду: Вы используете функцию обратного вызова для события onReceive. Эта функция обратного вызова вызывается один раз за передачу. Там вы можете прочитать все данные этой передачи. После этого данные выбрасываются (я думаю, что на самом деле они удаляются, когда принимается новая передача). Вы читаете только один раз с помощью Wire.read(). Таким образом, вы всегда читаете только первый байт каждой передачи, который всегда является буквой "с".

Что делать знать? Это зависит от того, как вы хотите, чтобы код вел себя. Поскольку вы устанавливаете 2 светодиода на основе полученного байта, было бы лучше отправлять только 1 байт передачи. Для этого вы можете изменить свой мастер-код. Например, вот так:

#include <Wire.h>

void setup() 
{
  Wire.begin();

  Serial.begin(9600); 
}

void loop()    
{
    // Отправить команду c
    Wire.beginTransmission(9);
    Wire.write('c');
    Wire.endTransmission();
    
    // Подождите 1 секунду, чтобы мы могли видеть соответствующее состояние светодиодов
    delay(1000);
 
    // Отправить команду a
    Wire.beginTransmission(9);        
    Wire.write('a');       
    Wire.endTransmission();

    // Подождите 1 секунду, чтобы мы могли видеть соответствующее состояние светодиодов
    delay(1000);
}

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

Примечание: На самом деле вам нужно объявить переменную x как volatile, потому что обратный вызов onReceive вызывается из ISR. С помощью этого ключевого слова вы сообщаете компилятору, что x может измениться в любое время (по ISR соответствующего прерывания I2C). Возможно, ваш текущий код работает, потому что компилятор был достаточно умен, чтобы увидеть это, но в целом вы всегда должны использовать volatile с каждой переменной, которая может измениться в ISR. Декларация тогда выглядит так:

volatile int x = 0;

И чтобы обрабатывать данные ISR действительно правильно, вам нужно будет сделать использование атомарным, потому что ISR может произойти в середине сравнения для операторов if. Int-это два байта, поэтому первый байт может быть обновлен, а второй - нет. Это повредит ваши данные. В вашем случае есть два способа справиться с этим:

  • Сделайте чтение x атомарным: Обычно вы поворачиваете прерывания, считываете изменчивые переменные в локальные переменные (как копию), а затем снова включаете прерывания. Таким образом, прерывание не может повредить данные в переменной во время чтения. Затем дальнейшие вычисления/сравнения будут выполняться с помощью локальной переменной.

  • Используйте один байт в качестве энергозависимых данных: Описанная проблема не применима к переменным, которые используют только 1 байт (так как на 8-битных ардуино, таких как Nano, чтение одного байта не может быть прервано). Поэтому просто сделайте x типа char (или uint8_t, или byte).

Вы должны использовать второй вариант.

,

Оператор использует два светодиода для отображения состояния, поэтому он должен видеть, какое из полученных писем было последним., @PMF

@PMF Да, он/она использует 2 светодиода. Но есть 4 состояния, каждое из которых имеет свой шаблон для этих 2 светодиодов. Для каждой из допустимых команд записываются оба состояния светодиодов. Таким образом, он/она может видеть только последнюю полученную команду. Если он/она отправляет две команды без какой-либо задержки между ними, первая команда отображается только очень короткое время (скорее всего, слишком короткое, чтобы ее увидеть). Таким образом, задержка, @chrisl