niedziela, 27 marca 2011

DIY: Prosta lampka LED RGB by Mesho

Autor: Mesho
Redakcja: Dondu



Lampka RGB LED


Pomysł stworzenia tego projektu powstał przez nadmiar puszek po Coca-Coli który robił bałagan w moim pokoju, więc postanowiłem że zrobię z nich użytek. Poprzez wycinanie różnych motywów na puszce i zamontowanie trójkolorowej diody led uzyskałem prostą i tanią w wykonaniu oraz efektowną w ciemności lampkę. Dodatkowy malunek niebieskim sprayem sprawił że wygląda jeszcze lepiej.



Różne motywy


Dioda led RGB sterowana jest programową modulacją szerokości impulsów z mikrokontrolera Attiny13. Dlaczego ten scalak? Poszukiwałem czegoś w małej obudowie, a wysterowanie tego za pomocą np. Atmega8 jest w moim odczuciu strzelaniem z armaty do wróbla.


Schemat układu



Schemat układu



Układ zasilany jest bateryjnie, za pomocą małej pastylki CR2025, dodany kondensator filtrujący. Rezystory dla każdej diody dobierałem doświadczalnie, warto zwrócić uwagę że różne kolory ledów mają różne napięcia przewodzenia.

Vf nieb > Vf ziel > Vf czerw


Jako że układ jest minimalistyczny, wyłącza się go za sprawą wyjęcia baterii, ewentualnie można się pokusić o zamontowanie zworki na zasilaniu.


Program

Zdecydowałem się na prostą sekwencję rozjaśnianych i ściemnianych kolejno kolorów. Zrealizowałem to za pomocą programowego PWM, metoda dość prosta i skuteczna. Dla czytelności rozbiłem program na main.c oraz piny.h, gdzie znajdują się deklaracje zmiennych oraz makra pinów. Myślę że komentarze powiedzą wszystko.

piny.h

/*
 * piny.h  
 *
 *  Created on: 05-12-2013
 *      Author: Marcin "Mesho" Popko
 *      Strona www: atmegan.blogspot.com
 */

#ifndef PINY_H_
#define PINY_H_


//marka uproszczające dostęp do portów
#define NIEB (1<<PB0)
#define ZIEL (1<<PB1)
#define CZER (1<<PB2)
#define LED_PORT PORTB
#define LED_DIR DDRB

volatile uint8_t pwm_ziel, pwm_nieb, pwm_czer; //definicje zmiennych przechowujących wartośc pwm
uint8_t i; //zmienna na potrzeby pętli for


#endif /* PINY_H_ */


main.c

/*
 * main.c  
 *
 *  Created on: 05-12-2013
 *      Author: Marcin "Mesho" Popko
 *      Strona www: atmegan.blogspot.com
 *
 *      Prosta lampka RGB LED sterowana programowym PWM
 *      F_CPU = 1,2MHz
 */

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <string.h>
#include "piny.h"

int main(void) {

 DDRB |= ZIEL | NIEB | CZER; //ustawiamy piny jako wyjścia
 PORTB &= ~(ZIEL | NIEB | CZER); //wyłączamy na początek wyjścia diody RGB

 PORTB |= (1<<PB3) | (1<<PB4); //załączamy pull-upy przy nieużywanych pinach
 DDRB &= ~((1<<PB3) | (1<<PB4)); //ustawiamy nieużywane piny jako wejścia

 TCCR0A |= (1 << WGM01); //timer w trybie CTC
 TCCR0B |= (1 << CS01) | (1 << CS00); //preskaler 64
 TIMSK0 |= (1 << OCIE0A); //odblokowujemy przerwanie timera 0
 ACSR |= (1<<ACD); //wyłączamy komparator analogowy - oszczędzamy energie

 sei(); //włączamy globalne zezwolenie na przerwania

 while (1) {
  for (i = 0; i < 255; i++) { //rozjaśniamy diodę zieloną
   pwm_ziel++; //inkrementacja zmiennej pwm diody zielonej
   _delay_ms(2); //odstep czasowy
  }
  //
  for (i = 0; i < 255; i++) { //ściemniamy diodę zieloną
   pwm_ziel--; //dekrementacja zmiennej pwm diody zielonej
   _delay_ms(2); //odstep czasowy
  }
  for (i = 0; i < 255; i++) { //rozjaśniamy diodę niebieską
   pwm_nieb++; //inkrementacja zmiennej pwm diody niebieskiej
   _delay_ms(2); //odstep czasowy
  }
  for (i = 0; i < 255; i++) { //ściemniamy diodę niebieską
   pwm_nieb--;  //dekrementacja zmiennej pwm diody niebieskiej
   _delay_ms(2); //odstep czasowy
  }
  //
  for (i = 0; i < 255; i++) { //rozjaśniamy diodę czerwoną
   pwm_czer++; //inkrementacja zmiennej pwm diody czerwonej
   _delay_ms(2); //odstep czasowy
  }
  for (i = 0; i < 255; i++) { //ściemniamy diodę czerwoną
   pwm_czer--; //dekrementacja zmiennej pwm diody czerwonej
   _delay_ms(2); //odstep czasowy
  }
 }

}

