Univerzális program RAM használathoz

Az utóbbi időben rákényszerültem külső memória használatára. Ennek kapcsán írtam egy olyan univerzális tároló függvényt, amivel különböző típusú adatokat lehet tárolni. Nem pusztán tárolja az adatokat, átlagol, minimum maximum értéket képez stb. Csak dobálom be az adatokat, és az jön ki, amire kíváncsi vagyok. Még egy érdekessége van ennek a programnak. A fiam elmagyarázta, hogyan lehet úgy használni egy függvényt, hogy meghíváskor a paraméterek száma különböző lehet, és ettől függően mást is csinál. A függvényt multimemo-nak neveztem el. 73 különböző adatot tud tárolni egy 512 byte-os ram-ban. Mind a 73 tároló cellája (én neveztem el így) különböző módon viselkedik, beállítható módon automatikusan képzi a bedobált adatoknak a minimumát, maximumát, átlagát, összegét, vagy csak simán az adatot is képes tárolni. Egy cella egyféle módon állítható be, ez gondolom egyértelmű. Meghívása nagyon kényelmes, így néznek a ki a különböző esetek:

  • multimemo(cím,tipus)  – létrehoz egy megadott típusú memória cellát (setup és reset)
  • multimemo(cím,adat)  – beír egy adatot a cím-el megadott cellába (write)
  • multimemo(cím) – kiolvas a cimről egy adatot (read)

Miközben írtam a programot megtanultam használni az „enum” felsorolás típusú változók használatát is. Sokat segített a program megírásában és áttekinthetőségében, nem kell kódokat megjegyezni, kevésbé fordult elő, hogy összekevertem azokat a bizonyos kódokat.

A multimemo használat igen egyszerű! Meghívjuk a függvényt a cím és a típus megadásával. Ekkor létrejön a tároló cella, megjegyzi a saját típusát és alaphelyzetbe áll. Az alaphelyzet nem feltétlenül nulla tartalmat jelent, mert pl. a minimum tároló cella esetében a long típus lehetséges legnagyobb értékét kell megadni, hiszen ez a cella csak akkor tárolja a megadott tartalmat, ha az kisebb mint az éppen tárolt érték. Úgy írtam meg a függvényt, hogy ebben az alap állapotban 0-át ad vissza, de ha egyetlen értéket is megadsz, már rögtön az lesz a minimum. Az átlagoló cellánál az átlag képzéséhez a beírás számlálót is törölni kell alaphelyzetbe állításkor.
Ezt követően, már csak írogatni kell az adatokat. Ha meghívod a függvényt két paraméterrel, a címmel és az adattal, akkor történik aminek történnie kell. Itt egy kicsi összefoglaló az egyes típusok működéséről:

  • MIN típusú cella: A tároló cella a beadott adatok közül mindig csak azt tárolja, ami kisebb az aktuálisan tárolt értéknél. Kezdő értéke 2.147.483.647, de a visszaadott érték ekkor 0. Ha bármilyen adatot bele írunk, akkor már azt fogja visszaadni (vagy ha az lesz kisebb akkor azt). A 2.147.483.647 értékre mindig 0-at ad vissza, így a tárolható értéktartomány -2.147.483.648-tól 2.147.483.646-ig!
  • MAX típusú cella: A tároló cella a beadott adatok közül mindig csak azt tárolja, ami nagyobb az aktuálisan tárolt értéktől. Kezdő értéke -2.147.483.648, de a visszaadott érték ekkor 0. Ha bármilyen adatot bele írunk, akkor már azt fogja visszaadni (vagy ha az lesz nagyobb akkor azt). A ‑2.147.483.648 értékre mindig 0-at ad vissza, így a tárolható értéktartomány -2.147.483.647-tól 2.147.483.647-ig!
  • SUM típusú cella: A tároló cella a beadott adatot hozzáadja az aktuális értékhez, így mindig az adatok összegét tudjuk. Kezdő érték 0.
  • AVG típusú cella: A tároló cella átlag érték tárolásra lesz használva. Ha beleírunk egy értéket, akkor azt hozzáadja az előzőleg tárolt értékhez, de növeli a beírások számlálóját is. Ha lekérdezzük majd a cella tartalmát, a tárolt szumma érték és a beírás számláló hányadosát (azaz az átlagot) adja vissza. Beírást követően azonnal visszaadja az új átlag értéket. Csak egész értékeket lehet megadni, tehát ha tizedes értékekkel akarunk számolni a 10, 100 stb. szorosát adjuk meg, és kiolvasáskor ugyanannyival osztanunk kell (float-ra alakítva az eredményt). A hányados képzéskor egészre kerekít vagyis pl. 32,666-ra 32-őt ad vissza és nem 33-at, ami kerekítéssel helyes lenne. Erre esetleg érdemes figyelni. Ezért pontosabb eredményt kapunk, ha tizedesek számában többet használunk mint amit a pontosság megkövetel. Tehát egy tizedes pontosság esetén 100-al szorozzunk tároláskor, és 100-al osszunk kiolvasáskor.
  • STO típusú cella: A beírt adat tárolásra kerül. Nem csinálunk vele semmit, csak tároljuk és kiolvasáskor visszaadjuk. Egy új adat beírásakor a régi felülíródik.

Írás után a függvény visszaadja a tároló cella új tartalmát. Ez ugyanaz, mintha kiolvasnánk a benne tárolt értéket.

Írtam egy demó programot a működés bemutatására. A setup részben csak néhány egyszerű művelettel mutatom be a működést. A loop-ban már véletlen szám generálással próbálkoztam. A véletlen szám generálás tulajdonságairól is szereztem tapasztalatokat, de ezzel majd a végén még foglalkozok egy kicsit.

Íme a forráskód:

#include <Wire.h>

//Az i2c címet az A2 és A1 kivezetések állapota határozza meg
//Például, ha A2 = VCC és A1 = GND, írjunk: FRAM fram (0b10);
byte A0A1=0b00;
byte _i2cAddress= (0b101000 | A0A1)<<1;
//_i2cAddress= (0b101000 | Address)<<1;
//byte _i2cAddress= 0b1010000;
byte Page=0;
String anev;
enum cella_tipus { MIN, MAX, SUM, AVG, STO };
long szam;

