Zápisník experimentátora
Hierarchy: Stmievač LED pásika pomocou ATtiny85
V tomto článku vysvetlím princíp činnosti stmievača LED pásikov. Je to tretí zo série článkov, na ktorých vám ukážem, ako postupne prebieha vývoj programov a ako diametrálne sa môže líšiť výsledný program. Postupne prejdeme zdrojové kódy programu od verzie 1.0 po verziu 1.3.
Tento text popisuje zastaranú verziu kódu. V čase písania článku bola aktuálna verzia kódu 1.3. Táto verzia je tu analyzovaná preto, aby ste si mohli pozrieť, ako prebieha vývoj programov metódou postupného približovania.
V ideálnom svete napíšete ideálny program hneď na prvý raz a nebudú v ňom žiadne chyby a bude robiť presne to, čo ste si predstavovali. A teraz vitajte v reálnom svete, v ktorom som sa pokúsil naprogramovať logiku stmievania tak, aby vyhovovala práci v kuchyni.
Kód bol vyvíjaný ešte pre verziu 1.0.6, ale bez problémov ho možno skompilovať aj v 1.6.13 alebo 1.8.2. Zaberá po skompilovaní 2894 (6110 so zapnutým debuggovaním) bajtov. Je nutné mať doinštalovanú podporu pre programovanie ATtiny85.
V tomto programe som sa sústredil na zmenu programu na stavový automat. Celý vývoj prebiehal na vývojovej doske pre ATtiny85, ktorú som už popisoval v predchádzajúcom článku.
Debuggovanie je založené na existencii alebo neexistencii makra RDEBUG
. Ak neexistuje (finálna verzia kódu), potom nie sú vôbec použité piny 3 a 4 na ktoré je nastavená knižnica SoftwareSerial
, čo znamená, že prekladač všetok ten kód zo zdrojáku jednoducho odstráni.
Ak je makro definované (ladiaca verzia kódu), na sériový port sa odosielajú ladiace výpisy. Ešte stojí za zmienku, že počas ladenia mám skrátený interval hold_value
na 5 sekúnd. Je to z praktických dôvodov, pretože počas ladenia nie je dôvod čakať desiatky sekúnd na zhasnutie osvetlenia.
//#define RDEBUG
#if defined RDEBUG
#include <SoftwareSerial.h>
const int dig_tx = 3;
const int dig_rx = 4;
SoftwareSerial s85(dig_rx, dig_tx); // RX, TX
#endif
#if defined RDEBUG
const unsigned long hold_value = 5*1000L;
#else
// base hold period
const unsigned long hold_value = 45*1000L;
#endif
void setup() {
pinMode(led, OUTPUT);
pinMode(dig, INPUT);
#if defined RDEBUG
s85.begin(9600);
s85.println(F("ATtiny85 LED dimmer"));
#else
for(int i=0;i<2;i++)
ShowLedIsLive();
#endif
state=stDark;
}
V programe mám tieto stavy:
enum states {stBoot, stDark, stOn, stHold, stOff, stOff30, stHold30};
states state=stBoot;
A jednotlivé stavy majú v kóde svoje funkcie, ktoré riadia správanie osvetlenia.
void loop() {
switch(state) {
case stDark:
LightDark();
break;
case stOn:
LightOn();
break;
case stHold:
LightHold();
break;
case stOff:
LightOff();
break;
case stHold30:
LightHold30();
break;
case stOff30:
LightOff30();
break;
}
}
Lineárne stmievanie je zabezpečené pomocou tabuľky predpočítaných hodnôt.
const byte table[] PROGMEM = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7,
7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10,
10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15,
15, 15, 16, 16, 16, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 21,
22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30,
31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 58, 59, 60, 62,
63, 64, 66, 67, 69, 70, 72, 73, 75, 77, 78, 80, 82, 84, 86, 88,
90, 91, 94, 96, 98, 100, 102, 104, 107, 109, 111, 114, 116, 119, 122, 124,
127, 130, 133, 136, 139, 142, 145, 148, 151, 155, 158, 161, 165, 169, 172, 176,
180, 184, 188, 192, 196, 201, 205, 210, 214, 219, 224, 229, 234, 239, 244, 250
};
Napríklad počas zhasínania vyzerá kód takto. Keď to teraz spätne popisujem, uvedomujem si, že táto funkcia má svoje muchy a časť kódu sa nikdy nevykoná. Lebo keď klesne hodnota v premennej value
pod 20, vyskočí sa von z funkcie a kód na jej konci sa nikdy nevykoná.
void LightOff() {
#if defined RDEBUG
s85.println(F("Light OFF"));
#endif
unsigned long ms=millis();
unsigned long m=0;
unsigned long m2=pot_value*6; // 6x on period
while(m<m2)
{
int p=digitalRead(dig);
if(p)
{
state=stOn;
#if defined RDEBUG
s85.print(F("Signal break at "));
//s85.println(value);
#endif
return;
}
value=map(m,0,m2,255,0);
int vm=pgm_read_byte(&table[value]);
value=vm;
analogWrite(led,vm);
if(value<20) {
state=stHold30;
return;
}
m=millis()-ms;
}
digitalWrite(led, LOW);
value=0;
#if defined RDEBUG
s85.println(F("Dark"));
#endif
state=stDark;
}
Toto je veľmi dôležitá úprava. Stmievanie sa totiž vykonáva iba pár sekúnd a pokiaľ sa začalo stmievanie preto, lebo ste sa v miestnosti prestali pohybovať (napríklad ste sa začítali do časopisu), nemali by ste dostatok času na zamávanie rukou, aby sa svetlo zase zaplo na plnú intenzitu. Preto je stmievanie rozdelené na dva intervaly. Medzi nimi je vložený udržiavací interval v šere, ktorý vám dáva dostatok času na reakciu.
///
/// Handled dusk
///
void LightHold30() {
#if defined RDEBUG
s85.println(F("Light HOLD 30"));
#endif
dig_last=millis()+hold_value;
while(millis()<dig_last)
{
int p=digitalRead(dig);
if(p) // still ON
{
#if defined RDEBUG
//s85.print(F("dig_last="));
//s85.println(dig_last);
#endif
state=stOn;
return;
}
}
state=stOff30;
}
///
/// From dusk to darkness
///
void LightOff30() {
#if defined RDEBUG
s85.println(F("OFF 30"));
#endif
unsigned long ms=millis();
unsigned long m=0;
unsigned long m2=pot_value;
while(m<m2)
{
int p=digitalRead(dig);
if(p)
{
state=stOn;
return;
}
int v=map(m,0,m2,value,0);
analogWrite(led,v);
value=v;
m=millis()-ms;
}
digitalWrite(led, LOW);
value=0;
state=stDark;
}
Zdrojový kód sa nachádza na GitHub.
10.05.2017