I2C управляем реле и отсылаем статус

beliy123
beliy123 аватар
Offline
Зарегистрирован: 25.05.2018

Добрый день. Прошу помощи у знатаков. Написал скетч для управления реле в сети I2C. В кчестве мастера: Arduino UNO, а в качестве слейва: Arduino Mega 2560 R3.

По задумке: Мастер отправлдяет слеву значения Relay_  для каждого реле (0 или 1) - это приказ включить или выключить. Слев принимает значения в массив REG_MASSIVE[] , переключает выходы в необходимое состояние и сохраняет статус своих выходов в переменную в массив REG_Array[]. Эти значения читает мастер и сохраняет в переменную ReversSlave__. 

Результат диагностики из сериал Мастер: 

Relay Master start.... (1-1-1-0) - это значение меняеться т. е правильно

Relay ReversSlave....  (0-0-1-1) - а это значение не меняеться!!! 

Результат диагностики из сериал Слейв:

Relay Master....  (0-0-232-3) -- здес х-рень какаето

Relay Slave (Status Array)....  (0-0-1-1) - совпадает с тем что принимает мастер но не правильно с приказом

Помогите разобраться. Пример брал отсюда https://lesson.iarduino.ru/page/urok-26-3-soedinyaem-dve-arduino-po-shin...

Библиатеку брал http://iarduino.ru/file/254.html

//Мастер читает сосояние реле и отправляет приказ на включение реле

#include <Wire.h>
#include <iarduino_I2C_connect.h> 
iarduino_I2C_connect I2C2; // объявляем переменную для работы c библиотекой iarduino_I2C_connect

//byte x = 0;
//char c = 0;

//bool  Relay[4]; //массив и 5 значсений (0.1.2...4)уставки реле их отправляет мастер
//byte ReversSlave[4];  //массив и 5 значсений их принимает мастер от слейва

byte Relay1  = 0;  // переменная для хранения уставки, которое пошлет мастер для выхода Slave
byte Relay2  = 0;
byte Relay3  = 0;
byte Relay4  = 0;
byte ReversSlaveR1  = 0;  // переменная для чтения состояния выхода Slave
byte ReversSlaveR2  = 0;
byte ReversSlaveR3  = 0;
byte ReversSlaveR4  = 0;


//int  VAR_Potentiometer = 0;   //// переменная для чтения состояния Аналог. входа Slave

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() 
{
  //Считываем данные:
  ReversSlaveR1   = I2C2.readByte(8,0);  // Считываем состояние выходов ведомого (адрес ведомого 8, номер регистра 0)
  ReversSlaveR2   = I2C2.readByte(8,1);
  ReversSlaveR3   = I2C2.readByte(8,2);
  ReversSlaveR4   = I2C2.readByte(8,3);
  
//  VAR_Potentiometer  = I2C2.readByte(0x02,1)<<8;      // Считываем старший байт значения потенциометра ведомого (адрес ведомого 0x02, номер регистра 1), сдвигаем полученный байт на 8 бит влево, т.к. он старший
//  VAR_Potentiometer += I2C2.readByte(0x02,2);         // Считываем младший байт значения потенциометра ведомого (адрес ведомого 0x02, номер регистра 2), добавляя его значение к ранее полученному старшему байту
 
 //Отправляем данные:
 // Временно сделаем случайные значения 0 или 1, 
 //потом данные значения заменить на значения из блютуз или Ethernet устройства
Relay1= random (2);
Relay2= random (2);
Relay3= random (2);
Relay4= random (2);

// если нужно проверить конкретные значения
//Relay1= 1;
//Relay2= 1;
//Relay3= 0;
//Relay4= 1;

// Отправляем приказ ведомому (адрес ведомого 8, номер регистра 6, состояние выхода)
  I2C2.writeByte(8,6,Relay1); 
  I2C2.writeByte(8,7,Relay2);           
  I2C2.writeByte(8,8,Relay3);
  I2C2.writeByte(8,9,Relay4);
  
  // ReversSlave[0]= Wire.read();
   // x = Wire.read(ReversSlave[3]);
   // c = Wire.read(); // сохраняем приятый байт как символ в переменную "с"
   
//Диагностика   что пришло от слейва 
    Serial.println("Relay ReversSlave...."); 
    Serial.println(ReversSlaveR1);
    Serial.println(ReversSlaveR2);
    Serial.println(ReversSlaveR3);
    Serial.println(ReversSlaveR4);
    Serial.println("===================");
    delay (1000);
    
  //Диагностика   что отправляем слейву 
   
  Serial.println("Relay Master start....");
  Serial.println(Relay1);
  Serial.println(Relay2);
  Serial.println(Relay3);
  Serial.println(Relay4);
  Serial.println("..........");
 delay (5000);

}


Код для слева

// Слейв принимает запрос от мастера и отвечает ему.
//Адреса слейв устройств по расположению в шкафу

////////
//8//9//
////10//
///////
// этот скетч для 8 адреса

#include <Wire.h>
#include <iarduino_I2C_connect.h>  // подключаем библиотеку для соединения arduino по шине I2C
// Объявляем переменные и константы:
iarduino_I2C_connect I2C2;         
//const byte PIN_LED1 = 13;  
int LED1 = 26;
int LED2 = 27;
int LED3 = 28;
int LED4 = 29;
byte REG_MASSIVE[4]; // Массив для хранения указаний от мастера
byte  REG_Array[4]; // Массив для хранения состояние выходов