void setup() {
  Wire.begin();  
  Serial.begin(9600);
  Serial.println("reset...");
  multimemo(0,MIN);  // A 0. fram cella minimum érték tárolásra lesz használva. Kezdő értéke 2.147.483.647,
                     // de a visszaadott érték ekkor 0. Ha bármilyen adatot bele írunk, akkor már azt fogja
                     // visszaadni (vagy ha az lesz kisebb akkor azt). A 2.147.483.647 értékre mindig 0-at ad vissza
                     // Az értéktartomány -2.147.483.648-tól 2.147.483.646-ig!
  multimemo(1,MAX);  // A 1. fram cella macimum érték tárolásra lesz használva. Kezdő értéke -2.147.483.648,
                     // de a visszaadott érték ekkor 0. Ha bármilyen adatot bele írunk, akkor már azt fogja
                     // visszadni (vagy ha az lesz nagyobb akkor azt). A -2.147.483.648 értékre mindig 0-at ad vissza
                     // Az értéktartomány -2.147.483.647-tól 2.147.483.647-ig!
  multimemo(2,SUM);  // A 2. fram cella szumma érték tárolásra lesz használva. Ha beleírunk egy értéket
                     // az mindíg hozzáadódik az addig tárolt adathoz, és az lesz a következő tárolt érték. 
                     // Kezdő tárolt adat 0.
  multimemo(3,AVG);  // A 3. fram cella átlag érték tárolásra lesz használva. Ha beleírunk egy értéket, akkor azt hozzáadja
                     // az előzőleg tárolt értékhez, de növeli a beírások számlálóját is. Ha lekérdezzük majd a cella
                     // tartalmát, a tárolt szumma éréték és a beírás számláló hányadosát (azaz az átlagot) adja vissza.
                     // csak egész értékeket lehet megadni, tehát ha tizedes értékekkel akarunk számolni a 10, 100 stb.
                     // szorosát adjuk meg, és kiolvasáskor ugyanannyival oszzunk (float-ra alakítva az eredméynt).
                     // A hányados képzéskor egészre kerekít vagyis pl. 32,666-ra 32-őt ad vissza és nem 33-at, ami 
                     // kerekítéssel helyes lenne. Erre esetleg érdemes figyelni. Érdemes tizedessek számában többet
                     // használni mint amit a pontosság megkövetel, tehát egy tizedes pontosság esetén 100-al szorozzunk tároláskor,
                     // és 100-al osszunk kiolvasáskor.
  multimemo(4,STO);  //A 4. fram cella tárolásra kerül. Nem csinálunk vele semmit, csak tároljuk.

  Serial.println("tárolás...");
  // Sokféle értéket írunk a cellákba, hogy láthassuk a működést. A visszadott érték a beírást
  // követő cella tartalmat adja vissza. Ha írni akarunk egy tároló cellába, akkor két paramétert
  // adunk meg, a cella címét és a tárolandó adot. Az adatot nem tároljuk, hanem értékeljük és tárolunk 
  // attól függően, hogy milyen típusú a tároló cellánk. A cella típusát reseteléskor (alap beállításkor) 
  // adjuk meg. Lásd előbbi két paraméteres meghívás, amikor a második paraméter string.
  Serial.print("MIN (10):");Serial.println(multimemo(0,10));
  Serial.print("MIN  (5):");Serial.println(multimemo(0,5));
  Serial.print("MAX (20):");Serial.println(multimemo(1,20));
  Serial.print("MAX (15):");Serial.println(multimemo(1,15));
  Serial.print("SUM (30):");Serial.println(multimemo(2,30));
  Serial.print("SUM (20):");Serial.println(multimemo(2,20));
  Serial.print("AVG (42):");Serial.println(multimemo(3,42));
  Serial.print("AVG (37):");Serial.println(multimemo(3,37));
  Serial.print("AVG (19):");Serial.println(multimemo(3,19));
  Serial.print("STO (50):");Serial.println(multimemo(4,50));
  
  Serial.println("Kiolvasott cella tartalmak:");
  Serial.print("MIN:");Serial.println(multimemo(0));
  Serial.print("MAX:");Serial.println(multimemo(1));
  Serial.print("SUM:");Serial.println(multimemo(2));
  Serial.print("AVG:");Serial.println(multimemo(3));
  Serial.print("STO:");Serial.println(multimemo(4));

  //a loop-ban található teszt programban tárolt adatokhoz a tároló cellák létrehozása
  multimemo(5,MIN);  // A 5. fram cella minimum érték tárolásra lesz használva. Ebben tároljuk a generált számok között előforduló minimális értéket
  multimemo(6,MAX);  // A 6. fram cella macimum érték tárolásra lesz használva. Ebben tároljuk a generált számokban előforduló maximális értéket
  multimemo(7,SUM);  // A 7. fram cella szumma érték tárolásra lesz használva. Ehhez adjuk hozzá a generált számot
  multimemo(8,SUM);  // A 8. fram cella szumma érték tárolásra lesz használva. Ebben számoljuk a generált számok mennyiségét
  multimemo(9,AVG);  // A 9. fram cella átlag érték tárolásra lesz használva. Ebben tároljuk a generált számok átlagát
  
}


void loop() {
  szam=random(-10000,20000);
  Serial.print("Generalt szam:");
  Serial.print(szam);
  Serial.print("  MIN:");Serial.print(multimemo(5,szam));
  Serial.print("  MAX:");Serial.print(multimemo(6,szam));
  Serial.print("  SUM:");Serial.print(multimemo(7,szam));
  Serial.print("  AVG");Serial.print(multimemo(9,szam));
  Serial.print("  Db:");Serial.print(multimemo(8,1));
  Serial.print("  szamitott atlag:");Serial.print(multimemo(7)/multimemo(8));
  Serial.println();
  delay(100);
}


long multimemo(byte cim) {
/***************************************************************************************************************
 * Ez a multimemo függvény akkor hívódik meg, ha csak egyetlen paramétert adtunk meg a függvény                *
 * meghívásakor (csak olvasni akarunk a tároló cellából)                                                       *
 * A három paraméteres multimemo függvényt hívja meg, de default értékkel tölti fel a maradék két paramétert   *
 * A megadott tároló cella címről adtot fogunk olvasni.                                                        *
 ***************************************************************************************************************/
  long adat=0;
  bool iras=0;
  return(multimemo(cim,adat,iras));
}


