elektronika
o.O wtFAQ – We Tinker, Fix and Question
◀ Arduino - korak po korak ☸

Djelilo napona, Arduino i PWM
Od mjerenja napona na potenciometru do LED-ice koja upozorava na prekoračenje praga

Kontrola svjetla

Na prošloj radionici vidjeli smo kako promjena otpora može utjecati na elektronički sklop. Danas ćemo tu ideju istražiti korak po korak: najprije ćemo potenciometrom napraviti podesivo djelilo napona i izmjeriti njegov izlaz, a zatim ćemo taj izlaz spojiti na Arduino Uno. Program će očitavati napon, upravljati jačinom LED-ice i na kraju je pretvoriti u signal upozorenja.

Potenciometar kao djelilo napona

Djelilo napona sastoji se od dva otpora spojena jedan za drugim između napajanja i mase. Napon mjerimo na mjestu između njih.
Promjenjivi otpornik (potenciometar) se ponaša kao dva otpornika spojena kao djelilo napona, s time da se njihova vrijednost mijenja mehaničkim putem (npr. okretanjem osovine potenciometra).

djelilo napona

Ako znamo oba otpora, izlazni napon možemo izračunati:

\[ V_{out} = U_{izvor} \times \frac{R_2}{R_1 + R_2} \]

Struja koja prolazi kroz neopterećeno djelilo napona iznosi:

\[ I = \frac{U_{izvor}}{R_1 + R_2} \]

​ Kroz dva serijski spojena dijela prolazi ista struja, pa napone na njima možemo izračunati i kao
\[ U_{1} = I \times R_1 \]
,
\[ U_{2} = I \times R_2 \]

Njihov zbroj jednak je ulaznom naponu. Ove formule pretpostavljaju da na izlaz djelila nije spojen potrošač koji uzima znatnu struju; djelilo služi za stvaranje mjernog signala, ne za napajanje jačeg uređaja.

Vježba 1: Izmjeri napon

Na breadboardu spajamo potenciometar od 10 kΩ:

djelilo napona

Za razliku od prošle vježbe, ovdje srednji izvod ne spajamo s krajnjim. Sada koristimo sva tri izvoda: dva krajnja određuju cijelu otpornu stazu, a srednji je pomični izlaz djelila. Okretanjem osovine mijenjamo koliko otpora ostaje iznad, a koliko ispod klizača.

Prije uključivanja provjerimo položaj sondi: crvena mora biti u priključku za mjerenje napona, obično VΩ, a ne u mA ili A. Instrument postavljamo na istosmjerni napon (V=) i mjerimo paralelno, između klizača i GND.

Što očekujemo?

  • klizač je blizu kraja spojenog na GND: oko 0 V
  • klizač je na sredini: oko 2.5 V
  • klizač je blizu kraja spojenog na +5 V: oko 5 V

Ako je ukupni otpor potenciometra 10 kΩ, a napajanje 5 V, kroz njegovu otpornu stazu prolazi približno 5 V / 10000 &Omega = 0.5 mA. Na polovici staze imamo približno 5 kΩ iznad i 5 kΩ ispod klizača, pa očekujemo oko 2.5 V. Stvarno očitanje može malo odstupati zbog položaja klizača i stvarnog napona izvora.

Za promatranje:

  • Okreći potenciometar polako. Mijenja li se napon postupno?
  • Možeš li namjestiti oko 1 V, 2.5 V i 4 V?

Vježba 2. Arduino očitava djelilo

Sada voltmetar ostaje koristan za provjeru, ali klizač potenciometra dodatno spajamo na analogni ulaz A0 Arduino Una. Krajnji izvodi ostaju na +5V i GND; svi dijelovi moraju koristiti zajednički GND.

LED-icu spajamo na PWM pin D9 preko serijskog otpornika od 220 Ω:

djelilo napona sa arduinom

Arduino Uno na A0 očitava napon i pretvara ga u cijeli broj od 0 do 1023. Približno: 0 V daje 0, 2.5 V daje vrijednost oko sredine raspona, a 5 V daje vrijednost blizu 1023. analogRead() ne vraća volte; vraća broj koji predstavlja izmjereni napon.

// C++
const int potPin = A0;
const int ledPin = 9;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int vrijednost = analogRead(potPin);
  int jacinaLedice = map(vrijednost, 0, 1023, 0, 255);

  analogWrite(ledPin, jacinaLedice);

  Serial.print(vrijednost);
  Serial.print(", ");
  Serial.println(jacinaLedice);

  delay(50);
}