void setup() {
  Wire.begin(8); // инициализируем i2c сеть с адресом 8
  Serial.begin(9600);
I2C2.begin(REG_MASSIVE);
I2C2.begin(REG_Array);  
//pinMode(PIN_LED1, OUTPUT);

  pinMode(LED1, OUTPUT); 
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
 

  digitalWrite(LED1,HIGH );  
  digitalWrite(LED2,HIGH ); 
  digitalWrite(LED3,HIGH ); 
  digitalWrite(LED4,HIGH ); 
 //delay(5000);
         
}

void loop() {
 // delay(1000);
// digitalWrite(PIN_LED1, REG_MassSlave [0]); 

//Включаем выход в соответствии со значениями в массиве  
digitalWrite(LED1,REG_MASSIVE[6]);
digitalWrite(LED2,REG_MASSIVE[7]);
digitalWrite(LED3,REG_MASSIVE[8]);
digitalWrite(LED4,REG_MASSIVE[9]);

//Диагностика что пришло в массив
Serial.println("Relay Master...."); 
Serial.println(REG_MASSIVE[6]);
Serial.println(REG_MASSIVE[7]);
Serial.println(REG_MASSIVE[8]);
Serial.println(REG_MASSIVE[9]);
Serial.println("..........");
//delay (1000);

Сохраняем состояние выходов в массив, их должен прочитать мастер 
REG_Array[0] = digitalRead(LED1); 
REG_Array[1] = digitalRead(LED2); 
REG_Array[2] = digitalRead(LED3); 
REG_Array[3] = digitalRead(LED4); 

//Диакностика что сохранили в массив состояние выходов
 Serial.println("Relay Slave (Status Array)...."); 
Serial.println(REG_Array[0]);
Serial.println(REG_Array[1]);
Serial.println(REG_Array[2]);
Serial.println(REG_Array[3]);
Serial.println("==========");
/*
Serial.println("Relay Slave (LED)....");
Serial.println(digitalRead(LED1)); 
Serial.println(digitalRead(LED1)); 
Serial.println(digitalRead(LED1)); 
Serial.println(digitalRead(LED1)); 
Serial.println("+++++++++++");
*/
delay (2000);

}

 

redsunset
Offline
Зарегистрирован: 12.09.2018

Доброго времени суток, «beliy123».

Вы в коде мастера считываете данные из регистров 0,1,2,3 и записываете в регистры 6,7,8,9 - это уже не понятно! Вам не нравятся цифры 4 и 5 ?

А в коде слейва вообще наворотили бед:

Во первых библиотека «iarduino_I2C_connect» позволяет открыть доступ только к одному массиву! Каждый элемент которого является как бы регистром ведомого, а Вы в строках 25 и 26 открываете доступ к двум массивам (REG_MASSIVE и REG_Array), значит библиотека будет работать только с последним (REG_Array), тоесть мастер будет принимать данные, но записывать их будет за пределы массива REG_Array.

И во вторых Вы объявляете массив byte REG_MASSIVE[4] значит в нём 4 элемента (19 строка скетча), а в строках 48-59 пытаетесь записать данные в элементы 6-9 это опять за пределами массива REG_MASSIVE.

beliy123
beliy123 аватар
Offline
Зарегистрирован: 25.05.2018

redsunset пишет:

Доброго времени суток, «beliy123».

Вы в коде мастера считываете данные из регистров 0,1,2,3 и записываете в регистры 6,7,8,9 - это уже не понятно! Вам не нравятся цифры 4 и 5 ?

Цифры 4 и 5 случайно пропустил. Здесь попытка хранить в регистрах 0,1,2,3 - статус выходов слейва, а в регистрах 6,7,8,9 - приказ что с этими выходами сделать. Таким образом с регистров 0,1,2,3 можно получить обратную связь, отработал ли слэйв или нет.

redsunset пишет:

А в коде слейва вообще наворотили бед:

Во первых библиотека «iarduino_I2C_connect» позволяет открыть доступ только к одному массиву! Каждый элемент которого является как бы регистром ведомого, а Вы в строках 25 и 26 открываете доступ к двум массивам (REG_MASSIVE и REG_Array), значит библиотека будет работать только с последним (REG_Array), тоесть мастер будет принимать данные, но записывать их будет за пределы массива REG_Array.

Тут видимо моя ошибка. Не знал что библиотека может работать только с одним массивом. 

redsunset пишет:

И во вторых Вы объявляете массив byte REG_MASSIVE[4] значит в нём 4 элемента (19 строка скетча), а в строках 48-59 пытаетесь записать данные в элементы 6-9 это опять за пределами массива REG_MASSIVE.

т. е нужно писать и читать в один массив , но в разные регистры?

redsunset
Offline
Зарегистрирован: 12.09.2018

Да! Все правильно!

Только уточню, не один массив и разные регистры, а номер элемента массива совпадает с номером регистра. Просто слейву доступен массив, а мастеру регистры, но данные и там и там одинаковые.

Библиотека превращает массив типа byte в то, что мастер видит как регистры. Один регистр занимает 1 байт и один элемент массива занимает 1 байт.

