Skocz do zawartości

Jak pisać biblioteki na Arduino?


macizet

Pomocna odpowiedź

@farmaceuta Sprawa jest prosta, deklaracja funkcji (czyli dokładnie to co podałeś - funkcja bez jej ciała/implementacji) musi zawierać jedynie nazwę typu/typów argumentów jakie przyjmuje - nie muszą te argumenty mieć nazw.

Dlatego możesz spotkać się właśnie z sytuacją gdy ktoś w .h przy deklaracjach nie podaje nazw argumentów lub je po prostu podaje - wtedy nie musisz zaglądać do pliku z implementacją żeby zrozumieć o co chodzi. Obie sytuacje są ok. Co więcej, w pliku .h możesz nazwać argument np:

// .h
void fun(int nazwa);

a w pliku z implementacją możesz ten argument nazwać inaczej:

// .c/.cpp
void fun(int innaNazwa){}

i też to będzie ok z punktu widzenia kompilatora, z punktu widzenia użytkownika biblioteki już niekoniecznie. 

Czasami możesz się też spotkać z sytuacją że dostawca oprogramowania dostarcza Ci plik .h a implementację już w postaci skompilowanej biblioteki - gdzie nie masz już dostępu do implementacji, jeśli argumenty w pliku .h nie byłyby nazwane to trochę trudno by się pracowało z taką biblioteką 😛   

  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

24 minuty temu, Matthew11 napisał:Dlatego możesz spotkać się właśnie z sytuacją gdy ktoś w .h przy deklaracjach nie podaje nazw argumentów lub je po prostu podaje - wtedy nie musisz zaglądać do pliku z implementacją żeby zrozumieć o co chodzi. Obie sytuacje są ok.

 

Tak wlasnie myslalem ze w pliku .h podaje nazwy do argumentow tylko po to zebym mogl szybciutko sprawdzic/domyslic sie do czego co sluzy zamiast wertowac mnostwo linii .cpp

Sprawdzalem tak dla testu z tymi nazwami i w kazdym przypadku biblioteka dziala prawidlowo, ale wolalem sie upewnic i zapytac madrzejszych zeby pozniej placzu nie bylo ze w jakiejs specyficznej sytuacji moga pojawic sie bledy w dzialaniu😉

Bardzo dziekuje za odpowiedz i poswiecony czas!🙂

A jeszcze takie pytanko..

Wszystkie zmienne ktore sa deklarowane po za funkcjami w pliku .cpp zachowuja sie jak zmienne globalne tak? Tylko czy one sa globalne tylko w bibliotece czy w  calym szkicu w ktorym jest glowny kod?

Link do komentarza
Share on other sites

4 minuty temu, farmaceuta napisał:

Wszystkie zmienne ktore sa deklarowane po za funkcjami w pliku .cpp zachowuja sie jak zmienne globalne tak? Tylko czy one sa globalne tylko w bibliotece czy w  calym szkicu w ktorym jest glowny kod?

Są "globalne" w tym konkretnym .cpp - tylko w nim są widoczne. 

  • Pomogłeś! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

46 minut temu, farmaceuta napisał:

To jaka bedzie roznica czy zadeklaruje zmienne w .cpp czy w pliku .h jako private: ?

To zależy od tego co chcesz osiągnąć. W pierwszym przypadku będziesz miał jeden wspólny stan (przez współdzielenie tej globalnej zmiennej w .cpp) dla każdego obiektu danej klasy, w drugim będziesz miał odrębne stany dla każdego obiektu klasy. W pierwszym przypadku zmiana w jednym obiekcie, będzie powodować zmianę dla wszystkich obiektów tej klasy - bo współdzielisz tą zmienną globalną. W drugim przypadku zmiana w jednym obiekcie, wpływa tylko na ten obiekt - i to jest to co chcesz zazwyczaj osiągnąć, raczej nie chcesz żeby zmiana w jednym obiekcie powodowała zmianę w każdym obiekcie tej klasy (chyba, że to chcesz osiągnąć - ale wtedy lepiej zastosować zmienną w postaci statycznego membera). Ten prosty program pokazuje mniej lub bardziej tą różnicę:

// case.h
#pragma once

class CaseB
{
  public:
    int getA() const;

    void changeA(const int a);

  private:
    int m_a;
};

class CaseA
{
  public:
    int getA() const;

    void changeA(const int a);
};

// case.cpp
#include "lib.h"

int a_global;

int CaseB::getA() const
{
    return m_a;
}

void CaseB::changeA(const int a)
{
    m_a = a;
}

int CaseA::getA() const
{
    return a_global;
}

void CaseA::changeA(const int a)
{
    a_global = a;
}

// main.cpp
#include "lib.h"
#include <assert.h>
#include <iostream>

int main()
{
    CaseA a1;
    a1.changeA(10);

    CaseA a2;
    a2.changeA(20);

    CaseB b1;
    b1.changeA(10);

    CaseB b2;
    b2.changeA(20);

    assert(10 == a1.getA());
    assert(20 == a2.getA());
    assert(10 == b1.getA());
    assert(20 == b2.getA());
}

 

  • Pomogłeś! 1
Link do komentarza
Share on other sites

15 godzin temu, Matthew11 napisał:

W pierwszym przypadku zmiana w jednym obiekcie, będzie powodować zmianę dla wszystkich obiektów tej klasy - bo współdzielisz tą zmienną globalną. W drugim przypadku zmiana w jednym obiekcie, wpływa tylko na ten obiekt - i to jest to co chcesz zazwyczaj osiągnąć,

Aha rozumiem...czyli dopoki mialbym jedna instancje to globalna zmienna "ujdzie" ,ale przy kilku to juz nie bo niezaleznie w ktorej instancji dokonam modyfikacji to zmienna jest wspolna dla wszystkich instancji bo jest globalna a jesli utworze ta zmienna w private: to zostanie ona jakby zdublowana dla kazdej instancji i tylko tam bedzie modyfikowana nie zaleznie od calej reszty instancji.

 

Jeszcze zapytam...powiedzmy ze mam klase w niej trzy funkcje, wszystkie identyczne (o roznych nazwach np. t1, t2, t3) to do kazdej tej funkcji musze utworzyc osobna zmienna w private: tak?  

 

Link do komentarza
Share on other sites

1 godzinę temu, farmaceuta napisał:

Jeszcze zapytam...powiedzmy ze mam klase w niej trzy funkcje, wszystkie identyczne (o roznych nazwach np. t1, t2, t3) to do kazdej tej funkcji musze utworzyc osobna zmienna w private: tak?  

To ponownie zależy od tego co chcesz osiągnąć. Jeśli potrzebujesz 3 osobnych zmiennych to musisz stworzyć trzy. Jeśli te trzy funkcje mogą korzystać z jednej zmiennej to tworzysz jedną. Stan jakiegoś obiektu jakiejś klasy może być reprezentowany przez jedną lub wiele zmiennych, gdzie jedna metoda może nie dokonywać zmian stanu lub zmieniać stan przez zmiany w wielu zmiennych itp - i to jest zupełnie normalnie. 

  • Pomogłeś! 1
Link do komentarza
Share on other sites

11 minut temu, Matthew11 napisał:

 Stan jakiegoś obiektu jakiejś klasy może być reprezentowany przez jedną lub wiele zmiennych, gdzie jedna metoda może nie dokonywać zmian stanu lub zmieniać stan przez zmiany w wielu zmiennych itp - i to jest zupełnie normalnie. 

Ok teraz rozumiem mniej wiecej:-)

W moim przypadku te kilka identycznych funkcji bedzie zapisywalo indywidualne czasy wiec juz wiem ze musze "tradycyjnie" stworzyc osobna zmienna do kazdej funkcji:-)