long multimemo(byte cim, long adat) {
/***************************************************************************************************************
 * Ez a multimemo függvény akkor hívódik meg, ha két paramétert adunk meg, a cella címét és a beírandó         *
 * adatot, vagyis írni akarunk a cellába. A tároló cella formázását és alaphelyzetbe állítását végző           *
 * két paraméteres multimemo függvénytől az különbözteti meg, hogy ennek második paramétere long típusú.       *
 * A három paraméteres multimemo függvényt hívja meg, de a harmadik paramétert (iras vagy olvasás) default     *
 * értékkel tölti fel (iras=1, azaz írunk). A megadott tároló cella címébe fogjuk az adtot beírni.             *
 ***************************************************************************************************************/
  bool iras=1;
  return(multimemo(cim,adat,iras));
}



long multimemo(byte cim, long adat, bool iras) {
/*****************************************************************************************************************************************************************
 * Ez a multimemo függvény akkor hívódik meg, ha három paraméterrel hívjuk. Ez végzi a tárolandó adat feldolgozását, tárolását, és visszaadja az eredményt       *  
 * visszatérő értékként. A tároló cella típusát az első tárolt byte adja meg, ezt az a multimemo függvény írja, melynek két paramétere van, és a második         *
 * cella_tipus (enum-al definiált felsorolás) típusú adatot vár.                                                                                                 *
 * A függvény bemenő paraméterei:                                                                                                                                *
 *    cim: 0-73 memória cella, egy cella 7 byte,                                                                                                                 *
 *    adat: long típusú 4 byte-os adat, amit beírunk a kijelölt memória cellába                                                                                  *
 *    iras: ez mondja meg, hogy írunk vagy olvasunk a tároló cellából. 0-olvasunk, 1-írunk                                                                       *
 * A tároló cella szerkezete:                                                                                                                                    *
 *    0. byte típus: 0=minimum cella, 1=maximum cella, 2=summa cella, 3=átlag cella, 4=tároló cella                                                              *
 *    1-4. byte adat long adattípus, ez a tényleges tárolt long adat 4 byte-on                                                                                   *
 *    5-6. byte átlag esetén az összegzett adatok száma int adattípusú Csak az átlagoló tároló cella használja, ebben számolja az írások számát.                 *
 *****************************************************************************************************************************************************************/
  int xcim=cim*7;
  byte page=0;
  byte adat0;
  byte adat1;
  byte adat2;
  byte adat3;
  if (xcim<256) {page=0;} else {page=1;xcim=xcim-256;}
  Wire.beginTransmission(_i2cAddress | (page&1));
  Wire.write(xcim);
  Wire.endTransmission(); 
  Wire.requestFrom(_i2cAddress | (page&1),7);
  byte xtipus=Wire.read();  //elkérjük az első byte-ot, ami a tárolt dat típusát adja meg
  long xadat=long(Wire.read()) | long(Wire.read())<<8 | long(Wire.read())<<16 | long(Wire.read())<<24 ; //elkérjük és betöltjük az adat aktuális értékét
  int xdb=int(Wire.read()) | int(Wire.read())<<8;  //elkérjük és betöltjük az adatok számát
  
  if (iras==0 and xtipus!=3) {return(xadat);}  //ha olvasás és nem átlagot kértünkvolt a művelet, akkor visszadjuk a kiolvasott long értéket
  if (iras==0 and xtipus==3) {if (xdb>0) {return((long)(xadat/xdb));} else {return(0);}}  //ha olvasás és átlagot kértünk, akkor osztani is kell, de csak ha xdb nem 0
  
  if (xtipus==0 and iras==1){ //ha írás ésminimum cella
      if (xadat>adat) { //csak akkor írjuk be az új adatot, ha az eddig tárolt adatnál kissebb
        multimemo_adatiras(page,xcim,adat); //visszaírjuk az új adatot a tároló cellába
        return(adat);
      }
      else {return(xadat);} //visszadjuk a cellában tárolt minimum értéket
    }  
      
  if (xtipus==1 and iras==1){  //ha írás és maximum cella
      if (xadat<adat) {  //csak akkor írjuk be az új adatot, ha az eddig tárolt adatnál nagyobb
         multimemo_adatiras(page,xcim,adat); //visszaírjuk az új adatot a tároló cellába
        return(adat);
      }
      else {return(xadat);} //visszadjuk a cellában tárolt maximum értéket
    }  
      
  if (xtipus==2 and iras==1){  //ha írás és summa cella
    adat=xadat+adat;  //összeadjuk az új adatot ez eddig beírt adatok (tárolt) összegével
    multimemo_adatiras(page,xcim,adat);  //visszaírjuk az új adatot a tároló cellába
    return(adat); //visszadjuk az új cella értéket
  } 

  if (xtipus==3 and iras==1){  //ha írás és átlag cella
    adat=xadat+adat;  //összeadjuk az új adatot ez eddig beírt adatok (tárolt) összegével
    xdb=xdb+1; //növeljük a beírások számát 1-el
    Wire.beginTransmission(_i2cAddress | (page&1));Wire.write(xcim+1); //a cella első byte-ját már nem kell írni, azt beállított a cella reset, azért cim+1-től írunk
    Wire.write(adat & 0xFF);Wire.write((adat>>8) & 0xFF);Wire.write((adat>>16) & 0xFF);Wire.write((adat>>24) & 0xFF); //beírjuk az új összegzett adatot
    Wire.write(xdb & 0xFF);Wire.write((xdb>>8) & 0xFF);  //beírjuk az írások számát
    Wire.endTransmission(); 
    return((long)(adat/xdb));  //visszadjuk az új átlag értéket
  } 

  if (xtipus==4 and iras==1){  //ha írás és sima tároló cella
    multimemo_adatiras(page,xcim,adat);
    return(adat);
  } 

}

void multimemo_adatiras(byte page, byte xcim, long adat) {
// az átlag cella kivételével mindet ugyanúgy kell beírni, ezért a konkrét írás ebben a közösen használt függvényben lett megvalósítva
  Wire.beginTransmission(_i2cAddress | (page&1));Wire.write(xcim+1); //a cella első byte-ját már nem kell írni, azt beállított a cella reset, azért cim+1-től írunk
  Wire.write(adat & 0xFF);Wire.write((adat>>8) & 0xFF);Wire.write((adat>>16) & 0xFF);Wire.write((adat>>24) & 0xFF);
  Wire.endTransmission(); 
}