Получается что если вы сделали доступным массив REG_Array, то элемент REG_Array[0] для мастера будет регистром №0, элемент REG_Array[1] для мастера будет регистром №1, ... , элемент REG_Array[9] для мастера будет регистром №9, и так далее.

Но если у вас 10 элементов массива (от REG_Array[0] до REG_Array[9]), то и объявлять этот массив надо так:

byte REG_Array[10]; // Объявляем массив из 10 элементов, а мастеру будут доступны 10 регистров.

 

strarbit
Offline
Зарегистрирован: 12.06.2016

redsunset пишет:
Библиотека превращает массив типа byte в то, что мастер видит как регистры. Один регистр занимает 1 байт и один элемент массива занимает 1 байт.

реально должно 1 байт == 8 пины регистр! библиотека плохая

redsunset
Offline
Зарегистрирован: 12.09.2018

strarbit, Моя ты понимать нет.

Попробуйте переформулировать Ваш комментарий, чтоб его можно было понять.

Какие пины? библиотека превращает массив однобайтных элементов в однобайтные регистры которые доступны мастеру по шине I2C. Шина I2C использует 2 пина (2 линии) для связи.

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

redsunset, что Вы подразумеваете под словом "регистр"?

bwn
Offline
Зарегистрирован: 25.08.2014

redsunset пишет:

strarbit, Моя ты понимать нет.

Попробуйте переформулировать Ваш комментарий, чтоб его можно было понять.

Какие пины? библиотека превращает массив однобайтных элементов в однобайтные регистры которые доступны мастеру по шине I2C. Шина I2C использует 2 пина (2 линии) для связи.

Дружок, девушка вообще то с Тайланда, делай скидку на трудности перевода. А донести она пытается до тебя, что в одном байте восемь бит, а не один.

redsunset
Offline
Зарегистрирован: 12.09.2018

bwn, спаибо что перевел, только где в тексте выше был хотя бы намек, что в одном байте, одном элементе массива, или одном регистре, всего один бит?

b707
Offline
Зарегистрирован: 26.05.2017

redsunset пишет:

где в тексте выше был хотя бы намек, что в одном байте, одном элементе массива, или одном регистре, всего один бит?

в последней строке сообщения #3 - вот. цитирую:

>>> byte REG_Array[10]; // Объявляем массив из 10 элементов, а мастеру будут доступны 10 регистров.

Из нее явно следует. что для тебя байты и регистры одно и тоже.

Гриша
Offline
Зарегистрирован: 27.04.2014

ну с тем что в одном байте используется один бит смириться можно. Ну назвать байты регистрами в области видимости слейва мастером (если я правильно понял) тоже можно, а вот понять очень сложно. Там на выходе один регистр, вот только данные в него толкаются разные.

на кой ляд катать столько строк на этапе дебага, может начать с одного реле?  Разделить 1 массив на 2 части - что писать и что записано.

да для такого кол-ва реле :))) одного байта в избытке.

redsunset
Offline
Зарегистрирован: 12.09.2018

b707, молодец теперь прогуляйся по просторам интернета и глянь, сколько БИТ доступно в одном регистре по стандартной реалицации шины I2C. Надеюсь ты не удивишься что их там будет 8, что равно одному байту!

redsunset
Offline
Зарегистрирован: 12.09.2018

Гриша, не миритесь с тем что в одном байте используется 1 бит, это НЕ ТАК!!! Поповоду того что мастер видит БАЙТЫ массива слейва как регистры, Вы поняли правильно!!! Почитайте описание библиотеки! Где Вы видели что не шине I2C слейв использует один регистр в который толкаются разные данные? Вы в курсе что мастер на шине I2C отправляет слейву адрес регистра с которым он желает работать (читать/записывать)? Регистров у слейва может быть 256 штук с разными данными в каждом?

sadman41
Offline
Зарегистрирован: 19.10.2016

Регистр - это, фактически, адрес. Прочитаешь, начиная с него, 3 байта - получишь 24 бита в регистре. 

Спецификация шины вообще называет register address - "some additional data".

redsunset
Offline
Зарегистрирован: 12.09.2018

sadman41, Точно, но не совсем! У каждого регистра есть свой адрес. То что написали Вы - называется пакетным чтением. Если указать адрес регистра, а прочитать не 1 байт из него а 3 байта, то вы прочитаете не 24 бита из одного регистра (адрес которого указали), а три байта из ТРЁХ регистров (24 бита из трёх регистров) где первым является регистр адрес которого Вы указали.

redsunset
Offline
Зарегистрирован: 12.09.2018

Предположим у слейва есть 5 регистров с адресами 0,1,2,3,4.

Каждый регистр хранит один байт данных (8 бит).

Предположим, что в регистре c адресом 0 хранится значение 0x01, в регистре с адресом 1 хранится значение 0x23, в регистре с адресом 2 хранится значение 0x45, в регистре с адресом 3 хранится значение 0x67 и в регистре с адресом 4 хранится значение 0x89.

Тогда если прочитать один байт из регистра с адресом 1, мы получим значение 0x23.

