Arduino: передача данных по UART

Универсальный асинхронный приёмопередатчик (Universal Asynchronous Receiver-Transmitter, UART) — один из самых популярных интерфейсов для передачи данных между электронными устройствами. Как правило все микроконтроллеры имеют встроенный узел связи UART. И микроконтроллер atmega328, на котором основана плата Ардуино, не является исключением.

Для чего используют UART? Вот несколько примеров:

  • Arduino подключается к персональному компьютеру через UART и следующий за ним USB-UART мост;
  • два контроллера часто соединяют между собой по UART;
  • как правило, GPS модули имеют только UART интерфейс;
  • bluetooth и другие радиомодули тоже часто используют UART.

Почти на каждом уроке мы практикуем передачу данных от Ардуино к персональному компьютеру для отладки программ и проверки различных датчиков. Да, мы используем при этом USB кабель, но фактически, Ардуино сначала передает данные в UART. И только потом, проходя через USB-UART мост, эти данные трансформируются в USB пакеты и улетают в компьютер.

Сегодня же мы соединим две платы Ардуино между собой непосредственно только с помощью UART интерфейса.

Список необходимых компонентов

Для выполнения всех экспериментов в этом уроке, потребуется два контроллера Arduino Uno или их любые аналоги, модуль фоторезистора, светодиод с токозадающим резистором и немного проводов.

Необходимые компоненты можно добавить в корзину прямо здесь, и затем оформить заказ в нашем интернет-магазине.

В корзину
В корзину
В корзину
В корзину
В корзину
В корзину

Подключение

Передача данных в интерфейсе UART осуществляется по двум проводам. При этом, обе стороны, участвующие в обмене данными, подключаются крест-накрест:

Интерфейс UART

RX — от английского receiver — приёмник, а TX — tranceiver — передатчик.

Микроконтроллер atmega328 имеет один встроенный UART узел, который соединен с контактами D0 и D1. Контакт D0 соответствует линии RX, а D1 — TX. Чтобы связать две платы Ардуино между собой, соединим линии RX и TX крест-накрест, а также свяжем платы общей линией питания Gnd.

Подключение двух Arduino по UART

Теперь приступим к написанию программ для обоих контроллеров.

Ведущий и ведомый (master/slave)

Пусть одна плата будет отправлять каждые 500мс какие-то данные второй плате. Вторая плата будет их принимать и сигнализировать об этом изменением состояния штатного светодиода на контакте №13 (то есть мигать).

В этой задаче первая плата будет называться ведущей, или по-английски master. А вторая ведомой — то есть slave.

Программа ведущего

Для этого мы используем две функции встроенного объекта Serial:

Serial.begin(9600); // инициализирует UART со скоростью 9600 бод
Serial.write(128); // отправляет в UART один байт данных, например, число 128

Полностью программа ведущего будет выглядеть так.

void setup() {
    Serial.begin(9600); // инициализируем UART со скоростью 9600 бод
}

void loop() {
    Serial.write(1); // отправляем в UART число 1
    delay(500); // ждём 500мс
}

Программа ведомого

Для чтения одного байта данных вызываем функцию read и присваиваем её результат переменной v:

byte v = Serial.read(); // считывает 1 байт из буфера UART

Также нам понадобится функция available(), которая возвращает количество байт принятые узлом UART. Таким образом, мы будем менять состояние светодиода только если в порт что-то пришло.

byte ledPin = 13;
byte ledState = 0;
byte v;

void setup() {
    Serial.begin(9600);
    pinMode( ledPin, OUTPUT );
}

void loop() {
    if( Serial.available() ){
        v = Serial.read(); // считываем один байт из UART
        digitalWrite( ledPin, ledState ); // включаем/выключаем светодиод
        ledState = !ledState; // меняем значение ledState
    }
}

Загружаем программы на платы и подаем питание. Ведомая плата должна начать мигать светодиодом.

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

В следующем примере попробуем передать что-то более полезное, чем просто число 1.

Передача показаний датчика

Пусть теперь первая плата Ардуино считывает значение датчика освещенности на основе фоторезистора и передает эти показания в UART. Вторая плата будет принимать данные, и менять яркость светодиода согласно полученному значению.

Для этого примера к ведущей плате подключим датчик по схеме из нашего урока. А к ведомой плате — светодиод к любому контакту с ШИМ выходом (для Arduino Uno это контакты: 3,5,6,9,10,11). ШИМ нам понадобится чтобы плавно менять яркость светодиода функцией analogWrite.

Программа ведущего

byte photoPin = A0;

void setup() {
    Serial.begin(9600);
    pinMode(photoPin, INPUT);
}

void loop() {
    int val = analogRead(photoPin);
    val = val*255/1023.0;
    Serial.write(val);
    delay(100);
}

В этой программе мы считываем значение АЦП, к которому подключен фоторезистор, с помощью функции analogRead. Вот только проблема в том, что в UART мы можем передать только один байт, а analogWrite возвращает нам число от 0 до 1023. Чтобы обойти эту неприятность, просто нормируем большое число в диапазон от 0 до 255. Делается это формулой:

val = val*255/1023.0;

Программа ведомого

byte ledPin = 3;

void setup() {
    Serial.begin(9600);
    pinMode( ledPin, OUTPUT );
}

void loop() {
    if( Serial.available() ){
        byte v = Serial.read();
        // передаем полученное из UART значение в функцию analogWrite
        analogWrite( ledPin, v);
    }
}

Загружаем программы на контроллеры и подаем питание. Для проверки закрываем фоторезистор рукой и наблюдаем за поведением светодиода, он должен менять яркость.

Как уже было замечено, в этой программе нам пришлось огрубить показания датчика, иначе они не влазили в UART. Эта проблема решается разложением числа на байты и последовательной передачей этих байт в UART (или любой другой последовательный интерфейс). Например, так:

int val = analogRead(photoPin);
Serial.write(val & 0xFF); // передаем младший байт числа val
Serial.write((val>>8) & 0xFF); // передаем старший байт числа val

В программе ведомого, соответственно, нужно будет поймать эти два байта и склеить, например, так:

    if( Serial.available()>1 ){
        byte v1 = Serial.read();
        byte v2 = Serial.read();
        int v = v1 | (v2<<8);
    }

Но это не тема нашего урока. Лучше попробуем передавать данные в оба направления!

Передача в обоих направлениях

Загрузим на оба контроллера две одну и ту же программу, которая будет и отправлять данные и принимать их.

byte ledPin = 13;
byte ledState = 0;
byte v;

unsigned long tm, send_next;
unsigned int send_to = 500; // отправка каждые 500мс

void setup() {
    Serial.begin(9600);
    pinMode( ledPin, OUTPUT );
}

void loop() {
    // проверка наличия данных в буфере UART
    if( Serial.available() ){
        v = Serial.read();
        digitalWrite( ledPin, ledState );
        ledState = !ledState;
    }
    // отправка данных по таймауту
    tm = millis();
    if( tm > send_next ){
        send_next = tm + send_to;
        Serial.write('A');
    }
}

После подачи питания, оба контроллера начнут мигать светодиодами, что означает — данные идут в оба направления!

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

Вот и всё. Теперь можно соединять Ардуино между собой и необязательно проводами. Например, тот же Bluetooth модуль или мощные радиомодемы работают по UART интерфейсу, а значит мы способны передавать данные по радиосвязи на приличные расстояния!


Изменено:

Arduino: передача данных по UART: 3 комментария

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.