- LPCXpresso Урок 12. UART. Взаимодействуем с компьютером.
- Схема
- Изучаем библиотеку
- Дорабатываем библиотеку
- Пишем код
- Запускаем
- UART — Универсальный Асинхронный Приёмопередатчик
- Общие сведения:
- Подключение:
- Пример соединения двух UNO:
- UART на Arduino:
- Arduino UNO/Piranha UNO
- Piranha ULTRA
- Arduino MEGA
- Отладка проектов при помощи UART
- Программный UART на Arduino
- UART на Raspberry Pi:
- Подробнее о UART:
- Параметры
- Кадрирование данных
- Скорость передачи данных
- Методы связи
LPCXpresso Урок 12. UART. Взаимодействуем с компьютером.
Предлагаю вашему вниманию поверхностное знакомство с UART интерфейсом в контроллере. Данный урок является вступлением к наиболее важной части (следующий урок) курса для новичков.
Что такое UART и как он выглядит можно почитать у DI HALT’а в статье AVR. Учебный курс. Передача данных через UART.
Схема
Основываться буден на схеме из пятого урока. Всё остальное можно и убрать и оставить, оно нам будет безразлично.
Для проверки связи с компьютером вам потребуется собрать и подключить один из следующих преобразователей интерфейсов:
- при наличие у компьютера COM-порта проще собрать преобразователь на MAX3232 (такой же как на MAX232) запитать его можно от вывода 3V3 платы;
- при желании подключить к USB-порту собрать преобразователь на FT232
- так же подойдет большинство data-кабелей от старых телефонов.
Хорошая новость состоит в том, что выводы RXD и TXD у контроллера LCP1343 являются 5V tolerant, то есть вы можете спокойно подавать на эти выводы сигналы напряжением 5 вольт, и это не испортит контроллер. Так что можно применить преобразователь и на MAX232, но питание 5В надо будет брать где-нибудь с другого места.
Если у вас нет возможности подключить преобразователь интерфейса, то вам все равно следует выполнить данный урок.
Изучаем библиотеку
Код для работы с UART уже присутствует в библиотеке LPC13xx_Lib и расположен в файлах uart.c и uart.h. Давайте рассмотрим его упрощенную версию (это вам пригодится для понимания работы контроллера, для использования библиотеки вам этого знать не надо). Я исключил для статьи условную компиляцию, дабы не перегружать вас информацией. Будучи уверенными что, вы поняли данный материал, попробуйте самостоятельно разобраться в смысле исключенного кода.
Всё с чего-то начинается. Мы начнём с инициализации которой мы передаем один параметр – желаемая скорость работы UART:
Зачем эта морока с разрешением доступа к регистрам коэффициента деления для скорости передачи, спросите вы. Таблица 186 UM10375 служит ответом. Просто регистр DLM делит один адрес с регистром IER, а регистр DLL с регистрами RBR и THR (Чем-то похоже на PIC’и, может MicroChip подрабатывал у NXP?).
По расчету коэффициент деления существует раздел 11.6.15 в UM10375. На самом деле он посвящен регистру FDR, позволяющему получить дробные коэффициенты деления. Но в нем приведены формула расчета скорости и примеры расчета. Поскольку мы не меняли значение регистра FDR, мы воспользовались простой формулой: системная частота делится на делитель системной шины, затем на делитель тактовой частоты UART, затем на 16 (так зашито в железку) и на желаемую скорость.
UART, как и SPI, имеет собственные буферы приёма и передачи. Размеры их одинаковые и составляет 16 байт. Очистка их проводится «для надежности».
Далее следовало бы упомянуть об описания «глобальных» переменных
Здесь применяется свой кольцевой буфер приёма (не путать его с внутренним RX буфером приёма). Прочитать что это такое можно, например, здесь.
Поскольку разрешены прерывания от модуля UART нужна и функция обработки этих прерываний:
Почему в двух местах чтение принятых данных? Прерывание RDR (Receive Data Ready) возникает, когда в регистре RBR (Receiver Buffer Register), а значит и в буфере приёма есть не считанные данные. Прерывание RDA (Receive Data Available) возникает по достижению в буфере приёма установленной границы или по доступности данных. В нашем случае, как можете наблюдать, срабатывает RDA.
И нам осталось разобраться только с передачей данных. Функция принимает в качестве параметров указатель на данные для передачи и количество байт, которые надо передать:
Флаг THRE (Transmitter Holding Register Empty) ни что иное, как признак возможности записи в регистр THR (Transmitter Holding Register) – регистр записи в буфер передачи.
Вот собственно и всё. Как видите, для чтения принятых данных нам предлагается некий кольцевой буфер. Но зато настройка порта и передача данных просты в применении.
Дорабатываем библиотеку
Нам надо сделать библиотеку чуть более удобной для использования. В частности добавить функцию чтения данных из кольцевого буфера приема:
Приведённая функция считает в буфер по адресу переданному в параметре BufferPtr все имеющиеся в буфере приема данные, но не более количества, указанного в параметре Size. Функция возвращает количество сохраненных байт. Если в буфере приёма нет данных (ничего не было принято), то функция просто вернёт нуль.
Так же напишем функцию получения одного символа:
Она сводится к вызову предыдущей функции, но в отличие от неё будет блокировать выполнение программы, пока символ не будет считан.
Так же добавлена функция UARTPutChar, для передачи по UART одного символа, она элементарна и сводится к вызову уже имеющейся функции UARTSend.
Пишем код
Передавать по COM-порту в компьютер данные с АЦП я не стану, это не интересно.
Итак, функция main кроме инициализации портов ввода-вывода обзавелась следующим функционалом:
Сразу после настройки UART мы передаем по нему символ ’>’ с простой целью – что-то же надо передать. Далее уходим в бесконечный цикл обработки принятых по UART данных:
- приняв символ вопроса – проверяем состояние кнопки и передаем по UART строку “down” с переводом строки если кнопка нажата или “up” с переводом строки если нет. Приведение типа (void*) нужно только что бы компилятор ни выдавал предупреждение о несоответствии типов;
- приняв символ плюс – зажигаем светодиод;
- приняв символ минус – гасим светодиод;
- приняв цифру от 0 до 8 – передаем по UART цифру на единицу больше.
Да, я долго думал над составлением самого «бесполезного и ненужного» примера.
Запускаем
Обладатели преобразователей интерфейсов для подключения к компьютеру могут запустить на выполнение полученные 4325 байт «бесполезного» кода. На компьютере можно воспользоваться программой Terminal v1.9b, стандартной для Windows программой Hyper Terminal или любой другой пригодной. Настраиваете COM-порт, к которому подключен контроллер, на скорость 9600 бод, с 8 битами данных, 1 стоп битом, без контроля четности, без аппаратного контроля передачи.
Выполнив обмен данными, вы увидите, что кроме демонстрации наличия обмена, ничего больше и не получите от данного примера.
Тех, у кого указанных средств не оказалось, я приглашаю в следующий урок (в процессе подготовки).
UART — Универсальный Асинхронный Приёмопередатчик
Общие сведения:
Универсальный асинхронный приёмопередатчик (Univsersal Asynchronos Reciever-Transmitter) — это физическое устройство приёма и передачи данных по двум проводам. Оно позволяет двум устройствам обмениваться данными на различных скоростях. В спецификацию UART не входят аналоговые уровни на которых ведётся общение между устройствами, UART это протокол передачи единиц и нулей, электрическую спецификацию на себя берут другие стандарты, такие как TTL (transistor-transistor logic — транзисторно-транзисторная логика), RS-232, RS-422, RS-485 и другие (RS [англ.recommended standard] — рекомендованный стандарт). На данный момент в микроконтроллерах используется в основном TTL (или точнее CMOS) UART для соединения не более двух устройств. В наших примерах мы часто называем его последовательным портом.
Подключение:
У каждого устройства, поддерживающего UART обычно обозначены два вывода: RX и TX. TX — означает transmit (передаю), RX — receive (принимаю). Отсюда становится понятно что RX одного устройства нужно подключать к TX другого. Если Вы подключите RX одного устройства к RX другого, то оба устройства будут слушать друг друга, вы соединили их входы. Если соединить TX и TX — это уже более опасно, это выходы низкого сопротивления устройств и если на одном будет логическая единица, а на втором ноль — по проводу пойдёт ток короткого замыкания (это зависит от конкретной программной или аппаратной реализации). Хотя в современных чипах от этого есть защита, на всякий случай, не стоит на неё ориентироваться. Так же необходимо объединить референсные уровни двух устройств (GND—GND), если не подразумевается гальваническая развязка.
Пример соединения двух UNO:
UART на Arduino:
На Arduino и Arduino-совместимых платах аппаратный UART обозначается символами RX и TX рядом с соответствующими выводами. На Arduino UNO/Piranha UNO это 0 и 1 цифровые выводы:
Arduino UNO/Piranha UNO
В скетче инициализируется функцией begin() в коде функции setup():
Пример:
Piranha ULTRA
На Piranha ULTRA присутствуют два аппаратных UART. Один на тех же выводах, что и UNO, второй на 8 (RX) и 9 (TX) выводах:
В Arduino IDE второй аппаратный UART называется Serial1 (Сериал один), и инициализируется так же как и первый:
Простой пример для копирования буфера первого UART’а во второй и наоборот:
Arduino MEGA
У Arduino MEGA, помимо UART’a на цифровых выводах 0 и 1 как и у UNO, присутствуют ещё три аппаратных UART. На плате это выводы 19 (RX1), 18 (TX1), 17 (RX2), 16 (TX2) и 15 (RX3), 14 (TX3) соответственно. UART совместимый по расположению с UNO обозначен RX0, TX0:
На заметку: На многих Arduino и Arduino-совместимых платах UART0 используется для загрузки скетчей, так что если Ваш скетч не загружается, проверьте эти выводы. Во время загрузки скетча к ним ничего не должно быть подключено.
Отладка проектов при помощи UART
В совокупности с монитором последовательного порта UART может быть очень полезен для отладки кода. Например, если не понятно доходит ли Ваш код до определённого места или какие значения принимает переменная, можно вызвать функцию println() и вывести значение переменной или какое-нибудь сообщение в монитор порта. В Arduino IDE монитор порта находится в меню Инструменты -> Монитор порта, так же его можно вызвать нажав пиктограмму лупы в правом верхнем углу главного окна Arduino IDE или вызвать с клавиатуры комбинацией клавиш Ctrl+Shift+M. Перед вызовом монитора порта необходимо выбрать порт с которым Вы хотите работать. Сделать это можно в меню Инструменты -> Порт.
Для удобства отладки можно указать директивы препроцессора языка Си #define , #ifdef , #ifndef
Пример:
Программный UART на Arduino
Помимо аппаратного UART в Arduino можно использовать программный. Программный порт хорошо подходит для простых проектов, не критичных к времени работы кода или для отладки проектов, позволяя не отключать модули использующие UART во время загрузки сетчей. При его использовании нужно лишь помнить что никакой другой код не может выполняться пока программа занимается считыванием данных из него и передача может осуществляться только в полудуплексном или симплексном режимах. Так же на программный RX можно назначать только те выводы, которые поддерживают прерывание по смене уровней. На UNO, например, это все цифровые выводы, кроме 13-го. Прежде чем собирать свой проект, проконсультируйтесь с инструкцией к конкретной плате.
Пример использования программного порта:
Далее к программному порту нужно обращаться через объект mySerial . Например: mySerial.write(data); .
UART на Raspberry Pi:
На Raspberry Pi UART находится на выводах колодки 8 — TX (GPIO14) и 10 — RX (GPIO15)
Перед работой с последовательным портом необходимо его включить. Сделать это можно из эмулятора терминала командой sudo raspi-config -> Interfacing options -> Serial -> No -> Yes -> OK -> Finish или из графической среды в главном меню -> Параметры -> Raspberry Pi Configuration -> Interfaces -> Serial Port
Пример работы с последовательным портом на Python:
Данный пример выводит строку «iArduino.ru» в последовательный порт Raspberry и ждёт данных из последовательного порта.
Подробнее о UART:
Параметры
При обозначении параметров UART принято использовать короткую запись ЦИФРА—БУКВА—ЦИФРА
- ЦИФРА — количество бит в кадре
- от 5 до 9 бит. Обычно 8.
- БУКВА — наличие и тип бита чётности
- N — None (Отсутствует) без бита чётности
- E — Even (Чётный). Проверка данных на чётность. Перед стоп-битом в кадр добавляется бит: 0 если в кадре было нечётное количество единиц, 1 — если чётное.
- O — Odd (Нечётный). Проверка данных на нечётность. Перед стоп-битом в кадр добавляется бит: 1 если в кадре было нечётное количество единиц, 0 — если чётное.
- ЦИФРА — длительность стоп-бита
- 1, 1.5, 2. Продолжительность стоп-бита (1, 1.5 или 2 битовых интервала)
Таким образом, стандартные настройки в Arduino: 8-N-1
Кадрирование данных
При приёме-передаче данных каждое устройство ориентируется на своё внутреннее тактирование. Обычно это тактирование от 8 до 16 раз быстрее скорости передачи данных и обычно отсчитывается от стартового бита. Именно поэтому необходимо чтобы оба устройства были настроены на одну и ту же скорость передачи.
Так же при передаче данных присутствуют синхронизирующие биты, именуемые старт-бит и стоп-бит. Старт-бит сигнализирует о начале передачи данных и стоп-бит, соответственно об окончании.
Рассмотрим кадр данных:
При разговорах о серийный протоколах принято использовать такие слова как кадр и пакет. Кадр — интервал от старт-бита до стоп-бита. Пакет — количество кадров полезных данных. При этом не стоит путать кадр и байт: байт — это только сами данные, не включающие в себя синхронизирующие и проверочные биты.
Старт-бит:
При отсутствии передачи линия удерживается в состоянии логической единицы (в случае TTL Arduino это 5 вольт или Vcc). Как только передающее устройство притягивает линию к 0 (GND или 0 вольт в случае Arduino), это сигнализирует принимающему устройству о том что сейчас будет передача данных.
Данные:
При появлении старт-бита на линии принимающее устройство начинает отсчитывать время в соответствии с установленной скоростью и считывать состояния линии через определённые промежутки времени в соответствии с установленным количеством бит данных, после этого.
Стоп-бит:
По завершении передачи данных принимающее устройство ожидает стоп-бит, который должен быть на уровне логической единицы. Если по завершении кадра удерживается логический ноль, значит данные неверны. Если логический ноль удерживается время, превышающее длину кадра в 1,5 раза, такое состояние именуется break (разрыв линии, исторически пошло от устройств, использующих токовую петлю для передачи данных). Некоторые передатчики вызывают это состояния специально перед посылкой пакета данных. Некоторые приёмники считают такое состояние за неправильно выставленную скорость и сбрасывают свои настройки на установки «по умолчанию».
Скорость передачи данных
Скорость изменения логических уровней (импульсов) на линии принято измерять в бодах. Единица измерения названа так в честь французского изобретателя Жана Мориса Эмиля Бодо.
Скорость при использовании UART может быть любой, единственное требование — скорости передающего и принимающего должны быть одинаковы. Стандартная скорость UART принята за 9600 бод. Arduino без проблем и лишних настроек может принимать и передавать данные на скоростях до 115200 бод.
Так как при передаче данных присутствуют синхронизирующие биты, именуемые старт-бит и стоп-бит, не совсем корректно говорить, что скорость 9600 бод равна 9600 битам в секунду. Если речь идёт о полезных данных, то реальная скорость на 20% ниже. Например, если выставлены параметры 8-N-1 и 9600 бод, то на передачу одного байта уходит десять бит, и 9600/10 = 960 байт, что равно 7680 битам в секунду.
Методы связи
UART позволяет одновременно передавать и принимать данные, однако не всегда это возможно или нужно. Например, если Вам нужно только получать не критические данные (которые можно проверить следующим пакетом, например расстояние, посылаемое лидаром каждые несколько сотен миллисекунд) от цифрового датчика или любого другого устройства и не нужно ничего передавать, такой метод называется симплексным. Всего различают три метода связи:
- Полнодуплексная — когда ведущий и ведомый могут одновременно принимать и передавать (одновременная передача в обе стороны)
- Полудуплексная — когда ведущий и ведомый поочерёдно принимают и передают (Поочерёдная передача в обе стороны)
- Симплексная — когда ведущий или ведомый только передают (Передача в одну сторону)