А если указать что мы хотим прочитать данные из регистра с адресом 1, но прочитать не один, а три байта, то мы получим байты со значениями 0x23, 0x45, 0x67. Тоесть фактически мы прочитали не один, а сразу три регистра слейва (хотя и указали вего один адрес).

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну, мне-то вобщем, все равно что я там прочту - регистры или не регистры. Есть у датчика метрика 32-битная, я прочту четыре байта начиная с определенного регистра. Зачем мне в этом случае побайтово (порегистрово, каждый в своей транзакции) - слабо себе представляю.

 

redsunset
Offline
Зарегистрирован: 12.09.2018

sadman41, Так посмотрите название темы.

Если Вы в скетч Arduino подключите библиотеку «iarduino_I2C_connect», создадите массив byte массив[5] = {0x01, 0x23, 0x45, 0x67, 0x89}; и дадите к нему доступ, то получите из этой Arduino устройство, которое другие мастера (в т.ч. и другие Arduino) будут видеть как обычный слейв на шине I2C с 5 регистрами.

При этом Вы сможете читать и менять данные как в массиве (в скетче той Arduino которая считается слейвом), так и любым мастером обращаясь к элементам этого массива как к регистрам слейва. Ну и разумеется массив может содержать не 5 элементов, а все 255.

 

b707
Offline
Зарегистрирован: 26.05.2017

redsunset пишет:

А если указать что мы хотим прочитать данные из регистра с адресом 1, но прочитать не один, а три байта, то мы получим байты со значениями 0x23, 0x45, 0x67. Тоесть фактически мы прочитали не один, а сразу три регистра слейва (хотя и указали вего один адрес).

Простите, вы пишете формально правильно, но явно ничего не понимаете в том, что сами пишете :)

Если вам нужно переслать по шине значение типа long длиной 4 байта - по вашему выходит, что его сначала нужно разбить на байты и распихать в 4 регистра, чтобы потом на приемной стороне снова собрать из 4 байт тип long?

Внимательно прочитайте то, что пишет sadman41 -через один регистр можно легко пересылать многобайтные посылки, не знамаясь онанизмом с разбиением данных на байты...

 

redsunset
Offline
Зарегистрирован: 12.09.2018

Просто понимая что каждый элемент массива станет для мастера адресом регистра (массив[0] - регистр с адресом 0, массив[1] - регистр с адресом 1, массив[2] - регистр с адресом 2) Вам легче будет создать из Arduino устройство-слейв на шине I2C.

b707
Offline
Зарегистрирован: 26.05.2017

redsunset пишет:

Просто понимая что каждый элемент массива станет для мастера адресом регистра (массив[0] - регистр с адресом 0, массив[1] - регистр с адресом 1, массив[2] - регистр с адресом 2) Вам легче будет создать из Arduino устройство-слейв на шине I2C.

сделайте еще один шаг и поймите, что элементы этого массива не обязательно должны быть типа байт

redsunset
Offline
Зарегистрирован: 12.09.2018

b707, прочтите название темы и Вы поймёте что сами не всосали о чём тут речь. Массив к которому даётся доступ для шины I2C должен быть только однобайтным! так как только так можно сохранить совпадение адресации элементов массива и регистров слейва!

redsunset
Offline
Зарегистрирован: 12.09.2018

b707, извини, после «не всосали» забыл смайлик поставить :)

b707
Offline
Зарегистрирован: 26.05.2017

redsunset пишет:

только так можно сохранить совпадение адресации элементов массива и регистров слейва!

это "совпадение" не обязательно сохранять

b707
Offline
Зарегистрирован: 26.05.2017

redsunset пишет:

b707, извини, после «не всосали» забыл смайлик поставить :)

мне пох, я обращаю внимания на слова только знакомых и значимых для меня людей

redsunset
Offline
Зарегистрирован: 12.09.2018

b707, Походу дела тот кто писал алгоритм передачи данных по шине I2C для Вас не очень значимый чел, если Вы до сих пор думаете что из одно регистра можно прочитать число типа float

b707
Offline
Зарегистрирован: 26.05.2017

redsunset пишет:

b707, Походу дела тот кто писал алгоритм передачи данных по шине I2C для Вас не очень значимый чел, если Вы до сих пор думаете что из одно регистра можно прочитать число типа float

В спецификации I2C - можно.

Но не исключаю, что в той конкретной библиотеке, которую вы используете - с этим проблемы. Это не удивительно, многие библиотеки написаны "не очень значимыми людьми". Пользователь starbit еще с десяток сообщений назад сказала, что библиотека - дерьмо.

beliy123
beliy123 аватар
Offline
Зарегистрирован: 25.05.2018

redsunset пишет:

Пользователь starbit еще с десяток сообщений назад сказала, что библиотека - дерьмо.

Так может есть лучшая библиотека, более подходящая под данную задачу?

Вообще контролеров будет 3 шт.,  на 2-х Slave (Arduino Mega) примерно  по 16 реле. В качестве мастера UNO. Пример выше сократил для точго чтобы код был короче. В любом случае эти ардуинки использовал под другую задачу. Новые уже заказал. Так что пока приедут попробывать Ваши рекомендации не могу. Но если кто-то подскажет более правильное решение буду благодарен.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

redsunset, памойму в Сургут придёца идти тебе самому. Без b707.

redsunset
Offline
Зарегистрирован: 12.09.2018

beliy123, лови пример:

Код мастера:

#include <iarduino_I2C_connect.h>                           // подключаем библиотеку для соединения arduino по шине I2C.
#include <Wire.h>                                           // подключаем библиотеку для работы с шиной I2C.
                                                            //
iarduino_I2C_connect I2C2;                                  // объявляем объект I2C2 для работы c библиотекой iarduino_I2C_connect.
                                                            //
byte Slave_1_Address = 0x10;                                // адрес первого слейва на шине I2C.
byte Slave_2_Address = 0x11;                                // адрес второго слейва на шине I2C.
                                                            //
byte Slave_1_Relay[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // определяем массив управления релюхами 1 слейва.
byte Slave_2_Relay[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // определяем массив управления релюхами 2 слейва.
                                                            //
void setup(){                                               //
    Wire.begin();                                           // инициируем подключение к шине I2C в качестве ведущего (master) устройства.
}                                                           //
                                                            //
void loop(){                                                //
//  Задаём значения:                                        //
    Slave_1_Relay[0 ]=1;                                    // включаем  у 1 слейва реле №0.
    Slave_1_Relay[15]=0;                                    // выключаем у 1 слейва реле №15.
    Slave_2_Relay[5 ]=1;                                    // включаем  у 2 слейва реле №5.
    Slave_2_Relay[10]=0;                                    // выключаем у 2 слейва реле №10.
//  Отправляем значения:                                    //
    for(int i=0; i<16; i++){                                // проходим по данным 16 реле.
        I2C2.writeByte(Slave_1_Address,i,Slave_1_Relay[i]); // Отправляем данные 1 слейву. writeByte(адрес ведомого, номер регистра, данные)
        I2C2.writeByte(Slave_2_Address,i,Slave_2_Relay[i]); // Отправляем данные 2 слейву. writeByte(адрес ведомого, номер регистра, данные)
    }                                                       //
    delay(1000);                                            // ждём что бы увидеть что указанные реле действительно переключились.
//  Задаём значения:                                        //
    Slave_1_Relay[0 ]=0;                                    // выключаем у 1 слейва реле №0.
    Slave_1_Relay[15]=1;                                    // включаем  у 1 слейва реле №15.
    Slave_2_Relay[5 ]=0;                                    // выключаем у 2 слейва реле №5.
    Slave_2_Relay[10]=1;                                    // включаем  у 2 слейва реле №10.
//  Отправляем значения:                                    //
    for(int i=0; i<16; i++){                                // проходим по данным 16 реле.
        I2C2.writeByte(Slave_1_Address,i,Slave_1_Relay[i]); // Отправляем данные 1 слейву. writeByte(адрес слейва, номер регистра, данные)
        I2C2.writeByte(Slave_2_Address,i,Slave_2_Relay[i]); // Отправляем данные 2 слейву. writeByte(адрес слейва, номер регистра, данные)
    }                                                       //
    delay(1000);                                            // ждём что бы увидеть что указанные реле действительно переключились.
}                                                           //
//  Если захотите считать состояния реле, то:               //
//  for(int i=0; i<16; i++){                                // проходим по данным 16 реле.
//      Slave_1_Relay[i]=I2C2.readByte(Slave_1_Address,i);  // Читаем данные 1 слейва. readByte(адрес слейва, номер регистра)
//      I2C2.writeByte(Slave_2_Address,i,Slave_2_Relay[i]); // Читаем данные 2 слейва. readByte(адрес слейва, номер регистра)
//  }                                                       //

Код первого слейва:

#include <iarduino_I2C_connect.h>                           // подключаем библиотеку для соединения arduino по шине I2C.
#include <Wire.h>                                           // подключаем библиотеку для работы с шиной I2C.
                                                            //
iarduino_I2C_connect I2C2;                                  // объявляем объект I2C2 для работы c библиотекой iarduino_I2C_connect.
                                                            //
const byte Slave_Address = 0x10;                            // адрес данного слейва на шине I2C.
byte Slave_Relay[16];                                       // объявляем массив к которому дадим доступ мастеру.
                                                            //
const byte PIN_Relay0 = 5;                                  // номер вывода к которому подключено реле №0.
const byte PIN_Relay1 = 6;                                  // номер вывода к которому подключено реле №1.
const byte PIN_Relay2 = 7;                                  // номер вывода к которому подключено реле №2.
const byte PIN_Relay3 = 8;                                  // номер вывода к которому подключено реле №3.
const byte PIN_Relay4 = 9;                                  // номер вывода к которому подключено реле №4.
const byte PIN_Relay5 = 10;                                 // номер вывода к которому подключено реле №5.
const byte PIN_Relay6 = 11;                                 // номер вывода к которому подключено реле №6.
const byte PIN_Relay7 = 12;                                 // номер вывода к которому подключено реле №7.
const byte PIN_Relay8 = 13;                                 // номер вывода к которому подключено реле №8.
const byte PIN_Relay9 = 14;                                 // номер вывода к которому подключено реле №9.
const byte PIN_Relay10 = 15;                                // номер вывода к которому подключено реле №10.
const byte PIN_Relay11 = 16;                                // номер вывода к которому подключено реле №11.
const byte PIN_Relay12 = 17;                                // номер вывода к которому подключено реле №12.
const byte PIN_Relay13 = 18;                                // номер вывода к которому подключено реле №13.
const byte PIN_Relay14 = 19;                                // номер вывода к которому подключено реле №14.
const byte PIN_Relay15 = 20;                                // номер вывода к которому подключено реле №15.
                                                            //
void setup(){                                               //
    Wire.begin(Slave_Address);                              // инициируем подключение к шине I2C в качестве ведомого (slave) устройства, с указанием своего адреса на шине.
    I2C2.begin(Slave_Relay);                                // даём доступ мастеру к массиву Slave_Relay.
    pinMode(PIN_Relay0, OUTPUT);                            // конфигурируем вывод к которому подключено реле №0, как выход.
    pinMode(PIN_Relay1, OUTPUT);                            // конфигурируем вывод к которому подключено реле №1, как выход.
    pinMode(PIN_Relay2, OUTPUT);                            // конфигурируем вывод к которому подключено реле №2, как выход.
    pinMode(PIN_Relay3, OUTPUT);                            // конфигурируем вывод к которому подключено реле №3, как выход.
    pinMode(PIN_Relay4, OUTPUT);                            // конфигурируем вывод к которому подключено реле №4, как выход.
    pinMode(PIN_Relay5, OUTPUT);                            // конфигурируем вывод к которому подключено реле №5, как выход.
    pinMode(PIN_Relay6, OUTPUT);                            // конфигурируем вывод к которому подключено реле №6, как выход.
    pinMode(PIN_Relay7, OUTPUT);                            // конфигурируем вывод к которому подключено реле №7, как выход.
    pinMode(PIN_Relay8, OUTPUT);                            // конфигурируем вывод к которому подключено реле №8, как выход.
    pinMode(PIN_Relay9, OUTPUT);                            // конфигурируем вывод к которому подключено реле №9, как выход.
    pinMode(PIN_Relay10, OUTPUT);                           // конфигурируем вывод к которому подключено реле №10, как выход.
    pinMode(PIN_Relay11, OUTPUT);                           // конфигурируем вывод к которому подключено реле №11, как выход.
    pinMode(PIN_Relay12, OUTPUT);                           // конфигурируем вывод к которому подключено реле №12, как выход.
    pinMode(PIN_Relay13, OUTPUT);                           // конфигурируем вывод к которому подключено реле №13, как выход.
    pinMode(PIN_Relay14, OUTPUT);                           // конфигурируем вывод к которому подключено реле №14, как выход.
    pinMode(PIN_Relay15, OUTPUT);                           // конфигурируем вывод к которому подключено реле №15, как выход.
}                                                           //
                                                            //
void loop(){                                                //
    digitalWrite(PIN_Relay0, Slave_Relay[0]);               // Устанавливаем на выводе PIN_Relay0 состояние из Slave_Relay[0].
    digitalWrite(PIN_Relay1, Slave_Relay[1]);               // Устанавливаем на выводе PIN_Relay1 состояние из Slave_Relay[1].
    digitalWrite(PIN_Relay2, Slave_Relay[2]);               // Устанавливаем на выводе PIN_Relay2 состояние из Slave_Relay[2].
    digitalWrite(PIN_Relay3, Slave_Relay[3]);               // Устанавливаем на выводе PIN_Relay3 состояние из Slave_Relay[3].
    digitalWrite(PIN_Relay4, Slave_Relay[4]);               // Устанавливаем на выводе PIN_Relay4 состояние из Slave_Relay[4].
    digitalWrite(PIN_Relay5, Slave_Relay[5]);               // Устанавливаем на выводе PIN_Relay5 состояние из Slave_Relay[5].
    digitalWrite(PIN_Relay6, Slave_Relay[6]);               // Устанавливаем на выводе PIN_Relay6 состояние из Slave_Relay[6].
    digitalWrite(PIN_Relay7, Slave_Relay[7]);               // Устанавливаем на выводе PIN_Relay7 состояние из Slave_Relay[7].
    digitalWrite(PIN_Relay8, Slave_Relay[8]);               // Устанавливаем на выводе PIN_Relay8 состояние из Slave_Relay[8].
    digitalWrite(PIN_Relay9, Slave_Relay[9]);               // Устанавливаем на выводе PIN_Relay9 состояние из Slave_Relay[9].
    digitalWrite(PIN_Relay10, Slave_Relay[10]);             // Устанавливаем на выводе PIN_Relay10 состояние из Slave_Relay[10].
    digitalWrite(PIN_Relay11, Slave_Relay[11]);             // Устанавливаем на выводе PIN_Relay11 состояние из Slave_Relay[11].
    digitalWrite(PIN_Relay12, Slave_Relay[12]);             // Устанавливаем на выводе PIN_Relay12 состояние из Slave_Relay[12].
    digitalWrite(PIN_Relay13, Slave_Relay[13]);             // Устанавливаем на выводе PIN_Relay13 состояние из Slave_Relay[13].
    digitalWrite(PIN_Relay14, Slave_Relay[14]);             // Устанавливаем на выводе PIN_Relay14 состояние из Slave_Relay[14].
    digitalWrite(PIN_Relay15, Slave_Relay[15]);             // Устанавливаем на выводе PIN_Relay15 состояние из Slave_Relay[15].
}                                                           //

Код второго слейва отличается от кода первого только тем, что у него другой адрес (не 0x10, а 0x11).

strarbit
Offline
Зарегистрирован: 12.06.2016

похой пример, если вы желаете изменить 1 сигнал на 1 вывод чип, в пример отбираете от чип контроллер 16 байт? (

Для этого требуется 2 байт. 1 байт 7 бит адрес I2C чип слуга(раб) + 1 команда запись данные. 2 байт данные - состояние биты P0 - P7 I/O порт.

 

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

strarbit пишет:

похой пример

Согласен - пример плохой.

redsunset
Offline
Зарегистрирован: 12.09.2018

strarbit пишет:

похой пример, если вы желаете изменить 1 сигнал на 1 вывод чип, в пример отбираете от чип контроллер 16 байт? (

Для этого требуется 2 байт. 1 байт 7 бит адрес I2C чип слуга(раб) + 1 команда запись данные. 2 байт данные - состояние биты P0 - P7 I/O порт.

Вот тут я с Вами полностью согласен! Код можно доработать и передавать не 16 байт, а всего 2! Всё верно! Но я написал РАБОЧИЙ ПРИМЕР и в нём легко рабобраться!!!

Этот пример легко изменить, увеличить или уменьшить количество реле, добавить другие модули.

А кто то еще, что то написал дельное по этой теме? Хоть один пример?

sadman41
Offline
Зарегистрирован: 19.10.2016

strarbit пишет:

похой пример, если вы желаете изменить 1 сигнал на 1 вывод чип, в пример отбираете от чип контроллер 16 байт? (

Для этого требуется 2 байт. 1 байт 7 бит адрес I2C чип слуга(раб) + 1 команда запись данные. 2 байт данные - состояние биты P0 - P7 I/O порт.

Если позволите, то я немного поправлю/добавлю.

Для изменения одного условного "вывода чипа" при хранении состояний восьми таковых "выводов" в одном регистре (байте) нужно сначала считать значение из регистра, выставить требуемый бит и послать результат обратно слейву. Т.е. для установки состояния одного "вывода" действительно эффективней иметь соответствие "регистр (байт) -> вывод". Однако, как только появляется необходимость "пакетного" выставления состояний, то данный метод начинает проигрывать по траффику на шине.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

redsunset пишет:

А кто то еще, что то написал дельное по этой теме? Хоть один пример?

А зачем писать, если всё уже разжёвано сто раз? Например, вот тут - вполне себе пример: https://www.arduino.cc/en/Tutorial/MasterWriter

Ну а значение одного бита в целом байте передавать - это, при любом раскладе, дичь.

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

Значит, нужно сделать антибитбандинг.

redsunset
Offline
Зарегистрирован: 12.09.2018

sadman41 пишет:

Однако, как только появляется необходимость "пакетного" выставления состояний, то данный метод начинает проигрывать по траффику на шине.

Согласен, но когда на шине пара слейвов с электромеханическими реле (если это так), то задержка в 2-3мс )))

sadman41
Offline
Зарегистрирован: 19.10.2016

redsunset пишет:

sadman41 пишет:

Однако, как только появляется необходимость "пакетного" выставления состояний, то данный метод начинает проигрывать по траффику на шине.

Согласен, но когда на шине пара слейвов с электромеханическими реле (если это так), то задержка в 2-3мс )))

