Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.
Pokazywanie postów oznaczonych etykietą DIY. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą DIY. Pokaż wszystkie posty

poniedziałek, 11 kwietnia 2011

Obrotomierz na AVR ATmega8.


Autor: Dondu

Obrotomierz.
Podczas realizacji projektów wykorzystujący silniki warto mieć pod ręką przyrząd pozwalający zmierzyć prędkość obrotową silnika. Tym przyrządem jest obrotomierz zwany też tachometrem  (ang. tachometer) o czym wszyscy zapewne wiedzą :-)

Ponieważ w najbliższym czasie opublikuję cykl artykułów o silniku BLDC, stąd zamieszczam schemat oraz program prostego obrotomierza wykonanego na ATmega8. Program łatwo dostosować do dowolnego innego mikrokontrolera AVR posiadającego możliwość wykorzystania timera posiadającego funkcjonalność Input Capture Unit.


Sposób pomiaru 

Do pomiaru wykorzystamy Timer1 oraz możliwość "łapania" stanu licznika w momencie pojawienia się zbocza na pinie ICP1. Szczegółów szukamy w rozdziale Input Capture Unit. Znajdziemy tam schemat timera dla wykorzystania tej funkcjonalności.

ATmega8 (AVR) Timer1 Input Capture Unit.


Jak działa Input Capture Unit?

Przyjmijmy dla uproszczenia wyjaśnienia sposobu pomiaru, że na jeden obrót silnika, przypada jeden cykl pracy transoptora, czyli dwa zbocza - jedno narastające i jedno opadające.

W skrócie pomiar realizowany jest tak:
  • do pinu ICP1 podłączamy sygnał z transoptora, który będzie naszym czujnikiem pomiarowym,
  • na tarczy silnika umieszczamy znaczniki, na które reagować będzie transoptor,
  • puszczamy swobodnie licznik w trybie normalnym bez preskalera, by pomiar był jak najbardziej dokładny,
  • ustawiamy przerwania od wystąpienia zbocza (np. narastającego) na pinie ICP1,
  • w momencie wystąpienia odpowiedniego zbocza (moment A), w rejestrze ICR1 zostanie zapamiętany aktualny stan licznika timera (TCNT1),
  • wywołane zostaje przerwanie, w którym zapamiętujemy złapany stan licznika,
  • czekamy na następne zdarzenie,
  • w momencie wystąpienia zdarzenia (moment B) ponownie zostaje zapamiętany aktualny stan licznika, który odczytujemy w przerwaniu.
W ten sposób otrzymujemy dwa stany licznika, jeden na początku, a drugi na na końcu cyklu pomiarowego.

Metoda pomiaru prędkości obrotowej silnika.


Teraz wystarczy od stanu końcowego licznika (w momencie B) odjąć stanu początkowy (w momencie A) z uwzględnieniem, iż licznik mógł się w między czasie przepełnić (raz lub więcej razy) i znając częstotliwość taktowania timera dokonać niezbędnych przeliczeń i zaprezentować wynik na LCD.

Istotą wykorzystanie  Input Capture Unit jest fakt, iż stan timera TCNT1 przepisywany jest do rejestru ICR1, dokładnie w momencie wystąpienia zdarzenia. Dlatego ewentualne nieznaczne opóźnienie obsługi przerwania od wykrycie zdarzenia, nie ma wpływu na wynik pomiaru.

Jednakże należy wziąć pod uwagę, że zbyt późne odczytanie rejestru ICR1 może doprowadzić do sytuacji, że następne zdarzenie nadpisze poprzednią wartość ICR1, zanim zdążysz ją odczytać. Dlatego ważne jest, by w czasie pomiaru przerwania nie były zbyt długo blokowane, a odczyt ICR1 powinno się wykonać, na samym początku funkcji przerwania:



Więcej impulsów na jeden obrót

Można oczywiście zastosować enkoder, który będzie dostarczał więcej impulsów na jeden obrót silnika. Należy to uwzględnić w programie. Jednakże trzeba być świadomym tego, że dokładność pomiaru pełnego obrotu jest większa niż dokładność pomiaru fragmentu obrotu. Poza tym, mniej obciążymy mikrokontroler, gdy enkoder będzie dawał jeden impuls na jeden obrót niż w sytuacji, gdy impulsów będzie więcej.

Wszystko oczywiście zależy od konkretnego celu jaki chcesz osiągnąć.


Eliminacja szumów sygnału z transoptora

Dokładność pomiarów zależy także od poprawności pracy zespołu złożonego z czujnika i enkodera. Dla wyeliminowania ewentualnych szumów sygnału z transoptora podłączonego do pinu ICP1, możemy skorzystać z wbudowanego modułu eliminatora szumów oznaczonego na schemacie blokowym timera jako Noise Canceler. W większości przypadków nie będzie jednak takiej potrzeby.


Komparator analogowy jako sygnał zdarzenia

Przy okazji zauważ, że patrząc na schemat blokowy Input Capture Unit, źródłem zdarzeń może być także komparator analogowy. Nie jest nam potrzebny do miernika prędkości, dlatego też nie będę go tutaj omawiał, ale zasada postępowania będzie podobna do omawianej w niniejszym artykule.


Czujniki stykowe

Ten sposób pomiaru uniemożliwia wykorzystanie czujników stykowych z pominięciem sprzętowej eliminacji drgań styków. Innymi słowy jeżeli chcesz wykorzystać jako czujnik np. kontaktron, przycisk krańcowy, czy nawet do testów, zwykły przycisk, to musisz wyeliminować sprzętowo drgania jego styków.


Transoptor

Transoptor może być praktycznie dowolnym transoptorem odbiciowym lub szczelinowym.

Jeżeli zastosujesz transoptor szczelinowy, wymagane będzie wykonanie jakiegoś elementu, który będzie połączony z wirnikiem i będzie przerywał strumień światła pomiędzy diodą LED, a fototranzystorem transoptora.

Przykładowe transoptory szczelinowe.
Rys. Przykładowe transoptory szczelinowe.


Jeżeli zastosujesz transoptor odbiciowy, powinieneś na wirniku przygotować enkoder w postaci namalowanych czarnych i białych pasków. Ważne by kontrast był jak największy, co zapewni dobry poziom sygnału wyjściowego z enkodera.


Przykładowe transoptory odbiciowe.
Rys. Przykładowe transoptory odbiciowe.


Enkodery

Tarcze enkoderów (plik PDF).
Możesz skorzystać z:



Schemat

Do budowy obrotomierza zastosujemy ATmega8 w wersji, która może pracować z kwarcem 16MHz (np. ATmega8L nie może pracować z kwarcem powyżej 8MHz).

Jako LCD wykorzystamy najpopularniejszy wyświetlacz LCD 2x16 znaków kompatybilny z HD44780.

Schemat jest bardzo prosty (dbając oczywiście o podstawy podłączania mikrokontrolera):


Schemat obrotomierza na ATmega8 (AVR).


Ponieważ transoptor ma wyjście typu otwarty kolektor, stąd do podciągnięcia go do Vcc, wykorzystamy wewnętrzny rezystor pull-up mikrokontrolera, włączając go za pomocą programu.


Program

Poniżej kompletny program pierwszej najprostszej wersji obrotomierza. Wszelkie wyjaśnienia znajdziesz w opisach kodu. Program nie zawiera uśredniania wyników, ale możesz to dodać we własnym zakresie.

/*
OBROTOMIERZ v.1

Obrotomierz przeznaczony do prostych pomiarów prędkości obrotowej.
Pokazuje:
- czas jednego obrotu,
- liczbę obrotów na sekundę (rps),
- liczbę obrotów na minutę (rpm).

Szczegóły: http://mikrokontrolery.blogspot.com/2011/04/obrotomierz-diy.html 

Autor: Dondu 
Data:  2012.11.25 
*/


#include <avr/io.h>
#include <stdio.h>
#include <avr/interrupt.h> 
#include <util/delay.h>
#include "HD44780.h"

//tutaj ustaw własne zasady pomiaru
#define POMIAR_ILOSC_IMPULSOW_NA_OBROT 4  //ilość inpulsów na jeden obrót
#define POMIAR_ILOSC_OBROTOW           10 //ile obrotów ma trwać pomiar


volatile unsigned long int  pomiar_ilosc_cykli;
volatile unsigned int       ilosc_przepelnien;
volatile unsigned char      pomiar_zbocze_nr;
volatile unsigned char      pomiar_gotowy;

volatile unsigned int       ICR1_moment_A;
volatile unsigned int       ICR1_moment_B;

char  wynik[16];        //bufor wyświetlacza

//------------------------------------------------------