Dziekuje Ci ponownie za pomoc i poswiecony czas! 🙂 uklony w twoja strone 🙂

Link do komentarza
Share on other sites

No i poleglem...🤔 niby dobrze nabazgralem a nie dziala tak jak bym chcial...

plik .h

#ifndef Milis_h
#define Milis_h

//#include "WProgram.h"
#include "Arduino.h"

class Milis
{
   public:
     uint8_t t0(uint32_t);
     uint8_t t1(uint32_t);
     uint8_t t2(uint32_t);
    
   private:
      uint32_t czas0;
      uint32_t czas1;
      uint32_t czas2;
};

#endif

plik .cpp

//#include "WProgram.h"
#include "Arduino.h"
#include "Milis.h"


uint8_t Milis::t0(uint32_t tim)
{
   if (millis() - czas0 > tim) {
     czas0 = millis();
     return true;
   } else {
     return false;
    }
}

uint8_t Milis::t1(uint32_t tim)
{
   if (millis() - czas1 > tim) {
     czas1 = millis();
     return true;
   } else {
     return false;
    }
}

uint8_t Milis::t2(uint32_t tim)
{
   if (millis() - czas2 > tim) {
     czas2 = millis();
     return true;
   } else {
     return false;
    }
}

no i kod..

#include <Milis.h>

Milis mili;

void setup() {
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
}

void loop() {
   if (mili.t0(100))  {
    digitalWrite(8, !digitalRead(8));
  }
  if (mili.t1(1000))  {
    digitalWrite(9, !digitalRead(9));
  }
}

no i tak...pomijam calkiem fakt przydatnosci takiego czegos itp. tak dla testu sobie to robilem..

dzialanie? dwie diody sie zaswieca, ale tylko jedna bedzie migac i to z regoly ta ktora ma krotszy czas..czasem zapala sie obie i juz swieca,jest tak rowniez gdy dam taki sam czas jako argument..wczesniej w private: czas0/1/2 mialem jako tablice ale bez zmian. inne nazwy dla argumentu funkcji "tim" tez bez zmian...nie mam pojecia co ja tu zle zrobilem...ma to poprostu dzialac tak ze diody maja mi migac z rozna czestotliwoscia niezaleznie z uzyciem millis(); 

ktos sie ulituje nademna nieszczesnym kmiotem pseudo_programistycznym?😪

Link do komentarza
Share on other sites

Ledwo pamiętam C++, ale zmienne czas0, czas1 i czas2 mają nieokreśloną wartość w momencie tworzenia obiektu. Zdaje się jest szansa, że ich wartość będzie dużo większa, niż liczba milisekund zwracana przez millis(), czyli nie wiadomo ile czasu minie zanim dioda mignie. Spróbuj je zainicjować przed pierwszym użyciem, najlepiej w setup(). Coś w rodzaju czas1 = czas2 = czas0 = millis(). Albo chociażby nadanie wartości 0.

  • Pomogłeś! 1
Link do komentarza
Share on other sites

Mala aktualizacja...wyglada na to ze..dziala:-o 

Tak mi przyszlo do glowy zeby zamiast diod sprobowac w serial monitorze wyswietlic cos za kazdym spelnionym if'em i ladnie cyklicznie wyswietla co ustalony czas...ale te diody nie chca za cholere dzialac..:-/ zwykle ledy zolte chyba 3mm (byc moze 2mm)...co prawda nie dalem rezystorow, ale to chyba by takich cyrkow nie bylo co? Arduino zasilane z usb..jednak jedna dioda tez nie koniecznie dziala...np. miga przez 20s gasnie na kilka s lub swieci i po chwili znow startuje...wyglada to jak by cos zaklocalo sie wieszalo? Jak macie jeszcze jakies sugestie chetnie wyslucham...dzieki za pomoc:-)

Edytowano przez farmaceuta
Link do komentarza
Share on other sites