Здесь еще нужно учитывать тот факт, что данные по шине не передаются просто для того, чтобы что-то попередавать. У них есть конечный потребитель. И, так получилось, что у потребителя условные "выходы" сгруппированы по 8шт и могут быть переконфигурированы одной операцией записью байта в регистр МК, а не восемью, помноженными на...  сколько там у digitalWrite уходит? Таким образом, небольшая экономия на траффике шины легко пожирается растратой вычислительного ресурса на стороне слейва. 

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

Просто все, кто на уровне железа работал с МК, уже настолько сжились с этой стратегией, что не задумываются о том, что кому-то она может быть непонятна. 

KosteT
Offline
Зарегистрирован: 17.07.2016

Это всё мелочи, по сравнению с проблемой ошибок в шине i2c. Я 32 реле в 4 байта упаковал, добавил crc8 и все равно думаю надо ещё считать подтверждения данных 2-3 раза, а у вас тут голые 16 каналов по шине бегают и проблемы не возникает, вот дела... Зы и это при длине кабеля 20 см и скорости 100кбит

dosikus
Offline
Зарегистрирован: 11.03.2017

KosteT пишет:
Зы и это при длине кабеля 20 см и скорости 100кбит

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

KosteT
Offline
Зарегистрирован: 17.07.2016