ISR(TIM0_COMPA_vect) { //przerwanie z f= ~18750Hz
 static uint8_t cnt = 0; //definicja zmiennej licznika przerywań
 cnt++;

 if (pwm_ziel > cnt) //jeśli wartośc zmiennej pwm jest wieksza od wartosci licznika
 PORTB |= ZIEL; //przełącz stan wyjścia na wysoki
 else PORTB &= ~ZIEL; //jeśli nie, przełącz stan wyjścia na niski

 if (pwm_czer > cnt) //jeśli wartośc zmiennej pwm jest wieksza od wartosci licznika
 PORTB |= CZER; //przełącz stan wyjścia na wysoki
 else PORTB &= ~CZER; //jeśli nie, przełącz stan wyjścia na niski

 if (pwm_nieb > cnt) //jeśli wartośc zmiennej pwm jest wieksza od wartosci licznika
 PORTB |= NIEB; //przełącz stan wyjścia na wysoki
 else PORTB &= ~NIEB; //jeśli nie, przełącz stan wyjścia na niski

 if (cnt > 255) cnt = 0; //reset licznika
}


Zasilanie

Jako że zastosowałem zasilanie bateryjne, warto było zadbać o to, aby mikrokontroler pobierał jak najmniej prądu. Zdecydowałem się ustawić taktowanie na fabryczne 1,2MHz. Porównałem pobór prądu pobieranego przez sam mikrokontroler przy różnych dostępnych takowaniach, (mierzyłem go od strony masy mikrokontrolera, więc prąd który pobierała dioda nie był uwzględniany).


Różne taktowania, to różny pobór prądu


Okazało się, że można zmniejszyć apetyt mikrokontrolera na prąd czterokrotnie bez większej utraty funkcjonalności. Z 2,8mA udało się zejść aż do 0,6mA!. Dzięki poprawkom w programie prąd ten spadł do 0,55mA.

Łącznie cały układ pobiera do ok. 3mA, bateria CR2025 podczas moich testów starczyła na ok. 2-3h świecenia.

Jedyny problem który napotkałem ze strony programu to spowolnione działanie komendy _delay_ms(x), jednak dla takich banalnych zastosowań dopasowałem doświadczalnie szybkość rozjaśniania/ściemniania.

Prosta konstrukcja


Ładnie pasuje :)


Ze względu na to że układ jest prosty, zdecydowałem się zmontować go na płytce uniwersalnej. Poprzez ładne wycięcie i zeszlifowanie uzyskałem konstrukcję pasującą do góry puszki. Dioda LED zamocowana od spodu kieruje światło bezpośrednio do puszki.


Efekt końcowy (film na youtube):





Pozdrawiam
Mesho






Uwagi redakcji

Minimalistyczny projekt przez to technicznych uwag niezbyt wiele.

Najistotniejsza, to bateria. W takim projekcie zastosowanie tak małej baterii kładzie projekt na łopatki. Bardzo fajnie, że autor rozumie ten problem i postanowił dołożyć starań w zakresie minimalizacji prądu pobieranego przez mikrokontroler.

Jednakże na tym polu są jeszcze dwa istotne kierunki modyfikacji:

1. zastosowanie diod LED o wysokim strumieniu świetlnym, przez co ograniczamy prąd pobierany z baterii - najważniejsza zmiana jaką należy rozpatrzyć,

2. zmiana programu w taki sposób, by część znajdująca się w pętli głównej także realizowana była przez przerwanie (to samo lub inne), po to by wyeliminować wszystkie instrukcje delay_ms(), podczas których mikrokontroler niepotrzebnie działa. Po tej modyfikacji pętla główna zawierać powinna wyłącznie rozkaz usypiania mikrokontrolera, do możliwie najgłębszego snu.

Więcej w temacie akumulatorów i zużycia energii możecie poczytać:Bateria zasila mikrokontroler
a o oszczędzaniu energii: SmartPIP - Elektroniczny dręczyciel

4 komentarze:

  1. Nie wiem jak to jest w przypadku tego układu, ale myślę że jak by zastosować timer i usypiać mikrokontroler choćby do stanu CPU idle zamiast tych delay to pochodziło by to troszkę dłużej na baterii.

    OdpowiedzUsuń
  2. Co masz na myśli pisząc cyt. "Jedyny problem który napotkałem ze strony programu to spowolnione działanie komendy _delay_ms(x)"?

    OdpowiedzUsuń
    Odpowiedzi
    1. Ograniczona częstotliwość taktowania proca spowodowała że opóźnienie 2ms trwało de facto około, nie pamiętam dokładnie ale gdzieś 20. Sytuacja taka jak w naszych komputerach PC, jak powłączamy za dużo programów, będą się one po prostu ciąć :P

      Usuń
    2. BTW. Długość pracy baterii z braku dokładnych testów w opisie na wyrost zaniżyłem, ale świecą u mnie po 10h i wciąż nie mają dość. Tak mocno zająłem się stroną artystyczną, że na program nie starczyło zbyt wiele czasu i głowy. Lekcja z usypianiem mikrokontrolera pozostanie mi jeszcze do odrobienia ;)

      Usuń