Обмен через последовательный порт
Для лучшего понимания, что именно мы будем делать дальше, кратко рассмотрим устройство последовательного порта. Сначала разберемся в терминах, которые имеют отношение к предмету разговора. В компьютерах ранее всегда присутствовал СОМ‑порт, часто кратко называемый портом RS‑232. Правильнее сказать так: СОМ‑порт передает данные, основываясь на стандарте последовательного интерфейса RS‑232. UART (Universal Asynchronous Receiver‑Transmitter , универсальный асинхронный приемопередатчик) есть основная часть любого устройства, поддерживающего RS‑232. Соответственно, UART как составная часть входит практически во все универсальные микроконтроллеры, в том числе и в ATmega328, лежащий в основе Arduino. В контроллере ATmega2560 (Arduino Mega) таких портов даже целых три.
Кроме UART в порт RS‑232 (в том числе в СОМ‑порт ПК) входит схема преобразования логических уровней в уровни RS‑232, где биты передаются разнополярными уровнями напряжения, притом инвертированными относительно UART. В UART действует положительная логика с обычными логическими уровнями, где логическая единица есть высокий уровень (+3 или +5 В), а логический ноль – низкий уровень (0 В). У RS‑232 логическая единица передается отрицательным уровнем от ‑3 до ‑12 В, а логический ноль – положительным уровнем от +3 до +12 В. Преобразователь уровня в МК, естественно, не входит, так что для стыковки с компьютером придется его изобретать.
Идея передачи по интерфейсу RS‑232 заключается в передачи целого байта по одному проводу в виде последовательных импульсов, каждый из которых может быть 0 или 1. Если в определенные моменты времени считывать состояние линии, то можно восстановить то, что было послано. При этом для приемника и передатчика, связанных между собой тремя проводами («земля» и два сигнальных провода «туда» и «обратно»), приходится задавать скорость передачи и приема, которая должна быть одинакова для устройств на обоих концах линии. Эти скорости стандартизированы и выбираются из ряда: 1200, 2400, 4800, 9600, 14 400, 19 200, 28 800, 38 400, 56 000, 57 600, 115 200 (более медленные скорости я опустил)[42]. Число это обозначает количество передаваемых/принимаемых битов в секунду.
Проблема состоит в том, что приемник и передатчик – это физически совершенно разные системы, и скорости эти для них не могут быть строго одинаковыми в принципе (из‑за разброса параметров тактовых генераторов), и даже если их каким‑то образом синхронизировать в начале, то они в любом случае быстро «разъедутся». Потому в RS‑232 передача каждого байта всегда сопровождается начальным (стартовым) битом, который служит для синхронизации. После него идут восемь (или девять – если используется проверка на четность) информационных битов, а затем стоповые биты, которых может быть один, два и более, но это уже не имеет принципиального значения. Общая диаграмма передачи таких последовательностей показана на рис. 21.3.
Рис. 21.3. Диаграмма передачи данных по последовательному интерфейсуRS232 в формате 8п2
В современных компьютерах СОМ‑порт, как правило, отсутствует. Конечно, его можно обеспечить с помощью дополнительных плат или (в ноутбуках) PCMCIA‑карт, но, в общем случае, это неудобно. Куда проще воспользоваться универсальным последовательным портом USB, имеющемся практически на каждом компьютерном устройстве. Микросхемы‑переходники, обеспечивающие преобразование USB/RS‑232, носят по наименованию выпускающей их фирмы название FTDI и являются составной частью любого устройства, обеспечивающего эмуляцию протокола RS‑232 через USB. Устройство при этом имеет лишь простой UART, а преобразование обеспечивается микросхемой, которая в случае Arduino встроена в плату.
При соединении такого устройства с компьютером через USB‑кабель драйвер распознает его, как виртуальный СОМ‑порт (см. раздел об установке Arduino в этой главе). Кстати, подобную связь с компьютером имеют многие дешевые мобильники – в них со стороны телефона имеется лишь UART, в точности так же, как в МК AVR, а для связи нужен специальный и иногда довольно дорогой кабель‑адаптер с установленной внутри микросхемой FTDI или аналогичной.
Как и взаимодействие с АЦП, работа через последовательный порт в Arduino относится к базовым функциям и не требует подключения внешних библиотек. Далее мы увидим, что физически передача через последовательный порт может быть реализована далеко не только с помощью USB, – чуть позже мы рассмотрим модуль, который «прозрачно» для программиста обеспечивает передачу по радиоканалу с помощью тех же самых функций.
Сам по себе обмен через последовательный порт в Arduino немногим сложнее чтения значения аналоговой величины в только что рассмотренном примере термостата и обеспечивается набором функций Serial (см. их описание в разделе Программирование [23]). Для успешной работы спроектированного устройства совместно с Windows, если Arduino IDE в ней не устанавливалась, необходимо установить драйвер ArduinoUSBSerial.inf , входящий в комплект Arduino IDE (находится в основном каталоге размещения среды Arduino). Для обмена данными библиотека Serial использует цифровые порты платы Arduino 0 (RX) и 1 (ТХ). Разумеется, если вы используете функции Serial , то нельзя одновременно с этим использовать порты 0 и 1 для других целей, – обратите внимание, что во всех наших проектах они остаются свободными.
Простейшая пробная программа для работы с функциями serial выглядит так:
Функция Serial.write() отличается от Serial.print() тем, что первая посылает данные, как числа, а вторая позволяет организовать вывод в различных строковых форматах (см. описание функции в [23]). Мы здесь употребляем вариант второй функции под названием serial.println , который дополнительно присоединяет к выводу символы перевода строки. Многочисленные примеры употребления этих операторов в разных вариантах вы встретите далее.
* * *
Подробности
Коммуникационные функции Arduino в случае приема нескольких байтов всегда требуют тщательной отладки на макете. На них (функциях) сказываются недостатки Arduino, заключающиеся в общей замедленности работы платформы. Отслеживание данных, приходящих через последовательный порт через непредсказуемые промежутки времени, представляет собой непростую задачу даже при использовании профессионального инструментария. Тем не менее и в простейшем виде, предлагаемом стандартными возможностями Aduino IDE, с этой задачей можно справиться.
Для лучшего понимания, как это делается, стоит учесть, что с последовательным портом в Arduino связан буфер размером 64 байта (не путать с аппаратным буфером самого UART). Чтобы действительно ничего не упустить, следует вызывать функцию Serial.available о с задержкой – тогда, когда в этом буфере уже что‑то имеется, иначе считается только первый пришедший байт, а остальные могут пропасть. Потому мы без зазрения совести ставим в программе временные задержки (функцияdelay () ) при приеме нескольких байтов из компьютера или другого устройства – операция, которая в случае отсутствия такого буфера, наоборот, только привела бы к гарантированной потере данных. При формировании задержек следует ориентироваться на то, что передача одного байта на скорости 9600 занимает примерно 1 миллисекунду. Так что при приеме в пределах десятка байтов будут разумными величины задержек порядка 10 миллисекунд или несколько более на весь цикл приема. Именно такой прием мы применим при установке часов из компьютера в главе 22 , где и познакомимся с приемом последовательности нескольких байтов.
* * *
Да, а как принять посланные байты в компьютере? Для этого годится абсолютно любая программа‑монитор, позволяющая устанавливать номер порта и скорость приема. В том числе такая программа входит и в Arduino IDE (Сервис | Монитор порта ). Она заслуженно вызывает многочисленные нарекания своей примитивностью, но для каких‑нибудь простых тестовых целей вполне годится – главное, что в ней не надо ничего устанавливать, связь с устройством доступна немедленно после загрузки программы. Для более «продвинутых» читателей я рекомендую свою программу‑монитор под названием Соm2000 (ее можно скачать с сайта автора по адресу http://revich.lib.ru/comcom.zip) – она отличается тем, что позволяет организовать обмен в любом удобном формате (численном в десятичной или шестнадцатеричной форме, а также в текстовом). Входит подобная программа, как составная часть, и в утилиту X–CTU для настройки радиомодулей по протоколу ХЬее, которыми мы будем заниматься в следующей главе.
Столь простое обращение с последовательным портом позволяет дополнить программы Arduino простейшим средством отладки. Такие средства напрочь отсутствуют в среде Arduino, а без них очень трудно отлаживать более‑менее сложные программы, – ту же настройку часов из компьютера (глава 22 ) без контроля за тем, что принимается в программе и в каком виде, создать было бы просто нереально. Для этого в нужных местах программы вы расставляете операторы Serial.print() или Seriai.write() , посылающие в компьютер переменные, значения которых необходимо контролировать, и отслеживаете их состояние через монитор порта.
Другие подробности работы с последовательным портом мы узнаем по ходу дела, а сейчас займемся задачей вывода значений на индикаторы. Это отдельная задача, и не слишком простая – имеющиеся в продаже дисплеи весьма разнообразны по типу и довольно капризны в обращении.
Работа с текстом на графическом дисплее MT‑12864J
Дисплеи для вывода цифр, текста и графики, как мы уже знаем из главы 7 , встречаются нескольких разновидностей. Здесь мы остановимся на небольших матричных дисплеях, в которых изображение составляется из точек, что позволяет формировать произвольные символы и элементарные изображения. Матричные дисплеи бывают графические и строчные, отличающиеся способом управления: у них разные контроллеры, ориентированные в одном случае на адресацию каждой точки в отдельности, во втором – на вывод символов по их коду, подобно тому, как это делается в программах для компьютеров. Разумеется, эта преимущественная ориентация не исключает вывод текста на графические дисплеи или вывод примитивных изображений на строчные. Именно задачей вывода текста на графический дисплей мы сейчас и займемся, а в следующей главе познакомимся со строчными разновидностями.
Для каждого из этих типов дисплеев имеется стандартный контроллер, по образцу которого строится управление любой аналогичной матрицей. Для символьных строчных экранов стандартный контроллер называется HD44780, а для графических ту же роль играет ks0108 – с ним совместимы все графические экраны небольшого размера. Мы воспользуемся популярным отечественным графическим ЖК‑модулем MT‑12864J фирмы МЭЛТ. На сайте «Амперки» в разделе Вики легко разыскать статью «Работа с ЖК‑матрицей 128x64», рассказывающую о подключении этого модуля в стандартном графическом режиме с помощью библиотеки GLCD.
Здесь мы подробнее остановимся на некоторых нюансах практического применения ЖК‑матриц на основе контроллера ks0108, а также рассмотрим вывод текста с помощью готовых шрифтов из библиотеки GLCD и вопросы их модернизации для вывода кириллических символов.
Подключение MT‑12864J
К сожалению, контроллер ks0108 имеет параллельный восьмибитовый интерфейс, и с учетом управляющих выводов нам придется занять аж 13 функциональных контактов платы Arduino Uno. Число соединений можно сократить, если подключить ЖК‑модуль через сдвиговый регистр или, что еще проще, дешифратор двоичного кода. В первом случае число линий данных сократится с восьми до одной плюс линия импульсов сдвига, во втором – до трех. Но в обоих случаях о стандартной библиотеке придется забыть, создавать свои схемы подключения и писать свои процедуры вывода.
Поэтому мы прибегнем к стандартному способу, но внесем в него некоторые изменения. Использовать именно те выводы Arduino, что предусмотрены библиотекой GLCD по умолчанию[43], при таком их количестве не только неудобно, но часто просто невозможно. Не забудем, что ведь мы еще подключаем к контроллеру всякие другие устройства по стандартным коммуникационным портам, и стоит постараться не занимать их выводы по максимуму.
Модернизированная с учетом этого обстоятельства схема подключения MT‑12864J к Arduino Uno приведена на рис. 21.4.
Рис. 21.4. Схема подключения MT‑12864J к Arduino Uno
Как видите, у нас оставлены, свободными контакты D1 и D2 (RX и ТХ последовательного порта) и контакты А4‑А5, которые, как мы еще узнаем, участвуют в обмене через порт 12С. Одновременная работа со стандартным подключением SD‑карты, требующим выводов интерфейса SPI (11, 12, 13 и, с некоторыми оговорками, 10) здесь уже оказывается невозможной. Этот дефицит выводов станет одной из причин, по которым в проекте метеостанции, рассматриваемом в следующей главе, мы выберем другой тип дисплея. Но и ЖК‑дисплей может оказаться полезным во многих случаях, особенно при проектировании малопотребляющих приборов, работающих на батарейках.
Обратим также внимание на резисторы R1 и R2, о которых почему‑то авторы статей о графических матрицах часто забывают упомянуть. Переменный или подстроечный резистор R1 служит для регулировки контрастности индикатора. На схеме он подключен так, как рекомендуется в документации фирмы МЭЛТ, однако может подключаться и по схеме потенциометра: одним концом к «земле», другим к питанию 5 В, а средним выводом к контакту Vo индикатора. Номинал его выбирается в пределах 10–20 кОм, и в конечном продукте R1 можно заменить на постоянный резистор подобранного номинала.
Резистор R2 – токоограничивающий для LED‑подсветки. Его можно не устанавливать вовсе (и в документации он не упоминается), поэтому на схеме он обозначен пунктиром. Согласно документации МЭЛТ, ток подсветки тогда составит около 64 мА. Установить токоограничивающий резистор R2 следует в двух случаях: если вы хотите уменьшить ток (и, соответственно, пожертвовать яркостью подсветки), или если подсветка питается от отдельного источника с повышенным напряжением. Последний вариант обычно реализуется при смешанном питании, когда подсветка подключается к нестабилизированному напряжению на выходе сетевого адаптера, а при переключении на батарейку отключается вовсе. Потребление контроллера модуля при выключенной подсветке тогда составит всего 4 мА (на подробностях реализации этого способа мы здесь останавливаться не будем). При обычном напряжении на выходе адаптера (7–9 В) резистор R2 должен погасить лишние 2–4 В, соответственно, его номинал должен составлять от 39 до 62 Ом.
Отметим, что на свету дисплей MT‑12864J при отсутствии подсветки выглядит даже лучше – больше контраст и углы обзора, а сама подсветка настолько тусклая, что при посторонней засветке экрана только снижает контраст, ухудшая различимость символов. То есть фактически она требуется только при эксплуатации экрана в темноте. Зато, вопреки ожиданиям, качество дисплея в отсутствии подсветки оказалось вполне на высоте.
Скачать библиотеку GLCD можно с официального ресурса по ссылке, приведенной в сноске 5. На момент подготовки этой книги последняя версия библиотеки носит номер 3. Распакуйте ее, как обычно, в папку libraries каталога Arduino. Так как мы меняли контакты, то для начала файл библиотеки, где обозначены выводы, придется «причесать» в соответствии с нашими потребностями. Для этого разыщите в папке libraries\glcd\config файл ks0108_Arduino.h. Согласно схеме (см. рис. 21.4), установите следующие выводы (в листинге приводятся только строки файла, которые подлежат правке, а старые номера выводов закомментированы):
#define glcdData0Pin 2 //8 – так было в оригинале
#define glcdData1Pin 3 //9
#define glcdData2Pin 4 //10
#define glcdData3Pin 5 //11
#define glcdData4Pin 6 //4
#define glcdData5Pin 7 //5
#define glcdData6Pin 8 //6
#define glcdData7Pin 9 //1
#define glcdCSEL1 10 //14
#define glcdCSEL2 11 //15
#define glcdRW 12 //16
#define glcdDI 11 //15
#define glcdEN 14 //18
Русификация модуля MT‑12864J
Никакого специального текстового режима в модулях MT‑12864J не существует. Текст в них выводится просто как картинка, для чего имеются таблицы шрифтов в виде специальной функции, вызываемой через оператор SeiectFont . Чтобы русифицировать этот индикатор, для него придется создать шрифт с русскими символами. Причем создать «руками» – рекомендуемый в описании библиотеки конвертер шрифтов Windows, к сожалению, не понимает никаких символов, кроме служебных и английских. Это объясняется тем, что в UTF‑8, принятой в качестве кодировки для файлов Arduino IDE, только эти символы из стандартной таблицы ASCII однозначно переводятся в однобайтовую кодировку. Нет никаких сомнений, что авторы конвертера могли бы с этой проблемой справиться, – но только представьте, какой объем работы им пришлось бы провернуть, чтобы охватить всего десяток‑другой самых популярных языков? Так что простим их и вспомним, что благодаря открытому коду все в наших руках.
Для этого мы модернизируем имеющийся в комплекте библиотеки GLCD английский шрифт 5x7 точек, размещающийся в файле SystemFont5x7.h (папка fonts). Никакие особые инструменты для этого не нужны – немного поразмыслив над приведенными там кодами (для наглядности каждый байт следует разложить в двоичное представление и записать все пять штук один над другим), вы легко разберетесь в принципе устройства таблицы и сможете ее менять и дополнять без какого‑либо визуального редактора. Вот как, например, кодируется заглавная буква «Р» в этой системе (последовательность 0x7F, 0x09, 0x09, 0x09, 0х06):
01111111 0x7F
00001001 0x09
00001001 0x09
00001001 0x09
00000110 0x06
Если вы еще не догадались, то мысленно поверните байтовый массив на 90 градусов влево, и вы увидите букву «Р», образованную единицами. Хитрость дополнения имеющейся таблицы состоит только в том, чтобы русские буквы соответствовали их кодам, получающимся при компиляции текстового файла с программой, – вводить текст в программе через номера кодов не слишком удобно, приятнее писать его прямо по‑русски.
Правила Arduino IDE для кодирования символов второй половины байтовой таблицы символов (т. е. с номерами более 127) соответствуют младшему байту кодировки UTF‑8 (именно в ней сохраняется текстовый файл программы. ino). В русскоязычной части таблицы UTF‑8 младшие байты кодов с 80h no 8Fh занимают строчные буквы от «р» до «я», далее идут подряд 32 заглавные от «А» до «Я» (исключая букву «Ё»), а в кодах от B0h до BFh размещены оставшиеся строчные от «а» до «п». Таким образом у нас еще остается в конце байтовой таблицы незадействованная часть размером в целых 64 символа (с кодами от C0h до FFh), куда можно при надобности поместить различные служебные символы, отсутствующие в оригинале (вроде степеней или индексов). При самостоятельном дополнении таблицы не забывайте, что номера символов должны идти подряд, начиная с указанного в заголовке функции System5x7 кода 0x20 (что соответствует пробелу во всех разновидностях кодировок, основанных на ASCII). А общее количество символов следует указать в последнем параметре функции – в оригинале там стоит 0x60 (десятичное 96), у нас это число возрастает до 0хА0 (160).
Один из таких отсутствующих символов нам понадобится уже в следующей главе – ни в системном английском шрифте, ни в доработанных разными умельцами вариантах не содержится значка градуса. Чтобы больше ничего не менять в заголовке файла, мы не будем дополнять таблицу, а подставим этот‑ значок вместо одного из редко используемых символов. Закодированный в этой системе символ градуса будет выглядеть так: 0х00, 0х00, 0x0f, 0x09, 0x0f. Для замены я выбрал знак «\» («обратный слэш»), номер которого в таблице равен 0х5С. Вместо значка градуса в операторе вывода тогда придется указывать либо его код (в среде Arduino IDE это удобнее делать в восьмеричном виде так: «\134»), либо просто двойной «обратный слэш» в соответствии с правилами синтаксиса языка С.
Кроме указанных изменений, в этом шрифте я исправил цифру 0 – вместо перечеркнутого «0» скопировал для символа 0x30 строку для буквы «О» (символ номер 0x4F). Перечеркнутый ноль давно удален из всех шрифтов в пользовательских устройствах, что иногда даже бывает неудобно, – например, когда требуется воспроизвести пароль с цифрами и буквами вперемешку. И только в таблицах шрифтов для подобных индикаторов он по инерции задержался с доисторических времен господства АЦПУ и алфавитно‑цифровых терминалов, но в современном антураже выглядит довольно дико. Обидно, что для строчных дисплеев со встроенными шрифтами, подобных тем, что вы также увидите в следующей главе, этот символ так просто исправить не удастся.
Архив с файлом русифицированного шрифта под именем SystemFont5x7R.h вы можете скачать с сайта автора (http://revich.lib.ru/AVR/Rus_Lcd.zip). При его создании использованы наработки пользователя SkyFort с сайта Robocraft.ru, который и проделал основную работу по прорисовке русских символов и переводу их в hex‑коды, применяя какой‑то хитрый софт. Файлы шрифта можно размещать прямо в папке с библиотекой GLCD (там же, где размещается файл библиотеки glcd.h ) или в ее подпапке fonts. В последнем случае в директиве #include к имени файла придется добавлять название каталога (ПО образцу: #include "fonts/SystemFont5x7.h" ). В том же архиве имеется файл тестовой программы‑примера ProbaJ_CD.ino, который выводит подряд символы русского алфавита и цифр, значок градуса и в нижней строке – наименование дисплея «MT‑12864J»:
#include <glcd.h> //подключим библиотеку
#include "SystemFont5x7R.h" //файл шрифта
void setup () {
GLCD.Init(); //инициализация
GLCD. ClearScreen () ;
}
void loop ()
{
GLCD.SelectFont(System5x7); //выбираем шрифт
GLCD.CursorToXY(0,0); //установим курсор в начальную позицию
GLCD. println ("АБВГДЕЖЗИЙКЛМНОП") ;
GLCD.println("PCTyOXU41imbbIb3roH") ;
GLCD.println ("абвгдежзийклмноп") ;
GLCD.println("рстуфхцчшщъыьэюя") ;
GLCD.println("1234567890") ;
GLCD.CursorToXY(19*6,4*8) ; //установим курсор в предпоследнюю позицию 5‑й строки
GLCD.print( \\С ) ; //градус С
GLCD.CursorToXY(4*6,7*8) ; //установим курсор в позицию 4 строки 8
GLCD.print("MT‑12864J");
}
Из приведенного примера понятно, как обращаться с текстом при выводе. Текстовая зона с данным шрифтом содержит 8 строк по 21‑му символу в каждой. При выводе строки длиннее 21 символа, ее конец автоматически перейдет на другую строку. Для принудительного перевода строки используйте функциюGLCD.println () .
Чтобы правильно позиционировать вывод текста, следует иметь в виду, что библиотечная функция cursorToXY() рассчитана на графический экран 128x64 точки. По этой причине при выводе текста указывать положение курсора удобно так, как показано в примере, с учетом того, что символ занимает 6 точек по ширине, а строка – 8 точек по высоте. Поскольку позиции в строке и сами строки нумеруются с нуля, то вывод символа в предпоследнюю (20‑ю, т. е. номер 19) позицию пятой (т. е. номер 4) строки предваряем оператором cursorToXY (19*6,4*8) .
Обратите внимание, что вывод на такой дисплей всегда должен начинаться с функции установки курсора на определенную позицию – чтобы выводимые символы заменяли старые на том же месте. Иначе в следующем цикле функции loop () строки быстро поползут вверх, не давая разглядеть результата. Результат выполнения тестовой программы приведен на рис. 21.5. Фото сделано при выключенной подсветке – на снимке она была бы практически не видна.
Рис. 21.5. Результат выполнения тестовой программы для дисплея MT‑12864J
ГЛАВА 22
Метеостанция на Arduino
Дата добавления: 2016-05-11; просмотров: 3258;