Конечно знаю, две меги стоят рядом(читай одна плата), кабель см 10 -15, витая пара, по спецификации i2c емкость кабеля до 400 пФ это около 2-3 метров витухи. Пробовал и вплотную меги ставить дюпонты по 3 см брал, все равно ошибки проскакивают,  обычно байт 0 читается, очень редко байты кривые прилетают. Скорости пробовал уменьшать до 10000, разницы в % соотношении к скорости большой не узрел. На обоих мегах встроенная подтяжка в платы 10к на sda scl, плюс встроенная в камень активна. Осцилл показывает что фронты с закругленниями прилетают, но так скажем не катастрофа ещё. Если 2 уны подцепить (а там только в камне подтяжка), то вообще ужос. Планирую 3 меги ставить потому оставлю наплатные подтяжки, если 2 уны - то по 5к на обе в самый раз.

В данном случае реле не успеет моргнуь как косячный байт тут же перечитается на целый. А вот если какие-нибудь триггеры ставить или сообщения отсылать по Ethernet или Mqtt - то сразу узреете помехоустойчивость шины, я поборол это CRC + 2 раза подтверждается посылка в обе стороны. Первые 4 байта инфа о 32 реле, байт CRC на чтение, байт CRC на отправку, и байт флаг передачи чтобы полудуплекс организовать.