Kako čitamo program?

  • setup() i loop() temelj su strukture Arduino programa: prvi priprema uređaj, a drugi ponavlja radnje.
  • const int potPin = A0; – Imenovali smo ulazni pin. const znači da se ta oznaka tijekom rada ne mijenja.
  • const int ledPin = 9; – Imenovali smo izlazni pin za LED-icu.
  • void setup() – Dio programa koji se izvršava jednom nakon pokretanja ili resetiranja.
  • pinMode(ledPin, OUTPUT); – Postavlja D9 kao izlaz.
  • Serial.begin(9600); – Pokreće serijsku komunikaciju s računalom pri brzini 9600 baud.
  • void loop() – Dio programa koji se stalno ponavlja dok Arduino radi.
  • int vrijednost = analogRead(potPin); – Očitava analogni ulaz i sprema rezultat u varijablu.
  • map(...) – Preračunava vrijednost iz jednog brojevnog raspona u drugi.
  • analogWrite(...) – Zadaje PWM vrijednost na D9.
  • Serial.print(...) – Ispisuje podatak i nastavlja pisati u istom retku.
  • Serial.println(...) – Ispisuje podatak i završava redak (prelazi u novi redak).
  • delay(50); – Pričeka 50 milisekundi prije sljedećeg prolaza kroz loop().

Zašto map()? Ulaz i izlaz nemaju isti broj mogućih vrijednosti: očitanje A0 ide od 0 do 1023, a vrijednost koju u ovoj vježbi šaljemo funkciji analogWrite() od 0 do 255. Tako se, primjerice, ulaz oko sredine raspona pretvara u izlaz oko sredine PWM raspona.

map()

Tekst i grafikon

Arduino šalje podatke računalu USB vezom preko serijske komunikacije. U Serial Monitoru vidimo retke brojeva. U Serial Plotteru isti se brojevi mogu prikazati kao linije na grafikonu. Između vrijednosti ispisujemo zarez, a Serial.println() završava redak kako bi Plotter znao gdje počinje sljedeći uzorak. Arduino dokumentacija pokazuje upravo ovakav ispis više vrijednosti, uključujući stalnu referentnu liniju.

Okreni potenciometar i usporedi:

  • Napon koji pokazuje voltmetar.
  • Broj vrijednost koji pokazuje Serial Monitor.
  • Vrijednost jacinaLedice.
  • Vidljivu jačinu LED-ice.

Broj 255 ne znači 255 volti. To je programska vrijednost kojom zadajemo najveći PWM izlaz u ovom primjeru.

Što je na pinu D9?

Sada voltmetrom mjerimo napon klizača prema GND, a voditelj osciloskopom promatra D9 prema GND. Sondu osciloskopa priključujemo samo na naš niskonaponski sklop; njezina masa ide na GND.

Na A0 imamo razmjerno miran, podesiv napon između 0 i 5 V. Na D9 nemamo glatki analogni napon koji se postupno mijenja od 0 do 5 V. Arduino Uno ondje šalje PWM signal: niz brzih prijelaza između približno 0 V i 5 V. Vrijednost od 0 do 255 određuje koliki je dio vremena signal uključen.

PWM
  • 0 → 0% → LED je ugašena
  • 64 → 25% → LED izgleda slabije
  • 128 → 50% → LED izgleda svjetlije
  • 255 → 100% → LED je najjače uključena

Na osciloskopu ćemo zato vidjeti da se mijenja širina uključenog dijela impulsa, a ne visina napona impulsa. Kod Arduino Uno pina D9 frekvencija PWM-a u uobičajenoj postavci iznosi približno 490 Hz. LED se uključuje i isključuje toliko brzo da obično opažamo promjenu njezine prividne jačine. Odnos zadane PWM vrijednosti i onoga što oku izgleda „dvostruko svjetlije” nije potpuno linearan.

Voltmetar na D9 može pokazati neku srednju vrijednost, ovisno o instrumentu. Osciloskop otkriva ono što takvo očitanje skriva: signal se zapravo stalno prebacuje između dviju razina.

Vježba 3. Može li Arduino biti osciloskop?

