LXF164:Создавать библиотеки
|
|
|
Электроника. Аппаратные проекты с открытым кодом, расширяющие ваш кругозор
Содержание |
Arduino: Пишем библиотеки
Последняя статья! Написав, как создать собственную библиотеку Arduino и еще немного порезвиться со светодиодами, Ник Вейч зачехляет паяльник.
В прошлый раз мы говорили о мультиплексировании методом Чарли для управления несколькими светодиодами. Сейчас мы вкратце рассмотрим, как вынести все проблемы наружу, воспользовавшись специальной микросхемой для реализации логики. Программируемые микросхемы могут сильно озадачить, когда вы сидите и читаете руководство за руководством, чтобы в голове отложились используемые контакты и протоколы. К счастью, в этом случае у нас есть удобная сторонняя библиотека, которая значительно упрощает дело.
Но до того... не написать ли нам несколько своих библиотек? В мире Arduino это обычно делается, чтобы просто и безопасно использовать ту или иную микросхему с собственной логикой – хотя на практике библиотека может делать все что угодно: сложные вычисления, преобразования, процедуры обработки прерываний – что угодно. И вот вам краткое руководство по написанию собственной библиотеки, которая станет частью среды Arduino.
Основы библиотек
Как вы, наверное, знаете из предыдущих уроков, исходный код Arduino написан на C++, и в нем много вспомогательных функций из библиотек Arduino. Чтобы написать собственную библиотеку, нам придется немного углубиться в темные пещеры языка C++.
Для библиотеки на C++ нужны как минимум два файла: сам код библиотеки (в файле с расширением .cpp, так как это код на C++) и заголовочный файл для этого кода (с расширением .h). Имена файлов должны быть одинаковыми, так компилятор сопоставит их друг с другом.
Заголовочный файл – нечто вроде оглавления основного кода. Он содержит «прототипы» функций (короткая форма со списком параметров и возвращаемым значением), помогающие компилятору связать код, откуда происходит обращение к библиотеке, с самими функциями. Сюда также стоит заглянуть, если к библиотеке нет документации, ибо здесь-то и означено, что делает библиотека.
Основной код – это обычный код, как то, что вы пишете в программе для Arduino... почти. Различие в том, что в библиотеке все нужно заключить в определение класса. Мы уже пользовались классами, и даже если вы не слишком знакомы с ними, беспокоиться особо не о чем. Класс – это просто сложная переменная. Вместо одного значения в ней может быть столько значений, сколько вам надо. Кроме того, у нее могут быть собственные функции (по отношению к классу обычно называемые «методами»), выполняющие определенные действия с переменными класса. Гораздо проще пояснить все на примере. Так как на этот раз мы снова возимся со светодиодами, можно создать класс LED («светодиод»; кстати, одноименный класс существует и является частью не основного дистрибутива, а проекта Arduino. Это не он – это просто пример создания собственного класса).
Итак, у нас есть класс LED; какие у него должны быть свойства? Подумайте, о каких аспектах (т. е. свойствах) объекта (в этом случае, представляющего реальное устройство) вам нужно знать. Также прикиньте, какие свойства по сути не являются параметрами реального устройства, но помогут вам управлять им.
Свойства объекта LED могут быть такими:
» Номер контакта (т. е. номер физического контакта, к которому он подключен)
» Яркость (если он подключен к контакту с ШИМ)
» Состояние (можно ли сохранить в нем состояние светодиода или прочесть его, чтобы сказать, включен он или нет)
Одно из этих свойств особенное – это номер контакта. Без него нельзя подключить светодиод или управлять им. Поэтому номер контакта следует сделать частью «конструктора» экземпляров класса. Это означает только то, что при создании объекта LED нам обязательно придется указать номер контакта. Можно было бы создать «виртуальный» светодиод, не подключенный ни к одному контакту, но тогда пришлось бы писать более сложные методы, проверяющие, задан ли номер контакта, поэтому проще задавать номер контакта при создании объекта в обязательном порядке. Тогда наш код в файле LXFLED.cpp будет начинаться так:
/* LXF LED library */
#include “Arduino.h”
#include “LXFLED.h”
LXFLED::LXFLED(int pin)
{
pinMode(pin_number, OUTPUT);
this->_pin_number = pin;
this->_state = false;
}
В верхней части файла обычно (да так и удобнее) размещается комментарий с информацией о том, что делает код, а также с информацией обо всех копирайтах и лицензиях и, возможно, с координатами автора.
Далее мы подключаем несколько файлов точно так же, как вы подключали файлы в своих программах для Arduino. В данном случае мы хотим подключить стандартную библиотеку Arduino (скорее всего, нашей библиотеке понадобятся некоторые функции Arduino) и, что чуть более необычно, заголовочный файл для нашего кода.
Затем следует объявление самого класса. Оно всегда начинается с конструкции <Name>::<Name>, за которой следует либо пара пустых скобок, либо список параметров с типами, которые нужно передать классу при создании экземпляра объекта. Мы решили, что нам нужен номер контакта, поэтому привели его здесь. Переменная, здесь указанная, никак не касается того, что находится вне библиотеки, так что не беспокойтесь о конфликтах с переменными в основном коде. В терминах программирования, «пространство имен» ограничивается файлами библиотеки. В фигурных скобках указываются действия, которые должны произойти при создании объекта; мы устанавливаем режим работы соответствующего контакта в OUTPUT, чтобы управлять светодиодом.
Здесь также нужно задать значения всех переменных класса. Конструкция this-> – стандартная конструкция C++, которая означает, что следующая за ней переменная является членом определяемого класса. Итак, создаваемый экземпляр класса получает свойства «номер контакта» (_pin_number) и «состояние» (_state). С символа подчеркивания принято начинать имена переменных, которые являются приватными для этого класса. Это означает, что их значения можно изменять только в коде самой библиотеки, и если вы захотите изменить состояние светодиода (или номер контакта), придется написать соответствующие функции (методы!), которые должны быть частью класса. Расширим наш код, добавив несколько простых методов:
void LXFLED::on()
{
digitalWrite(_pin_number, HIGH);
this->_state = true;
}
void LXFLED::off()
{
digitalWrite(_pin_number, LOW);
this->_state = false;
}
Эти два метода просто включают или выключают светодиод и обновляют его состояние. Теперь напишем другой метод, который будет возвращать состояние светодиода:
bool LXFLED::get_state()
{
return this->_state;
}
Названию метода здесь предшествует тип возвращаемого значения; в данном случае это bool (булевское, т. е. «истина (true)» или «ложь (false)»). Этот метод просто возвращает значение свойства _state объекта.
Прежде чем перейти к заголовочному файлу, нужно сделать еще кое-что. Мы сказали, что с помощью этой библиотеки также можно управлять и светодиодами с ШИМ. Тогда при включении нужно каким-то образом задавать их яркость. На уровне Arduino яркость устанавливается легко – включением контакта методом analogWrite(), но как работать с этим значением в библиотеке? Для него можно написать отдельный метод – скажем, PWM_on() или что-то вроде этого – но в C++ есть отличный трюк, которым мы можем воспользоваться, и это «перегружение [overloading]».
Снова определим метод on(), но на сей раз с параметром:
void LXFLED::on(int level)
{
analogWrite(this->_pin_number, level);
this->_state = true;
}
Это совершенно другой метод, который задает значение ШИМ, как мы и хотели, но название метода точно такое же. Он работает благодаря тому, что компилятору C++ хватает ума понять, что если мы вызываем метод с одним аргументом типа int, то нужно выполнить именно этот код. Если аргумента нет, выполняется предыдущий метод. Можно определить сколько угодно методов с одинаковыми именами при условии, что у них у всех будут разные списки параметров (примечание: здесь имеются в виду типы параметров, т. е. нельзя создать три метода, в каждом из которых будет по одному аргументу int с разными именами).
Теперь, когда основной код библиотеки готов, нужно создать соответствующий ему заголовочный файл. У нас он будет таким:
- ifndef LXFLED_h
- define LXFLED_h
class LXFLED{
public:
LXFLED(int pin);
void on();
void on(int level);
void off();
bool get_state();
private:
int _pin_number;
bool _state;
};
#endif
Конструкция #ifndef... в начале – еще одно стандартное соглашение C++. В сложных проектах может оказаться несколько частей кода, которые пытаются импортировать одну и ту же библиотеку. Если это произойдет, последствия могут быть неприятными, поэтому в качестве меры предосторожности в заголовочном файле объявляется переменная с именем этого файла. Затем мы проверяем ее в начале – если она уже существует, это означает, что библиотека уже была подключена в какой-то другой части кода и ее можно пропустить.
Единственное, что остается сделать здесь – написать «заглушки» класса в основном коде, т. е. перечислить все переменные класса и все методы с их аргументами. Для перегружаемого метода on() нужно привести все варианты.
Чтобы избежать ошибок, лучше взять копию основного файла .cpp и просто удалить из него код, оставив заголовки функций. Так вы обеспечите, чтобы аргументы в заголовочном файле и в коде совпадали.
Ключевые слова
Другой полезный файл, который можно найти в каждой библиотеке, называется keywords.txt. Вы будете смеяться, но в нем задаются ключевые слова для кода библиотеки. Есть два типа ключевых слов – переменные и имена объекта имеют тип KEYWORD1, а методы и функции – тип KEYWORD2.
Нужно просто перечислить все это в текстовом файле с таким именем. Сначала идет слово, затем табуляция (не пробелы!), затем – тип ключевого слова. Например, наш файл с ключевыми словами для библиотеки LED будет выглядеть так:
# Datatypes
LXFLED KEYWORD1
# methods
on KEYWORD2
off KEYWORD2
get_staus KEYWORD2
Включать файл keywords.txt в содержимое библиотеки необязательно, но лучше не полениться и сделать это – благодаря ему подсветка синтаксиса в редакторе кода Arduino будет работать правильно.
Приведем пример
Последний этап создания библиотеки – пример, иллюстрирующий ее работу. Его стоит правильно отформатировать и снабдить множеством полезных комментариев – если другие не смогут понять, как пользоваться вашей библиотекой, они подумают, что та не особо хороша, и почти наверняка не угостят вас пивком, столкнувшись с вами где-нибудь на “Maker Faire” [мероприятие, проводимое американским журналом Make – семинары и презентации научных проектов, – прим. пер.] или еще где-нибудь.
Создать пример просто. Для начала нужно создать каталог examples (буквы названия – обязательно в нижнем регистре). Туда можно поместить все примеры. Если вы сохраняете их из текстового редактора, помните, что каждый пример должен находиться в одноименном каталоге, а файл примера должен иметь расширение .ino, это новое расширение для файлов «эскизов» Arduino. Создавайте столько примеров, сколько хотите, но позаботьтесь о понятных именах для них.
Среда Arduino при запуске проверяет наличие примеров в файлах библиотек и добавляет их в меню, чтобы пользователи смогли найти и попробовать ваш код. Их можно найти в меню File > Examples > [имя вашей библиотеки] > [имя вашего примера].
Здесь мы проигнорировали собственный совет и не добавили ни одного комментария, и простой пример работы с нашей библиотекой выглядит так:
- include <LXFLED.h>
LXFLED light1(5);
void setup(){}
void loop() {
light1.on(2);
delay(2000);
if (light1.get_state()) {
light1.off();
}
delay(1000);
}
Обратите внимание на отличия. Конструкция #include в начале нам уже знакома, но затем мы определяем переменную light1, объект класса LXFLED. Созданная таким образом переменная наследует все свойства и методы класса, который мы объявили ранее. При инициализации мы планировали еще и задать номер контакта, который будет выходом. Теперь мы так и делаем (код конструктора класса вызывается при создании нового экземпляра класса, т. е. при объявлении переменной light1 этого типа). Среди прочего это означает, что нам ничего не нужно делать в методе setup() для данной переменной, так как все сделает библиотека.
Зажигаем свет, гасим свет
В основном цикле вызываются методы класса LXFLED прямо из созданной нами переменной – скажем, чтобы включить светодиод, мы вызываем light1.on(). Если бы мы подключили его к контакту с ШИМ, мы вызвали бы light1.on(xxx), чтобы включить его с заданной яркостью. По правде говоря, в нашем недоделанном коде этим методом можно пользоваться даже для контактов без ШИМ (кто-то должен это поправить).
Далее, обратите внимание на то, где мы проверяем состояние светодиода. Можно было бы вызвать light.off(), но он не проиллюстрирует наше замечательное умение проверять это состояние. Так как переменная состояния задается внутри методов включения и выключения и недоступна нашей программе, вы можете быть уверены в том, что она адекватно отражает состояние. Здесь мы проверяем булевское значение в операторе if, чтобы проверить, что светодиод включен, прежде чем пытаться его выключать.
В том, чтобы делать это именно так, нет ничего особенного, но приятно увидеть, что оно работает. Метод light1.get_state возвращает булевское значение, которое затем анализируется оператором if. Да, это не бог весть что – но оно работает и иллюстрирует все ограниченные возможности нашей скудной библиотеки.
В комплекте поставки Arduino можно найти массу примеров реальных библиотек, но еще больше библиотек на все случаи жизни создали пользователи Arduino. Поискав на форумах на сайте Arduino или на других сайтах об Arduino, которые вы обычно посещаете, можно озолотиться. Если библиотека загружается в виде файла ZIP, ее можно распаковать в каталог arduino/libraries, и после перезапуска редактора Arduino она появится в нем.
Большинство библиотек на самом деле представляют собой своего рода драйверы для распространенных компонентов, таких как память, устройства хранения данных, контроллеры и т. д. Особенно полезен драйвер для микросхемы TLC5940. Как мы узнали из прошлой статьи о мультиплексировании, большими дисплеями лучше всего управлять с помощью микросхемы драйвера. На рынке их множество, но TLC5490 обладает лучшей функциональностью и достаточно проста, чтобы использовать ее с Arduino.
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
У нее 16 выходных линий, каждая из которых может работать в режиме ШИМ со значением от 0 до 4096, что восьмикратно превышает разрешение контактов ШИМ в Arduino. В ней также есть собственная схема ограничения тока, и каждый светодиод получает именно такой ток, какой ему необходим – нужно просто установить эталонное значение, подключив подходящий резистор к одному из контактов. Единственная мелкая сложность с TLC5490 – это иногда чрезмерно сложный протокол установки значений отдельных каналов (прежде всего из-за дополнительных возможностей, таких как сообщения об ошибках, которые могут возникнуть из-за неисправных светодиодов).
Так или иначе, большая часть этих сложностей скрыта от мира Arduino, потому что кто-то написал библиотеку для этой микросхемы. Вам придется написать много кода, но схемы для управления дисплеями большего размера можно соединить в гирлянду (хотя, конечно, ее максимальная длина для немерцающего дисплея будет ограничена величиной задержки распространения сигнала).
Библиотека для TLC5490 есть на нашем DVD, и мы также приводим принципиальную схему ее типичного подключения. |