ISR(TIMER1_CAPT_vect){

 //Obsługa zdarzenia na ICP1 

 static unsigned int ICR1_aktualny; //wartość ICR1 złapana w aktualnie 
                                    //złapanym zboczu

 //odczytaj złapany stan licznika
 ICR1_aktualny = ICR1;

 //czy to pierwsze złapane zbocze?
 if(pomiar_zbocze_nr == 1){
  
  //tak, pierwsze zbocze pomiarowe, 
  
  //uruchamiamy zliczanie przepełnień Timer1

  //zgaś flagę przerwania od przepełnienia
  TIFR |= (1<<TOV1);
  
  //włącz przerwania od przepełnienia Timer1
  TIMSK |= (1<<TOIE1);

  //zeruj licznik przepełnień Timera1
  ilosc_przepelnien = 0;

  //zapamiętujemy stan licznika początku obrotu (moment A)
  ICR1_moment_A = ICR1_aktualny;

 }else{
   
  //każde następne zbocza pomiarowe

  //czy to ostatnie zbocze (koniec pełnego obrotu silnika)?
  if(pomiar_zbocze_nr ==  POMIAR_ILOSC_IMPULSOW_NA_OBROT 
                          * POMIAR_ILOSC_OBROTOW){

   //tak, wykryto ostatnie zbocze pomiarowe, 
   //czyli wykonał się pełny ostatni obrót tarczy

   //wyłączamy przerwania z Input Capture Unit 
   //oraz przepełnienia Timer1
   TIMSK &= ~( (1<<TICIE1) | (1<<TOIE1) );
   
   //zapamiętujemy zmienne dla main()
   ICR1_moment_B  = ICR1_aktualny;
   pomiar_gotowy  = 1;

  }
  
  //w pozostałych przypadkach nic nie robimy i czekamy na zliczanie 
  //dalszych zboczy
 
 }

 //zwiększ licznik wykrytych zboczy
 pomiar_zbocze_nr++;

}

//------------------------------------------------------

ISR(TIMER1_OVF_vect){

 //powiększ licznik przepełnień Timera1
 ilosc_przepelnien++;

}

//------------------------------------------------------


void wykonaj_pomiar(void){

 //Funkcja włącza pomiar 

 //zerujemy licznik przepełnień Timer0
 ilosc_przepelnien = 0;

 //ustawiamy nr pierwszego zbocza
 pomiar_zbocze_nr = 1;

 //zerujemy flagę gotowości pomiaru
 pomiar_gotowy = 0;

 //włącz przerwania z ICP1 Timer1
 TIMSK |= (1<<TICIE1);

}


//------------------------------------------------------

int main(void){

 //zmienne do obliczeń
 double czas_jednego_obrotu;
 double obrotow_na_sekunde;

 PORTB |= (1<<PB0);   //włącz pullup transoptora

 LCD_Initalize();       //inicjalizacja LCD i napis początkowy
 LCD_Clear();
 LCD_GoTo(0, 0);         
 LCD_WriteText("Obrotomierz");

 //Ustawienie Timer1
 TCCR1A = 0;
 TCCR1B = (1<<CS10);   //preskaler 1, bez noise 
                             //canceler dla ICP1
 
 //lub z modułem Noise Canceler:
 //TCCR1B = (1<<CS10) | (1<<ICNC1); //preskaler 1, włącz 
                                         //noise canceler dla ICP1
 
 sei();       //włącz przerwania globalne

 wykonaj_pomiar();     //włącz pierwszy pomiar

 //pętla główna
 while(1){
  
  if(pomiar_gotowy){
   
   // gdy pomiar został wykonany

   //obliczenia
   
   pomiar_ilosc_cykli  =  65536 * ilosc_przepelnien - ICR1_moment_A 
                          + ICR1_moment_B;
   
   czas_jednego_obrotu = (double) pomiar_ilosc_cykli 
                         / POMIAR_ILOSC_OBROTOW / F_CPU ;

   obrotow_na_sekunde = 1.0 / czas_jednego_obrotu;

   //wyświetlenie wyników

   LCD_Clear();

   LCD_GoTo(13, 0);
   LCD_WriteText("rpm");

   //czas jednego obrotu
   sprintf(wynik,"%5.2fms/obr", 1000.0 * czas_jednego_obrotu);
   LCD_GoTo(0, 0);
   LCD_WriteText(wynik);

   //obroty na sekundę
   sprintf(wynik,"%5.2frps ", obrotow_na_sekunde);
   LCD_GoTo(0, 1);
   LCD_WriteText(wynik);
  
   //obroty na minutę
   sprintf(wynik,"%7u ", (int) (60.0 * obrotow_na_sekunde));
   LCD_GoTo(9, 1);
   LCD_WriteText(wynik);
      
   //pomiary co pół sekundy
   _delay_ms(500);   

   //wykonaj następny pomiar
   wykonaj_pomiar();

  }
 }
}
Do pobrania program wraz z projektem AVR Studio 4 + plik hex: OBROTOMIERZ-v1.zip


Problem znaku zapytania

Jeżeli w trakcie realizacji własnego projektu (nie opartego o powyższy plik AVR Studio 4) napotykasz na problem, że zamiast liczby wyświetla się znak zapytania, przeczytaj artykuł: Problem znaku zapytania podczas konwersji float do znaków ASCII w celu wyświetlenia na np. LCD itp.



Efekt końcowy



Akualizacje programu

Ponieważ w miarę pisania o silnikach BLDC jest prawdopodobne, że będę rozbudowywał program obrotomierza, stąd jego aktualną wersję będę zamieszczał w niniejszym temacie.

UWAGA!
Jeżeli ktoś na bazie niniejszego kodu opracuje własną wersję obrotomierza na ten, czy inny mikrokontroler i zechce się nią podzielić z czytelnikami bloga, to proszę o kontakt i z góry w ich imieniu dziękuję :-)



Artykuł wykorzystano do:


Link do artykułu na forum: Pompa perystaltyczna

... wykorzystasz artykuł, opiszesz w sieci, daj nam znać zamieścimy link :-)


Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

poniedziałek, 4 kwietnia 2011

SmartPIP: Czujnik światła - testowanie


Autor: Dondu


Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel

Nadszedł czas na przetestowanie opracowanego czujnika światła. Schemat układu testowego ograniczymy do niezbędnego minimum:

SmartPIP: Układ testowy czujnika światła.


Do powyższego schematu należy dodać jedną z wersji interfejsu RS-232, które znajdziesz tutaj: RS-232: Komunikacja ATmega8 z komputerem. Na schemacie zaznaczyłem etykietami MIKR-RXD oraz MIKR-TXD piny, które należy połączyć z pinami interfejsu RS-232 oznaczonych tymi samymi etykietami.

Zamiast RS-232 możesz wykorzystać wyświetlacz LCD, ale to już opracuj we własnym zakresie.

Dioda LED dodana jest wyłącznie w celach  testowych (np. gdyby terminal nie wyświetlał danych), więc nie musi być zamontowana. Jeżeli ją zamontujesz, to w kierunku zgodnym z powyższym schematem, by nie pobierała prądu z pinu mikrokontrolera. Rezystor diody LED oznaczony jako R2 jest nieco większy niż standardowy, ponieważ chcemy ograniczyć skoki prądu w czasie testów. Najlepszym rozwiązaniem będzie usunięcie diody w czasie, gdy terminal będzie prawidłowo odbierał dane.

Schemat naszego układu testowego w zakresie minimalnych podłączeń odpowiada wersji totalne minimum. Dlaczego stosujemy wersję "totalne minimum"? Ponieważ spokojnie wystarczy nam rozdzielczość pomiarowa 8-bitów, a dodatkowy kondensator na pinie AREF, to dodatkowa upływność, której nie chcemy w naszym SmartPIP'ie.




Opis algorytmu


Program przygotowany jest (jak w każdej części cyklu o SmartPIP'ie) w wersji wieloplikowej z podziałem na funkcjonalności:
  • pomiar światła,
  • komunikacja RS-232,
  • program główny.

W programie na razie nie wykorzystujemy jeszcze licznika czasu, a opóźnienie między pomiarami wykonujemy za pomocą funkcji _delay_ms(x). Jest to celowy zabieg uproszczenia programu testowego, który w wersji wieloplikowej jest i tak (na pierwszy rzut oka) wystarczająco skomplikowany :-). Licznik czasu będziemy używać już w następnym artykule, gdzie dokonywać będziemy pierwszych pomiarów dobowych.

Do testowania czujnika będziemy dokonywać pomiarów światła z uwzględnieniem wykorzystania usypiania mikrokontrolera do trybu ADC Noise reduction, który jak już ustaliliśmy będziemy wykorzystywać w celu zwiększenia dokładności pomiaru i redukowania pobieranego prądu z baterii.

Dla potrzeb tego testu na stałe włączymy elementy czujnika światła (zasilanie czujnika i masę rezystora).

