AVR: pomiar temperatury i wilgotności
17.11.2014 | aktual.: 25.11.2014 18:25
Gdy przechowujemy owoce, warzywa czy drewno ważna jest kontrola temperatury i wilgotności. Zmiany wyżej wymienionych wartości powyżej dozwolonych progów mogą prowadzić do strat.
Poniższy projekt obejmuje tylko pomiar obu wartości fizycznych i alarmowanie. Niestety brak środków przeszkodził w realizacji układu automatycznej regulacji(chłodnica i nawilżacz). Układ przeznaczony jest dla prywatnej przechowalni jabłek.
Układ pomiarowy
Sercem układu jest układ Atmel Mega32(jednak warto rozważyć mniejsze wersję atmel mega a nawet atiny) taktowany wewnętrznym zegarem 8 MHz.
Do pierwszego wejścia a/c przyłączony został termometr oparty o układ KTY81-210. Jest to układ już opisywany prze zemnie kiedyś. Termometr ten działa na zasadzie zmiany rezystancji w zależności od temperatury.
W takim wypadku wystarczy zbudować dzielnik napięcia składający się z rezystora i KTY81-210. Następnie wykonujemy pomiar napięcia na dzielniku,z czego otrzymujemy opór jaki jest na KTY81-210, po czym za pomocą wielomianu czwartego stopnia dokonujemy aproksymacji temperatury. Wielomian można uzyskać poprzez spisanie z dokumentacji układu zależności temperatury od oporu(zbiór punktów) a następnie za pomocą gnu octave lub excela uzyskujemy żądany wielomian .
Wadą tego typu układów jest stosunkowo duża niedokładność ,a wykorzystanie aproksymacji wielomianowej wiąże się w tym przypadku z koniecznością użycia liczb typu float co niestety znacznie obciąża CPU.
Pomiar wilgotności wykonywany jest za pomocą układu SY‑230.
Układ należy zasilić napięciem 5V(nóżki 1 i 3) a na nóżce numer 2 w zależności od wilgotności pojawi się odpowiednie napięcie. W dokumentacji układu znajduje się krzywa opisująca zależność napięcia od wilgotności. Podobnie jak poprzednio spisujemy parę pkt. (im więcej tym lepiej) i dokonujemy aproksymacji. W tym wypadku jest to zależność liniowa(policzy każdy arkusz kalkulacyjny) co oznacza że możemy skorzystać z operacji na liczbach całkowitych(typ int) co znacznie mniej obciąża CPU.
Ponadto układ wyposażono w standardowy wyświetlacz 2x16. Cztery diody LED(2 czerwone i 2 niebieskie), dwie służą do sygnalizacji przekroczenia maksimum a dwie minimum mierzonych wartości. Ponadto na płytce znajdują się dwa przyciski umożliwiające zmianę nastaw alarmów. Napięcie zasilania jest jednocześnie nap. Odniesienia dla przetworników a/c. Oczywiście układ wyposażono w złącze programistyczne.
Oprogramowanie
Jeśli idzie o oprogramowanie Nie będę tu przytaczał samych aproksymacji czy pomiaru a/c. Są one dość standardowe. Ponieważ chciałem aby pomiędzy kolejnymi odświeżeniami ekranu(2 sekundy) możliwa była praca układu(czyli nie używam _delay_ms w while), konieczne było zastosowanie timera. Użyłem do tego Timera 0 (8‑bitowego, zlicza do 255 i wywołuje przerwanie). Układ czasowy jest taktowany zegarem F_CPU/1024 = 7182 Hz . Czyli przerwanie jest wywoływane co 0,032 s. Co wymusza zastosowanie wewnętrznego licznika w przerwaniu aby możliwe było otrzymanie odświeżania co około 2 s.
[code=C]ISR(TIMER0_OVF_vect) {[/code]
[code=C] timer_adc++; //[/code]
[code=C] if (timer_adc == 62) {[/code]
[code=C] glob_bool.b2 = 1;[/code]
[code=C] timer_adc = 0;}}[/code]
Wykorzystuję tu pola bitowe aby nie marnować pamięci. W dużym uproszczeniu pole bitowe jest to taka konstrukcja która pozwala na prosty dostęp do poszczególnych bitów(grup) w danym słowie. Przykład definicji pola poniżej(dla zmiennej 8‑bitowej).
[code=C]typedef struct{[/code]
[code=C] uint8_t b0:1; // zarezerwowane dla Przycisku 1[/code]
[code=C] uint8_t b1:1; //zarezerwowane dla Przycisku 2[/code]
[code=C] uint8_t b2:1;// timer dla adc[/code]
[code=C] uint8_t b3:1;[/code]
[code=C] uint8_t b4:1;[/code]
[code=C] uint8_t b5:1;[/code]
[code=C] uint8_t b6:1;[/code]
[code=C] uint8_t b7:1;[/code]
[code=C] } volatile IO;[/code]
Zmiennej b0 przydzielam jeden bit pamięci(ale mógłbym np. 4), co oznacza że może przechowywać tylko wartość 0 lub 1. Wracając do samego odświeżania. Gdy w nieskończonej pętli while program wykryje zmianę bitu b2 na 1 wtedy odświeża ekran(i dokonuje pomiaru), zeruje b2 i przechodzi dalej.
Ponieważ układ wyposażony jest w dwa przyciski konieczne jest wyeliminowanie drgania styków co osiągam za pomocą użycia Timera1(16 bitowego). Co parędziesiąt milisekund wywoływany jest timer który sprawdza czy nie przyciśnięto przycisku. Jeśli tak się stało dodaje on do zmiennej debouncing_key, wartość jeden. Gdy osiągnie on wartość 21(czyli upłynie paręset ms) ustawiamy bit b0 lub b1(w zależności od przycisku, b0 zmiana menu, b1 dodanie wartości do nastawy alarmu). W nieskończonej pętli while wywołujemy odpowiednie odświeżenie ekranu i dodanie jedynki do aktualnej nastawy alarmu.
[code=C]ISR(TIMER1_OVF_vect) {[/code]
[code=C] if (!(PIND & _BV(PD2))) {[/code]
[code=C] debouncing_key++;[/code]
[code=C] if (debouncing_key == 21) {[/code]
[code=C] glob_bool.b0 = 1;[/code]
[code=C] debouncing_key = 0;[/code]
[code=C] }[/code]
[code=C] } else if (!(PIND & _BV(PD3))) {[/code]
[code=C] debouncing_key++;[/code]
[code=C] if (debouncing_key == 21) {[/code]
[code=C] glob_bool.b1 = 1;[/code]
[code=C] debouncing_key = 0;}}}[/code]
Gdy nastawa alarmu przekroczy maksymalna wartość, przypisywana jest jej minimalna możliwa wartość.
Ponadto układu „pilnuje” 2 sekundowy watchdog.
Rozbudowa
Układ jest w obecnej postaci mało zaawansowany. Jeśli posiadałbym agregat chłodzący i nawilżać skusiłbym się na następująca rozbudowę:
- Dołożenie przekaźników odpalających chłodnicę i nawilżacz.
- Użycie w pomiarze a/c trybu Low Noise. Tryb ten pozwala na zmniejszenie szumów od procesora atmegi. Konkretnie usypiamy rdzeń, dokonujemy pomiaru i wybudzamy go. Pierwotnie zaimplementowałem go niemniej Atmel Studio sobie z niewiadomych przyczyn nie radziło z tym trybem a nawet Timerami... Ostatecznie skorzystałem z eclipse + wtyczka, ale nie uruchomiłem ponownie trybu low noise ponieważ włożyłem tam już klasyczny pomiar :).
- Można pomyśleć o dołączeniu np. modułu radiowego/bluettoth i dzięki temu możemy uzyskać pogląd na pomiary wprost z PC.
Poniżej kod w C + powyższy artykuł. KLIK! ERRATA: W kodzie źródłowym jest drobny chochlik, zmienna odpowiedzialna za temperaturę ma typ uint16_t a powinna mieć int16_t. Uniemożliwia to pomiar ijemnych temperatur.