Stmívač LED pomocí timeru

Zápisník experimentátora

Hierarchy: Časovač (timer)

Stmívání LED diody lze naprogramovat několika způsoby. V tomto článku se zaměříme na to, abychom neměli v programu funkci delay, která brzdí běh programu tak, že se už nemůže věnovat ničemu jinému. Nebudeme ani používat funkci millis, kterou se dá odstranit zdržování programu. Namísto toho budeme používat timer, který si nastavíme tak, aby nám v pravidelných intervalech dával signál, kdy máme provést další krok stmívání.

Využijeme k tomu mou online kalkulačku, která vypočítá nastavení registrů pro CTC timer v mikrokontroléru ATmega328P, který se nachází v Arduinu. Timer bude volat přerušení každou 1 ms a od tohoto základního kroku si jednoduchým výpočtem odvodíme větší kroky, které budou nastavovat PWM signál na výstupním pine.

V tomto článku použijeme elektrický obvod z minulého článku o NPN tranzistoru. Bázi tranzistoru tam máme připojenou na pin 6, který můžeme ovládat také pomocí PWM signálu. Takto si můžeme vyzkoušet, že PWM signál může ovládat i elektrické obvody, které nelze přímo připojit na výstupní pin Arduina. Ve schématu se nachází také OLED displej. Ten na experiment nepotřebujeme, umožní nám to ale předvést to, že v programu se na nic nečeká a tak můžeme ve funkci loop vypisovat informace na OLED displej.

Pokud ale nemáte tranzistor, výkonovou led nebo OLED displej, můžete všechny tyto součástky vynechat. Stačí když na pin 6 připojíte LED diodu s rezistorem a program si vyzkoušíte v takové podobě. Napsal jsem dva programy. Jeden je použitelný na takové základní zapojení a druhý je použitelný spolu s výkonovou LED a OLED displejem.

Seznam součástek

Potřebujeme tyto součástky.

  • Breadboard {linkBreadboard}
  • Rezistor 100R, 10R, 1k {linkR}
  • LED dioda {linkLED}
  • NPN Tranzistor BC547B {linkTransistor}
  • OLED displej {linkOLED}
  • Propojovací vodiče
  • Arduino {linkArduino}

Program v základním tvaru

Timer volá přerušení každou milisekundu. Kdybychom takto rychle animovali stmívání LED, bylo by to příliš rychlé. Proto mám definováno, že jeden krok se provede pouze jednou za 10 ms (timerStep). To celou animaci přehraje za 2550 ms. PWM (pwmvalue) se animuje nejprve od 0-255 (direction) a pak zase od 255-0. To vyvolává dojem, že LED dioda dýchá. Plynule se rozsvítí a plynule i zhasne. Všimněte si funkci loop. Neobsahuje žádný 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 displejem

V tomto programu je kód stmívání stejný jako v předchozím příkladu. Doplnil jsem OLED displej. Ten zobrazuje aktuální hodnoty proměnných. Na vykreslení informace na displej používám funkci loop. Všimněte si v ní zajímavé makro ATOMIC_BLOCK. To zajišťuje atomicity proměnných, protože přerušení může nastat kdykoliv v průběhu hlavního kódu. Makro zajišťuje, abychom mohli vytvořit lokální kopie některých proměnných, jejichž hodnota se 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 se nachází na serveru GitHub.



Video


21.03.2020


Menu