Ne u ovoj jednostavnoj izvedbi. Za dodatni eksperiment možemo žicom spojiti D9 na slobodni analogni ulaz A1 i očitati taj pin. Pritom ne odspajamo klizač potenciometra s A0: A0 ostaje ulaz za djelilo, a A1 postaje ulaz za promatranje PWM signala.

djelilo napona sa arduinom

Na A1 ne dodajemo drugi izvor napajanja. D9 i A1 su pinovi istog Arduino Una, pa već dijele masu. Žicu spajamo uz isključen USB i tek nakon provjere uključujemo sklop. D9 smije biti spojen na A1 samo dok je A1 analogni ulaz; ne smijemo ga u drugom programu postaviti kao suprotno upravljani izlaz.

// C++
const int potPin = A0;
const int ledPin = 9;
const int monPin = A1;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int vrijednost = analogRead(potPin);
  int jacinaLedice = map(vrijednost, 0, 1023, 0, 255);

  analogWrite(ledPin, jacinaLedice);

  int signal = analogRead(monPin);

  Serial.print(vrijednost);
  Serial.print(", ");
  Serial.print(jacinaLedice);
  Serial.print(", ");
  Serial.println(signal);

  delay(50);
}

Ovo je važan eksperiment upravo zato što rezultat može iznenaditi. A1 uzima trenutni uzorak PWM signala: može zateći približno 0 V ili približno 5 V, pa očitanje često bude blizu 0 ili 1023. Uzastopni uzorci ne moraju dati urednu krivulju širine impulsa. Serijski grafikon crta uzorke poslane računalom; on ne prikazuje osciloskopski oblik signala. Za stvarno promatranje impulsa koristimo osciloskop.

Vježba 4. Kad vrijednost prijeđe prag

Sada programu dodajemo novo pravilo:

  • kada očitanje na A0 prijeđe zadani prag, LED-ica će treperiti.

To može predstavljati upozorenje pri dostizanju kritične vrijednosti.

Prag 800 nije točno 80% od 1023: točno 80% iznosi približno 818. Za početak ćemo ipak koristiti 800 kao lako čitljiv, zaokružen prag – oko 78% raspona. Ako želiš prag bliže 80%, zamijeni 800 s 818.

// C++
const int potPin = A0;
const int ledPin = 9;
const int monPin = A1;
const int prag = 800;

bool ledMode = true;
int count = 10;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int vrijednost = analogRead(potPin);
  int jacinaLedice = map(vrijednost, 0, 1023, 0, 255);

  if (vrijednost > prag) {
    if (!ledMode) {
      jacinaLedice = 0;
    }

    count--;

    if (count == 0) {
      count = 10;
      ledMode = !ledMode;
    }
  }

  analogWrite(ledPin, jacinaLedice);
  int signal = analogRead(monPin);

  Serial.print("Prag:");
  Serial.print(prag);
  Serial.print(", Ulaz:");
  Serial.print(vrijednost);
  Serial.print(", PWM:");
  Serial.print(jacinaLedice);
  Serial.print(", A1:");
  Serial.println(signal);

  delay(50);
}

Što je novo u programu?

  • const int prag = 800; – Granica iznad koje aktiviramo upozorenje.
  • bool ledMode = true; – Pamti je li trenutačna faza treperenja uključena ili isključena.
  • int count = 10; – Broji prolaze kroz loop() prije promjene faze.
  • if (vrijednost > prag) – Izvršava posebna pravila samo kada ulaz prijeđe prag.
  • if (!ledMode) – Znak ! znači nije: ako je faza isključena, PWM postaje 0.
  • count--; – Smanjuje brojač za jedan.
  • if (count == 0) – Provjerava je li brojač stigao do nule; == uspoređuje, a = pridružuje vrijednost.
  • ledMode = !ledMode; – Mijenja stanje iz true u false ili obrnuto.

Uz delay(50), deset prolaza kroz petlju traje približno pola sekunde, ali ne točno: i očitavanje te serijski ispis troše vrijeme. LED je tako približno pola sekunde u jednoj fazi i pola sekunde u drugoj. Kod namjerno zadržava zadnju fazu i vrijednost brojača kada se potenciometar vrati ispod praga. To znači da pri ponovnom prelasku preko 800 upozorenje može nastaviti usred prethodnog ciklusa. Za radionicu to je zanimljiv primjer stanja koje program pamti, ali za predvidljiv uređaj trebalo bi pri izlasku iz upozorenja vratiti ledMode = true i count = 10.

