Jak IDE organizuje rzeczy
Po pierwsze, w ten sposób IDE organizuje „szkic”:
- Plik główny
.ino
ma taką samą nazwę jak folder, w którym się znajduje. Tak więc, foobar.ino
w foobar
folderze - głównym plikiem jest foobar.ino.
- Wszelkie inne
.ino
pliki w tym folderze są łączone razem, w kolejności alfabetycznej, na końcu pliku głównego (niezależnie od tego, gdzie znajduje się plik główny, alfabetycznie).
- Ten skonkatenowany plik staje się
.cpp
plikiem (np. foobar.cpp
) - jest umieszczany w tymczasowym folderze kompilacji.
- Preprocesor „pomocnie” generuje prototypy funkcji dla funkcji znalezionych w tym pliku.
- Główny plik jest skanowany w poszukiwaniu
#include <libraryname>
dyrektyw. To powoduje, że IDE kopiuje również wszystkie odpowiednie pliki z każdej (wspomnianej) biblioteki do folderu tymczasowego i generuje instrukcje, aby je skompilować.
- Wszelkie
.c
, .cpp
czy .asm
pliki w folderze szkicu są dodawane do procesu budowania jako oddzielne jednostki kompilacji (to znaczy, że są kompilowane w zwykły sposób jako osobne pliki)
- Wszelkie
.h
pliki są również kopiowane do tymczasowego folderu kompilacji, aby można było do nich odwoływać pliki .c lub .cpp.
- Kompilator dodaje do standardowych plików procesu kompilacji (jak
main.cpp
)
- Proces kompilacji następnie kompiluje wszystkie powyższe pliki do plików obiektowych.
- Jeśli faza kompilacji się powiedzie, są one łączone razem ze standardowymi bibliotekami AVR (np. Dając ci
strcpy
itp.)
Efektem ubocznym tego wszystkiego jest to, że można uznać główny szkic (pliki .ino) za C ++ do wszystkich celów i celów. Generowanie prototypu funkcji może jednak prowadzić do niejasnych komunikatów o błędach, jeśli nie będziesz ostrożny.
Unikanie dziwactwa preprocesora
Najprostszym sposobem uniknięcia tych osobliwości jest pozostawienie głównego szkicu pustego (i nie używanie innych .ino
plików). Następnie utwórz kolejną kartę ( .cpp
plik) i umieść w niej swoje rzeczy w następujący sposób:
#include <Arduino.h>
// put your sketch here ...
void setup ()
{
} // end of setup
void loop ()
{
} // end of loop
Pamiętaj, że musisz to uwzględnić Arduino.h
. IDE robi to automatycznie dla głównego szkicu, ale w przypadku innych jednostek kompilacji musisz to zrobić. W przeciwnym razie nie będzie wiedział o takich rzeczach jak String, rejestry sprzętu itp.
Unikanie paradygmatu setup / main
Nie musisz biegać z koncepcją setup / loop. Na przykład plik .cpp może być:
#include <Arduino.h>
int main ()
{
init (); // initialize timers
Serial.begin (115200);
Serial.println ("Hello, world");
Serial.flush (); // let serial printing finish
} // end of main
Wymuś włączenie biblioteki
Jeśli korzystasz z koncepcji „pustego szkicu”, nadal musisz uwzględnić biblioteki używane w innym miejscu projektu, na przykład w .ino
pliku głównym :
#include <Wire.h>
#include <SPI.h>
#include <EEPROM.h>
Wynika to z faktu, że IDE skanuje tylko główny plik w celu użycia biblioteki. W rzeczywistości można uznać główny plik za plik „projektu”, który określa, które biblioteki zewnętrzne są w użyciu.
Problemy z nazewnictwem
Nie nazywaj głównego szkicu „main.cpp” - IDE zawiera własny main.cpp, więc będziesz miał duplikat, jeśli to zrobisz.
Nie nazywaj pliku .cpp taką samą nazwą jak główny plik .ino. Ponieważ plik .ino faktycznie staje się plikiem .cpp, również spowodowałoby to konflikt nazw.
Zadeklarowanie klasy w stylu C ++ w tym samym pojedynczym pliku .ino (słyszałem o działaniu, ale nigdy nie widziałem - czy to w ogóle możliwe);
Tak, kompiluje się OK:
class foo {
public:
};
foo bar;
void setup () { }
void loop () { }
Jednak prawdopodobnie najlepiej jest postępować zgodnie ze zwykłą praktyką: Umieść swoje deklaracje w .h
plikach, a definicje (implementacje) w .cpp
(lub .c
) plikach.
Dlaczego „prawdopodobnie”?
Jak pokazuje mój przykład, możesz złożyć wszystko w jednym pliku. W przypadku większych projektów lepiej być bardziej zorganizowanym. W końcu dostajesz się na scenę w średnim i dużym projekcie, w którym chcesz podzielić rzeczy na „czarne skrzynki” - to znaczy, klasę, która robi jedną rzecz, robi to dobrze, jest testowana i jest niezależna ( tak daleko jak to możliwe).
Jeśli ta klasa zostanie następnie użyta w wielu innych plikach w twoim projekcie, to tutaj oddzielne .h
i .cpp
pliki wchodzą w grę.
.h
Plik deklaruje klasę - to znaczy, że zapewnia wystarczającą ilość szczegółów dla innych plików wiedzieć, co robi, jakie funkcje to ma, a jak są one nazywane.
W .cpp
pliku definiuje (narzędzia) klasa - to znaczy, że faktycznie zapewnia funkcje i statycznych członków klasy, które sprawiają, że klasy, co robi. Ponieważ chcesz go zaimplementować tylko raz, jest to osobny plik.
.h
Plik jest tym, co zostaje włączone do innych plików. .cpp
Plik jest skompilowany raz IDE do realizacji funkcji klasy.
Biblioteki
Jeśli zastosujesz się do tego paradygmatu, możesz bardzo łatwo przenieść całą klasę ( pliki .h
i .cpp
) do biblioteki. Następnie można go współdzielić między wieloma projektami. Wszystko, co jest wymagane, to utworzenie folderu (np. myLibrary
) I umieszczenie w nim plików .h
oraz .cpp
(np. myLibrary.h
I myLibrary.cpp
), a następnie umieszczenie tego folderu w libraries
folderze, w którym przechowywane są szkice (folder szkicownika).
Uruchom ponownie IDE i teraz wie o tej bibliotece. Jest to naprawdę banalnie proste i teraz możesz udostępniać tę bibliotekę w wielu projektach. Często to robię.
Trochę więcej szczegółów tutaj .