Ещё кстати маскировочный массив из данной библиотеки обрабатывается как то криво, так что лучше не юзайте

sadman41
Offline
Зарегистрирован: 19.10.2016

А по питанию осциллограф что показывает? В моем мониторинге используются всякие I2C датчики, и они тоже зачастую на 10см витой висят, но я бы не сказал, что видел неестественные скачки на графиках. С дюпойнтами я, конечно, не связываюсь в продакшне...

strarbit
Offline
Зарегистрирован: 12.06.2016

KosteT пишет:

Конечно знаю, две меги стоят рядом(читай одна плата), кабель см 10 -15, витая пара, по спецификации i2c емкость кабеля до 400 пФ это около 2-3 метров витухи. Пробовал и вплотную меги ставить дюпонты по 3 см брал, все равно ошибки проскакивают,  обычно байт 0 читается, очень редко байты кривые прилетают. Скорости пробовал уменьшать до 10000, разницы в % соотношении к скорости большой не узрел. На обоих мегах встроенная подтяжка в платы 10к на sda scl, плюс встроенная в камень активна. Осцилл показывает что фронты с закругленниями прилетают, но так скажем не катастрофа ещё. Если 2 уны подцепить (а там только в камне подтяжка), то вообще ужос. Планирую 3 меги ставить потому оставлю наплатные подтяжки, если 2 уны - то по 5к на обе в самый раз.

В данном случае реле не успеет моргнуь как косячный байт тут же перечитается на целый.

помеха от реле, применяйте хороший адаптер питания

KosteT
Offline
Зарегистрирован: 17.07.2016

Пока всё на макетке, в будущем конечно всё будет по феншую FTP, пара scl+gnd и вторая пара sda + gnd, всё запаяю, никаких дюпонтов, gnd и фольгу экрана на землю с одного конца. По питанию всё отлично, ибп 12 - dc dc понижайка 5в, земля общая.5в - электролит 100мкФ + 0.1мкФ керамика. чаще всего весь байт = 0, бывает раз в минуту проскакивает такое, бывает каждую секунду. Реже раз в 100 какой ни будь бит косячит, ЛА всё так и показывает. Когда байт = 0 думаю ошибка в либе, а вот когда бит кривой читает - так и не докопался до сути, накатил проверку CRC и забил. 

KosteT
Offline
Зарегистрирован: 17.07.2016

И без реле тоже самое - голые 2 платы между собой в тепличных условиях на столе, ошибки CRC раз в 10 секунд стабильно на скорости 100кБит.

sadman41
Offline
Зарегистрирован: 19.10.2016

Просто датчики на стандартном Wire тоже ошибками валят? А то, может, это просто ошибка в библиотечке? 

KosteT
Offline
Зарегистрирован: 17.07.2016

Библиотека явно неидеальна, попробуйте маскировочный массив использовать - у меня не пишет в [6] байт массива, хотя маскировочный был {1,1,1,1,0,1,1}. датчиик попробую, те же dht22 у них в стоке CRC есть - как раз понаблюдаю, на крайняк опять две дуни включу, одна будет выдавать 0101010101 вторая читать

sadman41
Offline
Зарегистрирован: 19.10.2016

DHT22 наврядли по i2c согласится болтать. AM2320 - может.

strarbit
Offline
Зарегистрирован: 12.06.2016

KosteT пишет:

И без реле тоже самое - голые 2 платы между собой в тепличных условиях на столе, ошибки CRC раз в 10 секунд стабильно на скорости 100кБит.

На обоих мегах встроенная подтяжка в платы 10к на sda scl, плюс встроенная в камень активна. Осцилл показывает что фронты с закругленниями прилетают, но так скажем не катастрофа ещё. Если 2 уны подцепить (а там только в камне подтяжка), то вообще ужос.

Скорость имеет ограничение от (на) максимум. если питание хорошее, то ошибки даные недолжны появится, выбирите резисторы спецификация I2C. смотрите http://dsscircuits.com/articles/effects-of-varying-i2c-pull-up-resistors

redsunset
Offline
Зарегистрирован: 12.09.2018

strarbit пишет:

Скорость имеет ограничение от (на) максимум. если питание хорошее, то ошибки даные недолжны появится, выбирите резисторы спецификация I2C. смотрите http://dsscircuits.com/articles/effects-of-varying-i2c-pull-up-resistors

Согласен с Вами! KosteT эсли и без реле вылитают ошибки, то уменьшайте сопротивление резисторов. Рекомендую 1кОм и даже 470 Ом, на осциле сразу увидите спрямление фронтов импульсов.

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

Что-то странно это.

У меня с резисторами 4к7 устойчиво работает через кабели dupont на частоте 2 МГц. (Причем, 2 ограничение МГц, чувствуется, аппаратное, потому что на 2.1 МГц не работает совсем)