Последовательный приемник (Arduino Micro ATmega32U4) в плохом состоянии
Я настроил два Arduino Micro как пару мультиплексоров/демультиплексоров, взаимодействующих через Tx и Rx.
Все работает отлично, за исключением одной мелочи. Если я сбрасываю отправитель (мультиплексор), то приемник (демультиплексор) переходит в неисправное состояние, что приводит к случайным низким и высоким выходным сигналам.
Сброс приемника возвращает его в нормальное состояние, но я надеюсь, что кто-то может иметь лучшее решение. Используя последовательный монитор, я заметил, что во время процесса сброса на отправителе в конце передаются некоторые, казалось бы, случайные биты. Не уверен, связано ли это как-то с этим.
Код отправителя:
byte PINBold;
byte PINDold;
byte PINFold;
byte PBmask = B11111110;
byte PDmask = B11010011;
byte PFmask = B11110011;
void setup() {
Serial1.begin(3704400); // Скорость
DDRB = DDRB & B00000001; // Устанавливает контакты на цифровой вход: 11,10,9,8,MI,MO,SCK
DDRD = DDRD & B00101100; // Устанавливает контакты на цифровой вход: 2,3,4,6,12
DDRF = DDRF & B00001100; // Устанавливает контакты на цифровой вход: A0,A1,A2,A3,A4,A5
}
void loop() {
if (((PINB & PBmask) != PINBold) |((PIND & PDmask) != PINDold) | ((PINF & PFmask) != PINFold)) {
Serial1.write(PINB);
Serial1.write(PIND);
Serial1.write(PINF);
PINBold = PINB & PBmask;
PINDold = PIND & PDmask;
PINFold = PINF & PFmask;
}
}
Код для приемника:
void setup() {
Serial1.begin(3704400); // Скорость
DDRB = DDRB | B11111110; // Устанавливает контакты на цифровой выход: 8,9,10,11,MI,MO,SCK
DDRD = DDRD | B11010011; // Устанавливает контакты на цифровой выход: 2,3,4,6,12
DDRF = DDRF | B11110011; // Устанавливает контакты на цифровой выход: A0,A1,A2,A3,A4,A5
PORTB = PORTB | B11111110;
PORTD = PORTD | B11010011;
PORTF = PORTF | B11110011;
}
void loop() {
if (Serial1.available() > 2) {
PORTB = Serial1.read();
PORTD = Serial1.read();
PORTF = Serial1.read();
}
}
@Michael , 👍1
Обсуждение1 ответ
Лучший ответ:
Хорошо, в вашем коде есть некоторые проблемы. Во-первых, вы уверены, что скорость передачи данных 3,7 Мбит/с — это то, что вам нужно? Не могли бы вы сделать ее немного медленнее?
Если вы имеете дело с механическими частями (например, кнопками), то частота обновления 100 Гц не может быть замечена; для отправки 3 байтов вам нужно 30 бит, а при 9600 бит/с это означает 320 Гц частоты. Уменьшение скорости также увеличивает подавление шума, поэтому я бы выбрал более низкую скорость (даже 38,4 кбит/с будет достаточно).
Значит, вы слишком много раз читаете регистры PINx, что может привести к несогласованности. Что произойдет, если значение изменится после if, но до обновления значения? Лучше сохранить его в переменной.
Однако это не является основной причиной вашей проблемы. Основная проблема, как и предполагал Jot, заключается в том, что вы не используете протокол для синхронизации двух элементов. Это означает, что при загрузке получатель не знает, находится ли он в начале «кадра» или в середине. Более того, начальные условия, когда отправитель не в сети, не игнорируются.
Так что "протокол" должен быть согласован между двумя. Самый простой из них, который я могу придумать, вращается вокруг того факта, что вы можете зарезервировать немного для управления потоком.
Глядя на ваши маски портов, я вижу, что если мы разделим значения PINB на два полубайта, а затем переместим самый младший из них на один бит влево, то получим, что бит 3 никогда не будет использоваться для передачи данных контакта:
PINB 76543210 -> 7654x321 (pin 0 was not used)
следовательно, мы используем это как контрольный бит. Он имеет значение 1 для первого бита в кадре и 0 в противном случае. На стороне приемника вы ищете байт с этим установленным битом, затем записываете также два других, затем проверяете, есть ли у двух других 0 в этой позиции, и, если это подтверждается, у вас есть ваш кадр.
Это решение имеет самую высокую пропускную способность, но может быть немного нестабильным при наличии некоторого шума (возможен фальстарт кадров).
По этой причине я предлагаю вам добавить еще один байт в пакет, таким образом, передав четыре байта. Опять же, первый имеет бит, установленный в положение 3, а другой имеет ноль. Но в этом случае первый байт имеет предопределенное значение, и, следовательно, приемник должен просто искать это конкретное значение (и надежность увеличивается.
Ладно, народ, я слышу ваш вопрос. Где код? Ну, вот он. Для отправителя вам нужно выполнить небольшую манипуляцию с байтами, которые нужно отправить. Я выбрал шестнадцатеричное значение 0x5A, потому что у него много переходов (трудно воспроизвести из шума), и у него есть бит 1 в позиции 3. Я также изменил то, что было сказано в начале (скорость передачи и чтение PINx только один раз); не стесняйтесь отменять изменения, если у вас другие потребности. Более того, я инициализировал старые переменные и задал направление контактов на основе маски, а не делал это вручную (легче поддерживать).
ПРАВКА: Я добавил возможность получения текущего статуса путем отправки символа на последовательный интерфейс (полезно при загрузке приемника)
byte PINBold;
byte PINDold;
byte PINFold;
const byte PBmask = B11111110;
const byte PDmask = B11010011;
const byte PFmask = B11110011;
const byte StartOfFrame = 0x5A; // бит 3 всегда равен '1'
void setup()
{
Serial1.begin(38400); // Скорость
DDRB = DDRB & (~PBmask); // Устанавливает контакты на цифровой вход: 11,10,9,8,MI,MO,SCK
DDRD = DDRD & (~PDmask); // Устанавливает контакты на цифровой вход: 2,3,4,6,12
DDRF = DDRF & (~PFmask); // Устанавливает контакты на цифровой вход: A0,A1,A2,A3,A4,A5
PINBold = ~PBmask; // Принудительно ввести 1 в запрещенной позиции -> принудительно отправить в первом раунде
PINDold = ~PDmask;
PINFold = ~PFmask;
}
void loop() {
byte currPINB = PINB & PBmask;
byte currPIND = PIND & PDmask;
byte currPINF = PINF & PFmask;
bool shouldSendData = false;
if ((currPINB != PINBold) || (currPIND != PINDold) || (currPINF != PINFold))
{
PINBold = currPINB;
PINDold = currPIND;
PINFold = currPINF;
shouldSendData = true;
}
while (Serial1.available())
{
Serial1.read();
shouldSendData = true;
}
if (shouldSendData)
{
Serial1.write(StartOfFrame);
Serial1.write((currPINB & 0xF0) | ((currPINB >> 1) & 0x07)); // отправить currPINB 7654_321, где _ — это «0»
Serial1.write(currPIND); // бит 3 всегда равен '0'
Serial1.write(currPINF); // бит 3 всегда равен '0'
}
}
На стороне приемника вам нужно дождаться начала кадра, а затем получить данные. Я предпочитаю реализовать это как очень маленькую машину состояний. Я также добавил бонусную функцию тайм-аута для выхода из состояния полезной нагрузки после 10 мс бездействия.
EDIT: Добавлена отправка символа в начале, чтобы вызвать повторную отправку статуса от мастера (если доступно). Включен также функциональный код из вопроса, так как OP добавил получателя
const byte StartOfFrame = 0x5A; // бит 3 всегда равен '1'
const byte PBmask = B11111110;
const byte PDmask = B11010011;
const byte PFmask = B11110011;
// Состояния конечного автомата
const byte SM_WaitingForSOF = 1;
const byte SM_WaitingForPayload = 2;
byte SM_CurrentState;
bool receivedData;
byte receivedPortB;
byte receivedPortD;
byte receivedPortF;
uint8_t startOfTransmissionMillis;
const byte transmissionTimeout_ms = 10;
void setup()
{
Serial1.begin(38400); // Скорость
DDRB = DDRB | PBmask; // Устанавливает контакты на цифровой выход: 8,9,10,11,MI,MO,SCK
DDRD = DDRD | PDmask; // Устанавливает контакты на цифровой выход: 2,3,4,6,12
DDRF = DDRF | PFmask; // Устанавливает контакты на цифровой выход: A0,A1,A2,A3,A4,A5
SM_CurrentState = SM_WaitingForSOF;
receivedData = false;
receivedPortB = 0;
receivedPortD = 0;
receivedPortF = 0;
Serial1.write('H'); // Любой байт допустим; он используется для запуска отправки статуса
}
void loop() {
receivedData = false;
switch (SM_CurrentState)
{
case SM_WaitingForSOF:
while (Serial1.available() > 0) // также может быть "if"
{
if (Serial1.read() == StartOfFrame)
{
SM_CurrentState = SM_WaitingForPayload;
startOfTransmissionMillis = (uint8_t)millis();
break;
}
}
break;
case SM_WaitingForPayload:
if (Serial1.available() >= 3)
{
receivedPortB = Serial1.read();
receivedPortD = Serial1.read();
receivedPortF = Serial1.read();
if (((receivedPortB & 0x08) | (receivedPortD & 0x08) | (receivedPortF & 0x08)) == 0)
{ // Все биты полезной нагрузки имеют бит 3, равный '0', поэтому связь прошла успешно
receivedData = true;
}
SM_CurrentState = SM_WaitingForSOF;
}
else if ((((uint8_t)millis()) - startOfTransmissionMillis) > transmissionTimeout_ms)
{
SM_CurrentState = SM_WaitingForSOF;
}
break;
default:
SM_CurrentState = SM_WaitingForSOF;
break;
}
if (receivedData)
{
PORTB = (PORTB & (~PBmask)) | (receivedPortB & 0xF0) | ((receivedPortB & 0x07) << 1);
PORTD = (PORTD & (~PDmask)) | receivedPortD;
PORTF = (PORTF & (~PFmask)) | receivedPortF;
}
}
}
Осторожно: непроверенный код. Я уверен, что никаких серьезных ошибок нет, но некоторые настройки могут потребоваться
- Как использовать SPI на Arduino?
- Нажать клавишу Windows, используя «keyboard.press();»
- Как подключить вывод INT MPU 6050?
- Улучшенное циклическое переключение цветов RGB.
- Проблема с загрузкой моего скетча на Ардуино
- Отправка мультимедийных клавиш клавиатуры с помощью библиотеки клавиатур
- Распиновка аналога Arduino Pro Micro
- Как преобразовать скетч примера Arduino в полный проект C++?
В вашем коде есть ошибка; в строке ████ вы должны были написать ████████ вместо █████████. Попробуйте изменить это, и это заработает, @frarugi87
Создайте протокол для последовательной связи с начальным байтом, контрольной суммой и конечным байтом. Во время сброса вывод tx устанавливается как вход, поэтому он имеет высокое сопротивление и будет улавливать шум. Вы забыли подтягивающий резистор?, @Jot
@frarugi87 отредактировал, включив код., @Michael
@Jot Я использовал понижающие резисторы, должно быть все в порядке, верно?, @Michael
@Michael, в вашем коде есть некоторые проблемы; позвольте мне написать более полный ответ, чтобы их исправить., @frarugi87
@Michael только один вопрос: вам нужна полная передача 3*8 байт или вам нужно только 7+5+6 байт? Я имею в виду, вы исключили некоторые контакты из трех портов.., @frarugi87
Сигнал tx находится в состоянии ожидания на высоком уровне. Первый низкий импульс — это стартовый бит. С помощью резистора pulldown вы создаете стартовый бит сразу после сброса arduino. Используйте резистор pullup., @Jot
@frarugi87 Я использую только контакты, указанные как входы, и мне нужно отправлять данные всякий раз, когда состояние любого контакта меняется., @Michael
Без стартового байта, контрольной суммы и концевого байта, по крайней мере, используйте стартовый байт как синхробайт. Когда вы отправляете замаскированное значение вместо фактического значения порта, возможно ли отправить 0xff или 0x01 как синхробайт? Я думаю, это возможно., @Jot
@Jot Переключение на подтягивающий резистор, похоже, заставило все работать правильно!! Ранее сегодня у меня не получилось с байтом синхронизации, придется еще с этим поиграться., @Michael
@frarugi87 Я отредактировал свой пост, включив код моего приемника. По общему признанию, это предел моих возможностей. Я попробовал ваши коды, модифицирующие приемник, чтобы включить PORTX = receivedPortX, однако приемник ничего не делает. Все контакты высокие. Мой код работает, но вы сделали несколько важных замечаний, и какой-то тип протокола был бы замечательным., @Michael
@Michael Я включил ваш код в код приемника и добавил одно условие, о котором я раньше не думал (при включении приемника сигнал не отправляется; теперь отправляется). Пожалуйста, скажите мне, работает это или нет; если нет, просто скажите мне, и я попробую это проверить, @frarugi87
@frarugi87 В новом коде никаких изменений не произошло., @Michael
@Michael извините, была ошибка. в приемнике, внутри коммутатора, тест с доступным последовательным портом должен быть >= 3, а не >3; сейчас исправлено; я проверил его на двух UNO, используя только четыре бита PORTB (но теперь он должен работать даже в вашем случае). Проверьте два кода, @frarugi87
@frarugi87 хм, по-прежнему никаких изменений., @Michael
@Michael Еще одна ошибка исправлена: Serial1 был Serial в некоторых строках (при отправке символа 'H' и при получении байтов). Пожалуйста, проверьте код сейчас и посмотрите, работает ли он (держим кулачки), @frarugi87
@frarugi87 Это сработало! Кажется, все работает правильно! Я тщательно протестирую его и сообщу позже. Я не против сказать вам, что это часть оборудования в действительно крутом физическом эксперименте!, @Michael
@Michael Рад, что сработало :) Как я и писал, я не смог это протестировать, поэтому и возникли баги. Но в конце концов нам удалось заставить это работать ;), @frarugi87