Как разбить программу на несколько файлов
- Войдите на сайт для отправки комментариев
Пнд, 22/04/2013 - 15:41
Доброго времени суток!
Помогите разбить программу на три файла.
Вот известный пример:
//Sample using LiquidCrystal library #include <LiquidCrystal.h> /******************************************************* This program will test the LCD panel and the buttons Mark Bramwell, July 2010 ********************************************************/ // select the pins used on the LCD panel LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // define some values used by the panel and buttons int lcd_key = 0; int adc_key_in = 0; #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 195) return btnUP; if (adc_key_in < 380) return btnDOWN; if (adc_key_in < 555) return btnLEFT; if (adc_key_in < 790) return btnSELECT; return btnNONE; // when all others fail, return this... } void setup() { lcd.begin(16, 2); // start the library lcd.setCursor(0,0); lcd.print("Push the buttons"); // print a simple message } void loop() { lcd.setCursor(9,1); // move cursor to second line "1" and 9 spaces over lcd.print(millis()/1000); // display seconds elapsed since power-up lcd.setCursor(0,1); // move to the begining of the second line lcd_key = read_LCD_buttons(); // read the buttons switch (lcd_key) // depending on which button was pushed, we perform an action { case btnRIGHT: { lcd.print("RIGHT "); break; } case btnLEFT: { lcd.print("LEFT "); break; } case btnUP: { lcd.print("UP "); break; } case btnDOWN: { lcd.print("DOWN "); break; } case btnSELECT: { lcd.print("SELECT"); break; } case btnNONE: { lcd.print("NONE "); break; } } }
Хочу разделить его на файлы примерно так:
File_1.ino
/******************************************************* This program will test the LCD panel and the buttons Mark Bramwell, July 2010 ********************************************************/ void setup() { init_LCD(); } void loop() { lcd.setCursor(9,1); // move cursor to second line "1" and 9 spaces over lcd.print(millis()/1000); // display seconds elapsed since power-up lcd.setCursor(0,1); // move to the begining of the second line lcd_key = read_LCD_buttons(); // read the buttons switch (lcd_key) // depending on which button was pushed, we perform an action { case btnRIGHT: { lcd.print("RIGHT "); break; } case btnLEFT: { lcd.print("LEFT "); break; } case btnUP: { lcd.print("UP "); break; } case btnDOWN: { lcd.print("DOWN "); break; } case btnSELECT: { lcd.print("SELECT"); break; } case btnNONE: { lcd.print("NONE "); break; } } }
File_2.cpp
#include <LiquidCrystal.h> LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // define some values used by the panel and buttons int lcd_key = 0; int adc_key_in = 0; #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5
File_3.cpp
int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 195) return btnUP; if (adc_key_in < 380) return btnDOWN; if (adc_key_in < 555) return btnLEFT; if (adc_key_in < 790) return btnSELECT; return btnNONE; // when all others fail, return this... } void init_LCD() { lcd.begin(16, 2); // start the library lcd.setCursor(0,0); lcd.print("Push the buttons"); // print a simple message }
сделайте библиотеку
http://www.arduino.ru/Hacking/LibraryTutorial
В программировании на C++ не силен. Пробовал сделать библиотеку. Не получилось. Да и не очень это удобно с точки зрения Arduino.
Мне надо просто разбить на файлы, которые будут лежать в одном каталоге.
Если не трудно: не надо давать советов, лучше покажите как это делается. Как оформить так, что бы не выдавало ошибок. Идея такая: один файл главный, один файл с описанием глобальных переменных (путь это будет *.h или *.cpp) и один или несколько файлов для функций, которые используют глобальные переменные.
Как из данного примера сделать то, что я хочу?
Покажите пожалуйста на конкретном примере.
Это
не надо давать советов
и этоЦитата: лучше покажите как это делается.
как бы противоречит друг другу...
Поэтому на вопросЦитата: Как оформить так, что бы не выдавало ошибок. Идея такая: один файл главный, один файл с описанием глобальных переменных (путь это будет *.h или *.cpp) и один или несколько файлов для функций, которые используют глобальные переменные.
Как из данного примера сделать то, что я хочу?
Покажите пожалуйста на конкретном примере.
посоветую разнести куски в разные .pde-файлы, причем главный из них (с функциями setup() и loop()) должен иметь то же имя, что и каталог проекта.
Показывать не буду - для следования вышеуказанному совету не требуется особых познаний/умений.
И еще посоветую - пользоваться в IDE кнопочкой вверху справа ("стрелка вправо"). Там как раз скрывается меню, необходимое для создания/удаления файлов проекта.
Мдяааа... Все с точностью наоборот.
Попросил не советывать - посветывали. Попросил показать - не показали.
Спасибо!
Неужели так трудно привести пример кода, который можно скопировать и запустить?
"пример кода, который можно скопировать и запустить" - в вашем первом посте.
Осталось положить куски в три файла с расширением .pde
Начиная с версии IDE 1.0 файлы имеют расширение .ino
эээм,вомозжно просто скажем делите ваш проэкт на 3 файла,один из них основной (содержит setup() и loop())
остальным двум присваиваете имена с окончанием .h или .c (хотя думаю там вообще не принципиально с каким окончанием можно даже .txt)и затем в основном файле прописываете
думаю как то так,хотя не проверял
Ну так проверьте, а после того как не скомпилируется отпишитесь... Делается как написал step962 только с разными расширениями в зависимости от версии IDE.
Если это было бы так просто, я бы не задавал вопросы.
Попробуйте сами скомпилировать пример из первого поста. Выдвет кучу ошибок. Это и понятно, поскольку надо все это еще оформить. Вот я и прошу, помогите избавиться от ошибок.
эээм,вомозжно просто скажем делите ваш проэкт на 3 файла,один из них основной (содержит setup() и loop())
остальным двум присваиваете имена с окончанием .h или .c (хотя думаю там вообще не принципиально с каким окончанием можно даже .txt)и затем в основном файле прописываете
думаю как то так,хотя не проверял
Это не поможет! Попробуйте
ой.. а чего так сложно-то?
Все гораздо проще реализуется.
1. Создаете (или открываете готовый) скетч
2. Нажимаете комбинацию кнопок CTRL+Shift+N (или мышкой правее вкладки на выпадающий список - "Новая закладка")
3. Вводите имя "дополнительного" файла (только имя, расширение файла среда сама подставит). И нажимаете "ОК".
Все. Проект разнесен на несколько файлов. Переключение между ними с помощью закладок в среде. В компляции участвуют все.
Да не покажешь, не выложешь это на форум. Потому как сильно зависит имено от файловой системы. И начинайте разбиратся не громоздким примером (где вы могли нахомутать в чем-то помимо разбиения на файлы), а с каким-то простеньким. Где в отдельный файл вынесена функция типа printHelloWorld(), и в главно - она вызывается.
Общая идея "разбивки" - такая.
1. Главный файл (в котором setup() и loop()) должен по имени совпадать с именем папки в котором все это лежит. Это самое главное.
2. Все остальные .ino/.pde, .cpp,.h - просто ложим в ту же папку.
3. .h файлы - нужно подключать через include.
4. .ino/.pde файлы - "подключатся сами" (в момент компиляции IDE просто объеденит их с главным файлом, для компилятора это будет "один большой скетч". Правда порядок подключения - IDE будет сама выбирать (скорее всего - тупо по алфавиту).
5. В .h файлах, возможно еще потребутеся делать #include "Arduino.h" или #include "WProgram.h" (в зависимости от того какая у вас версия), что-бы использоватся в .h/.cpp ардуиновские типы, константы, функции.
Вообщем если хотите что-бы вам помогли, то
1. Не рассказывайте как именно должны помогать. Возможно и со способом помощи вы ошибаетесь.
2. Скажите конкретно какие именно ошибки вы видите.
2. Сделайте упрощенный скетч. Использующий только Serial. Без всяких экранов и стронних библиотек проч. - что-бы любой мог запустить его у себя и увидеть те же ошибки что и вы. Вообщем вывернете наоборот свое "привести пример кода, который можно скопировать и запустить". Вы его приведите, а мы посмотрим "что с ним не так".
А по поводу "вашего примера" из стартового поста. Вам нужно либо читать как соотносятся .h/.cpp. Кроме .cpp файлов - нужно создать соотвествующие им .h файлы, где эти функции будут объявлены.
Либо... попробуйте просто переименовать File_2.cpp и File3_.cpp в File_2.ino и File3_.ino
Разбил программу на 4 файла:
Test.ino
Test1.cpp
Test2.cpp
Test3.cpp
Выдает ошибку:
In file included from Test.ino:4:
/Test2.cpp: In function 'int read_LCD_buttons()':
Test2.cpp:4: error: 'analogRead' was not declared in this scope
не видит. попробуйте уговорить что она есть. объявите ее в самом начале.
типа так.
#include <LiquidCrystal.h>
int read_LCD_buttons(void);
#include "d:\test\test\Test1.cpp"
#include "d:\test\test\Test2.cpp"
#include "d:\test\test\Test3.cpp"
Test.ino
Test1.cpp
Test2.cpp
Test3.cpp
Ошибка:
Test2.cpp: In function 'int read_LCD_buttons()':
Test2.cpp:5: error: 'adc_key_in' was not declared in this scope
Test2.cpp:8: error: 'btnNONE' was not declared in this scope
Test2.cpp:9: error: 'btnRIGHT' was not declared in this scope
Test2.cpp:10: error: 'btnUP' was not declared in this scope
Test2.cpp:11: error: 'btnDOWN' was not declared in this scope
Test2.cpp:12: error: 'btnLEFT' was not declared in this scope
Test2.cpp:13: error: 'btnSELECT' was not declared in this scope
Test2.cpp:14: error: 'btnNONE' was not declared in this scope
Перестаньте заниматься херней!!! хотя... можете продолжать если нравится. Вам уже 4 человека (ustas, step962, leshak и я) написали как это делается, а вы все .cpp файлы пытаетесь подключить...
Глобальные переменные должны быть объявлены в основном файле:
File_1.ino
File_2.ino
Я кажись понял в чем фигня. ИДЕ подтягивает все файлы в папке, инклуд игнорит. Кладем их в отдельную папку и дело в шляпе, тогда они подтянуться через инклуд. Тогда лучше смело спрыгивать с иде на голый winavr, зачем такие извращения.
Перестаньте заниматься херней!!! хотя... можете продолжать если нравится. Вам уже 4 человека (ustas, step962, leshak и я) написали как это делается, а вы все .cpp файлы пытаетесь подключить...
Извините, но я по-моему говорил, что С не знаю, и вообще я не программист. Поэтому мне что cpp, что ino все равно. По человечески ведь прошу, приведите работающий код. Если это трудно, то лучше не отвечайте и закроем тему. И не надо ругаться. Если будет работающий пример, то потом по тихоньку буду его разбирать. А следующим этапом перейду на создание классов.
Максим-то ругается не потому что вы не знаете, а потому что не читаете что вам пишут. "не программист" - не является оправданием в нежелании разбиратся. Вот вы загуглили, за это время что такое .h и как они работают в паре с .cpp? Похоже что нет.
Вам то может и нет разницы, что .cpp, что .ino . А вот компилятору - есть. И что-то мне подсказывает что из двух вариантов:
1. Уговорить компилятор не видеть разницы и
2. Вам разобратся в разнице и использовать нужно расширение
второй вариант выглядит реальней.
Вот я написал что инклудами подключаются только .h файлы, почему вы пытались .cpp подключить?
Я написал каждому .cpp файлу нужен свой .h файл (и его подключать), вы завели его (а для этого нужно таки загуглить что это за зверь)?
Ладно. Специально для тех кто не хочет разбиратся (хотя если вы хотите с классами, в будущем работать все равно прийдется) - есть специально "Арудино Путь". Просто сделать расширение файло .ino (и ничего не инклудить). Все файлы .ino из папки прикомпиляции IDE невидимо для вас объединит в одну (и это тоже писал).
Вот Максим прочитал "Либо... попробуйте просто переименовать File_2.cpp и File3_.cpp в File_2.ino и File3_.ino" и сделал это. И у него скомпилировалось (хотя, конечно он знал это и без чтения). Потому что он програмист?
Кстати перед тем как переходить к разбирательству с классами почитать Создание библиотек для Arduino из раздела сайта Программирование
Собственно это и есть пример "как создать класс". Разница только в том - куда вы положите .h/.cpp файлы. Если в папку с проектом, то это будет обычный классы. Если же вы положите в какую-то подпапку arduino/libraries - то они же станут носить гордое имя "библиотеки".
Если совсем кратко:
1. .h файл - заголовочный. Содержит "декларацию", какие типы, функции, классы у нас есть. Это как-бы обещание "мы вот эти функции/классы где-нибудь реализуем". Вот его и нужно инклудить.
2. .cpp файл. А это - выполнение обещаний. В него мы ложим реализацию функций/классов (и он - тоже должен делать инклуд .h файла). Сам .cpp инклудить не нужно. Достаточно что-бы он лежал в папке скетча.
Вот если мы в .h файле что-то "пообещаем", а ни в одном .cpp файле не выполним обещание - тогда будет ошибка.
В качестве "примера" - вы можете пойти в папку arduino, зайти в libraries и походить по библиотекам. Посмотреть как выглядят внутри .h / .cpp файлы.
Кстати - весьма эффективный способ учится. Смотреть чужой код. Если не понятно/не известно - гуглить "а че это за штука такая и зачем она тут".
Вам то может и нет разницы, что .cpp, что .ino . А вот компилятору - есть. И что-то мне подсказывает что из двух вариантов:
Нет. Копилятору тоже побарабану расширение файлов. Это только для людей. Компилятор всё равно создает один большой файл из всех разных, включая эти разные файлы там где есть include.
Вот если мы в .h файле что-то "пообещаем", а ни в одном .cpp файле не выполним обещание - тогда будет ошибка.
Наоборот, если сделаем не пообещав, то будет ошибка.
Даже не так. Если мы будем вызывать функции, которые выше главной функции main, то можно не "обещать", если ниже, то надо описать прототип функуции. и не важно в каком файле, главное выше main.
Вот Максим прочитал "Либо... попробуйте просто переименовать File_2.cpp и File3_.cpp в File_2.ino и File3_.ino" и сделал это. И у него скомпилировалось (хотя, конечно он знал это и без чтения). Потому что он програмист?
Ну я вот (на скрине видно) и .cpp скомпилил... я не программист?
Все файлы .ino из папки прикомпиляции IDE невидимо для вас объединит в одну (и это тоже писал).
А вот и правильный ответ! С какого буя эта ИДЕ компилит всё что в папке? Стандарт Си предпологает компиляцию файлов, которые указаны в include. И тот-же winavr, что использует эта иде, тоже стандартный.
Поэтому я задал вполне логичный вопрос, зачем морочиться с этой ИДЕ, если человек хочет делать как положено?
Но то, что этот человек не видит ответов это да, есть.
Библиотека с примером. только смените расширение на rar и разверните в каталог с библиотеками
/sites/default/files/u4655/testlib.jpg
Очень много букв написали. В книгах букв еще больше! По поводу желания разбираться: интересно, а я чем занимаюсь?
Теперь резюме:
Наконец то я получил работающий код собранный из нескольких файлов. Да действительно расширение играет роль, т.к. если взять эти файлы с работающим кодом и переименовать их с расширением cpp (только первому оставить ino), то копиляция уже будет с ошибками. А делал я это потому, что в одной очень умной книжке прочитал, что все остальные подключаемые файлы должны быть написаны на C++ и иметь соответствующие имена.
Теперь о порядке подключения и именования файлов: если писать в такой упрощенной методике как у меня, то файлы должны именоваться в алфавитном порядке в соответствии с их употреблением. Файл, содержащий описание переменных должен идти первым по алфавиту. "Инклюдить" ничего не надо!!! Если именовать файлы в другом порядке, то приходится делать ссылки типа extern.
Вот собственно и все правила. Потрачено три дня переписки и разборок. Если бы уважаемый maksim раньше привел свой пример, времени бы было потрачено меньше. Что собственно я и добивался от вас всех.
Теперь о том, что я не умею читать. Внимательно перечитайте сами, что я у вас всех просил и что вы мне написали и сравните что ответил уважаемый maksim. Ему спасибо, хоть он и не очень хорошо выражается.
Да вот еще __Alexander тоже натолкнул меня на некоторые мысли. Тоже спасибо!
Все тема закрыта.
Категорически не согласен - требую продолжения банкета и рассказа о разбиении программы на 2,5 файла!
Доброго времени суток. Заинтересовался разбивкой файла на части. Вроде понял один способ и попробовал вынести в новый Таб (вкладку) часть файла, например, нужную функцию. При этом Ардуино Иде сама сохраняет нужную вкладку с расширением ino и все компилируется без вопросов. Но интересно разбить на файлы с расширением .h/.cpp. Пока не получается. Хотя какой-то простенький работает, типа:
Главный файл
file1.h
file1.cpp
Но разбить свой файл по тому же принципу пока не получилось. Может кто глянет и покажет как разбить мой файл?
Что именно не получилось?
Хотел вынести функцию conekt_to_wifi() в отдельный файл и подключить его. Пробовал разные варианты, получал разные ошибки. То дважды объявлял переменные, то наоборот не объявлены, то ещё какие-то.
Руководствуйтесь этим правилом: все переменные, функции и объекты должны быть объявлены ранее их использования.
Хотел вынести функцию conekt_to_wifi() в отдельный файл и подключить его. Пробовал разные варианты, получал разные ошибки.
главное правило - выносимая в отдельный файл функция не должна использовать никаких "внешних" переменных, структур, данных... а у вас ваша функция нуждается в массивах ssid и pass, например.
Если функции нужны какие-то данные из других частей программы - они должны передаваться как параметры. Глобальные переменные в других файлах НЕ РАБОТАЮТ!
То же самое с результатом работы функции - функция не должна менять какие-то глобальные переменные, все что она выполнила - должно передаваться через возвращаемые значения или через параметры, переданные по ссылке.
Кстати, кто мешает в вашем случае поместить ssid и pass в тот же файл, что и саму функцию?
Вообще, чтобы разбивать программу на отдельные файлы - ее, программу, надо с самого начала проектировать с расчетом на это.
Кстати, кто мешает в вашем случае поместить ssid и pass в тот же файл, что и саму функцию?
В принципе никто не мешает. Я делал такой вариант, были другие ошибки. С другой стороны, хотелось бы в одном месте видеть все вводимые, получаемые данные, а их обработку делать в другом месте. Тем более, что уж если начать разделять файл, то потом его нужно будет поделить больше чем на две части, разбить на группы функций.
И в принципе понимаю, что программу нужно планировать с самого начала. Но я ведь только учусь. Я эту программу не сразу написал. И она требует доработки. Хотя уже в такой стадии, что уже можно пробовать её разбивать. Вот когда научусь писать программы, тогда и начну изначально писать именно с разбивкой, если она того будет требовать.
Пробую, только не совсем понятно, что в каком порядке объявляется, когда файл разбит. Возможно я не правильно понял ситуацию, но ведь для этого и делают два файла .h/.cpp. В одном объявляют, в другом реализуют/используют. А вот как они соотносятся с главным файлом, для меня пока не понятно
Глобальные переменные в других файлах НЕ РАБОТАЮТ!
Как так? А модификатор extern ?
test.ino:
ext.h:
Не проверял, но компилируется и где-то уже использовал подобное...
Кстати, кто мешает в вашем случае поместить ssid и pass в тот же файл, что и саму функцию?
В принципе никто не мешает. Я делал такой вариант, были другие ошибки. С другой стороны, хотелось бы в одном месте видеть все вводимые, получаемые данные, а их обработку делать в другом месте.
другой путь - передавать нужные данные из основной программы в функцию как параметры. Но тогда надо думать о правильной организации данных. Например все те же массивы ssid и pass у вас организованы не слишком удобно - как массивы неопределенного числа ссылок на строки неизвестного размера... придется передавать не только ссылки на данные, но и их размер как отдельный параметр. Впрочем, для ссылок на массивы это стандартная практика
Лень читать.) Нужно разбивать на файлы по логике. К примеру led.cpp и led.h описание и т.д. Это как положено. Другое дело что Арудино позволяет создавать множество ino файлов, которые собирает автоматом. Это для ленивых, но временами, удобно.)
другой путь - передавать нужные данные из основной программы в функцию как параметры. Но тогда надо думать о правильной организации данных. Например все те же массивы ssid и pass у вас организованы не слишком удобно - как массивы неопределенного числа ссылок на строки неизвестного размера... придется передавать не только ссылки на данные, но и их размер как отдельный параметр. Впрочем, для ссылок на массивы это стандартная практика
Сейчас как раз пробую с этим разобраться и ещё с многомерными массивами. Ну как сейчас... Типа в ближайших планах. Но пока примеров маловато. Не все понимаю.
Лень читать.) Нужно разбивать на файлы по логике. К примеру led.cpp и led.h описание и т.д. Это как положено. Другое дело что Арудино позволяет создавать множество ino файлов, которые собирает автоматом. Это для ленивых, но временами, удобно.)
Это вы про себя, что вам лень читать? Бывает. Только не понятно, что конкретно вам лень читать.
Я то с вами согласен. Самый простой способ тупо разбить файл на нужное число частей, допустим по одному файлу на одну функцию и сохранить с расширением ino. Об этом варианте я написал выше. Он работает. Но мне интересно разобраться с разбивкой на файлы .h/.cpp
многомерные массивы.... не увлекайтесь ими.
Массивы a[2][2] и b[4] организованы в памяти одинаково и занимают одинаковое место, но если передавать данные между процедурами или писать на носители - одномерный значительно проще и удобнее
Пока что я не понял, как организовать цикл перебора многомерного массива, а там будет видно. И конечно, может окажется, что в данной ситуации одномерный выгоднее. Но очень хотелось бы больше удобств при добавлении данных. Чтоб не по отдельности добавлять ссид в один массив, потом к нему пароль в другой массив, а сразу парой, типа ключ:значение или индекс:значение или словарь:список. Кому как удобней и привычней понимать.
Да и если вдруг как-то эти данные придется использовать в джсоне, то удобней именно многмерный массив. Но я до конца структуру программы не продумал. Это ж для Ардуино, фактически Си. Мне тут все новое.
Тем более, что читал, вроде есть возможность на некоторые платы делать заливку через вэб. Да и вообще, мало ли что захочу передать через вэб. Вот, пока тренируюсь
Дядя Гена, тренируйтесь. "Не дойдёт через голову - дойдёт через..." руки. А не дойдёт - тогда ой.(