Проблема с передачей последовательных данных между Arduino и ATtiny туда и обратно
Я работаю над взаимодействием между Arduino и ATtiny13, и мне нужно, чтобы это взаимодействие работало. Есть Arduino Nano, питаемая по USB-кабелю от ПК, и ATtiny13, питаемая от Nano по линиям 3V3/GND. Контакты 2 Nano и Tiny соединены одним проводом длиной 20 см.
Nano отправляет один байт информации Tiny, а затем Tiny отправляет тот же байт обратно Nano. По крайней мере, идея в этом, но это не совсем работает.
Это скетч Arduino (своего рода мастер):
#define DATA_PIN 2
void setup() {
pinMode(DATA_PIN, OUTPUT);
digitalWrite(DATA_PIN, HIGH);
Serial.begin(9600); //
}
void loop() {
byte dataToSend = 0x55; // отправка некоторого байта
byte receivedData;
sendByte(dataToSend);
receivedData = receiveByte();
pinMode(DATA_PIN, OUTPUT);
digitalWrite(DATA_PIN, HIGH);
Serial.print("Sent: 0x");
Serial.print(dataToSend, HEX);
Serial.print(", Received: 0x");
Serial.println(receivedData, HEX);
delay(1000); // небольшая пауза для удобства пользователя
}
void sendByte(byte data) {
pinMode(DATA_PIN, OUTPUT); // в режим вывода
digitalWrite(DATA_PIN, LOW);
delayMicroseconds(500);
for (int i = 0; i < 8; i++) {
digitalWrite(DATA_PIN, (data & (1 << i)) ? HIGH : LOW); // отправить один бит
delayMicroseconds(500); // длительность одного бита
}
digitalWrite(DATA_PIN, HIGH); // Состояние ожидания
}
byte receiveByte() {
pinMode(DATA_PIN, INPUT); // переключиться в режим ввода
byte received = 0;
while (digitalRead(DATA_PIN) == HIGH); // ждем LOW (стартовый бит)
delayMicroseconds(750); // ждем до половины первого бита
for (int i = 0; i < 8; i++) {
if (digitalRead(DATA_PIN) == HIGH) {
received |= (1 << i); // прочитал один бит
}
delayMicroseconds(500); // до конца бита
}
pinMode(DATA_PIN, OUTPUT); // вернуться в режим вывода
digitalWrite(DATA_PIN, HIGH); // состояние ожидания
return received;
}
Это скетч ATtiny13 (это будет ведомое устройство):
#define DATA_PIN 2
void setup() {
pinMode(DATA_PIN, INPUT);
}
void loop() {
byte receivedData = receiveByte(); // ждем сообщение от ардуино
sendByte(receivedData); // отправить эти данные обратно
}
byte receiveByte() {
pinMode(DATA_PIN, INPUT); // переключиться в режим ввода
byte received = 0;
while (digitalRead(DATA_PIN) == HIGH); // ждем LOW (стартовый бит)
delayMicroseconds(750); // ждем половину бита
for (int i = 0; i < 8; i++) {
if (digitalRead(DATA_PIN) == HIGH) {
received |= (1 << i); // вернем бит
}
delayMicroseconds(500); // до конца бита
}
return received;
}
void sendByte(byte data) {
pinMode(DATA_PIN, OUTPUT); // в режим вывода
digitalWrite(DATA_PIN, LOW);
delayMicroseconds(500);
for (int i = 0; i < 8; i++) {
digitalWrite(DATA_PIN, (data & (1 << i)) ? HIGH : LOW); // отправить один бит
delayMicroseconds(500); // длительность одного бита
}
pinMode(DATA_PIN, INPUT); // обратно в режим ввода
}
Всё, что я получаю, это это, снова и снова:
...
Sent: 0x55, Received: 0x0
Sent: 0x55, Received: 0x0
Sent: 0x55, Received: 0x0
...
Теперь, когда я отправляю некоторые жестко запрограммированные данные из Tiny в Arduino, они прекрасно отображаются на последовательном мониторе.
Проблема где-то при отправке данных с Arduino на Tiny, помогите, пожалуйста!
@Milos, 👍2
Обсуждение2 ответа
В вашем коде есть как минимум три проблемы. Первая заключается в том, что отправитель не отправляет стартовый бит, который ожидает получатель, а вторая заключается в том, что получатель ждёт стартовый бит для каждого полученного бита и выбирает этот стартовый бит вместо последующих. Таким образом, код должен выглядеть примерно так:
void sendByte(byte data) {
pinMode(DATA_PIN, OUTPUT); // в режим вывода
// Отправить стартовый бит
digitalWrite(DATA_PIN, LOW);
delayMicroseconds(500);
// Отправить биты данных
for (int i = 0; i < 8; i++) {
digitalWrite(DATA_PIN, (data & (1 << i)) ? HIGH : LOW); // отправить один бит
delayMicroseconds(500); // длительность одного бита
}
digitalWrite(DATA_PIN, HIGH); // Состояние ожидания
pinMode(DATA_PIN, INPUT); // обратно в режим ввода
}
byte receiveByte() {
byte received = 0;
// ждем LOW (стартовый бит)
while (digitalRead(DATA_PIN) == HIGH);
delayMicroseconds(500)
for (int i = 0; i < 8; i++) {
delayMicroseconds(250); // ждем половину бита
if (digitalRead(DATA_PIN) == HIGH) {
received |= (1 << i); // прочитать бит
}
delayMicroseconds(250); // до конца бита
}
return received;
}
Кроме того, могут возникнуть проблемы с синхронизацией. При тестировании с ATtiny1614 вместо ATtiny13 было обнаружено, что delayMicroseconds даёт слишком короткие задержки, что приводит к получению неверного байта, слишком раннему началу ответа и неправильной синхронизации ответа. Эту проблему можно решить, включив
#define F_CPU 16000000UL
#include <avr/delay.h>
в верхней части файла ATtiny,
устанавливаем частоту Attiny1614 на 10 МГц(!),
и заменив delayMicroseconds() на _delay_us().
Спасибо, GPT дал мне какой-то дурацкий код, но он всё равно не работает., @Milos
Ваш код ничем не лучше оригинала. Вы просто по-другому распределили задержки. Но обе версии не передают стоповый бит, что создаёт проблемы при последовательной отправке байтов., @Dave Tweed
Стартовый бит — важная часть, стоповый бит здесь не так уж и нужен, так как приёмная программа ожидает только 8 бит после стартового бита и просто прекращает чтение после того, как весь байт будет прочитан. Я просто не могу найти проблему..., @Milos
ATtiny13 питается от 3V3/GND от Nano
Это проблема. Arduino Nano использует логику 5 В. Когда он устанавливает данные,
на вывод OUTPUT HIGH, он будет подавать избыточный ток через
Диод защиты от электростатического разряда ATtiny. Это может привести к повреждению выходного сигнала Arduino,
Вход ATtiny, или оба. Более того, когда данные передаются в обратном направлении,
В направлении, высокий уровень ATtiny (3,3 В) довольно близок к
«Высокое» входное напряжение Arduino (VIH = 3 В). Если Arduino
Выход 3,3 В немного слабый, логический уровень может упасть ниже
VIH. Выходное напряжение 3,3 В для Arduino обеспечивается через USB/последовательный порт.
мост, и он рассчитан всего на 30 мА.
Я предлагаю следующее:
Убедитесь, что выход Arduino и вход ATtiny по-прежнему работают.
Питайте ATtiny от контакта 5 В платы Arduino.
Используйте отдельные провода для Arduino → ATtiny и ATtiny → Arduino. Направления коммуникации. Если это слишком непрактично, то поставьте последовательный резистор сопротивлением не менее 220 Ом, включенный последовательно между двумя контактами данных. Это должно предотвратить перегорание GPIO в случае, если оба контакта выйдут из строя. быть выходами одновременно (программное обеспечение должно предотвратить это) происходит, но ошибки в программном обеспечении случаются).
Если после того, как вы это исправили, оно все еще не работает, тогда возьмите зонд и посмотрите на сигналы, проходящие по проводу. Это должно быть По крайней мере, покажет, какая сторона слабеет. Что ещё стоит попробовать:
Каждый раз, когда у вас есть
pinMode(DATA_PIN, OUTPUT); digitalWrite(DATA_PIN, HIGH);Поменяйте местами эти строки. Это должно предотвратить ошибку, когда строка может находиться в состоянии
OUTPUTLOWв течение очень короткого времени, что приемник может интерпретировать как стартовый бит.Замените все вхождения
INPUTнаINPUT_PULLUP. Это должно предотвратить плавающую линию, которая может привести к появлению ложных стоп-битов быть обнаруженным.Заставьте отправителей выводить стоп-бит. Да, я знаю, что получатели этого не делают. Ожидайте стоп-бит. Однако стоп-бит даёт приемнику время получить сам готов принять следующий стартовый бит. Или, ещё лучше, добавить пять Стоп-биты. Это поможет вам легче различать кадры. отличаются по области применения.
Используйте прямое управление портами вместо
digitalRead()/digitalWrite(). Эти функции из Arduino. ядра очень медленные, и они могут сбить ваши тайминги.Используйте аппаратный таймер вместо
delayMicroseconds(). Таймер Точность не будет снижена за счет времени, которое процессор тратит на выполнение другой код, включая прерывания таймера.
Вместо прямого управления портами я с большим успехом и тем же результатом использую библиотеку [digitalWriteFast](https://reference.arduino.cc/reference/en/libraries/digitalwritefast/). Её функции можно использовать где угодно, где пины являются константами, известными на этапе компиляции., @the busybee
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- В чем разница между библиотеками Software Serial? Какая из них совместима с Arduino Nano?
- Как отправить команду AT на sim800l с помощью SoftwareSerial
- Как получить больше 64 байт на Arduino?
- Проблемы с последовательной связью от Arduino к Bluetooth HC-05
- Как Arduino может проверить, подключен ли он к ПК и включен ли компьютер?
- Как отправлять и получать беззнаковые целые (unsigned int) от одного arduino к другому arduino
- Использование последовательных контактов TX/ RX для связи по USB
9600 предназначен только для последовательного монитора, эта связь между микроконтроллерами имеет произвольную длину бита 500 микросекунд., @Milos
отладьте это, изменив скорость до 2 секунд на бит... мигание светодиода на обоих устройствах между передачей битов и при чтении битов... это покажет вам, считываются ли биты в правильное время, @jsotola
Justme: да, он был сгенерирован искусственным интеллектом, а позже исправлен, чтобы появился начальный бит... Я просто взял его из искусственного интеллекта, не проверяя..., @Milos
jstola: спасибо, это отличная идея, я так и сделаю!, @Milos
Разве для дуплексной «связи» не нужны три провода?, @Antonio51
"*да, это сгенерировано ИИ,...*" Это проблема. Модель ИИ обучалась как на неправильном, так и на правильном коде и не видит разницы. Код в вашем сообщении будет скопирован ИИ-ботами и добавлен в модель, способствуя «коллапсу модели»., @Transistor
ИИ полезен только в том случае, если у вас достаточно опыта, чтобы оценить результат. Подавляющее большинство пользователей, с которыми я сталкиваюсь, не обладают им. Поэтому общий совет — не использовать ИИ в программировании., @the busybee
Поскольку создается впечатление, что эскизы были отредактированы, но миграция удалила историю, пожалуйста, [отредактируйте] свой вопрос, чтобы уточнить: текущие эскизы по-прежнему ведут себя так, как показано?, @the busybee
Возможно, вам захочется добавить подтягивающий резистор, чтобы гарантировать наличие определённого уровня на проводе в случае, если ни один из микроконтроллеров им не управляет. Можно использовать внешний резистор или
pinMode(DATA_PIN, INPUT_PULLUP);., @the busybeeATtiny13 может пропускать полученные данные из-за неправильного времени ожидания., @liaifat85