З посиланням на дошки Arduino Uno, Mega2560, Leonardo та подібні дошки:
- Як працює послідовний зв’язок?
- Наскільки швидко відбувається серійний?
- Як з'єднати відправника та одержувача?
Зверніть увагу: це розроблено як опорне запитання.
З посиланням на дошки Arduino Uno, Mega2560, Leonardo та подібні дошки:
Зверніть увагу: це розроблено як опорне запитання.
Відповіді:
Асинхронна послідовна (зазвичай її називають послідовною) комунікацією використовується для передачі байтів з одного пристрою на інший. Пристрій може бути одним або декількома з таких:
На відміну від SPI / USB / I2C послідовний зв’язок не має тактового сигналу. Годинник вибірки - це узгоджена швидкість вибірки (відома як швидкість передачі). І відправник, і одержувач повинні бути налаштовані так, щоб вони використовували однакову швидкість, або приймач отримає безглузді дані (через те, що біти не відбираються у вибірку з тією ж швидкістю, яку вони надіслали).
Передача є асинхронною, що в основному означає, що байти можна надсилати в будь-який час, з різними проміжками між ними. Ця графіка ілюструє один байт, що надсилається:
На графіку вище показано, що літера "F" передається. У ASCII це 0x46 (у шістнадцятковій) або 0b01000110 (у двійковій). Чи не менш значуще (молодший) біт передається першим, таким чином , у наведеному вище малюнку ви бачите біти , які надходять у порядку: 01100010
.
Час "простою" між байтами передається як безперервні біти "1" (фактично, лінія передачі постійно тримається високо).
Щоб вказати початок байта, Початковий біт завжди вказується, потягнувши лінію низько, як показано на графіці. Як тільки приймач бачить біт запуску, він чекає в 1,5 рази більше часу вибірки, а потім відбирає біти даних. Він чекає 1,5 рази, щоб він:
Якщо, наприклад, швидкість передачі даних становить 9600 бод, то швидкість вибірки складе 1/9600 = 0.00010416
секунди (104,16 мкс).
Таким чином, при 9600 бодах, після отримання стартового біта, приймач чекає 156,25 мкс, а потім проби кожні 104,16 мкс.
Мета стоп -біту - переконатися, що між кожним байтом однозначно є 1 біт. Без біт зупинки, якби байт закінчився нулем, апаратне забезпечення неможливо було б визначити різницю між цим та початковим бітом наступного байта.
Для отримання вищевказаного виводу на Uno ви можете написати цей код:
void setup()
{
Serial.begin(9600);
Serial.print("F");
}
void loop ()
{
}
Щоб заощадити час передачі (за старих часів, хе), вам було дозволено вказувати різні кількості бітів даних. Апаратне забезпечення AtMega підтримує біти даних, що налічують від 5 до 9. Очевидно, що чим менше бітів даних, тим менше інформації можна надіслати, але тим швидше це буде.
Ви можете додатково мати біт парності. Це обчислюється, якщо потрібно, підраховуючи число символів 1, а потім переконайтесь, що це число непарне або навіть встановивши біт парності на 0 або 1, як потрібно.
Наприклад, для літери "F" (або 0x46 або 0b01000110) ви можете побачити, що там є 3 (у 01000110). Таким чином, ми вже маємо непарний паритет. Отже, біт паритету буде таким:
Біт парності, якщо він присутній, з’являється після останнього біта даних, але перед бітом зупинки.
Якщо приймач не отримує правильний біт парності, це називається "помилка парності". Це вказує на наявність певної проблеми. Можливо, відправник і одержувач налаштовані на використання різних швидкостей передачі (біт), або в лінії з'явився шум, який повернув нуль до одиниці або навпаки.
Деякі ранні системи також використовували «позначення» паритету (де біт парності завжди був 1 незалежно від даних) або «пробіл» паритет (де біт парності завжди був 0 незалежно від даних).
Деякі засоби зв'язку використовують 9-бітні дані, тому в цих випадках біт парності перетворюється на 9-й біт. Існують спеціальні прийоми для відправки цього 9-го біта (регістри - це 8-бітні регістри, тому 9-й біт потрібно помістити кудись ще).
Раннє обладнання, як правило, дещо повільніше в електронному вигляді, тому, щоб дати одержувачу час обробляти вхідний байт, іноді було вказано, що відправник посилає два стоп-біта. Це, по суті, додає більше часу, коли рядок даних утримується високо (ще один раз), перш ніж з'явиться наступний біт запуску. Цей додатковий бітовий час дає одержувачу час для обробки останнього вхідного байта.
Якщо приймач не отримує логічну 1, коли передбачається стоп-біт, це називається "помилкою обрамлення". Це вказує на наявність певної проблеми. Цілком можливо, відправник і одержувач налаштовані на використання різних швидкостей передачі (біт).
Зазвичай послідовний зв'язок позначається, повідомляючи про швидкість, кількість бітів даних, тип паритету та кількість стоп-бітів, як це:
9600/8-N-1
Це говорить нам:
Важливо, щоб відправник та одержувач домовились сказаного, інакше комунікація навряд чи буде успішною.
У Arduino Uno є цифрові штифти 0 та 1, доступні для апаратної послідовності:
Для з'єднання двох Arduinos ви помінятися Tx і Rx , як це:
Підтримується широкий діапазон швидкостей (див. Графіку нижче). "Стандартні" швидкості зазвичай кратні 300 бодів (наприклад, 300/600/1200/2400 тощо).
Іншими "нестандартними" швидкостями можна керувати, встановивши відповідні регістри. Клас HardwareSerial робить це за вас. напр.
Serial.begin (115200); // set speed to 115200 baud
Як правило, якщо припустити, що ви використовуєте 8-бітові дані, то ви можете оцінити кількість байтів, які ви можете передати за секунду, діливши швидкість передачі на 10 (через біт запуску і біт зупинки).
Таким чином, при 9600 бодах ви можете передавати 960 байт ( 9600 / 10 = 960
) в секунду.
Швидкість передачі в Atmega генерується шляхом поділу системного годинника, а потім підрахунку до заданого числа. Ця таблиця з таблиці показує регістрові значення та відсотки помилок для тактової частоти 16 МГц (наприклад, таблиці на Arduino Uno).
Біт U2Xn впливає на дільник тактової частоти (0 = ділиться на 16, 1 = ділиться на 8). Реєстр UBRRn містить число, до якого зараховується процесор.
Отже, з наведеної вище таблиці ми бачимо, що ми отримуємо 9600 бод від тактової частоти 16 МГц так:
16000000 / 16 / 104 = 9615
Ділимо на 104, а не 103, тому що лічильник є нульовим відносним. Таким чином, помилка тут є 15 / 9600 = 0.0016
близькою до сказаної в таблиці (0,02%).
Ви помітите, що деякі показники боду мають більшу кількість помилок, ніж інші.
Відповідно до таблиці, максимальний відсоток помилок для 8 бітів даних знаходиться в діапазоні від 1,5% до 2,0% (докладнішу інформацію див. У таблиці).
У Arduino Leonardo та Micro є різний підхід до послідовного зв'язку, оскільки вони підключаються безпосередньо через USB до хост-комп'ютера, а не через послідовний порт.
Через це потрібно зачекати, коли Serial стане "готовим" (оскільки програмне забезпечення встановлює USB-з'єднання), з додатковою парою рядків, як це:
void setup()
{
Serial.begin(115200);
while (!Serial)
{} // wait for Serial comms to become ready
Serial.print("Fab");
}
void loop ()
{
}
Однак якщо ви хочете реально спілкуватися за допомогою штифтів D0 та D1 (а не за допомогою кабелю USB), тоді вам потрібно скористатися Serial1, а не Serial. Ви робите так, як це:
void setup()
{
Serial1.begin(115200);
Serial1.print("Fab");
}
void loop ()
{
}
Зауважте, що Arduino використовує рівні TTL для послідовного зв'язку. Це означає, що він очікує:
Старіші серійні пристрої, призначені для підключення до послідовного порту ПК, ймовірно, використовують рівні напруги RS232, а саме:
Мало того, що це "перевернуто" відносно рівнів TTL ("один" є більш негативним, ніж "нульовим"), Arduino не може обробити негативні напруги на вхідних штирях (ані позитивні більше 5 В).
Таким чином, вам потрібна схема інтерфейсу для спілкування з такими пристроями. Тільки для входу (до Arduino) це зробить простий транзистор, діод і пара резисторів:
Для двостороннього зв’язку потрібно вміти генерувати негативні напруги, тому потрібна більш складна схема. Наприклад, мікросхема MAX232 зробить це разом з чотирма конденсаторами 1 мкФ, щоб діяти в якості ланцюга зарядного насоса.
Існує бібліотека під назвою SoftwareSerial, яка дозволяє здійснювати послідовний зв’язок (до певної точки) в програмному, а не в апаратному забезпеченні. Це має перевагу в тому, що ви можете використовувати різні конфігурації контактів для послідовного зв'язку. Недоліком є те, що виконання послідовних програм у програмі є більш процесорним та більш схильним до помилок. Докладнішу інформацію див. У програмі Serial Software .
У Arduino "Mega" є 3 додаткові апаратні послідовні порти. Вони позначені на дошці як Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3. Їх слід використовувати, якщо можливо, SoftwareSerial. Щоб відкрити ці інші порти, ви використовуєте імена Serial1, Serial2, Serial3, як це:
Serial1.begin (115200); // start hardware serial port Tx1/Rx1
Serial2.begin (115200); // start hardware serial port Tx2/Rx2
Serial3.begin (115200); // start hardware serial port Tx3/Rx3
Як відправлення, так і отримання, використовуючи бібліотеку HardwareSerial, використовують переривання.
Коли ви робите Serial.print
, дані, які ви намагаєтеся надрукувати, розміщуються у внутрішньому буфері передачі. Якщо у вас є 1024 байт або більше оперативної пам’яті (наприклад, в Uno), ви отримуєте 64-байтовий буфер, інакше ви отримуєте 16-байтовий буфер. Якщо в буфері є місце, то Serial.print
повертається негайно, тим самим не затримуючи код. Якщо немає місця, він "блокує", чекаючи, поки буфер буде спорожнений достатньо, щоб було місце.
Потім, коли кожен байт передається апаратним забезпеченням, викликається переривання (переривання "USART, реєстр даних порожній") і програма переривання пересилає наступний байт з буфера з послідовного порту.
Після отримання даних, що надходять, викликається процедура переривання (переривання "USART Rx Complete") і вхідний байт поміщається в буфер "прийому" (той же розмір, як буфер передачі, згаданий вище).
Коли ви телефонуєте, Serial.available
ви дізнаєтесь, скільки байтів доступно в тому буфері "отримання". При дзвінку Serial.read
байт видаляється з буфера прийому і повертається до вашого коду.
У Arduinos, що має 1000 байт оперативної пам’яті, не потрібно поспішати видаляти дані з буфера прийому, за умови, що ви не дозволяєте їх заповнювати. Якщо вона заповнюється, будь-які подальші вхідні дані відкидаються.
Зауважте, що через розмір цього буфера немає сенсу чекати надходження дуже великої кількості байтів, наприклад:
while (Serial.available () < 200)
{ } // wait for 200 bytes to arrive
Це ніколи не буде працювати, оскільки буфер не може так сильно вмістити.
Перед читанням завжди переконайтеся, що дані доступні. Наприклад, це неправильно:
if (Serial.available ())
{
char a = Serial.read ();
char b = Serial.read (); // may not be available
}
Serial.available
Тест тільки гарантує , що ви є один байт доступні, однак код намагається прочитати два. Це може спрацювати, якщо в буфері є два байти, якщо ні, ви отримаєте -1 повернення, яке буде виглядати як "ÿ", якщо надруковано.
Будьте в курсі, скільки часу потрібно для надсилання даних. Як згадувалося вище, при 9600 бодах ви передаєте лише 960 байт в секунду, тому спроба надіслати 1000 показань з аналогового порту, при 9600 бод, не буде дуже успішною.