Zašto ispisujemo liniju Prag: 800?
Stalna vrijednost 800 daje vodoravnu referentnu liniju u Serial Plotteru. Dok okrećemo potenciometar, možemo vidjeti gdje krivulja Ulaz prelazi tu liniju i usporediti taj trenutak s početkom treperenja. Oznake poput Prag: i Ulaz: pomažu razlikovati krivulje; Arduino Serial Plotter podržava imenovane numeričke vrijednosti u istom retku.

Važno je čitati osi grafikona: Ulaz i A1 su očitanja od približno 0 do 1023, dok je PWM zadana vrijednost od 0 do 255. Sve krivulje mogu biti na istoj osi, ali ne predstavljaju iste fizikalne veličine.

Prag nije uvijek mirna granica

Ako se očitanje zadržava oko 800 i malo oscilira, LED-ica može neuredno ulaziti u način upozorenja i izlaziti iz njega. U stvarnom uređaju dodali bismo dvije granice, primjerice uključivanje upozorenja iznad 820 i isključivanje tek ispod 780. To se zove histereza.

Dodatni zadatak: podešavanje brzine

Sastavi još jedno djelilo napona s drugim potenciometrom od 10 kΩ, koji ćeš spojiti na analogni pin A2.

Prvi potenciometar na A0 određuje kada ćemo prijeći prag. Drugi potenciometar na A2 određuje koliko brzo LED treperi.

Želimo očitanje A2 od 0 do 1023 pretvoriti u broj koraka brojača od 0 do 50. Ali postoji zamka: ako count postavimo na 0, a sljedeća naredba je count--, brojač postaje −1 i uvjet count == 0 više se neće ostvariti. Zato smijemo izračunati raspon 0–50, ali za stvarni brojač moramo odabrati barem 1.

Ne pokušavamo analogRead(A2) upisati kao početnu vrijednost globalne varijable prije setup(); Arduino najprije mora pokrenuti svoj hardver. Brojač inicijaliziramo nakon pokretanja i ponovno ga očitamo pri svakom dolasku do nule.

Zamijeni početni dio programa i mjesto resetiranja brojača ovim:

// C++
const int brzinaPin = A2;
int count = 1;

int procitajBrojKoraka() {
  int ulazBrzine = analogRead(brzinaPin);
  int koraci = map(ulazBrzine, 0, 1023, 0, 50);
  return max(1, koraci);
}

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  count = procitajBrojKoraka();
}

A unutar postojećeg uvjeta zamijeni:

// C++
if (count == 0) {
  count = procitajBrojKoraka();
  ledMode = !ledMode;
}

Funkcija procitajBrojKoraka() je vlastita funkcija: okuplja nekoliko naredbi koje imaju jedan zadatak i vraća rezultat. max(1, koraci) sprječava da broj koraka bude 0. Što je broj koraka veći, to dulje čekamo promjenu faze – LED treperi sporije. Pri vrlo malim vrijednostima treperenje može biti teško jasno vidjeti; delay(50) i serijski ispis također utječu na stvarno trajanje. Za precizno mjerenje vremena kasnije ćemo koristiti millis().

Gdje bi to bilo korisno?

Isti osnovni obrazac možemo primijeniti na upozorenje kad neka izmjerena veličina prijeđe zadanu granicu:

senzor ⇒ analogni ulaz ⇒ usporedba s pragom ⇒ trepereća LED

Potenciometar nam danas glumi senzor čiju vrijednost možemo lako kontrolirati. U budućem projektu mogli bismo umjesto njega koristiti prikladno spojen senzor razine vode, temperature ili osvjetljenja. Prije stvarne uporabe trebalo bi provjeriti kako taj senzor stvara signal, što znači njegov prag i što uređaj mora učiniti kada senzor ili žica zakaže.

Pitanja za razmišljanje

Kako bismo zadanu PWM vrijednost od 0 do 255 prikazali kao postotak od 0% do 100%? Kako bi se taj broj mogao prikazati korisniku?
Programom možemo preračunati, primjerice:
int postotak = map(jacinaLedice, 0, 255, 0, 100);
Za prikaz bismo mogli upotrijebiti OLED ili LCD zaslon, sedmerosegmentni prikaz ili niz LED-ica kao jednostavan pokazivač. Pritom postotak govori koliki smo PWM udio zadali, ne da smo izmjerili stvarnu svjetlinu LED-ice. Za mjerenje svjetline trebao bi nam prikladan svjetlosni senzor.