long multimemo(byte cim, cella_tipus tipus) {
/*******************************************************************************************************************************************************************  
 * Ez a multimemó függvény akkor hívódik meg, ha két paraméterrel hívjuk, és a második cella_tipus típusú változó, amit enum-al hoztunk létre a program elején.    *
 * A függvény elvégzi egy cella típusának beállítását (a cella első byte-ja), és alaphelyzetbe állítja a tárolt adatokat.                                          *
 * Bemenő paraméterek:                                                                                                                                             *
 *    cim: 0-73 db memória cella, egy cella 7 byte,                                                                                                                *
 *    típus: felsorolás típusú (enum) paraméter, ami meghatározza a cella tárolási módját. Lehetséges értékei 0-4 között.                                          *
 * Típus által képzett cella típusok, és azok tárolási módja, az elvégzett műveletek leírása:                                                                      *
 *  MIN: Az íráskor megkapott adatot csak akkor tárolja, ha az kissebb mint az éppen tárolt adat. Mielőtt elkezdjük a minimumot gyüjteni, be kell írni             *
 *       a lehető legnagyobb long értéket, különben nem biztos, hogy megjegyzi a legelső értéket. A számláló cellarészt nem használja.                             *
 *  MAX: Az íráskor megkapott adatot csak akkor tárolja, ha az nagyobb mint az éppen tárolt adat. Mielőtt elkezdjük a maximumott gyüjteni, be kell írni            *
 *       a lehető legkissebb long értéket, különben nem biztos, hogy megjegyzi a legelső értéket.                                                                  *
 *  SUM: Képzi a beírt adatok összegét. Alapértelmezetten a tartalma 0.                                                                                            *
 *  AVG: képzi a beírt adatok átlagát. Minden beít értéket szummáz az adat mezőben, számolja a beírások számát, és kiolvasáskor osztja az adatot a beírás számmal  *
 *       Alpértelmezetten az adat és a számláló is 0.                                                                                                              *
 *  STO: csak úgy simán tárolja az adatot, nem csinál vele semmit, ha volt bent előtte adat, azt felülírja. Alepértelmezett tartalma 0.                            *
 *******************************************************************************************************************************************************************/
  int xcim=cim*7;
  byte page;
  if (xcim<256) {page=0;} else {page=1;xcim=xcim-256;}
  Wire.beginTransmission(_i2cAddress | (page&1));
  Wire.write(xcim);
  if (tipus==MIN){
      // 2147483647 kezdő értéket írunk a tároló cellába, mert nincs nagyobb szám long esetén, így csak ennél kisebbek jöhetnek.
      Wire.write(0);Wire.write((long)2147483647 & 0xFF);Wire.write(((long)2147483647>>8) & 0xFF);Wire.write(((long)2147483647>>16) & 0xFF);Wire.write(((long)2147483647>>24) & 0xFF);
      Wire.write(0);Wire.write(0);
      Wire.endTransmission(); 
      return(0);
    }    
  if (tipus==MAX){
      // -2147483648 kezdő értéket írunk a tároló cellába, mert nincs kisebb szám long esetén, így csak ennél kisebbek jöhetnek.
      Wire.write(1);Wire.write((long)-2147483648 & 0xFF);Wire.write(((long)-2147483648>>8) & 0xFF);Wire.write(((long)-2147483648>>16) & 0xFF);Wire.write(((long)-2147483648>>24) & 0xFF);
      Wire.write(0);Wire.write(0);
      Wire.endTransmission(); 
      return(0);
    }    
  if (tipus==SUM or tipus==AVG or tipus==STO) {
      //össegző mező esetén csak simán törölni kell mindent
      Wire.write(tipus);Wire.write(0);Wire.write(0);Wire.write(0);Wire.write(0);
      Wire.write(0);Wire.write(0);
      Wire.endTransmission(); 
      return(0);
    }  
    Wire.endTransmission(); 
}

…és a soros port-ra kiírt eredmény első néhány sora:

reset...
tárolás...
MIN (10):10
MIN  (5):5
MAX (20):20
MAX (15):20
SUM (30):30
SUM (20):50
AVG (42):42
AVG (37):39
AVG (19):32
STO (50):50
Kiolvasott cella tartalmak:
MIN:5
MAX:20
SUM:50
AVG:32
STO:50
Generalt szam:6807  MIN:6807  MAX:6807  SUM:6807  AVG6807  Db:1  szamitott atlag:6807
Generalt szam:15249  MIN:6807  MAX:15249  SUM:22056  AVG11028  Db:2  szamitott atlag:11028
Generalt szam:73  MIN:73  MAX:15249  SUM:22129  AVG7376  Db:3  szamitott atlag:7376
Generalt szam:3658  MIN:73  MAX:15249  SUM:25787  AVG6446  Db:4  szamitott atlag:6446
Generalt szam:18930  MIN:73  MAX:18930  SUM:44717  AVG8943  Db:5  szamitott atlag:8943 

Érdekes eredményre vezetett ez a program. -10.000 és +20.000 közötti véletlen számot generáltam, és a multimemo segítségével tároltam a generált számok minimumát, maximumát, összegét, átlagát és ellenőrzési céllal minden generáláskor növeltem az egyik cella értékét. Az a nem túlságosan meglepő eredmény született, hogy véletlen generátor nem teljesen ideális. Ahogy növeltem a generált számok mennyiségét, az átlagnak 5.000-re kellett volna adódnia. Azonban nem így történt, kis mértékben mindig eltért tőle. Nem is olyan kicsi mértékben. Mivel kiírtam a generált db számot is, látható, hogy 20.000 véletlen szám átlaga az ideális 5000 helyett 5038.Ez szerintem elég durva eltérés. Másik érdekes megfigyelés lehet, hogy a maximum értéke 20000 generált szám esetében is csak 19992. Ez még nem rossz eredmény, nem számoltam ki a valószínűségét, hogy ennyi generált szám esetén mi a várható maximum. Pár sort ki is emeltem a program output-ból, hogy látható legyen miről beszélek:

Generalt szam:6807  MIN:6807  MAX:6807  SUM:6807  AVG6807  Db:1  szamitott atlag:6807
Generalt szam:15249  MIN:6807  MAX:15249  SUM:22056  AVG11028  Db:2  szamitott atlag:11028
Generalt szam:73  MIN:73  MAX:15249  SUM:22129  AVG7376  Db:3  szamitott atlag:7376
Generalt szam:3658  MIN:73  MAX:15249  SUM:25787  AVG6446  Db:4  szamitott atlag:6446

Generalt szam:-2793  MIN:-10000  MAX:19992  SUM:24894628  AVG4979  Db:4999  szamitott atlag:4979
Generalt szam:-4573  MIN:-10000  MAX:19992  SUM:24890055  AVG4978  Db:5000  szamitott atlag:4978
Generalt szam:5258  MIN:-10000  MAX:19992  SUM:24895313  AVG4978  Db:5001  szamitott atlag:4978
Generalt szam:-8634  MIN:-10000  MAX:19992  SUM:24886679  AVG4975  Db:5002  szamitott atlag:4975 

Generalt szam:16085  MIN:-10000  MAX:19992  SUM:49578804  AVG4958  Db:9998  szamitott atlag:4958
Generalt szam:16315  MIN:-10000  MAX:19992  SUM:49595119  AVG4960  Db:9999  szamitott atlag:4960
Generalt szam:-1935  MIN:-10000  MAX:19992  SUM:49593184  AVG4959  Db:10000  szamitott atlag:4959
Generalt szam:13406  MIN:-10000  MAX:19992  SUM:49606590  AVG4960  Db:10001  szamitott atlag:4960

Generalt szam:-8997  MIN:-10000  MAX:19992  SUM:75662129  AVG5044  Db:14998  szamitott atlag:5044
Generalt szam:17916  MIN:-10000  MAX:19992  SUM:75680045  AVG5045  Db:14999  szamitott atlag:5045
Generalt szam:7885  MIN:-10000  MAX:19992  SUM:75687930  AVG5045  Db:15000  szamitott atlag:5045
Generalt szam:15235  MIN:-10000  MAX:19992  SUM:75703165  AVG5046  Db:15001  szamitott atlag:5046 

Generalt szam:-8163  MIN:-10000  MAX:19992  SUM:100768475  AVG5038  Db:19998  szamitott atlag:5038
Generalt szam:-2033  MIN:-10000  MAX:19992  SUM:100766442  AVG5038  Db:19999  szamitott atlag:5038
Generalt szam:10914  MIN:-10000  MAX:19992  SUM:100777356  AVG5038  Db:20000  szamitott atlag:5038
Generalt szam:-798  MIN:-10000  MAX:19992  SUM:100776558  AVG5038  Db:20001  szamitott atlag:5038 

Fényerő szabályozó kapcsoló

Ezt a függvényt azért írtam, mert olyan fali kapcsolóra volt szükségem, amivel fel lehet kapcsolni a világítást, és ugyanazzal a kapcsolóval a fényerőt is lehet szabályozni. Valójában a lakásunk világítás vezérlő programját írtam meg először, ami teljesen jól működik, de már én sem látom át a működését annyira bonyolult lett. Annak forrását is megtalálod, azzal 6 szobát lehet egyszerre vezérelni, és setup funkciókkal is meg lett spékelve. Beállíthatod a fel és le fényerő szabályozási időket stb. és ezt ugyanarról a nyomógombról. Túl sokat tud, ezért azt a programot leegyszerűsítettem és jó alaposan felkommenteztem, hogy más programokban is tudjam használni. Esetleg valaki másnak is hasznos lehet jelen állapotában.

Ennek a programnak (függvénynek) a funkciói:

  • rövid gombnyomással fel illetve lekapcsolja a világítást.
  • Felkapcsoláskor a fényerő fokozatosan növekszik 0-tól a legutoljára beállított szintre
  • Lekapcsoláskor fokozatosan csökken a fényerő minimum értékre, majd minimumon világít beállítható időre és csak ennek leteltekor kapcsol ki teljesen a fény. A nyomógomb megnyomásakor egy rövidet villan a világítás, hogy látható legyen, megindult a fényerő csökkentés.
  • Ha nincs bekapcsolva a világítás, akkor kapcsoló folyamatos nyomva tartásával bekapcsolódik a világítás minimum fényerőn, és folyamatosan növekszik a fényerő. Az elengedés pillanatában érvényes fényerőn világít tovább, és ez lesz a beállított fényerő.
  • Ha a világítás be van kapcsolva, a kapcsoló folyamatos nyomva tartásával a fényerő növekedni (vagy csökkenni) kezd, ha maximumra ér, akkor villan egyet, hogy lássuk elértük a maximumot. Bármikor elengedjük a kapcsolót, a fényerőt megjegyzi, és az lesz a beállított fényerő, aminél megáll a fényerő növekedés a következő felkapcsoláskor.
  • A következő folyamatos nyomva tartáskor a fényerő ellentétes irányba változik, mint az előző folyamatos nyomva tartáskor. Növelés után egy rövid kapcsoló elengedés és újbóli nyomva tartás csökkenteni fog.

A minta program tartalmazza a lampa_on_off_fenyero() függvényt, ami a fentebb leírt funkciókat megcsinálja. Ha ezt használni akarod, akkor elég sok globális változót kell deklarálni a program elején, ezek részei a függvénynek. Nagy előnye a függvénynek, hogy nem tartalmaz időzítést, így nem lassítja jelentősen a loop működését. Tetszőleges egyéb programrészek helyezhetők el a loop-ban. Természetesen azok sem tartalmazhatnak időzítést, különben ez sem fog tudni működni. Annyira gyors a működés, hogy akár egyszerre több nyomógombot is ki tud szolgálni, de ekkor a függvényből többet kell csinálni különböző neveken, különböző névvel megduplikálni a változókat is, vagy lehet egy függvényt, és tömböket használni az egyes világítások változóihoz. Utóbbi esetben a függvény belsejét még egy for ciklusba kell helyezni, hogy minden egyes világításon végigmenjen minden meghíváskor. Ezzel a technikával dolgozik az említett világítás vezérlésem.