Wyniki pomiarów będziemy obserwować za pośrednictwem komputera podłączonego do mikrokontrolera za pomocą prostego interfejsu RS-232. Algorytm wysyłania opracowany jest dla wersji opartej o przerwania i tablicę bufora tekstu w pamięci SRAM mikrokontrolera.






dd_swiatlo.c
dd_swiatlo.h


Pliki te zawierają funkcjonalności dot. pomiaru światła za pomocą naszego czujnika z wykorzystaniem przetwornika ADC.


dd_swiatlo_adc_ini()

Funkcja odpowiedzialna za przygotowanie do pomiarów, czyli:

Włączenie czujnika - ustawienie odpowiednich stanów na pinach zasilania fototranzystora oraz masy rezystora, według ustalonych wcześniej zasad.

Inicjalizacja przetwornika ADC - ustawienie parametrów przetwornika ADC realizowane jest przez funkcję:
  • ustawiamy kanał wejściowy ADC1 (PC1) zgodnie ze schematem,
  • włączamy zasilanie czujnika światła oraz masę jego rezystora,
  • ponieważ używamy tylko ośmiu najbardziej znaczących bitów, stąd wykorzystujemy dostępne w tym przypadku wyrównanie wyniku do lewej (ADLAR). 
  • preskaler ADC ustawiamy go na 8, co daje nam częstotliwość taktowania przetwornika ADC równą 125kHz (przy taktowaniu mikrokontrolera wewnętrznym generatorem RC o częstotliwości 1MHz).
  • włączamy przerwania zakończenia pomiaru ADC, by móc wykorzystać tryb ADC Noise reduction oraz przygotowujemy odpowiedni tryb snu.

dd_swiatlo_adc_pomiar_start()

Funkcja ta zawiera jedynie rozkaz uśpienia mikrokontrolera dlatego, że tę funkcję będziemy rozbudowywać w dalszych artykułach o SmartPIP'ie.

ISR(ADC_vect)

Funkcja przerwania wywoływana po zakończeniu pomiaru przez przetwornik ADC jest pusta, a przyczyna wyjaśniona w komentarzu w kodzie. Podobnie jak funkcja powyżej, także i ta będzie rozbudowywana w dalszych artykułach.


Plik dd_swiatlo.c:
/*************************************************************************
* SmartPIP - Elektroniczny dręczyciel
* 
* Data:  kwiecień 2013
* Autor: Dondu
* www:   http://mikrokontrolery.blogspot.com/2011/04/SmartPIP-elektroniczny-dreczyciel-spis-tresci.html
* 
* Plik:  dd_swiatlo.c
* Opis:  Funkcjonalności dot. pomiaru światła
**************************************************************************/


#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#include "dd_main.h"
#include "dd_swiatlo.h"


//-------------------------------------------------------------------------

void dd_swiatlo_adc_ini(void){

 //Funkcja włącza czujnik światła oraz inicjuje przetwornik ADC

 //KROK 1 - włącz czujnik światła
 //włącz zasilanie fototranzystora
 ADC_PIN_ZAS_DDR  |= (1<<ADC_PIN_ZAS_NAZWA); //wyjście
 ADC_PIN_ZAS_PORT |= (1<<ADC_PIN_ZAS_NAZWA); //załącz napięcie
 //włącz masę rezystora pomiarowego
 ADC_PIN_REZ_DDR  |=  (1<<ADC_PIN_REZ_NAZWA);//wyjście
 ADC_PIN_REZ_PORT &= ~(1<<ADC_PIN_REZ_NAZWA);//załącz masę (zero)


 //KROK 2 - przygotuj przetwornik ADC
 //korzystamy tylko z 8 najbardziejznaczących bitów (wyrównanie do lewej)
 //częstotliwość ADC = 1MHz + preskaler 8 = 125kHz
 //ustaw pin pomiarowy ADC
 ADC_PIN_POM_DDR   &=   ~(1<<ADC_PIN_POM_NAZWA); //wejście
 ADC_PIN_POM_PORT  &=   ~(1<<ADC_PIN_POM_NAZWA); //wyłącz pull-up
 //inicjuj przetwornik ADC
 ADMUX  =      (1<<REFS0)        //VCC jako napięcie referencyjne
             | (1<<ADLAR)        //wyrównaj pomiar do lewej
             | ADC_PIN_POM_MUX;  //przełącz na kanał wybranego pinu ADC
 ADCSRA =      (1<<ADEN)         //włącz ADC
             | (1<<ADIE)         //włącz przerwania 
             | (1<<ADPS0)        //preskaler 8
             | (1<<ADPS1);

 //ustaw tryb snu ADC Noise Reduction
 set_sleep_mode(SLEEP_MODE_ADC); 
 sleep_enable();
}


//-------------------------------------------------------------------------

void dd_swiatlo_adc_pomiar_start(void){

 //Funkcja odpowiedzialna za rozpoczęcie pomiaru światła
 //rozpocznij pomiar światła poprzez uśpienie mikrokontrolera
 sleep_cpu();  
}


//-------------------------------------------------------------------------

ISR(ADC_vect){

 //Przerwanie ADC - pomiar zakończony
 //Funkcja pusta ponieważ odczyt ADC dokonujemy w main()
 //tej funkcji nie można usunąć po nieważ pomiar światła
 //dokonywany jest w trakcie snu mikrokontrolera
 //a pomiar kończy się przerwaniem
}


Plik dd_swiatlo.h:
/*************************************************************************
* SmartPIP - Elektroniczny dręczyciel
* 
* Data:  kwiecień 2013
* Autor: Dondu
* www:   http://mikrokontrolery.blogspot.com/2011/04/SmartPIP-elektroniczny-dreczyciel-spis-tresci.html
* 
* Plik:  dd_swiatlo.h
* Opis:  Plik nagłówkowy pliku dd_swiatlo.c
*************************************************************************/


//Definicje pinów czujnika światła oraz ustawienia kanału multipleksera ADC
//pin pomiarowy
#define ADC_PIN_POM_DDR   DDRC
#define ADC_PIN_POM_PORT  PORTC
#define ADC_PIN_POM_NAZWA PC1
#define ADC_PIN_POM_MUX   (1<<MUX0) //kanał multipleksera
//pin zasilania czujnika
#define ADC_PIN_ZAS_DDR   DDRC
#define ADC_PIN_ZAS_PORT  PORTC
#define ADC_PIN_ZAS_NAZWA PC0
//pin masy rezystora
#define ADC_PIN_REZ_DDR   DDRC
#define ADC_PIN_REZ_PORT  PORTC
#define ADC_PIN_REZ_NAZWA PC2


//--- Funkcje -------------------------------
extern void dd_swiatlo_adc_ini(void);
extern void dd_swiatlo_adc_pomiar_start(void);




dd_usart.c
dd_usart.h

Obserwacja wyników za pomocą RS-232 i terminal zrealizujemy, wykorzystują opisane wcześniej rozwiązanie wraz z omówieniem obsługi programu terminala Realterm. W związku z tym, nie będę tutaj opisywał dokładnie jak działa transmisja RS-232. Dodam jedynie tę część programu by można było ją przeanalizować.


Plik dd_usart.c:
/*************************************************************************
* SmartPIP - Elektroniczny dręczyciel
* 
* Data:  kwiecień 2013
* Autor: Dondu
* www:   http://mikrokontrolery.blogspot.com/2011/04/SmartPIP-elektroniczny-dreczyciel-spis-tresci.html
* 
* Plik:  dd_usart.c
* Opis:  Funkcje komunikacji RS-232
*************************************************************************/


#include <avr/io.h>
#include <avr/interrupt.h>

#include "dd_usart.h"

//USART - tablica bufora wysyłki oraz zmienna indeksu tablicy
char usart_bufor[USART_BUFOR_DLUGOSC] = "Testowanie czujnika swiatla:"; 
volatile unsigned int usart_bufor_ind;


//--------------------------------------------------------------

void usart_inicjuj(void)
{
 //definiowanie parametrów transmisji za pomocą makr zawartych w pliku
 //nagłówkowym setbaud.h. Jeżeli wybierzesz prędkość, która nie będzie 
 //możliwa do realizacji otrzymasz ostrzeżenie: 
 //#warning "Baud rate achieved is higher than allowed"

 #define BAUD 9600         //tutaj podaj żądaną prędkość transmisji
 #include <util/setbaud.h>  //linkowanie tego pliku musi być 
                            //po zdefiniowaniu BAUD
 
 //ustaw obliczone przez makro wartości
 UBRRH = UBRRH_VALUE;  
 UBRRL = UBRRL_VALUE;
 #if USE_2X
    UCSRA |= (1 << U2X);
 #else
    UCSRA &= ~(1 << U2X);
 #endif

 //Ustawiamy pozostałe parametry moduł USART
 //zobacz: http://mikrokontrolery.blogspot.com/2011/04/Pulapki-AVR-Rejestry-pod-tym-samym-adresem.html 
 UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);   //bitów danych: 8
                                                 //bity stopu:   1
                                                 //parzystość:   brak
 //włącz nadajnik
 UCSRB |= (1<<TXEN);
}


