Jednoduchý projekt, v ktorom využijeme znalosti o AD prevodníku a o PWM. Plus sa naučíme ešte niečo nové. Tým novým budú externé prerušenia – časť Prerušovacieho podsystém v AVR mikrokontroleroch.

Externé prerušenia

Externé prerušenia sú súčasťou prerušovacieho podsystému AVR mikrokontrolérov. Tento systém umožňuje obsluhu určitých udalostí, na ktoré má mikrokontrolér reagovať. Ak sa vyskytne definovaná udalosť mikrokontrolér preruší beh programu a venuje sa programovej obsluhe udalosti, ktorá dané prerušenie vyvolala. Po skončení obsluhy mikrokontrolér pokračuje vo vykonávaní hlavného programu.

No a aký význam majú vôbec tieto prerušenia ? Ich význam je hlavne v zrýchlení a zefektívnení behu programu.

Veľmi často sa externé prerušenia využívajú napríklad na čítanie rotačných enkodérov alebo na sledovanie užívateľského vstup (stlačenie tlačítka a pod.).

Ak chceme využiť externé prerušenia v Arduinu použijeme na to funkciu attachInterrupt().

Táto funkcia má tri parametre: attachInterrupt(interrupt, function, mode)

  • interrupt – číslo externého prerušenia
  • function – funkcia, ktorá sa má vykonať ak nastane prerušenie (táto funkcia nesmie mať žiadne parametre a tiež nesmie vracať žiadnu hodnotu)
  • mode – určuje kedy sa má spustiť prerušenie. Existujú 4 preddefinované konštanty:
    • LOW spustí prerušenie, keď je na vývode log 0,
    • CHANGE spustí prerušenie, keď sa zmení stav na vývode,
    • RISING spustí prerušenie, keď vývod prejde z log 0 na log 1,
    • FALLING spustí prerušenie, keď vývod prejde z log 1 na log 0

Prehľad externých prerušení v jednotlivých verziách Arduina.

Verzie Arduina / Číslo ext. prerušenia int.0 int.1 int.2 int.3 int.4 int.5
UNO, Ethernet 2 3        
Mega2560 2 3 21 20 19 18
Leonardo 3 2 0 1 7  

Vypnutie externého prerušenia

Na vypnutie externého prerušenia existuje funkcia detachInterrupt(interrupt), ktorá má ako jediný parameter číslo externého prerušenia (viď tabuľka vyššie).

Kedy túto funkciu použiť ? Funkciu detachInterrupt použijeme vtedy ak potrebujeme na určitú dobu pozastaviť ext. prerušenia a chceme aby beh hlavného program nebol prerušovaný a ovplyvňovaný (napr. zmenami hodnôt premenných vo funkcii, ktorá sa vykonáva pri spustený prerušenia).

Príklad - Meranie a regulácia otáčok PC ventilátora

Ukážeme si na príklade ako fungujú externé prerušenia v praxi. Budeme merať a regulovať otáčky ventilátora z PC. Takže čo k tomu budeme potrebovať:

  • 1x Arduino UNO R3 alebo jeho kompatibilný klon
  • 1x Ventilator z PC (4. vývodový)
  • 1x 10kΩ
  • 1x zdroj napätia 12V
  • 1x LCD display 16x2
  • 1x Trimer 10kΩ ( nastavenie kontrastu displeja)
  • 1x Potenciometer 5kΩ
  • 1x Rezistor 150-470Ω (na zníženie jasu displeja)

Ventilátor z CPU chladiča - popis vývodov konektora

V tomto projekte využijeme starší štvorvodičový ventilátor z CPU (box chladič). Tieto ventilátory vieme regulovať pomocou PWM signálu generovaného z Arduina. Taktiež majú výstup na meranie otáčok. Popis vývodov konektora je na obrázku:

Zapojenie na kontaktnom poli


 

Zapojenie je pomerne jednoduché, vyžaduje však zdroj napätia 12V kvôli ventilátoru. Otáčky regulujeme pomocou potenciometra, ktorý máme pripojený k analógovému vstupu. Na základe hodnoty získanej z AD prevodníka, vypočítame PWM hodnotu, ktorú následne pošleme cez PWM výstup do ventilátora (modrý vodič). Snímanie otáčok je riešené tak, že zelený vodič od ventilátora je pripojený na vývod číslo 2 cez pullup rezistor. Tento vývod je vlastne prerušenie 0. Monitorujeme teda pulzy, ktoré prichádzajú na tento vývod a po určitom čase vypočítame počet otáčok za minútu a zobrazíme ich na displeji.