/****************************************************************************
 * azok a változók, paraméterek melyek közvetlenül befolyásolják a működést *
 ****************************************************************************/
  int tartasi_ido=3000;        //ennyi milisec-ig tartja minimumon a fényerot a lekapcsolás után
  int klikk_ido=400;           //ennyi idő után tartjuk benyomottnak a nyomógombot, és elkezdjük a fényerő szabályozást
  byte min_fenyero=10;         //lekapcsoláskor a minimális fényerő értéke. Ezen a fényerőn tartjuk a lámpát teljes kikapcsolásig
  int fenyero=255;             //lámpa fényereje 5..255-ig. Felkapcsoláskor az ebben tárolt fényerőre kapcsolja be a lámpát. 
                               //Nyomvatartáskor ennek az értékét változtatja a program. Azért int, hogy kihasználhassuk a 
                               //max fényerőt (255), mert ha 255-höz adunk hozzá 1-et növeléskor, abból 0 lenne ha byte.
  byte valtozasi_sebesseg=8;   //ennyi milisekondumonként változtatjuk a fényerőt nyomvatartott nyomógombnál, minél kissebb, 
                               //annál gyorsabb a változás. A teljes fényerő intervallum vágig változtatása 10msec esetén: 8msec*255=2 másodperc
  byte fel_le_valt_sebesseg=5; //ennyi milisekondumonként változtatjuk a fényerőt fel és lekapcsoláskor, minél kissebb, 
                               //annál gyorsabb a változás. A teljes fényerő intervallum vágig változtatása 10msec esetén: 5msec*255=1,27 másodperc
/****************************************************************************                              
 * Kapcsoló és lámpa állapotok és egyéb futás közbeni változók              *                  
 ****************************************************************************/
  byte lamp_on=0;         //lámpa bekapcsolt állapota esetén 1, kikapcsolva 0
  byte sw_on=0;           //kapcsoló nyomvatartás alatt értéke 1
  byte nyomvatart=0;      //jelzi, hogy nyomvatarjuk a nyomógombot, amig nyomjuk éstéke 1
  byte valtozas_fel_le=1; //a fenyero változás iránya nyomvatartáskor 1-fel 0-le
/****************************************************************************                              
 * segéd változók, melyek a működéshez kellenek                             *                  
 ****************************************************************************/
  byte sw_prell=0;            //kapcsoló prell figyeléshez
  long sw_time=0;             //kapcsoló lenyomás prell figyeléshez eltelt idő az első kontaktus óta
  byte sw_prev_stat=0;        //kapcsoló előző ciklusbani állapotát tárolja. Kapcsoló lenyomás és elengedés érzékeléséhez kell
  int  fenyero_tmp=0;          //a fényerő felés leusztatááskor az éppen aktuális fényerő értéke
  long fenyero_nov_time2=0;   //fenyero novelés során segédváltozó időméréshez
  byte lamp_on_prev_stat=0;   //lámpa előző állapota bekapcsolt állapota esetén 1, kikapcsolva 0
  long fenyero_nov_time=0;    //a fenyero novelesnel az idozítés segédváltozója
  byte nyomvatart_prev_stat=0;//nyomvatartás végének észrevételéhez segédváltozó 
  long sw_tart_time=0;        //kapcsoló lenyomás és elengedés közötti időmérés segéd változója. Első lenyomáskor 
                              //indul az időmérés, és ha egy ideig nem engedjük el, akkor lenyomottnak tekintjük 
                              //a nyomógombot, ami fényerőszabályozást eredményez
  byte villan=0;              //ha elérte a fényerő maximumot a fényerő növelés, akkor villan a lámpa, 
                                                //ezt engedélyezi, alapértelmezette 1 mert alapértelmezett változási irány a növelés
  byte lekapcs_eng=1; //nyomvatartott nyomógomb esetén elengedéskor nem engedjük kikapcsolni a lámpát, hiszen fényerőszabályozás volt
                      //ilyenkor értéke 0. Ha csak megnyomtuk a nyomógombot és és lehet lekapcsolni a lámpát, akkor értéke 1.                                

void setup() {
  pinMode(3,INPUT);     //nyomógomb bemenet
  digitalWrite(3,HIGH); //felhúzó ellenállás bekapcsolása a bemeneten
  pinMode(11,OUTPUT);   //erre kötjük a led-et
} 


void loop()
{
  /*
   * Ez a függvény egy nyomógombot és egy PWM kimenetet kezel
   * Bemenő paraméter sorrendje: bemenet, PWM kimenet
   */
  lampa_on_off_fenyero(3,11);
}