//--------------------------------------------------------------

void usart_wyslij_bufor(void){

 //funkcja rozpoczyna wysyłanie, wysyłając pierwszy znak znajdujący się
 //w tablicy wynik[]. Pozostałe wyśle funkcja przerwania, 
 //która zostanie wywołana automatycznie po wysłaniu każdego znaku.

 //Dodaj do tekstu wyniku znaki końca linii (CR+LF), by na 
 //ekranie terminala wyniki pojawiały się w nowych liniach
 unsigned int z;
 for(z=0; z<USART_BUFOR_DLUGOSC; z++){
  if(usart_bufor[z]==0){   //czy to koniec tekstu w tablicy
   //tak znaleziono koniec tekstu, dodajemy znaki CR i LF
   usart_bufor[z]    = 13; //znak powrotu karetki CR (Carrige Return)
   usart_bufor[z+1]  = 10; //znak nowej linii LF (Line Feed)
   usart_bufor[z+2]  = 0;  //znak końca ciągu tekstu w tablicy
   break;                  //przerwij pętlę for()
  }
 }

 //Zaczekaj, aż bufor nadawania będzie pusty
 while (!(UCSRA & (1<<UDRE))); 

 //następny znak do wysyłki to znak nr 1
 usart_bufor_ind = 0;

 //włącz przerwania pustego bufora UDR, co rozpocznie transmisję
 //aktualnej zawartości bufora 
 UCSRB |= (1<<UDRIE);

}


//--------------------------------------------------------------

ISR(USART_UDRE_vect){

 //przerwanie generowane, gdy bufor nadawania jest już pusty, 
 //odpowiedzialne za wysłanie wszystkich znaków z tablicy usart_bufor[]
 //z wyjątkiem pierwszego

 //static unsigned char znak = 1;

 //sprawdzamy, czy bajt do wysłania jest znakiem końca tekstu, czyli zerem
 if(usart_bufor[usart_bufor_ind]!= 0){
  //załaduj znak do rejestru wysyłki i ustaw indeks na następny znak
  UDR = usart_bufor[usart_bufor_ind++]; 
 }else{
  //osiągnięto koniec napisu w tablicy usart_bufor[]
  UCSRB &= ~(1<<UDRIE); //wyłącz przerwania pustego bufora nadawania
 }
}


Plik dd_usart.h:
/*************************************************************************
* SmartPIP - Elektroniczny dręczyciel
* 
* Data:  kwiecień 2013
* Autor: Dondu
* www:   http://mikrokontrolery.blogspot.com/2011/04/SmartPIP-elektroniczny-dreczyciel-spis-tresci.html
* 
* Plik:  dd_usart.h
* Opis:  Plik nagłówkowy pliku dd_usart.c
*************************************************************************/

//USART - tablica bufora wysyłki oraz zmienna indeksu tablicy
#define USART_BUFOR_DLUGOSC  30    //rozmiar bufora
extern char usart_bufor[USART_BUFOR_DLUGOSC]; //bufor
extern volatile unsigned int usart_bufor_ind; //indeks bufora

//Funkcje
extern void usart_inicjuj(void);
extern void usart_wyslij_bufor(void);





dd_main.c
dd_main.h

Program główny zawiera jedynie funkcję main() wraz z niezbędnymi definicjami w pliku nagłówkowym.

Zadania:

  • dobre praktyki w zakresie dbania o to, by każdy pin miał wymuszony poziom napięcia,
  • wywołanie funkcji przygotowujących mikrokontroler do pracy,
  • wysłanie początkowego napisu do terminala,
  • inicjowanie pomiarów,
  • przetwarzanie wyniku,
  • inicjowanie wysłania wyniku do terminala.

Sądzę, że nie trzeba tutaj omawiać dokładnie każdego z powyższych punktów, bo są opisane w kodzie programu.

Fusebity

Należy wspomnieć, że poniższy program pracuje poprawnie z domyślnymi ustawieniami fusebitów (fabrycznymi), czyli:
  • low: 0xE1
  • high: 0xC9
W celu przyspieszenia wybudzenia mikrokontrolera można ustawić fusebity także tak:
  • low: 0xC1
  • high: 0xC9
O docelowym ustawieniu fusebitów zadecydujemy w następnym artykule.

Plik dd_main.c:
/*************************************************************************
* SmartPIP - Elektroniczny dręczyciel
* 
* Data:  kwiecień 2013
* Autor: Dondu
* www:   http://mikrokontrolery.blogspot.com/2011/04/SmartPIP-elektroniczny-dreczyciel-spis-tresci.html
* 
* Plik:  dd_main.c
* Opis:  Testowanie czujnika światła
*
* Mikrokontroler:  ATmega8A
* Fusebity:        low:  0xC1
*                  high: 0xC9
* Kompilator:    GCC
* 
* W opcjach projektu należy ustawić:  zegar:         1000000Hz
*                                     optymalizację: -Os
*************************************************************************/


#include <avr/io.h> 
#include <stdio.h>  
#include <util/delay.h> 
#include <avr/interrupt.h> 
#include <avr/sleep.h>

#include "dd_main.h"
#include "dd_usart.h"
#include "dd_swiatlo.h"


//-------------------------------------------------------------------------

int main(void) 
{ 

 //--- Dobre praktyki ---
 //Po resecie (włączeniu zasilania) piny ustawione są domyślnie jako 
 //wejścia. Włączamy rezystory pull-up. To bardzo istotne w projektach
 //zasilanych z baterii! Dla ATmega8A włączamy wszystkie z wyjątkiem
 //pinu RESET ponieważ mamy tam podłączony zewnętrzny rezystor pull-up
 //szczegóły: http://mikrokontrolery.blogspot.com/2011/04/zakocenia-w-pracy-mikrokontrolerow.html
 PORTB  = 0xff; 
 PORTC  = 0xff;
 PORTD  = 0x3f;

 //jeżeli włączony tryb testowy (szczegóły w pliku dd_main.h)
 #ifdef  TRYB_TEST
  //Ustaw pin LED jako wyjście
  LED_TEST_DDR |= (1<<LED_TEST_PIN_NAZWA);
  LED_TEST_ON;
 #endif

 //przygotuj moduł USART do transmisji danych do komputera
 usart_inicjuj();

 //włącz przerwania globalne
 sei(); 

 //wysyłamy napis początkowy domyślnie umieszczony w tablicy usart_bufor[]
 //patrz plik: dd_usart.c
 usart_wyslij_bufor();
 //opóźnienie na poprawne wysłanie napisu startowego
 _delay_ms(1000); 

 //przygotuj czujnik światła oraz przetwornik ADC
 dd_swiatlo_adc_ini();


 //pętla główna
 while(1){ 

  //wykonaj pomiar
  dd_swiatlo_adc_pomiar_start();

  //konwertuj wynik na ciąg znaków i umieść w tablicy usart_bufor[]
  sprintf(usart_bufor, "%u", ADCH);

  //wyślij dane do komputera
  usart_wyslij_bufor();
  
  #ifdef  TRYB_TEST
   //zmień stan LED na przeciwny
   LED_TEST_TOGGLE;
  #endif

  //opóźnienie między kolejnymi pomiarami
  _delay_ms(2000);
 } 
}



Plik dd_main.h:
/*************************************************************************
* SmartPIP - Elektroniczny dręczyciel
* 
* Data:  kwiecień 2013
* Autor: Dondu
* www:   http://mikrokontrolery.blogspot.com/2011/04/SmartPIP-elektroniczny-dreczyciel-spis-tresci.html
* 
* Plik:  dd_main.h
* Opis:  Plik nagłówkowy pliku dd_main.c
*
*************************************************************************/


//======= TESTOWE =====================
//Definicje do fragmentów programu wykorzystywanych do testów
#define TRYB_TEST 1 //zakomentowanie tej linijki wyłącza tryb testowy
#ifdef  TRYB_TEST
 //definicje dot. testowej diody LED
 #define LED_TEST_DDR        DDRB
 #define LED_TEST_PORT       PORTB
 #define LED_TEST_PIN_NAZWA  PB0
 #define LED_TEST_ON         LED_TEST_PORT &= ~(1<<LED_TEST_PIN_NAZWA)
 #define LED_TEST_OFF        LED_TEST_PORT |=  (1<<LED_TEST_PIN_NAZWA)
 #define LED_TEST_TOGGLE     LED_TEST_PORT ^=  (1<<LED_TEST_PIN_NAZWA)
#endif






Do pobrania

Poniżej link do spakowanego:
  • projektu AVR Studio 4,
  • plików .c oraz .h
  • pliku .hex skompilowanego dla 1MHz i fusebitów określonych powyżej.

