Почему требуется задержка (1) перед Wire.requestFrom?
У меня возникла проблема при обмене данными между nodeMCU и Arduino Pro Mini с помощью I2C.
Чтобы найти виновника, я, наконец, написал Echo Server на Arduino: он получает байт и отвечает тем же байтом. В nodeMCU я написал тестовую программу: она отправляет байты на сервер Echo и проверяет ответ, в дополнение к любым условиям ошибки в методах Wire
.
Оба процессора соединены GND-GND, VCC-VCC, SDA-SDA и SCL-SCL. SDA и SCL имеют подтягивающие резисторы 10K. Оба подключены через USB к моему ПК, и у меня есть два последовательных монитора для отслеживания выполнения.
В строке 38 программы тестера есть delay(1)
. Если вы закомментируете его, какой-то requestFrom()
случайным образом завершится ошибкой с возвращаемым значением != 1, но Echo Server все равно получит байт.
Поскольку ATmega328 включила TWI в микросхему, я не могу понять, что здесь не так и почему delay(1)
, кажется, решает проблему. Что мне здесь не хватает?
Программа тестировщика
/*
* Тест эхо-сервера
* Отправьте один байт на сервер Echo и прочитайте его.
* Сделайте это для байтов от 0x01 до 0xDD
*/
#include <Wire.h>
#define SLAVE_ADDRESS 42
byte car = 1; // автомобиль для отправки на сервер Echo.
void setup() {
Serial.begin(9600);
// У нас есть nodeMCU с I2C на контактах D5 и D6.
Wire.begin(D5, D6);
delay(1000);
Serial.println("");
Serial.println(" Echo Server Tester");
Serial.println("---------------------");
Serial.println("### Sent Received");
delay(1000);
}
/*
* Send one byte in each loop().
*/
void loop() {
byte status;
if(car > 0) {
do {
Wire.beginTransmission(SLAVE_ADDRESS);
int written = Wire.write(car);
status = Wire.endTransmission();
switch(status) {
case 0:
delay(1); // Закомментируйте это и посмотрите, как requestFrom() не работает.
if(Wire.requestFrom(SLAVE_ADDRESS, 1) == 1) {
byte r = Wire.read();
printChar(car, r);
car++;
} else {
Serial.println("error in return");
status = 4;
}
break;
case 1:
Serial.println("Data too long");
break;
case 2:
Serial.println("NACK on address");
break;
case 3:
Serial.println("NACK on data");
break;
case 4:
Serial.println("Some error");
break;
}
}
while(status != 0);
}
}
/*
* Utility: print in bytes sent and received
*/
void printChar(byte s, byte r) {
Serial.print(s);
Serial.print(" ");
printBits(s);
Serial.print(' ');
printBits(r);
if(r != s) {
// Отметить недопустимые ответы.
Serial.print(" *");
}
Serial.println();
}
/*
* Print a simple byte in binary
*/
void printBits(int car) {
byte bit = 0x80;
for(int i = 0; i < 8; i++) {
int digit = car & bit;
Serial.print(digit ? '1' : '0');
bit >>= 1;
}
}
Программа Echo Server
/*
* Эхо-сервер I2C
* Получите один байт от Мастера, повторите тот же байт Мастеру.
*/
#define MY_ADDRESS 42
#include <Wire.h>
volatile byte received;
volatile int count = 0;
void setup() {
Serial.begin(9600);
Wire.begin(MY_ADDRESS);
Serial.println("--- Echo Server ---");
Wire.onReceive(receiveCommand);
Wire.onRequest(sendAnswer);
}
void loop() {
static int lastCount = 0;
if (count != lastCount) {
Serial.print(lastCount + 1); Serial.print(" ");
printChar(received);
lastCount++;
}
}
/*
* Receive a byte from Master
*/
void receiveCommand(int howMany) {
received = Wire.read();
count++;
}
/*
* Send a byte to Master
*/
void sendAnswer() {
Wire.write(received);
}
/*
* Utility print byte received/sent
*/
void printChar(byte car) {
Serial.print("Char ");
byte bit = 0x80;
for (int i = 0; i < 8; i++) {
byte digit = car & bit;
Serial.print(digit ? '1' : '0');
bit >>= 1;
}
Serial.println();
}
@, 👍2
Обсуждение1 ответ
Лучший ответ:
1 миллисекунда не нужна, попробуйте delayMicroseconds с 50 или, может быть, 20 мкс.
Ардуино как ведомое устройство полностью отличается от, например, датчика. Arduino в качестве ведомого использует комбинацию аппаратного и программного обеспечения. Функции onRequest и onReceive выполняются, пока Arduino поддерживает линию SCL на низком уровне. Это называется растяжением тактового импульса.
С Arduino Uno (микроконтроллер ATmega328P), подключенным к Arduino Uno, эта проблема задержки между записью и чтением на шине I2C существовала долгое время. Несколько лет назад она была решена. Это была ошибка, настоящая ошибка. В этой задержке больше нет необходимости.
Есть еще способы сделать его ненадежным, например, слишком долго отключая прерывания в подчиненном устройстве.
Я читал, что Arduino до сих пор существует Due. При использовании Due в качестве ведущего и, например, ATmega328P (на 3,3 В) в качестве ведомого, для Due по-прежнему требуется задержка.
У nodeMCU и Raspberry Pi могут возникать проблемы при использовании Arduino в качестве ведомого устройства. Arduino в качестве ведомого устройства должен работать как минимум при напряжении 3,3 В или следует использовать переключатель уровня I2C.
Я не знаю, поддерживает ли библиотека nodeMCU Wire растяжение тактовых импульсов.
Эта задержка все еще может понадобиться. Даже тогда я не знаю, насколько надежной будет связь.
Wire.endTransmission — это функция, которая передает данные (а не Wire.write). Если за этим слишком рано следует Wire.requestFrom и в библиотеке Wire есть ошибка, это может вызвать проблемы.
- Отправка и получение различных типов данных через I2C в Arduino
- Как работают функции вне цикла void?
- Как отображать переменные на 0,96-дюймовом OLED-дисплее с библиотекой u8glib?
- Как отправить строку на мастер с помощью i2c
- Как выбрать альтернативные контакты I2C на ESP32?
- Что означает в I2C «NACK получен»?
- NodeMCU с RFID RC522 и LCD-модулем интерфейса I2C вместе
- Несколько датчиков I2C с одинаковым адресом
интерфейс TWI должен ждать целый цикл перед повторным запросом, иначе часы и буфер могут быть перепутаны., @tuskiomi
ЭТО исправило мои проблемы с I2C, задержку (1) Я не видел этого нигде ранее, поэтому спасибо за вопрос и ответы, поскольку я ходил по кругу, почему мои первые 2 чтения работали, но не мое второе чтение 2, хотя комментируя первые 2 читаются, тогда пусть вторые 2 ведут себя правильно. Теперь я узнаю тактовый цикл и изменю задержку (1) на задержку в микросекундах., @jak Kennedy