Schéma zapojenia

Zdrojový kód

Zdrojový kód v súbore ino.
#include <LiquidCrystal.h>

#define PIN_FAN_PWM 3 // pin na ktorý pripojíme modrý vodič ventilátora
#define PIN_FAN_SENSOR 0 // pin (prerušenie) na ktorý pripojíme zelený vodič ventilátora
#define PIN_REGULATOR A0 // analógový vstup pre potenciometer

volatile byte pulseCounter; // počitadlo pulzov
unsigned int rpm; // výsledná hodnota otáčok za minútu (rpm)
// pomocné premenné na výpočet otáčok
unsigned long previousMillis = 0;
unsigned long interval = 2000;
unsigned int regulatorValue = 0; // pwm hodnota, ktorá sa posiela na riadenie otáčok vent.
unsigned int oldRegulatorValue = 0;
uint8_t fullCharIndex = 0;
byte fullChar[8] = {
	0b11111,
	0b11111,
	0b11111,
	0b11111,
	0b11111,
	0b11111,
	0b11111,
	0b11111
};
LiquidCrystal lcd(12, 11, 7, 8, 9, 10);

/* Prototyp metódy na počítanie otáčok */
void rpmFan();
void updateDisplay(unsigned int rpmValue, unsigned int regValue);

void setup()
{
	/* Nastavenie displaya */
	lcd.createChar(fullCharIndex, fullChar);
	lcd.begin(16, 2);
	/* Pripojíme prerušenie na digitálny pin 2 (teda prerušenie 0) */
	attachInterrupt(PIN_FAN_SENSOR, rpmFan, FALLING);
	/* Nastavíme počiatočné hodnoty premenných */
	pulseCounter = 0;
	rpm = 0;
}

void loop()
{
	// načítame hodnotu z analógového vstupu a vydelíme 4mi,
	// hodnota zo vstupu môže byť 0-1023
	// a pwm hodnota ktorú pošleme na pwm pin môže byť len v intervale 0-255
	// tzn. hodnotu z analógového vstupu musíme preto podeliť 4, aby výsledná hodnota mohla byť použitá
	// ako PWM hodnota na riadenie otáčok pripojeného ventilátora
	regulatorValue = round(analogRead(PIN_REGULATOR) / 4);
	delay(50);
	if (regulatorValue!=oldRegulatorValue)
	{
		// vypočítanú hodnotu pošleme cez pwm pin do ventilátora
		analogWrite(PIN_FAN_PWM, regulatorValue);
		
		updateDisplay(rpm, regulatorValue);

		oldRegulatorValue = regulatorValue;
	}
	
	unsigned long currentMillis = millis();
	if(currentMillis - previousMillis > interval) // každú sekundu vypočítame otáčky
	{
		// výsledné otáčky vypočítame nasledovne		
		rpm = 15*pulseCounter;
		previousMillis = currentMillis;
		pulseCounter = 0; // vynulujeme počitadlo

		// vypočítane hodnoty zobrazíme na pripojenom displayi
		updateDisplay(rpm, regulatorValue);
	}
}

/*
Táto metóda sa volá ak nastane prerušenie na pine 2, tzn. v našom prípade ak cez
zelený vodič príde logická 0, teda LOW hodnota
*/
void rpmFan()
{
	// zvýšime počet otáčok vždy o 1
	++pulseCounter;
}

void updateDisplay(unsigned int rpmValue, unsigned int regValue)
{
	//vymažeme display
	lcd.clear();
	//nastavíme kurzor na prvý riadok
	lcd.setCursor(0,0);
	//zobrazíme otáčky
	lcd.print("RPM:");
	lcd.print(rpm);
	//zobrazíme otáčky v percentách
	unsigned int percent = map(regValue, 0, 255, 30, 100);
	lcd.setCursor(12,0);
	lcd.print(percent);
	lcd.print("%");

	unsigned int charsCounter = map(regValue, 0, 255, 0, 17);
	lcd.setCursor(0,1);
	for (int i = 0; i < charsCounter; i++)
	{
		lcd.write(fullCharIndex);
	}
}

Popis zdrojového kódu

Veľa je popísané v samotnom kóde, takže len stručne popíšem niektoré funkcie a časti.

Meranie otáčok ventilátora

Prejdem rovno k časti, ktorá meria otáčky ventilátora. Meranie prebieha tak, že snímame impulzy, ktoré prichádzajú na vývod číslo 2 a po určitom čase (máme nastavené 2 sekundy) vypočítame otáčky za minútu (ot./min). Je možné to robiť aj inak, napríklad počítať impulzy 1 minútu a až potom vypočítať otáčky za túto 1 minútu, ale ak chceme mať hodnotu skoro hneď na displeji musíme to urobiť za kratší čas, preto 2 sekundy.