void lampa_on_off_fenyero(byte bemenet, byte kimenet)
/*
 * Ez a függvény egy nyomógomb megnyomásait figyeli és egy PWM képes kimenetten fel és lekapcsol egy led-et (lámpát).
 * Felkapcsoláskor a fényerő lassan kúszik fel egy beállított fényerőre. Lekapcsoláskor a fényerő lassan csökken 
 * egy beállított minimumra, és egy beállítható ideig minimumom tartja a fényerőt, hogy ne aludjon ki egy darabig
 * a fény, ne kelljen sötétben botorkálni. Mivel a lekapcsolás lassan indul meg, és szeretnénk biztosak lenni abban, 
 * hogy megtörtént, egy villanással jelzi a fényerő csökkenés megkezdését.
 * A nyomógomb hosszan nyomvatartva változtatja a fényerőt. Elengedve és újra hosszan nyomvatartva a fényerő 
 * ellentétes irányba változik. Ha változtatás közben a fényerő maximumra ért, akkor a lámpa villan egy rövidet, 
 * jelezve, hogy nem érdemes tovább nyomvatartani. 
 * Bekapcsolás után (reset) nem lehet fényerőt szabályozni, egyszer ki és be kell kapcsolni, nem tudom a hiba okát
 */
{
 /*
  * Ez a programrész a nyomógomb prellmentesítését végzi, valamint figyeli azt, hogy mennyi ideje tartjuk nyomva a nyomógombot
  * Ha a nyomvatartási idő hosszabb mint az előre beállított konstans klikk_ido változóban megadott érték, akkor úgy tekinti
  * hogy nyomva tartjuk a nyomógombot, vagyis fényerőt akarunk szabályozni. Ha a megnyomás kezdetén még nem világított a led,
  * akkor növelni kell a fényerőt (és bekapcsolni a lámpát), de ha már világított, akkor a fel le változást is ki kell jelölniű
  * a program későbbi részeiben.
  */
  if (sw_on==0 and digitalRead(bemenet)==LOW and sw_prell==0)                   //első lenyomás érzékelése sw_prell=1 jelzi, hogy már volt egy kontaktus,
    {sw_prell=1;sw_time=millis();}                                              //és 50msec múlva már biztosan megállt a prell a kontaktuson. sw_time 
                                                                                //tárolja az első kontaktus időpontját az 50msec méréséhez
  if (sw_on==0 and digitalRead(bemenet)==LOW and sw_prell==1 and millis()>sw_time+50) // már 50msecv óta nyomva van, most már biztos, hogy lenyomták és nem prellezik
    {sw_on=1;sw_prell=0;}                                                       //sw_prell=0, mert már biztosan nem prelles
                                                                                //sw_on=1, mert a kapcsoló meg lett nyomva (épp nyomva van)
  if (sw_on==1 and digitalRead(bemenet)==HIGH and sw_prell==0)                  //első elengedés érzékelése, itt is figyelni kell, 
    {sw_prell=1;sw_time=millis();}                                              //hogy prellezik-e a nyomógomb, sw_time tárolja az elengedés pillanatát, amikortól
                                                                                //kezdősik a prellezés, tehát sw_prell=1
  if (sw_on==1 and digitalRead(bemenet)==HIGH and sw_prell==1 and millis()>sw_time+50)//már 50msecv óta elengedte a nyomógombot, most már biztos, hogy nincs prell 
    {sw_on=0;sw_prell=0;}                                                       //sw_on=0 jelzi, hogy nincs nyomva nomógomb, sw_prell=0 jelzi, hogy már nincs prell
     
  if (sw_on==1 and sw_prev_stat==0)                                             //a kapcsoló előző állapota 0, és most megnyomták, tehát felfutó él érkezett
    {sw_tart_time=millis();}                                                    //sw_tart_time tárolja a benyomás időpontját, és innen figyeljük, hogy 500msec-en
                                                                                //elengedi-e a nyomógombot. 

  if (sw_on==0 and sw_prev_stat==1 and nyomvatart==0)                           //nem nyomvatartott állapotban engedtük el a nyomógombot (lefutó él)
    {                                                                           //lehet a lámpát fel vagy lekpcsolni
    if (lamp_on==0) {lamp_on=1;}                                                //lámpa nem volt bekapcsolva, lámpa bekapcsolása,
    else {lamp_on=0;}                                                           //lámpa fel volt kapcsolva, lampa lekapcsol
    lekapcs_eng=1;                                                              //jelezzük a későbbi programrészeknek, hogy lekapcsolható a lámpa
    }                   
    
  if (sw_on==0 and sw_prev_stat==1 and nyomvatart==1)                           //nyomvatartott állapotban engedtük el a nxomógombot (lefutó él)
    {nyomvatart=0;lekapcs_eng=0;}                                               //lámpa felkapcsolva marad beállított fényerőn, mert lekapcs_eng=0
                                                                                //a következő cilusban tudnunk kell a nyomógomb előző állapotát,
                                                                                //hogy detektálni tudjuk a nyomógomb bekapcsolását, és ez csak egyszer 
                                                                                //következzen be. Ehhez használjuk a sw_prev_stat változót.     

  if (sw_on==1 and sw_prev_stat==1 and millis()>sw_tart_time+klikk_ido)         //benyomva a nyomógomb és telik az idő. Ha klikk_ido eltelt, akkor nyomvatartottnak 
    {                                                                           //vesszük, ami fényerőszabályozást eredményez
    nyomvatart=1;                                                               //a nyomvatart változó jelzi, hogy nyomva van a nyomógomb
    if (lamp_on==0)                                                             //ha még nincs bekapsolva a lámpa, akkor bekapcsoljuk (nyomvatartással is bekapcsolható
      {lamp_on=1;fenyero=min_fenyero;valtozas_fel_le=1;villan=1;                //ha nyomvatartással kapcsoltuk fel, akkor minimum fényerőről indul a szabályozás
      fenyero_nov_time2=millis();lekapcs_eng=0;}                                //valtozas_fel_le=1 jelzi, hogy növelni kell a fényerőt, engedélyezük a villanást is max fényerőnél
   }                                                                            //fenyero_nov_time2 megjegyzi a növelés elkezdésének időpontját

  sw_prev_stat=sw_on;        //megjegyezzük a nyomógomb előző állapotát, hogy a következő ciklusban észrevegyük a változást
 /******************************************************************************************************
  * Ezek a programrészek a nyomvatortott nyomógomb esetén növelik vagy csökkentik a fényerőt, valamint *
  * nyomvatartás után elengedett nyomógomb esetén váltják a csökkentés vagy növelés irányát            *
  ******************************************************************************************************/
  if (fenyero<min_fenyero+1)                             //fényerő minimumra értünk. 
    {fenyero=min_fenyero+2;}                             //Azért állunk meg előtte egy értékkel, mert így kikapcsoláskor lesz segédfény
         
  if (fenyero>255)                                       //fenyero maximumhoz értünk
    {fenyero=255;}                                       //255 fölé nem nőhet a fényerő

  if (valtozas_fel_le==1 and nyomvatart==1 and millis()>fenyero_nov_time+valtozasi_sebesseg)//nyomvatartjuk a gombot és növeljuk a fényerőt maximumig
    {
    if (fenyero<255){fenyero=fenyero+1;}                                        //ha még kisebb a fényerő mint 255, akkor növelünk
    fenyero_nov_time=millis();                                                  //a fenyerő növelés következő lépéséhez megjegyezzük a pillanatnyi időpontot
    }
         
  if (fenyero_tmp==255 and nyomvatart==1 and villan==1)                         //nyomvatartjuk a kapcsolót és elértük a maximumot megvillantjuk 
    {analogWrite(kimenet,128);fenyero_nov_time2=millis();villan=0;}             //a lámpát rövid fényerő csökkentéssel, de a további villanásokat tiltjuk
         
  if (fenyero_tmp==255 and nyomvatart==1 and millis()>fenyero_nov_time2+200)    //200msec múlva visszakapcsolojuk a fényt maximumra (nyomvatartja a 
    {analogWrite(kimenet,255);}                                                                   //kapcsolót és elértük a maximumot, villanás vége, újra max fényerő)
         
  if (valtozas_fel_le==0 and nyomvatart==1 and millis()>fenyero_nov_time+valtozasi_sebesseg) // nyomvatartjuk a gombot és csökkentjük a fényerőt
    {fenyero=fenyero-1;fenyero_nov_time=millis();}
        
  if (nyomvatart==0 and nyomvatart_prev_stat==1)                                //nyomvatartás után elengedtük a nyomógombot
    {
    if (valtozas_fel_le==1) {valtozas_fel_le=0;}                                //ha eddig "fel" irány volt, akkor most "le" jön
    else {valtozas_fel_le=1;villan=1;}                                          //ha eddig "le" irány volt, akkor most "fel" jön. Engedélyezzük a villanást is
    }                                                                           //arra az esetre, ha a következő növeléskor elérte a mximumot a fényerő
      
  nyomvatart_prev_stat=nyomvatart;     //Ha a nyomógombot nyomva tartottuk, akkor a következő ciklusban 
                                       //tudnunk kell az előző ciklus állapotát, hogy felfedezzük a változást, mert következő
                                       //nyomvatartáskor meg kell változtatni a fényerő növelés vagy csökkentés irányát
        
  /******************************************************************************************************************************  
   * Ez a programrész figyeli a lamp_on változó értékét. Ha 1, vagyis bekapcsoltuk a lámpát, akkor a fényerőt mindíg beállítja  *
   * a fenyero nevű változó értékéhez, de ezt lassan csinálj azzal, hogy valójában egy fenyerp_tmp nevű változó határozza meg   *
   * a fényerőt, és ennek értékét fokozatosan növeli vagy csökkenti fenyero változó értékére.                                   *
   ******************************************************************************************************************************/
  if (lamp_on==1 and millis()>fenyero_nov_time2+fel_le_valt_sebesseg)   //Ha felkapcsoltuk a lámpát akkor a fenyero változóig növeljuk a fényerőt, 
                                                                        //ehhez minden ciklusban megnézzük eltel-e már fel_le_valt_sebesseg változóban 
                                                                        //megadott konstans idő. Ha igen, akkor megnézzük, hogy fenyero_tmp és fenyero 
                                                                        //változók között van-e különbség. Ha igen, akkor változtatjuk fenyero_tmp értékét.
                                                                        //Így a fenyero_tmp változó csak követi fenyero változó erteket, a fel_le_valt_sebesseg
                                                                        //változóban beállított sebességgel. A nyomógombbal fenyero változó értékét változtatjuk,
                                                                        //illetve bekapcsoláskor ez 255, kikapcsoláskor 0. Ezért kúszik fel és le a fényerő.
    {                                                  
    if (fenyero_tmp>=255) {fenyero_tmp=255;}      //fenyero_tmp nem nőhet 255 fölé,mert az a max fényerő, de ez a programban máshol előfordulhat, ezért kell figyelni
    if (fenyero_tmp<fenyero)                      //még nem értük el a szükséges fényerőt a fenyero változóban meghatározott fényerőt
      {fenyero_tmp=fenyero_tmp+1;                 //növeljük fenyero_tmp értékét                 
       fenyero_nov_time2=millis();                //tároljuk fenyero_nov_time2-ben az aktuálisidőt, mert ehhez hasonlít következő ciklusban 
       analogWrite(kimenet,fenyero_tmp);}              //beállítjuk a fényerőt fenyero_tmp értékére
            
    if (fenyero_tmp>fenyero)                      //ha a fényerő nagyobb mint a beállított fényerő, akkor elkezdjük csökkenteni
      {fenyero_tmp=fenyero_tmp-1;                 //csökkentjük fenyero_tmp értékét, mert a fenyero változóban megszabott fenyerőnél nagyobb afényerő
       fenyero_nov_time2=millis();                //tároljuk fenyero_nov_time2-ben az aktuálisidőt, mert ehhez hasonlít következő ciklusban 
       analogWrite(kimenet,fenyero_tmp);}              //beállítjuk a fényerőt fenyero_tmp értékére
   }
      
  /******************************************************************************************************************************  
   * Ez a programrész figyeli a lamp_on változó értékét. Ha 0, vagyis lekapcsoltuk a lámpát, akkor a fényerőt 0-ra állítja      *
   * de ezt lassan csinálj azzal, hogy valójában egy fenyero_tmp nevű változó értékét csökkenti időnként. Ha egy beállított     *
   * minimumra ért a fenyero_tmp értéke, akkor egy időre megál a csökkentéssel (tartasi_ido változó), és csak aztán kapcsolja   *
   * le teljesen a fényerőt. Amikor érzékeljük, hogy lekapcsoltuk a lámpát, egy rövid időre lecsökkentjük a fényerőt 0-ra,       *                                                                                                     
   * És ezzel jelezzük, hogy megindult a lekapcsolái folyamat                                                                   *
   ******************************************************************************************************************************/
  if (lamp_on==0 and millis()>fenyero_nov_time2+fel_le_valt_sebesseg and lekapcs_eng==1)       //lekapcsoltuk a lámpát, csökkenteni kell a fénmyerőt 0-ig 
    {                                                                                          //ebbe minden fel_le_valt_sebesseg értékével meghatározott időnként bele megy
    if (fenyero_tmp==min_fenyero and millis()>fenyero_nov_time2+tartasi_ido)                   //Ha már minimum fényerőn van, akkor még tartasi_ido értékig tartja a min fényerőt
      {analogWrite(kimenet,0);fenyero_tmp=0;}                      
            
    if (fenyero_tmp>min_fenyero)                                                               //még nem értük a el a minimum fenyerőt 
      {fenyero_tmp=fenyero_tmp-1;fenyero_nov_time2=millis();analogWrite(kimenet,fenyero_tmp);}      //csökkentjük a fényerőt, megjegyezzük a pillanatnyi időt a köv. csökkentéshez
    }  
  if (lamp_on==0 and lamp_on_prev_stat==1)                                                     //lampa lekapcsoláskor csökkemnteni fogjuk a fenyerőt egy rövid időre, 
    {analogWrite(kimenet,0);fenyero_nov_time2=millis()+70;}                                         //hogy lássul elkezdődött a lekapcsolás

  lamp_on_prev_stat=lamp_on;                                                                   //led állapotának tárolása a változás megállapításához
}