Arduino: как создать свою библиотеку кода? |
![]() |
Добавил(а) microsin |
На основе примера генератора кода Морзе здесь показано, как сконвертировать его функции в библиотеку (перевод официальной документации [1]). Это позволит другим людям проще использовать код, и проще обновить свой проект, когда Вы улучшите библиотеку. Начнем с простого примера скетча, который генерирует морзянку, мигая светодиодом (после его запуска он постоянно выдает сигнал SOS): int pin = 13; В этом скетче есть несколько разных частей, которые нам нужно перевести в библиотеку. Первое, конечно же, это функции dot() и dash(), которые выполняют основную работу по миганию светодиодом . Второе это переменная ledPin, которая используется функциями - эта переменная определяет, какой вывод порта использовать. И наконец, здесь есть вызов pinMode(), который настраивает вывод порта как выход. Итак, давайте превратим код скетча в библиотеку. Для библиотеки нам нужно как минимум 2 файла: файл заголовка (header file, файл с расширением .h) и файл исходного кода (с расширением .cpp). В заголовочном файле есть определения для библиотеки: в основном здесь кратко описано все, что есть внутри библиотеки; файл исходного кода в то же время содержит действительный код. Назовем нашу библиотеку "Morse", так что заголовочный файл получит имя Morse.h. [Заголовочный файл библиотеки] Ядро файла заголовка состоит из нескольких строк, в каждой из которых описана одна функция, и все это объединено в класс вместе со всеми переменными, которые Вам нужны: class Morse { public: Morse(int pin); void dot(); void dash(); private: int _pin; }; Класс это просто набор функций и переменных, содержащихся в одном месте. Функции и переменные имеют атрибут public, который означает, что эти функции доступны для всех, кто использует библиотеку, или private, который означает, что доступ возможен только из самого класса. В каждом классе есть специальная функция, называемая конструктором, которая используется для создания экземпляра (instance) класса. Конструктор всегда имеет то же самое имя, что и имя класса, и у не указан возвращаемый тип (потому что заранее известно, что он возвратит тип класса). Примечание: определение класса в заголовке можно рассматривать как пользовательский ТИП (по аналогии с typedef struct), а экземпляр класса можно рассматривать как ПЕРЕМЕННУЮ, которая имеет этот тип. Конструктор предназначен для инициализации этой переменной. Может быть также еще и деструктор, который предназначен для уничтожения переменной класса. Наличие и конструктора, и деструктора необязательно: это зависит от реализации класса. Кроме определения класса, в заголовочный файл нужно добавить подключение заголовка Arduino.h директивой #include. Это дает доступ к стандартным типам и константам языка Arduino. Заголовочный файл Arduino.h автоматически добавляется при компиляции скетча (см. [3]), но для библиотек это нужно сделать самому. Подключение заголовка Arduino.h будет выглядеть следующим образом: #include "Arduino.h"
В завершение нужно обернуть заголовочный файл в стандартную конструкцию #ifndef / #define / #endif (это защита от повторного включения заголовка []). Также в начало заголовка добавляют комментарии, описывающие содержимое библиотеки. В результате получится вот такой заголовок: /* Morse.h - библиотека для генерации кода Morse. Автор David A. Mellis, 2 ноября 2007. Выпущено для публичного использования. */ #ifndef Morse_h #define Morse_h #include "Arduino.h" class Morse { public: Morse(int pin); void dot(); void dash(); private: int _pin; }; #endif [Файл исходного кода библиотеки] Займемся конструктором. В нем расположен код, который вызывается при создании экземпляра класса. В нашем примере пользователь в параметре конструктора указывает номер цифрового порта, который будет использоваться. Таким образом, конструктор у нас будет конфигурировать ножку порта, и сохранит номер используемого порта в частную переменную _pin (эта переменная будет использоваться функциями класса): Morse::Morse(int pin) { //Конфигурируем порт как выход: pinMode(pin, OUTPUT); //Сохраним номер порта во внутреннюю переменную класса: _pin = pin; } В коде конструктора видно кое-что, с чем мы раньше не встречались. Первое это необычный префикс Morse:: перед именем функции. Это говорит о том, что функция входит в состав класса Morse. Тот же самый префикс мы увидим позже в определении других функций класса. Второе это private-переменная _pin. В принципе имя этой переменной может быть любым, лишь бы оно соответствовало определению класса в файле заголовка. Добавление символа подчеркивания к имени файла является стандартным соглашением - чтобы сразу было видно, что это частная (private) переменная, и чтобы получить отдельное имя, отличающееся от имени входной переменной функции конструктора. Теперь напишем код для функций библиотеки. Код будет выглядеть точно таким же, как он был в скетче, за исключением того, что к имени функции будет добавлен префикс Morse::, и будет использоваться частная переменная _pin вместо глобальной переменной pin. Нужно также добавить подключение заголовочных файлов и текст комментария в начало файла. В результате получится файл исходного кода (модуль) библиотеки: /* Morse.h - библиотека для генерации кода Morse. Автор David A. Mellis, 2 ноября 2007. Выпущено для публичного использования. */ #include "Arduino.h" #include "Morse.h" Morse::Morse(int pin) { pinMode(pin, OUTPUT); _pin = pin; } void Morse::dot() { digitalWrite(_pin, HIGH); delay(250); digitalWrite(_pin, LOW); delay(250); } void Morse::dash() { digitalWrite(_pin, HIGH); delay(1000); digitalWrite(_pin, LOW); delay(250); } [Как использовать библиотеку] Файлы библиотеки готовы. Теперь надо разобраться, как использовать библиотеку в среде разработки Arduino. Сначала создайте папку Morse как поддиректорию в папке libraries (папка libraries находится в каталоге установки системы Arduino, полный путь до папки Morse будет наподобие C:\Program Files\Arduino\libraries\Morse). Скопируйте файлы Morse.h и Morse.cpp в директорию Morse. После этого запустите среду разработки Arduino IDE. Если Вы зайдете в меню Sketch -> Import Library (Скетч -> Импортировать библиотеку), то среди списка библиотек Вы должны увидеть Morse. Библиотека будет компилироваться вместе с теми скетчами, которые её используют. Если у Вас имеются проблемы при создании или компиляции библиотеки, то проверьте, что файлы действительно имеют расширения .cpp и .h - например, без дополнительного расширения .pde или .txt (имейте в виду, что подобная ситуация может произойти, когда в Проводнике Windows отключено отображение расширений зарегистрированных типов файлов). Теперь давайте посмотрим, как можно создать скетч, который будет делать то же самое, что и раньше, но с использованием библиотеки: #include < Morse.h > Morse morse(13); void setup() { } void loop() { morse.dot(); morse.dot(); morse.dot(); morse.dash(); morse.dash(); morse.dash(); morse.dot(); morse.dot(); morse.dot(); delay(3000); } Очевидно, что есть несколько различий от старого скетча (основанные на том, что некоторая часть кода перемесилась в библиотеку). Кроме того, код значительно сократился, и стал читабельнее. Давайте рассмотрим эти отличия. Директива #include. Первое отличие - добавлен оператор #include в самом начале скетча. Это делает библиотеку Morse доступной для использования внутри скетча, и её код (при компиляции) будет добавлен в общий код, который будет впоследствии прошит в память микроконтроллера платы Arduino. Это означает, что если больше в скетче не нужна библиотека, то Вы должны удалить подключение её заголовочный файла (закомментировать или удалить строку с соответствующей директивой #include). Обратите внимание, что имя подключаемого файла теперь указано в угловых скобках, а не в двойных кавычках. Это обычная практика программирования на языках C/C++: имена стандартных библиотечных файлов в директиве #include указываются с использованием угловых скобок, а подключение заголовочных файлов пользователя указывается с помощью двойных кавычек. Вызов конструктора класса. Второе отличие - как создается переменная класса Morse (в нашем примере мы создаем переменную класса morse): Morse morse(13); Когда выполняется эта строка кода (это происходит до вызова функции setup()), то будет вызвана функция конструктора класса Morse, и ей будет передан аргумент (в нашем случае номер цифрового порта 13). Обратите внимание, что функция setup() теперь пуста; это потому, что вызов pinMode() происходит внутри библиотеки (когда конструктор создает экземпляр переменной класса). Вызов функций класса. Последнее отличие - функции dot() и dash() вызываются с префиксом из имени экземпляра класса и точки (morse.). У нас может быть несколько экземпляров класса Morse, каждый из них будет использовать свой номер порта. Путем вызова функции с отдельным экземпляром класса, мы указываем, какие экземпляры переменных используются для вызова функции. К примеру, если у нас есть два экземпляра класса: Morse morse1(13); Morse morse2(12); то внутри вызова morse2.dot(), значение переменной _pin будет 12. [Подсветка синтаксиса библиотеки] Если Вы попробуете использовать библиотеку в новом скетче, то не увидите, что наша библиотека не будет подсвечиваться особым образом в редакторе кода Arduino IDE, как это принято для стандартных библиотек (например, для общеизвестной библиотеки Serial). К сожалению, программное обеспечение среды разработки Arduino не может автоматически распознать определения в Вашей библиотеке (что было бы приятной возможностью), так что Вам нужно ей немного в этом помочь. Чтобы настроить подсветку синтаксиса для Вашей библиотеки, создайте файл keywords.txt в каталоге Morse. Его содержимое должно выглядеть так: Morse KEYWORD1 dash KEYWORD2 dot KEYWORD2 В каждой строке должно быть имя ключевого слова, за которым через табуляцию (не через пробелы) должен идти тип ключевого слова. Имя класса должно быть обозначено как KEYWORD1, и это дает оранжевую подкраску; функции (методы) класса должны обозначены как KEYWORD2, и имена функций будут коричневыми. После создания файла keywords.txt в каталоге Morse нужно перезапустить среду разработки Arduino, чтобы она распознала новые ключевые слова. [Пример кода использования библиотеки] Вместе с библиотекой полезно предоставить пример скетча с демонстрацией использования библиотеки. Для этого в директории Morse создайте подкаталог examples. Затем скопируйте или переместите папку, содержащую код скетча (пусть эта папка называется SOS), в папку examples. Где находится копируемый скетч можно узнать из меню Sketch -> Show Sketch Folder (Скетч -> Показать папку скетчей). Если после этого Вы еще раз перезапустите среду разработки Arduino, то в меню File -> Sketchbook -> Examples (Файл -> Примеры) увидите Morse, и внутри неё пример SOS. Также для кода примера можно создать дополнительные комментарии, чтобы лучше разъяснить, как использовать Вашу библиотеку. Если Вы хотите проверить содержимое готовой библиотеки, то можете загрузить её: Morse.zip [3]. Вы также можете просмотреть реализации других готовых библиотек, которые можете найти в папке libraries каталога установки Arduino. Дополнительную информацию по Arduino-стилю оформления библиотек можете получить из [2]. Если у Вас есть вопросы, то можете задать их на форуме разработчиков Arduino [4]. [Ссылки] 1. Writing a Library for Arduino site:arduino.cc. |