Stmievač LED pomocou timera

Zápisník experimentátora

Hierarchy: Časovač (timer)

Stmievanie LED diódy možno naprogramovať viacerými spôsobmi. V tomto článku sa zameriame na to, aby sme nemali v programe funkciu delay, ktorá brzdí beh programu tak, že sa už nemôže venovať ničomu inému. Nebudeme ani používať funkciu millis, ktorou sa dá odstrániť zdržiavanie programu. Namiesto toho budeme používať timer, ktorý si nastavíme tak, aby nám v pravidelných intervaloch dával signál, kedy máme vykonať ďalší krok stmievania.

Využijeme na to moju online kalkulačku, ktorá vypočíta nastavenie registrov pre CTC timer v mikrokontroléri ATmega328P, ktorý sa nachádza v Arduine. Timer bude volať prerušenie každú 1 ms a od tohoto základného kroku si jednoduchým výpočtom odvodíme väčšie kroky, ktoré budú nastavovať PWM signál na výstupnom pine.

V tomto článku použijeme elektrický obvod z minulého článku o NPN tranzistore. Bázu tranzistora tam máme pripojenú na pin 6, ktorý môžeme ovládať aj pomocou PWM signálu. Takto si môžeme vyskúšať, že PWM signál môže ovládať aj elektrické obvody, ktoré nie je možné priamo pripojiť na výstupný pin Arduina. V schéme sa nachádza aj OLED displej. Ten na experiment nepotrebujeme, umožní nám to ale predviesť to, že v programe sa na nič nečaká a tak môžeme vo funkcii loop vypisovať informácie na OLED displej.

Ak ale nemáte tranzistor, výkonovú led alebo OLED displej, môžete všetky tieto súčiastky vynechať. Stačí keď na pin 6 pripojíte LED diódu s rezistorom a program si vyskúšate v takejto podobe. Napísal som dva programy. Jeden je použiteľný na takéto základné zapojenie a druhý je použiteľný spolu s výkonovou LED a OLED displejom.

Zoznam súčiastok

Potrebujeme tieto súčiastky.

  • Breadboard {linkBreadboard}
  • Rezistor 100R, 10R, 1k {linkR}
  • LED dióda {linkLED}
  • NPN Tranzistor BC547B {linkTransistor}
  • OLED displej {linkOLED}
  • Prepojovacie vodiče
  • Arduino {linkArduino}

Program v základnom tvare

Timer volá prerušenie každú milisekundu. Keby sme takto rýchlo animovali stmievanie LED, bolo by to príliš rýchle. Preto mám definované, že jeden krok sa vykoná iba raz za 10 ms (timerStep). To celú animáciu prehrá za 2550 ms. PWM (pwmvalue) sa animuje najprv od 0-255 (direction) a potom zase od 255-0. To vyvoláva dojem, že LED dióda dýcha. Plynulo sa rozsvieti a plynulo aj zhasne. Všimnite si funkciu loop. Neobsahuje žiaden kód.

#define ledPin 6
#define timerStep 10

volatile int divider = 0;
volatile int pwmvalue = 0;
volatile int direction = 1;

void setupTimer1() {
  noInterrupts();
  // Clear registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // 1000 Hz (16000000/((249+1)*64))
  OCR1A = 249;
  // CTC
  TCCR1B |= (1 << WGM12);
  // Prescaler 64
  TCCR1B |= (1 << CS11) | (1 << CS10);
  // Output Compare Match A Interrupt Enable
  TIMSK1 |= (1 << OCIE1A);
  interrupts();
}

void setup() {
  pinMode(ledPin, OUTPUT);
  setupTimer1();
}

void loop() {
}

void pwmStep() {
  analogWrite(ledPin, pwmvalue);
  pwmvalue += direction;
  if (pwmvalue == 0)
    direction = 1;
  if (pwmvalue == 255)
    direction = -1;
}

ISR(TIMER1_COMPA_vect) {
  if (divider == 0)
    pwmStep();
  divider++;
  divider %= timerStep;
}

Program s OLED displejom

V tomto programe je kód stmievania rovnaký ako v predchádzajúcom príklade. Doplnil som OLED displej. Ten zobrazuje aktuálne hodnoty premenných. Na vykreslenie informácie na displej používam funkciu loop. Všimnite si v nej zaujímavé makro ATOMIC_BLOCK. To zabezpečuje atomicitu premenných, pretože prerušenie môže nastať hocikedy v priebehu hlavného kódu. Makro zabezpečuje, aby sa mohli vytvoriť lokálne kópie niektorých premenných, ktorých hodnota sa vypíše na displej.

#include <U8g2lib.h>
#include <util/atomic.h>

U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);

#define ledPin 6
#define timerStep 10

volatile int divider = 0;
volatile int pwmvalue = 0;
volatile int direction = 1;

void setupTimer1() {
  noInterrupts();
  // Clear registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // 1000 Hz (16000000/((249+1)*64))
  OCR1A = 249;
  // CTC
  TCCR1B |= (1 << WGM12);
  // Prescaler 64
  TCCR1B |= (1 << CS11) | (1 << CS10);
  // Output Compare Match A Interrupt Enable
  TIMSK1 |= (1 << OCIE1A);
  interrupts();
}

void setup() {
  u8g2.begin();
  u8g2.setFont(u8g2_font_ncenB10_tr);
 
  pinMode(ledPin, OUTPUT);
  setupTimer1();
}

void drawLogo() {
  int y = 12;
  int stp = 14;
  int cpwmvalue;
  int cdirection;
  char prt[10];
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    cpwmvalue = pwmvalue;
    cdirection = direction;
  }
  u8g2.firstPage();
  do {

  u8g2.drawStr(0, y, "Arduino Slovakia");
  u8g2.drawStr(0, y + stp * 1, "timer1");
  u8g2.drawStr(0, y + stp * 2, "LED fader");
  u8g2.drawStr(0, y + stp * 3, cdirection==1 ? "UP" : "DOWN");
  sprintf(prt, "%d", cpwmvalue);
  u8g2.drawStr(70, y + stp * 3, prt);
  } while ( u8g2.nextPage() );
}

void loop() {
  drawLogo();
}

void pwmStep() {
  analogWrite(ledPin, pwmvalue);
  pwmvalue += direction;
  if (pwmvalue == 0)
    direction = 1;
  if (pwmvalue == 255)
    direction = -1;
}

ISR(TIMER1_COMPA_vect) {
  if (divider == 0)
    pwmStep();
  divider++;
  divider %= timerStep;
}

Zdrojový kód

Zdrojový kód sa nachádza na serveri GitHub.



Video


21.03.2020


Menu