Plik: SmartPIP_Czujnik_testowanie.zip





Wyniki pomiarów

W przypadku mojej wersji czujnika światła i powyższego programu zgodnie z założeniami, czujnik na oświetlenie reaguje w sposób bardzo "agresywny",  co przejawiać się będzie stromymi zboczami na wykresie pomiarów, gdyż już przy niewielkim oświetleniu wartości pomiarów szybko rosną.

Taka charakterystyka czujnika jest podyktowana wymogiem oszczędzania baterii i w niczym nam nie będzie przeszkadzać, a wręcz pomagać podczas analizy wyników przez program.

Efekt jaki powinieneś zobaczyć na terminalu Realterm, jest następujący:

SmartPIP: Terminal Realterm (RS-232) - parametry transmisji oraz pierwsze dane.


Specjalnie pokazuję zakładkę Port, abyś wiedział jak ustawić parametry odbioru. Po ich ustawieniu kliknij przycisk Change. W prawym dolnym rogu są (w skrócie) pokazane aktualne ustawienia terminala - muszą być identyczne, by program działał poprawnie.

Pierwszy pomiar

Na powyższym ekranie możesz zauważyć, że pierwszy pomiar znacząco odbiega od pozostałych. Jest to związane z faktem, że po zmianie ustawień ADC (wybór napięcia odniesienia) pierwszy pomiar może być nieprawidłowy.

Informację w tej sprawie znajdziesz w dalszej części tego cyklu.

W zakładce Display, możesz włączyć opcję Ansi, co wyłączy pokazywanie znaków specjalnych:


Terminal Realterm (RS-232): Zakładka Display - usunięcie znaków specjalnych (ansi).


Więcej o ustawieniach terminala Realterm pisałem tutaj.





Podsumowanie

Mamy już przetestowany i działający czujnik światła, o dość agresywnej charakterystyce. Jest on prosty i zużywa bardzo mało energii z baterii. Powinniśmy teraz przetestować jakie straty poniesiemy, na kondensatorze pomiarowym i o tym będzie kolejny artykuł.


Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel
Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

SmartPIP: Czujnik światła - opóźnienie pomiaru


Autor: Dondu

Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel

W poprzednim artykule ustaliliśmy, że będziemy odłączać rezystor od masy, by zachowywać stan ładunku w kondensatorze pomiarowym i nie tracić energii baterii na ponowne jego ładowanie.

Zwróciłem także uwagę na to, że po włączeniu czujnika powinniśmy odczekać chwilkę, by sygnał na wyjściu czujnika zdążył się ustabilizować, ponieważ mogą nastąpić trzy sytuacje, w których napięcie aktualnie ustalone przez fototranzystor i rezystor:
  1. odpowiada napięciu na kondensatorze,
  2. jest niższe od napięcia na kondensatorze,
  3. jest wyższe od napięcia na kondensatorze,.

W przypadku nr 1, można by dokonać pomiaru zaraz po włączeniu czujnika, jednak w pozostałych dwóch przypadkach (2 i 3) należy zaczekać, aż kondensator ustabilizuje napięcie na wyjściu czujnika. 

Innymi słowy jest potrzebne nam opóźnienie.





Czas trwania opóźnienia

Musimy więc ustalić, jak duże powinno być opóźnienie (czas stabilizacji) po włączeniu czujnika. Czas ten zależy jest od:
  • aktualnego ładunku zgromadzonego w kondensatorze,
  • stopnia oświetlenia fototranzystora,
  • wartości kondensatora
  • wartości rezystora,

Wnioskować więc należy, że czas stabilizacji może być różny i nie jesteśmy w stanie przewidzieć kiedy, który przypadek wystąpi. Nie pozostaje nam nic innego jak przyjąć jakiś stały odcinek czasu.

Stały odcinek czasu, ale jak długi? Możemy go spróbować obliczyć.


Ładowanie kondensatora

Dla przypadku, gdy kondensator jest rozładowany i następuje jego ładowanie przez fototranzystor czas potrzebny na naładowanie się kondensatora do stabilnej wartości napięcia odpowiadającego aktualnemu poziomowi oświetlenia czujnika, zależy właśnie od tego jak bardzo czujnik jest oświetlony. Stwierdziliśmy to na podstawie datasheet w tym artykule.

Prąd, który ładować będzie kondensator będzie z reguły wielokrotnie większy, niż ten który będzie go rozładowywał (prąd rezystora). Dlatego spokojnie możemy pominąć obliczenia dla tego przypadku.


Rozładowanie kondensatora

Dla przypadku rezystora i kondensatora (układ RC) zakładając, że kondensator jest naładowany maksymalnie, a rezystor musi go rozładować do zera musimy obliczyć stałą czasową układu RC.

Wzór stałej czasowej jest następujący:




a wyrażana jest w sekundach.

Wcześniej określiliśmy wartość rezystora na 270kΩ, a wartość kondensatora przyjąłem doświadczalnie na 4,7nF. Wyznaczamy więc naszą stałą czasową:




Co oznacza ta stała? Oznacza ona, że w czasie zbliżonym do 1,3ms (milisekundy) kondensator rozładuje się do około 37% swojej pojemności. My jednak chcemy obliczyć kiedy rozładuje on całkowicie, a nastąpi to dopiero po czasie zbliżonym do 5 wartości τ :


Rys. Wykres rozładowania kondensatora
źródło: Wikipedia

W związku z tym, nasz czas opóźnienia pomiaru powinien wynosić:




Reasumując:
  • po włączeniu czujnika należy odczekać co najmniej 6,5ms i dopiero dokonać pomiaru światła,
  • jeżeli zastosujesz inne wartości RC powinieneś zastosować inne opóźnienie pamiętając, że włączony czujnik zużywa baterię, stąd należy zastosować możliwie najkrótsze opóźnienie.





Jak zrealizujemy opóźnienie pomiaru?

Dla przypomnienia, w SmartPIP'ie wykorzystujemy Timer2 pracujący w trybie asynchronicznym, w celu odliczania 8 sekundowych odstępów czasowych i to on będzie nam dawał sygnał (przerwanie) do rozpoczęcia procedury pomiaru (włączenia czujnika).

Dla odmierzenia opóźnienia 6,5ms moglibyśmy więc wykorzystać w programie jakąś funkcję opóźnienia delay(), ale to oznaczałoby, że w tym czasie mikrokontroler jest w trybie pracy i zużywa niepotrzebnie energię baterii, pobierając ponad 1mA prądu. Tę możliwość odrzucamy definitywnie.

Należy więc znaleźć inne rozwiązanie. Widzę tutaj dwie możliwości:

Wersja 1

W momencie, w którym należy wykonać pomiar oprócz włączenia czujnika włączmy dodatkowy timer np. Timer0 i usypiamy mikrokontroler. Timer0  odliczy nam czas około 6,5ms i wybudzi mikrokontroler w celu wyłączenia samego siebie i zlecenia dokonania pomiaru światła przez ADC.

W ten sposób w czasie czekania na ustabilizowanie się napięcia mierzonego, mamy uśpiony mikrokontroler i włączony główny Timer2 oraz pomocniczy Timer0. Takie rozwiązanie zużywa znacznie mniej energii niż aktywny mikrokontroler odliczający opóźnienie za pomocą funkcji delay().


Wersja 2

Standardowo Timer2 pracując w trybie asynchronicznym jako nasz zegarek czasu rzeczywistego ustawiony byłby w tryb pracy Normal, w którym po prostu odliczałby kolejne sekundy (w pakietach po 8 sekund). Nic nie stoi jednak na przeszkodzie, by nadal będąc w trybie asynchronicznym, korzystać z innych trybów pracy jakie umożliwia ten timer. Zaglądamy więc do datasheet:




Proponuję przyglądnąć się trybowi Fast PWM, który jak zapewne już wiesz pozwala oprócz generowania przerwania w momencie przepełnienia, także na zgłaszanie drugiego przerwania w momencie, w którym licznik timera (TCNT2) zrówna się z rejestrem komparatora (OCR2).

Co nam to da?

Ustawimy Timer2 w taki sposób, by poprawnie odmierzał założone odcinki czasu, ale jednocześnie był w stanie wygenerować nam dodatkowe przerwanie, odliczając ustalone przez nasz opóźnienie niezbędne do ustabilizowania się napięcia na wyjściu czujnika.

Innymi słowy, Timer2 da nam:
  • przerwania co 8 sekund niezbędne do prawidłowego odliczania czasu rzeczywistego (zegarek),
  • przerwanie opóźnienia pomiaru po włączeniu czujnika.

W ten sposób zaoszczędzimy jeszcze więcej energii niż w pierwszej proponowanej powyżej wersji.


Jaką wartość powinien mieć rejestr OCR2?