Ešte jednu vec spomeniem, ktorá je dôležitá na výpočet otáčok. Jedna otáčka na ventilátore „vygeneruje“ 2 pulzy, podľa špecifikácii týchto ventilátorov.

Na meranie využijeme externé prerušenie 0 na vývode číslo 2. Na tento vývod pripojíme zelený vodič a budeme snímať zmenu z log. 1 na log. 0, viď nastavenie v setup metóde na riadku 37. Ako funkciu, ktorá sa má vykonať pri spustený prerušenia máme nastavenú rpmFan. Vo funkcii rpmFan inkrementujeme len premennú pulseCounter.

Upozornenie

Funkcie, ktoré sa vykonávajú po spustení prerušenia, by mali vykonávať čo najmenej operácii a pokiaľ možno čo najjednoduchších (napr. zmena premennej a pod.).

Na riadkoch 62 až 72 máme podmienku v ktorej sa vypočítajú otáčky za minútu každé dve sekundy. Všimnite si riadok 66.

rpm = 15 * pulseCounter;

Ako som prišiel k číslu 15 ? Hneď si to vysvetlíme. Premenná pulseCounter uchováva počet pulzov za 2 sekundy. Nie teda počet otáčok za 2 sekundy. Ak by sme chceli počet otáčok za 2 sekundy museli by sme premennú pulseCounter podeliť číslom 2. Keďže vychádzame z toho, že 2 pulzy znamenajú jednu otáčku:

rpm = pulseCounter / 2; // ot. / 2 sekundy

No ale my chceme predsa otáčky za minútu, nie za dve sekundy. Takže stačí urobiť jednoduchú matematickú operáciu, celý ten náš výraz vynásobíme číslom 30. Čo predstavuje 30 sekúnd a máme počet otáčok za 60 sekúnd teda 1 minútu.

rpm = 30 * pulseCounter / 2; // ot. / min

Upravme tento náš výpočet. Ušetríme jednu matematickú operáciu.

rpm = 15 * pulseCounter;  // ot. / min

Tak a už viete odkiaľ sa zobralo číslo 15 :).

Regulácia otáčok

Na reguláciu som zvolil potenciometer zapojený na analógový vstup. Meriam teda napätie na tomto vstupe a podľa nameranej hodnoty si pomocou funkcie map prepočítam hodnotu, ktorú pošlem ako PWM signál do ventilátora. Je to veľmi jednoduché riešenie. Ak ste čítali predchádzajúci článok tak viete ako funguje AD prevodník, túto časť teda nemusím dopodrobna popisovať. Časť kódu, ktorá súvisí s reguláciou nájdete na riadkoch 50 až 60. Ako je vidieť na riadku 52 je podmienka, kde porovnám starú hodnotu s novou a nastavenie PWM signálu robím len v prípade ak sú tieto hodnoty rozdielne. Pretože nie je nutné nastavovať stále každým cyklom rovnaké PWM ak sme ho už raz nastavili. Urobíme to len pri nejakej zmene, v našom prípade je to zmena napätia na analógovom vstupe resp. otočenie potenciometra.

V tejto časti ešte spomeniem, že regulácia otáčok u takýchto ventilátorov nie je možná od 0 (nuly). Čiže je možná len asi od 30% do 100% otáčok, takže ak potenciometer „dáme“ na minimum, ventilátor sa bude stále točiť a to otáčkami asi 30% z maximálnych.

Určite ste si všimli funkciu updateDisplay, ktorá má dva parametre. Prvý parameter je hodnota otáčok za minútu a druhy parameter je pwm hodnota. Táto funkcia ako jej názov napovedá nám nastaví aktuálne hodnoty otáčok a pwm signálu na LCD displej. Telo funkcie nebudem nejak popisovať, pretože o displejoch už niečo viete a knižnica LiquidCrystal a jej funkcie sú vám známe z článku o LCD displejoch.

Záver

Na tomto jednoduchom zapojení ste sa naučili používať externé prerušenia v Arduinu. Viete teda použiť funkciu attachInterrupt(interrupt, function, mode). Nastaviť vhodné parametre ako číslo prerušenia, funkciu a mód. Myslím, že tento projekt je celkom zaujímavý, určite vás napadnú nejaké úpravy, ktoré by ste urobili, čo je fajn. Ak máte nejaké otázky alebo ste niečomu nerozumeli, napíšte mi do komentára.