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

5 komentarzy:

  1. Terminal Realterm a nie realtime ;)

    OdpowiedzUsuń
  2. Mam pytanie czy pojawią się kolejne części cyklu?

    OdpowiedzUsuń
    Odpowiedzi
    1. Niestety dopiero jesienią. Teraz muszę rozpocząć obiecany temat dot. robotów.

      Usuń
    2. Którego roku?

      Usuń