Pozostało nam policzyć, jaką wartość powinniśmy ustawić w rejestrze OCR2, by Timer zgłosił przerwanie po upływie 6,5ms od przerwania przepełnienia timera.

Ustaliliśmy wcześniej, że ośmiobitowy Timer2 będzie pracował z kwarcem 32768Hz i z preskalerem 1024. Obliczmy więc, jaki czas przypada na jednostkę czasu odliczanego przez timer:




Czyli zmiana licznika timera o 1 wykonywana jest co 31,25ms. My natomiast potrzebowaliśmy zaledwie 6,5ms. Niestety mniej nie jesteśmy w stanie uzyskać (nie da się ustawiać ułamkowych wartości licznika) stąd czas naszej zwłoki wynosić będzie  31,25ms.

Można byłoby się zastanowić, czy z punktu widzenia oszczędzania energii lepszą opcją jest jednak wykorzystanie innego timera do odliczenia tego czasu.

Ja jednak przyjmę, że wykorzystamy tylko Timer2.


W trybie FastPWM przerwanie przepełnienia Timera2 zgłaszane jest, gdy osiągnie on maksymalną wartość:


czyli w naszym wypadku 255 (bo Timer2 jest ośmiobitowy).

Skoro więc przerwanie opóźnienia ma być jeden cykl Timera2 później, to tym momentem jest stan, w którym Timer2 jest równy zero, czyli TCNT2 = 0. W tym momencie powinno nastąpić przerwanie opóźnienia. Aby to nastąpiło rejestr OCR2 musi mieć taką samą wartość, czyli zero (OCR2=0).

Innymi słowy przerwanie odliczające odcinki czasowe co 8 sekund zostanie wywołane, gdy Timer2 będzie miał wartość 255, a przerwanie opóźnienia pomiaru, gdy będzie miał wartość 0.

Przerwanie opóźnienia, będziemy oczywiście włączać tylko wtedy, gdy będzie nam potrzebne, by niepotrzebnie nie wybudzało mikrokontrolera.


Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

SmartPIP: Czujnik światła - oszczędzamy energię


Autor: Dondu

Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel


Pamiętasz, jak brzmi nasze główne motto?:

Oszczędzamy energię "na maksa"!

Mamy już wybrany schemat układu zamiany światła otoczenia na napięcie wraz z wartościami elementów, który zostałby podłączony do mikrokontrolera na przykład tak:




Pinem pomiarowym będzie więc pin ADC1 (PC1).

Czy można jednak na tym fragmencie schematu SmartPIP'a, zaoszczędzić trochę energii?
Można i należy to zrobić!


1. Odłączamy zasilanie czujnika 

W naszym układzie czujnika, gdy fototranzystor będzie dobrze oświetlony, prąd pobierany przez czujnik będzie wynosił nie więcej niż 10µA niezależnie od tego, czy w danym momencie dokonujemy pomiaru, czy też mikrokontroler śpi w najlepsze.

W okresie letnim przez większą część doby w pomieszczeniu będzie jasno (słońce w dzień, a światło sztuczne wieczorem). Zimą z reguły proporcja czasu, gdy w pomieszczeniu jest jasno do czasu, gdy jest w nim ciemno będzie oczywiście inna (z reguły mniejsza), ale i tak w znacznej części doby w pomieszczeniu będzie jasno.

Oznacza to, że czujnik przez znaczną część doby będzie zużywał energię pomimo, że z niego korzystać będziemy raz na kilka minut.

Wniosek: Warto wyłączać czujnik, gdy nie jest używany, ponieważ to zbędny pożeracz energii :-)

Zgodnie z założeniami projektu, mamy minimalizować ilość użytych elementów elektronicznych, a także wielkość i koszt urządzenia. W związku z tym, najprostszym rozwiązaniem jest wykorzystanie pinu mikrokontrolera, jako klucza włączającego i wyłączającego zasilanie czujnika.

Możemy sobie na to pozwolić, ponieważ czujnik pobiera zaledwie 10µA, a pin tego mikrokontrolera, może spokojnie dostarczyć 20mA, a nawet 40mA.


Schemat może wyglądać więc na przykład tak:




Czyli aby włączyć czujnik ustawimy pin PC0 jako wyjście i ustawimy na nim stan wysoki, czyli jedynkę logiczną.

Powinniśmy sobie jednak zadać pytanie, jaki będzie faktyczny poziom napięcia zasilania czujnika, gdy pin mikrokontrolera będzie miał ustawioną jedynkę logiczną, zasilając w ten sposób nasz układ pomiarowy?

Zerkamy do datasheet do tabelki z parametrami elektrycznymi pinów i odnajdujemy odpowiedni parametr:




W naszym przypadku (zasilanie z 3V) tabelka DC Characteristics informuje nas, że możemy się spodziewać co najmniej 2,2V, ale niestety nie jest podane ile typowo powinno być. Jednakże zauważamy, że parametr ten podany jest dla prądu pinu wynoszącego aż 10mA. W naszym przypadku mamy prąd 1000 razy mniejszy, bo zaledwie 10µA.

Intuicja mówi nam, że powinniśmy poszukać bardziej szczegółowej informacji. Zaglądamy więc do części z wykresami, gdzie znajdujemy wykres zależności napięcia od prądu, dla pinu ustawionego jako wyjście z jedynką logicznym (source current), przy zasilaniu 3,0V:




Okazuje się więc, że dla naszego prądu 10µA, na wyjściu pinu otrzymamy nie 2,2V, ale pełne 3,0V, co jest dla nas bardzo istotne i oznacza, że przy takim prądzie pin działający jako klucz włączający zasilanie czujnika spełni swoją rolę "idealnego klucza"  i nie będzie wprowadzał zmian do naszych obliczeń.

Algorytm pomiaru będzie więc następujący:
  1. włączamy czujnik ustawiając pin PC0 jako wyjście i podajemy na niego jedynkę logiczną,
  2. czekamy chwilkę na ustabilizowanie się napięcia na kondensatorze,
  3. dokonujemy pomiaru pinu ADC1,
  4. po zakończonym pomiarze wyłączamy czujnik poprzez ustawienie pinu w stan wysokiej impedancji, czyli jako wejście z wyłączonym rezystorem pull-up.

W ten sposób będziemy włączać czujnik tylko wtedy, gdy jest nam potrzebny, minimalizując straty energii z baterii.






2. Odłączamy rezystor 

Można coś jeszcze zaoszczędzić?
Zastanówmy się co się dzieje z energią zgromadzoną w kondensatorze, gdy zakończyliśmy pomiar i  wyłączyliśmy zasilanie czujnika:




Prosta sprawa - wredny rezystor rozładuje nam kondensator :-)

Co to oznacza?

Za każdym razem (z wyjątkiem ciemności), będziemy ponownie ładować kondensator od zera, a po dokonaniu pomiaru rezystor nam go rozładuje. To znowu duża strata!

Jeżeli więc damy radę przechować chociażby część ładunku w kondensatorze, to będzie to zyskiem zmniejszającym zużycie ładunku baterii.

Co należy zrobić, by rezystor nie rozładowywał nam kondensatora do zera? Trzeba go odłączyć, a możemy zrobić to podobnie jak z zasilaniem czujnika, czyli wykorzystać następny pin mikrokontrolera:





W ten sposób w czasie, gdy wyłączymy zasilanie oraz masę rezystora, nie będzie on rozładowywał kondensatora.


UWAGA!

W dalszej części tego cyklu artykułów będziemy posługiwać się powyższym schematem, ale narysowanym nieco inaczej:


Jeżeli się przyglądniesz, ten i poprzedni schemat są dokładnie takie same.


Podobnie jak w przypadku odłączania zasilania czujnika, także i w tym przypadku powinniśmy sobie zadać pytanie, jaki będzie faktyczny poziom napięcia na pinie sterującym rezystorem, gdy pin ten będzie miał ustawione zero logiczne, czyli zwierał rezystor do masy?

Zerkamy do datasheet do tej samej tabelki z parametrami elektrycznymi pinów i odnajdujemy parametr:




W naszym przypadku (zasilanie z 3V) tabelka DC Characteristics informuje nas, że możemy się spodziewać aż 0,6V. Skoro aż tyle, to nasze obliczenia wykonane powyżej należałoby skorygować. Ponownie zauważamy, że parametr ten podany jest dla prądu pinu wynoszącego aż 10mA, a w naszym przypadku mamy przecież prąd 1000 razy mniejszy.

Po nabraniu doświadczenia z punktu 1 wiemy, że powinniśmy poszukać bardziej szczegółowej informacji na wykresach. Znajdujemy więc wykres dla pinu I/O ustawionego jako wyjście z zerem logicznym (sink current), przy zasilaniu 3,0V:




Ponownie okazuje się, że dla naszego prądu 10µA, na wyjściu pinu nie będziemy mieli spodziewanego 0,6V, ale 0V, co jest dla nas bardzo istotne i oznacza, że przy takim prądzie pin działający jako klucz włączający masę, nie będzie wprowadzał zmian do naszych obliczeń.

Uzupełniamy nasz algorytm pomiaru:
  1. włączamy czujnik ustawiając pin PC0 jako wyjście i podajemy na niego jedynkę logiczną,
  2. włączamy masę rezystora ustawiając PC2 jako wyjście i podajemy na niego zero logiczne,
  3. czekamy chwilkę na ustabilizowanie się napięcia na kondensatorze,
  4. dokonujemy pomiaru pinu ADC1,
  5. po zakończonym pomiarze wyłączamy czujnik poprzez ustawienie pinów zasilania i masy rezystora w stan wysokiej impedancji, czyli jako wejście z wyłączonym rezystorem pull-up.






Podsumowanie pkt. 1 i 2

Dzięki zastosowaniu rozwiązań opisanych w punktach 1 i 2 zminimalizowaliśmy do granic możliwości zużycie energii w tej części schematu SmartPIP'a.

Należy jednak wspomnieć tutaj o zjawisku upływności:
  • kondensatora,
  • pinów mikrokontrolera.

W przypadku kondensatora upływność można znaleźć w datasheet tego, który zastosujesz.

W przypadku naszego mikrokontrolera ATmega8A, upływność pinu ustawionego jako wejście, określona jest w datasheet następująco:




Jak widać, jest to wartość maksymalna w dodatku podana dla napięcia 5V. Możemy się więc spodziewać, że w naszym przypadku będzie to znacznie mniej niż 1µA na jeden pin. Tutaj niestety nic już nie policzymy, więc należałoby dokonać pomiarów.

Czas trwania stabilizacji sygnału czujnika

Należy tutaj wspomnieć jeszcze o tym, że gdy załączymy czujnik i od razu rozpoczniemy pomiar, to możemy narazić się problem otrzymania niewłaściwego pomiaru, co jest związane z czasem trwania stabilizowania się napięcia na wyjściu czujnika. Dlatego:

Po włączeniu czujnika (zasilanie + rezystor) należy zaczekać na ustabilizowanie się napięcia na wyjściu czujnika tym bardziej, gdy zastosujemy kondensator stabilizujący.

Jak to zrealizujemy opiszę w następnym artykule.





3. Wyłączamy ADC, gdy tylko jest to możliwe


Przetwornik analogowo-cyfrowy (ADC), który używamy do pomiarów światła także zużywa energię, a jest nam potrzebny zaledwie co kilka minut i w dodatku tylko na ułamek sekundy. Należy więc zadbać o to, by niepotrzebnie nie generował nam strat energii baterii.

W przypadku mikrokontrolera ATmega8 mamy w tym zakresie dwie możliwości:
  • włączania i wyłączania przetwornika,
  • specjalnego trybu pomiaru "ADC Noise Reduction".


Dlaczego włączanie i wyłączanie przetwornika ADC jest istotne?

Zobaczmy w dokumentacji jaki prąd pobiera przetwornik ADC. Znajdziesz tam wykres dla przypadku, który zastosujemy, czyli gdy napięcie referencyjne AREF (napięcie odniesienia) będzie równe napięciu zasilania mikrokontrolera Vcc:




Na wykresie zaznaczyłem jest fragment, w którym pracować będzie nasz SmartPIP, czyli dla napięć zasilania przede wszystkim z przedziału od 2,7V do 3V, ponieważ taką baterię wybraliśmy. Dla uproszczenia przyjmijmy, że prąd ten będzie równy 150µA.

Malutko prawda? Ale czy aby na pewno?

W poprzednim artykule ustaliliśmy, że w czasie snu przy działającym Timer2 odliczającym czas, mikrokontroler będzie pobierał zaledwie 10µA. Włączony ADC pobierać będzie więc:



... 15 razy więcej prądu niż mikrokontroler w czasie snu i odliczania czasu!

Wniosek jest oczywisty:

Należy bezwzględnie wyłączać przetwornik ADC pomiędzy kolejnymi pomiarami, gdyż pobiera on strasznie dużo ładunku z baterii.

Tryb pomiaru ADC Noise Reduction

Drugą możliwością w zakresie oszczędności uzyskanych z przetwornika ADC jest skorzystanie podczas pomiarów światła ze specjalnego trybu snu ADC Noise Reduction mode.

Tryb ten został zaprojektowany przede wszystkim po to, by zmniejszyć ilośc szumów generowanych przez wewnętrzne układy mikrokontrolera, które mogą przenosić się na wejście przetwornika ADC w czasie trwania pomiaru. Dla nas oczywiście jest to także istotne.

Jednakże naszym zyskiem jest dodatkowo fakt, iż w trybie tym spada zapotrzebowanie na energię co związane jest z zatrzymaniem pracy niektórych wewnętrznych układów. Niektórych, czyli?:


Niestety w tym zakresie nie jesteśmy w stanie stwierdzić na podstawie datasheet jakie z tego tytułu będą oszczędności, ale na pewno będą spore patrząc na to, co jest wyłączone :-)






Podsumowanie

Sądzę, że po analizie niniejszego artykułu stwierdzisz, że oszczędzanie energii wcale nie jest trudne i nie są to jakieś magiczne zaklęcia. Ot po prostu kombinowanie i logiczne myślenie poparte sprawdzaniem datasheet.

Jednakże zawsze możesz trafić na moment, w którym będziesz miał wątpliwości lub w datasheet nie znajdziesz odpowiednich informacji. Wtedy pozostaje droga empiryczna w postaci eksperymentu.

Najprostszym jest więc symulowanie pracy przy zasilaniu z naładowanego kondensatora. Jak taki eksperyment wygląda możesz zobaczyć na przykładzie pokazanym przez inżynierów ATmel'a.

W następnych artykułach przetestujemy nasz czujnik i dokonamy testowych dobowych pomiarów.


Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel


Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

SmartPIP - Czujnik światła - obliczenia


Autor: Dondu

Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel


Nadszedł moment, w którym powinniśmy dokonać obliczeń dla naszego układu pomiarowego wybranego w poprzednim artykule. W obliczeniach możemy pominąć kondensator i wejście przetwornika ADC:




Prąd płynący przez fototranzystor będzie zwierany do masy przez rezystor. Skoro wiemy, że nasz mikrokontroler oraz układ czujnika światła będą zasilane z baterii 3V, to możemy spróbować obliczyć wartość rezystora przyjmując wartość prądu fototranzystora taką, jakiej nie chcielibyśmy przekraczać, niezależnie od rodzaju źródła i natężenia światła oświetlającego czujnik.

Aby policzyć wartość rezystora musimy znać napięcie jakie się na nim "odłoży" przy danej wartości prądu. Napięcie to będzie wynikało ze wzoru:




Istotnym jest więc ustalenie, jaki napięcie przy wybranym przez nas prądzie będzie występowało na złączu kolektor-emiter fototranzystora (UFT).

Zaglądamy do datasheet fototranzystora i w tabelce odnajdujemy parametr Collector Emitter Saturation Voltage, czyli napięcie kolektor-emiter w czasie jego nasycenia:


Niestety wszystkie parametry z tabeli podane są dla napięcia zasilania wynoszącego 5V, a ten parametr dodatkowo podany jest dla prądu kolektora równego 10mA, a w naszym przypadku będziemy starali się, by prąd ten był 1000 razy mniejszy.

Nie ma także żadnego wykresu, z którego moglibyśmy odczytać jak zachowywać się będzie ten parametr dla innych napięć zasilania. Dlatego nie pozostaje nam nic innego, tylko do obliczeń przyjąć wartość 0,4V.


Rezystor

Zaczniemy od przyjęcia, że chcemy aby prąd płynący przez tranzystor miał wartość 10µA.

Dlaczego akurat tyle? Musimy oszczędzać, a fototranzystor, który wybrałem, umożliwia nam pracę z takim prądem co ustaliliśmy już wcześniej.

Dane do obliczeń:
  • napięcie zasilania: 3V
  • napięcie emiter-kolektor: 0,4V
  • żądany prąd fototranzystora: 10µA

Najpierw obliczymy spadek napięcia na rezystorze:




a następnie skorzystamy z prawa Ohma dla układu:



i policzymy wartość rezystora:




i na koniec ustalamy, że z szeregu rezystorowego wybieramy rezystor 270kΩ.


Kondensator

Zadaniem kondensatora jest stabilizowanie poziomu sygnału z fototranzystora, który mierzymy przetwornikiem ADC. Im większy będzie kondensator tym stabilniejsze będą pomiary.

Niestety z punktu widzenia oszczędzania energii, powinniśmy minimalizować jego wartość, ponieważ każde naładowanie i rozładowanie kondensatora to strata energii z baterii.

