Добрый день. Мне нужно вынести функции из программы в отдельный файл (их слишком много и код постоянно приходится пролистывать). Проблема в том, что мне не нужны классы и прочие навороты (как описано в мануалах на сайте ардуино, тем более, у меня не получилось создать свою библиотеку), нужно просто скрыть их с глаз, но чтобы они работали. Как это можно сделать? Хоть бы даже простой txt файлик подключить, они ведь без форматирования. Спасибо.
Простой способ, работающий, но совершенно ни разу не правильный (не соответствует догматам C/C++): Выделяем часть кода в отдельный файл, укладываем рядом со скетчем, а потом в том месте скетча, где оно должно находиться, вставляем конструкцию #include "имя нашего файла с выделенными кусками кода". Препроцессор еще до компиляции врежет содержимое дополнительного файла в скетч в том самом месте, где находится директива #include Вариант второй, более правильный: Выделяем часть кода, содержащую требуемые функции в отдельный файл. При этом данные функции не должны использовать никаких функций, переменных и классов из основного файла программы. Требование необязательное, но его несоблюдение потребует построения дополнительных связей в виде объявления переменных из другого модуля как extern, описания прототипов функций и создает излишнюю сложность связей кода и серьезно осложняет повторное использование кода. Короче говоря, лучше без крайней необходимости так не делать. файлу даем какое-нибудь имя и расширение .cpp. Например, file.cpp. Делаем второй файл с прототипами функций, которые будут использоваться в основном файле программы. Даем ему то же имя, но другое расширение - .h Выкладываем получившиеся два файла в папку со скетчем. добавляем в начало скетча строку #include "имя получившегося файла с расширением.h" Сверхпростой и в общем-то бессмысленный пример. Допустим, что в наш файл была выделена функция function, имеющая параметром int и возвращающая int. Файл с функцией file.cpp: Код (Text): int function(int i) { return(i); } Файл с прототипом функции file.h: Код (Text): int function(int); Скетч (test.ino): Код (Text): #include "file.h" setup() { Serial.begin(9600); } loop() { Serial.println(function(millis()%2)); } Вариант третий, еще более правильный. То, что получилось во втором варианте копируется в папку с библиотеками в подпапку с именем, которое совпадает с именем файла с функциями без расширения. А еще лучше, оформляется в архив, как библиотека, чтобы было просто ее устанавливать в IDE. PS. Arduino IDE не содержит вменяемых средств для работы с проектами, содержащими более одного файла. Она умеет по факту только открывать уже готовый такой проект, показывая все его файлы (они должны все лежать в одной папке) во вкладках, и подключать к проекту внешние библиотеки. Так что придется делать все это расчленение во внешнем текстовом редакторе, укладывая результаты в папку со скетчем, а потом уже открывать его в Arduino IDE. Если же делается библиотека (вариант три) то файлы библиотеки придется всегда править чем-то внешним по отношению к Arduino IDE.
если нужно "нужно просто скрыть их с глаз, но чтобы они работали", то используй вкладки в IDE (тыц). Следующий этап - использовать функции, как расписано выше. Ну и для профи - классы и библиотеки.
Забыли еще про "guard headers" рассказать. Содержимое mylibrary.h должно быть оформлено со следующими директивами Код (Text): #ifndef MYLIBRARY_H #define MYLIBRARY_H // прототипы функций #endif Если этого не сделать, повторное включение заголовочного файла через #include вызовет ошибки.
В классах и библиотеках ничего особо сложного нет, нужно просто один раз сесть и разобраться. Если испытываете трудности, заводите тему, будем разбираться.
я испытываю к ним отношение как к чему-то большому и страшному Подробная статья по их написанию и применению была бы очень уместна.
для меня пока одно от другого мало чем отличается. Вот тут все толково расписано, но пинка для ускорения самообразования не хватает.
К слову, библиотеки в Arduino, это не совсем то же самое, что и библиотеки в общепринятом смысле. Вернее, кардинально отличается способ их использования. Ну вот, смотрите. Был у вас такой файл main.c Код (Text): #include <iostream> int foo(int a, int b) { return a+b; } int main() { int a,b; std::cin>>a>>b; std::cout<<foo(a,b); return 0; } И тут вы решили, что все, кроме main(), здесь лишнее и сделали так: foo.h Код (Text): #ifdef FOO_H #define FOO_H int foo(int a, int b); #endif foo.c Код (Text): #include "foo.h" int foo(int a, int b) { return a+b; } main.c Код (Text): #include <iostream> #include "foo.h" int main() { int a,b; std::cin>>a>>b; std::cout<<foo(a,b); return 0; } Поздравляю, вы создали исходный код приложения с библиотекой "foo".
Обычно, библиотеки компилируются отдельно от основного проекта, но в среде Arduino они всегда являются его неотъемлемой частью и при компиляции сливаются со скетчем. Библиотеки бывают статические (файлы *.a, *.lib, и т.п.) - они также включаются в проект во время компиляции (а еще точнее, компоновки), но не в виде исходного кода, а в виде готового заранее скомпилированного кода, и динамические (*.so, *.dll и т.п.) - они подключаются во время исполнения программы. На микроконтроллерах гарвардской архитектуры динамические библиотеки смысла не имеют, к использованию пригодны только статические в виде скомпилированных бинарников или исходного кода.
Вот как происходит компиляция стандартного примера Blink: Из файла скетча Blink.ino создается Blink.cpp путем добавления в начало файла строки #line 1 "Blink.ino" Потом код скетча компилируется. Из исходного файла Blink.cpp получается объектный файл Blink.cpp.o Поскольку библиотека Wiring подключается не в собранном виде, а в виде отдельных исходных файлов, сначала каждый из них компилируется в объектный файл *.o :
Затем, объектные файлы библиотеки Wiring собираются в статическую библиотеку core.a После чего производится компоновка (линковка) исполняемого файла Blink.cpp.elf И, наконец, генерируется hex-файл прошивки.
Припозднился с ответом, но благодарю за такой подробный комментарий. С библиотеками осовился и на радостях создал сразу три, чтобы часто путаться Единственное, у меня еще вопрос: как я понял, при директиве #include <something.h> препроцессор просто подгружает в исходный файл кусок текста из файла "something.h". Но если в этом файле уже есть дополнительно директива #include <defines.h> и происходит новое включение, то почему мне не удалось собрать всё, до тех пор, пока я не включил в главный файл эту же строку <defines.h>. Выглядит так, что файл приходится подключать к проекту два раза, несколько странно.