ligtbulb Što smo naučili?

  • Potenciometar s krajnjim izvodima na +5 V i GND te izlazom na srednjem izvodu radi kao podesivo djelilo napona.
  • Napon djelila računamo iz omjera dvaju otpora, a struju kroz neopterećeno djelilo iz njihova ukupnog otpora.
  • Voltmetar mjeri napon između klizača i GND; Arduino A0 isti signal pretvara u broj od 0 do 1023.
  • setup() priprema pinove i serijsku komunikaciju, a loop() stalno ponavlja očitavanje, obradu i upravljanje izlazom.
  • int čuva cijele brojeve, bool logičko stanje, a const označava vrijednost koju u programu ne mijenjamo.
  • Serial Monitor prikazuje poslane retke, a Serial Plotter od njih crta grafikon.
  • analogWrite() na D9 ne daje glatki analogni napon: šalje PWM signal između približno 0 V i 5 V.
  • Osciloskop prikazuje oblik PWM impulsa; jednostavno očitanje D9 na A1 daje tek pojedine uzorke, ne zamjenjuje osciloskop.
  • Uvjet if, logička varijabla i brojač omogućuju da LED promijeni ponašanje kada očitanje prijeđe prag.
  • Konstantna linija praga na grafikonu pomaže nam vidjeti kada se uvjet za upozorenje ostvaruje.

Mali kviz

1. Kako spajamo tri izvoda potenciometra kada ga koristimo kao djelilo napona?
Dva krajnja izvoda spajamo na +5 V i GND, a srednji izvod, klizač, daje promjenjivi izlazni napon.
2. Koliki napon približno očekujemo na klizaču ako je točno na sredini potenciometra spojenog između 5 V i GND?
Približno 2,5 V. Stvarno mjerenje može malo odstupati.
3. Koja je formula za izlazni napon djelila s dva serijska otpornika?
Uout = Uin × Rdonji / (Rgornji + Rdonji), ako izlaz nije znatno opterećen.
4. Mjeri li analogRead(A0) napon u voltima?
Ne. Na Arduino Uno pločici vraća cijeli broj od 0 do 1023 koji predstavlja napon na A0 u odnosu na referentni napon.
5. Koja je razlika između setup() i loop()?
setup() se izvrši jednom nakon pokretanja ili resetiranja i služi za pripremu. loop() se nakon toga stalno ponavlja.
6. Zašto koristimo map(vrijednost, 0, 1023, 0, 255)?
Zato što očitanje s A0 i zadana PWM vrijednost imaju različite raspone. map() pretvara broj iz raspona 0–1023 u približno odgovarajući broj iz raspona 0–255.
7. Što osciloskop pokazuje na D9 kada je analogWrite(9, 128)?
Pokazuje brze impulse koji se prebacuju između približno 0 V i 5 V. Pin je uključen približno polovicu vremena; ne daje stalan napon od 2,5 V.
8. Zašto očitanje pina A1 spojenog na D9 nije isto što i osciloskopski prikaz PWM-a?
A1 očitava pojedini trenutak PWM signala i može zateći nisku ili visoku razinu. Osciloskop prikazuje promjenu signala kroz vrijeme i omogućuje nam vidjeti širinu impulsa.
9. Što znači uvjet if (vrijednost > prag)?
Naredbe unutar uvjeta izvršavaju se samo kada je očitanje vrijednost veće od zadane granice prag.
10. Čemu služi stalna vrijednost 800 na Serial Plotter grafikonu?
Ona daje vodoravnu referentnu liniju. Tako lako vidimo kada očitanje na A0 prijeđe prag i kada bi treperenje trebalo početi.
11. Zašto brojač za treperenje ne smije započeti od nule ako ga prvo smanjujemo naredbom count--?
Zato što bi odmah postao −1, pa uvjet count == 0 ne bi bio ispunjen. Izračunati raspon može uključivati nulu, ali brojač prije odbrojavanja postavljamo na najmanje 1.
12. Znači li PWM vrijednost 128 da LED svijetli točno 50% svjetline koju vidi naše oko?
Ne nužno. Ona približno znači da je signal uključen polovicu vremena. Naš doživljaj svjetline ne raste potpuno linearno s tom vrijednošću.
◀ Arduino - korak po korak ☸