Dlatego jeśli się da, to można zrezygnować z kondensatora tym bardziej, że będziemy stosować programowe uśrednianie wyników.

Warto jednak przewidzieć ewentualnie miejsce na płytce PCB, żeby w razie konieczności wlutować kondensator.

Jaką więc wartość wybrać, gdy kondensator będzie potrzebny?

Odpowiedź jest prosta - sprawdzić doświadczalnie jak "pływają" pomiary z różnymi wartościami kondensatora i wybrać najmniejszy możliwy kondensator zapewniający w miarę stabilne pomiary. Może to być mocno zależne od rodzaju oświetlenia, przede wszystkim sztucznego. Ja testowałem różne, a mój kompromis znalazłem na poziomie 4,7nF.


Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

SmartPIP - Czujnik światła - fototranzystor


Autor: Dondu

Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel


Przyszedł czas na wybór odpowiedniego fototranzystora. Wyboru możemy dokonać na zasadzie "mam jakiś w szufladzie" - tak było i w moim przypadku, ponieważ miałem sporo fototranzystorów: KPS-3227SP1C (datasheet)

Jest to fototranzystor przeznaczony do pomiaru światła otoczenia (ang. ambient light). Podstawowe parametry:



Najistotniejszym parametrem, który determinuje, czy fototranzystor nadaje się do wykorzystania w naszym projekcie jest zakres długości fali światła, dla którego został zaprojektowany. Nas interesuje oczywiście światło widzialne (Wikipedia), czyli w zakresie 400-700nm.

Sprawdzamy więc odpowiednie parametry w datasheet:




Na podstawie tabelki ustalamy, że fototranzystor jest przeznaczony dla zakresu 390-700nm, z maksymalną czułością dla 580nm, a to oznacza, że idealnie nadaje się do pomiarów światła widzialnego. Dodatkowo w datasheet możemy znaleźć wykres jego czułości w zależności od długości fali światła (rysunek powyżej).


Kąt detekcji (widzenia, obserwacji)

Istotnym dla nas parametrem jest także kąt  pod jakim "widzi" fototranzystor. Kąt ten określony został przez producenta w następujący sposób:




W tabelce podano, iż kąt obserwacji dla połowy jego czułości wynosi 120º. Oznacza to, że mamy szerokie pole widzenia, co w naszym przypadku jest pożądane ze względu na to, że podkładając ofierze SmartPIP'a, nie będziemy musieli "celować" czujnikiem we właściwym kierunku.

Dla porównania inny fototranzystor przeznaczony do pomiaru światła otoczenia TEPT4400 (datasheet):




Dość ciekawy "walentynkowy :-)" kształt pola widzenia czujnika, jakże odmienny od poprzednika. Z wykresu wynika, że kąt detekcji czujnika TEPT4400 wynosi około 60º. Potwierdzeniem tego jest kąt określony w tabelce, gdzie podano kąt ±30º:




Znak ± oznacza, że kąt ten należy pomnożyć przez 2, by uzyskać faktyczny kąt widzenia czujnika. Jest to zgodne z rysunkiem powyżej, czyli 60º.

Wniosek jest oczywisty, fototranzystor ten nadaje się do naszego urządzenia, ale należy być świadomym wartości parametru kąta widzenia, który jest dwukrotnie mniejszy niż pierwszego fototranzystora.


Prąd fototranzystora vs natężenie oświetlenia

Teraz należy zastanowić się nad doborem elementów w układzie czujnika światła. Musi on pobierać w miarę możliwości jak najmniejszy prąd w czasie pracy. Jednocześnie sam fototranzystor musi poprawnie pracować w szerokim zakresie oświetlenia.

Zerkamy więc do datasheet w poszukiwaniu istotnych w tym zakresie parametrów. Zostawiłem tylko te, które nas będą interesować:



Zwróć uwagę na notatki pod tabelką, które są odnośnikami do numerków w nawiasach kwadratowych w kolumnie Conditions i oznaczają one:
  1. oświetlenie fluorescencyjne (6200K),
  2. standardowe domowe oświetlenie żarówką wolframową (2856K),
  3. oświetlenie światłem słonecznym (4600K).

Przyda się nam znajomość definicji:

Luks [lx] - to jednostka układu SI służąca do określenia natężenia oświetlenia wytworzonego przez strumień świetlny jednego lumena [lm] równomiernie rozłożony na powierzchni jednego metra kwadratowego [m2].


Lumen [lm] - to strumień świetlny wysłany w jednostkowy kąt bryłowy (steradian) przez izotropowe punktowe źródło światła o światłości jednej kandeli umieszczone w wierzchołku tego kąta.


Steradian [sr]- to kąt bryłowy o wierzchołku w środku kuli, wycinający z powierzchni tej kuli pole równe kwadratowi jej promienia.

oraz wiedza na temat barwy światła (Wikipedia).


W tabelce producent operuje luksami, więc by było łatwiej zrozumieć dalszą część artykułu przedstawimy definicję luksa za pomocą wzoru:



Zerkamy na parametry z tabelki:

Collector dark current
To prąd, który płynie przez tranzystor w momencie, gdy jest ciemno, czyli poziom oświetlenia fototranzystora wynosi 0lx. Z tabelki wynika, że powinniśmy się spodziewać zaledwie 10nA, maksymalnie 100nA. To bardzo mało, ale należy pamiętać, że w pomieszczeniu ofiary zapewne nie będzie idealnie ciemno. Trudno więc przyjmować, że faktycznie będzie tylko 10nA.


Light Current(1),(2),(3),(4)
To prąd, który płynie przez fototranzystor w momencie, gdy jest oświetlony określonym światłem (rodzaj i strumień światła). W kolumnie conditions rodzaj światła określony jest numerkiem w nawiasach kwadratowych, a jego natężenie podane w luksach. Możemy się więc spodziewać nawet 950µA (czyli prawie 1mA) przy oświetleniu światłem żarowym o natężeniu 1000lx. Dla nas oczywiście tak duży prąd jest nie do zaakceptowania i będziemy go ograniczać rezystorem.



Ile to jest 100lx?

Powinniśmy sobie zadać pytanie, jak jasne jest światło o natężeniu 100lx, które według powyższej tabelki warunkuje nam osiągnięcie prądu 6µA dla światła fluorescencyjnego (patrz: light current 1). Sama liczba nie daje nam pojęcia, jaki to jest poziom oświetlenia czujnika.

Najprościej będzie porównać się do standardowej żarówki o strumieniu światła równym 100 lumenów. Żarówką, która emituje oświetlenie o natężeniu 100lm jest zwykła żarówka o mocy 15W:


Źródło: Paulmann Licht GmbH

czyli:



Zapewne jesteś świadomy, jak mało światła daje standardowa żarówka o mocy 15W.

Wyobraź sobie, że cały strumień świetlny tej żarówki (czyli 100lm) skierujesz za pomocą bezstratnego zwierciadła równomiernie na wycinek sfery kuli o powierzchni 1m2 (patrz: steradian (Wikipedia)). W ten sposób zgodnie z powyższą definicją i wzorem, natężenie oświetlenia na tej powierzchni będzie miało 100 luksów:



Następnie na tym wycinku powierzchni sfery (1m2) umieszczasz nasz malutki fototranzystor o wymiarach 2 x 2mm. Światło, które będzie go oświetlało będzie miało natężenie także 100lx.


Wartości pośrednie i zależności od rodzaju źródła światła

Teraz popatrzmy na wykres zależności prądu fototranzystora od natężenia i rodzaju oświetlenia zawarty w datasheet naszego fototranzystora. Niestety producent tak przygotował ten wykres, że za pomocą zamieszczonej do niego legendy nie można stwierdzić, która linia symbolizuje światło fluorescencyjne, a która standardowe żarowe.

Zaznaczyłem więc kolorami wartości natężenia światła z tabelki, czyli 100lx oraz 1000lx:



Odczytując poziomy prądu wyjściowego fototranzystora dla poszczególnych przypadków z tabelki możemy zidentyfikować poprawnie obie linie, co oznaczyłem strzałkami.

Zauważ, że prądu poniżej 10µA dla oświetlenia żarowego (30lx) oraz 5µA dla fluorescencyjnego (80lx), oznaczone są na wykresie linią przerywaną, a nie ciągłą jak pozostała ich część. Możemy więc się domyślać, że w tym zakresie pracy fototranzystora producent nie gwarantuje, że zależność prądu od oświetlenia czujnika będzie nadal liniowa i przewidywalna.

Zauważyć należy także, że ten fototranzystor znacznie lepiej reaguje dla światła żarowego niż fluorescencyjnego.



Podsumowanie

Mamy wybrany fototranzystor oraz ustalone jego parametry możemy więc przejść do obliczeń, o czym piszę w następnym artykule.


Artykuł jest częścią cyklu: SmartPIP - Elektroniczny dręczyciel

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.