Odpisze tu bo byc moze sie komus przyda...ta "biblioteka"  dziala prawidlowo...mianowicie z serial bezblednia a z led problem, wiec przyjrzalem sie temu co bylo podejrzane czyli

digitalWrite(8, !digitalRead(8))

Nigdy wczesniej nie negowalem stanu w ten sposob , ale uzylem bo juz kilkukrotnie widzialem taka skladnie i wydalo mi sie to wygodne...po zmianie na zwykle zmienne stanu diody dziala bez zarzutu...widocznie powyzszy zapis sprawia problemy w niektorych sytuacjach jak w mojej np...

Link do komentarza
Share on other sites

 

Od kilku tygodni zastanawiałem się, dlaczego w Arduino zdecydowano się na C++. Obiekty i ośmiobitowiec? Podobno narzut po skompilowaniu w stosunku do C jest żaden, ale po co? Trochę sobie odświeżyłem wiedzę o operatorach new i delete, i pewnie po części o to chodzi. Dynamiczna alokacja pamięci w ramach języka. Bez wywoływania funkcji bibliotecznych w rodzaju malloc().

Z ciekawości popracowałem nad tą klasą od migania diodami, i teraz niby w czasie tworzenia obiektu można sobie w zależności od potrzeb zadeklarować ile (do 256). Z góry sorki za styl kodowania.

typedef uint8_t token ;

class Tst
{
	struct dioda
	{
		token pin ;
		uint32_t czas ;
		uint32_t czas_przel ;
	} ;

	dioda * diody ;
	token znacznik ;
public:
	Tst( uint8_t ile_diod ) ;
	token dodaj( uint8_t pin, uint32_t czas ) ;
	uint8_t czy_czas( token dioda ) ;
	uint8_t pin( token dioda ) ;
} ;

Tst::Tst( uint8_t ile_diod )
{
	diody = new dioda[ ile_diod ] ;
	znacznik = 0 ;

	for( uint8_t i = 0 ; i < ile_diod ; i ++ )
		diody[ i ].pin = diody[ i ].czas = diody[ i ].czas_przel = 0 ;
}

token Tst::dodaj( uint8_t pin, uint32_t czas )
{
	diody[ znacznik ].pin = pin ;
	diody[ znacznik ].czas = czas ;
	diody[ znacznik ].czas_przel = millis() ;

	return znacznik ++ ;
}

uint8_t Tst::czy_czas( token dioda )
{
	if( millis() - diody[ dioda ].czas_przel >= diody[ dioda ].czas )
	{
		diody[ dioda ].czas_przel = millis() ;
		return true ;
	}

	return false ;
}

uint8_t Tst::pin( token dioda )
{
	return diody[ dioda ].pin ;
}

 

Rzeczywiście część "użytkownika" robi się prosta, chociaż jeszcze np. stan pinu trzeba pilnować "ręcznie":

 

Tst * tst = NULL ;
token t0 ;

void setup()
{
	tst = new Tst( 16 ) ;
	pinMode( 13, OUTPUT ) ;

	t0 = tst->dodaj( 13, 800 ) ;
}

void loop()
{
	static uint8_t stan = 0 ;

	if( tst->czy_czas( t0 ) )
	{
		digitalWrite( tst->pin( t0 ), stan ) ;
		stan = ~stan ;
	}
	delay( 10 ) ;
}

 

No i rzeczywiście to działa! Kompiluje, ładuje, miga. Jak zobaczyłem, to uwierzyłem. UWAGA! Pisałem to kilkadziesiąt minut, przetestowałem w zasadzie raz na jedynej pokładowej diodzie. Używacie na własną odpowiedzialność.

Edytowano przez jargraw
Link do komentarza
Share on other sites

A jeszcze przy okazji zapytam...jest jakis prosty sposob na odwolanie sie do struktury/tablicy z poziomu biblioteki? Powiedzmy tworze strukture/tablice normalnie w szkicu i mam nad nimi pelna kontrole z poziomu biblioteki...cos takiego mozliwe??

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.