На основе примера генератора кода Морзе здесь показано, как сконвертировать его функции в библиотеку (перевод официальной документации [1]). Это позволит другим людям проще использовать код, и проще обновить свой проект, когда Вы улучшите библиотеку. Начнем с простого примера скетча, который генерирует морзянку, мигая светодиодом (после его запуска он постоянно выдает сигнал SOS):
int pin = 13;
void setup()
{
pinMode(pin, OUTPUT);
}
void loop()
{
dot(); dot(); dot();
dash(); dash(); dash();
dot(); dot(); dot();
delay(3000);
}
void dot()
{
digitalWrite(pin, HIGH);
delay(250);
digitalWrite(pin, LOW);
delay(250);
}
void dash()
{
digitalWrite(pin, HIGH);
delay(1000);
digitalWrite(pin, LOW);
delay(250);
}
В этом скетче есть несколько разных частей, которые нам нужно перевести в библиотеку. Первое, конечно же, это функции 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 будет выглядеть следующим образом:
В завершение нужно обернуть заголовочный файл в стандартную конструкцию #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):
Когда выполняется эта строка кода (это происходит до вызова функции 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. 2. Arduino Style Guide for Writing Libraries site:arduino.cc. 3. Morse.zip - архив копии каталога библиотеки, содержимое архива следует поместить в папку libraries каталога установки Arduino IDE. 4. Arduino Forum > Software > Development site:forum.arduino.cc. |