Co prawda w zamierzeniu miał to być obszerny artykuł na temat pomiaru temperatury przez układ mikroprocesorowy na bazie ATmegi 48P, ale po dłuższych przemyśleniach doszedłem do wniosku, że o wiele lepiej będzie po prostu pokazać schemat układu i listingi programów, z niewielkim, niezbędnym komentarzem.
Mając do dyspozycji mikrokontroler ATmega 48P możemy zbudować prostu termometr na co najmniej dwa sposoby. Pierwszy z nich może się opierać na odczytywaniu temperatury z wewnętrznego sensora, jednak aby uzyskać w miarę zadowalające wyniki, należy przeprowadzić kalibrację - szczegóły znajdziecie w specjalnie tej tematyce poświęconym dokumencie, dostępnym na stronach firmy Atmel.
Poniższy listing pokazuje, jak możemy wykorzystać wewnętrzny sensor (wyniki wyświetlane są na "szeregowym" LCD mojej produkcji):
Kalibracji dokonałem z grubsza, nie dysponując ani możliwością uzyskania znacznej różnicy temperatur, ani też dobrym termometrem wzorcowym. Zauważmy, że z wbudowanym czujnikiem temperatury komunikujemy się w taki sam sposób, jak z przetwornikiem analogowo-cyfrowym; jest to jakby dodatkowy kanał ADC.
Drugi sposób, o wiele ciekawszy, polega na wykorzystaniu zewnętrznego, scalonego czujnika temperatury. Na rynku znajdziemy wiele tego typu układów - najciekawsze z nich to układy, z którymi mikrokontrolery komunikują się za pomocą magistral I2C czy 1Wire, jednak w moim termometrze zastosowałem układ scalony analogowy LM35DZ. Ta wersja pozwala na pomiar temperatur w zakresie od 0 do 100 st. C, czyli nadaje się doskonale do zastosowań typu indoor. Wartość napięcia, na którą układ konwertuje temperaturę otoczenia (właściwie jest to temperatura jego obudowy), przetwarzana jest przez przetwornik analogowo-cyfrowy mikrokontrolera i prezentowana przez szesnaście diod LED, przy czym dla zbyt niskich i zbyt wysokich temperatur przewidziana jest specjalna sygnalizacja - osobne, pulsujące LED-y (PWM). Dodatkowo wyniki pomiarów można śledzić na opcjonalnym LCD.
Diody LED są sterowane za pomocą dwóch ekspanderów magistrali I2C (PCF8574P), dzięki czemu obsługa wyświetlania wyników realizowana jest przez dwa wyprowadzenia mikrokontrolera (stały problem: jak rozszerzyć liczbę wejść/wyjść mikrokontrolerów).
Oto schemat układu:
i listing programu:
Schemat, dla poprawy czytelności, nie zawiera wartości zastosowanych elementów biernych, oto więc spis:
Przedstawione rozwiązanie z wielu względów nie jest optymalne - jakoś szczególnie go nie dopracowywałem - weźmy choćby pobór prądu przez szesnaście święcących diod...
Jednak efekt ciekawy, szczególnie, gdy dobierzemy różnokolorowe diody dla poszczególnych przedziałów temperatur.
Mając do dyspozycji mikrokontroler ATmega 48P możemy zbudować prostu termometr na co najmniej dwa sposoby. Pierwszy z nich może się opierać na odczytywaniu temperatury z wewnętrznego sensora, jednak aby uzyskać w miarę zadowalające wyniki, należy przeprowadzić kalibrację - szczegóły znajdziecie w specjalnie tej tematyce poświęconym dokumencie, dostępnym na stronach firmy Atmel.
Poniższy listing pokazuje, jak możemy wykorzystać wewnętrzny sensor (wyniki wyświetlane są na "szeregowym" LCD mojej produkcji):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
' Prosty termometr, wykorzystujący wbudowany czujnik temperatury | |
' mikrokontrolera ATmega48P | |
$regfile = "m48pdef.dat" | |
$crystal = 4000000 | |
$hwstack = 40 | |
$swstack = 16 | |
$framesize = 32 | |
' Plik z definicjami stałych używanych do sterowania modułem | |
' wyświetlacza szeregowego. | |
$include "sdrv-const.bas" | |
' Parametry kalibracji | |
' - kalibracja 1 | |
'Const T_offset = 39.4 | |
'Const K = 0.85 | |
' - kalibracja 2 | |
Const T_offset = 34.22 | |
Const K = 0.65 | |
' Napięcie referencyjne dla ATmega48P i wewn. sensora (miliwolty) | |
Const Vref = 1100 | |
' Współczynnik przeliczenia wartości odczytanej z przetwornika | |
' (z konwersją na stopnie Celsjusza) | |
Const Wspolczynnik = Vref / 10240 | |
' Progi teperatur (wg. dokumentacji ATmega48P) | |
Const Temp_m45 = 242 ' -45 st. C | |
Const Temp_25 = 314 ' 25 st. C | |
Const Temp_0 = 287 ' 0 st. C licząc od dołu | |
Config Serialout = Buffered , Size = 32 | |
Config Adc = Single , Prescaler = Auto , Reference = Internal | |
' Timer1 generuje przerwanie co ok. 1 sekundę | |
Config Timer1 = Timer , Prescale = 64 | |
On Timer1 On_timer_tick | |
Enable Interrupts | |
Enable Adc | |
Enable Timer1 | |
Dim Odczyt As Word | |
Dim Temp As Single | |
Dim Temp_str As String * 3 | |
Dim Timer_tick As Bit | |
' Dajemy czas wyświetlaczowi na inicjalizację | |
Waitms 500 | |
Timer_tick = 0 | |
Printbin Clean | |
Printbin Coff | |
Do | |
If Timer_tick = 1 Then | |
Printbin Clean | |
Temp = Odczyt - T_offset | |
Temp = Temp * K | |
Temp_str = Fusing(temp , "#.&") | |
Replacechars Temp_str , "." , "," | |
Print Temp_str ; "`C" | |
Timer_tick = 0 | |
End If | |
Loop | |
End | |
On_timer_tick: | |
Odczyt = Getadc(8) | |
If Odczyt >= Temp_25 Then | |
Odczyt = Odczyt - Temp_25 | |
Odczyt = Odczyt + 25 | |
Elseif Odczyt <= Temp_0 Then | |
Odczyt = Odczyt - Temp_m45 | |
Odczyt = Odczyt - 45 | |
Else | |
Odczyt = Odczyt - Temp_0 | |
End If | |
Timer_tick = 1 | |
Return |
Drugi sposób, o wiele ciekawszy, polega na wykorzystaniu zewnętrznego, scalonego czujnika temperatury. Na rynku znajdziemy wiele tego typu układów - najciekawsze z nich to układy, z którymi mikrokontrolery komunikują się za pomocą magistral I2C czy 1Wire, jednak w moim termometrze zastosowałem układ scalony analogowy LM35DZ. Ta wersja pozwala na pomiar temperatur w zakresie od 0 do 100 st. C, czyli nadaje się doskonale do zastosowań typu indoor. Wartość napięcia, na którą układ konwertuje temperaturę otoczenia (właściwie jest to temperatura jego obudowy), przetwarzana jest przez przetwornik analogowo-cyfrowy mikrokontrolera i prezentowana przez szesnaście diod LED, przy czym dla zbyt niskich i zbyt wysokich temperatur przewidziana jest specjalna sygnalizacja - osobne, pulsujące LED-y (PWM). Dodatkowo wyniki pomiarów można śledzić na opcjonalnym LCD.
Diody LED są sterowane za pomocą dwóch ekspanderów magistrali I2C (PCF8574P), dzięki czemu obsługa wyświetlania wyników realizowana jest przez dwa wyprowadzenia mikrokontrolera (stały problem: jak rozszerzyć liczbę wejść/wyjść mikrokontrolerów).
Oto schemat układu:
i listing programu:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
' Prosty termometr wykorzystujący układ LM35DZ | |
' (analogowy czujnik temperatury 0 - 100 st. C) | |
' Założenie: 16 diod pokazuje 48 + 3 st. C (jedna dioda co 3 stopnie) | |
' [ dodatkowo odczyt kontrolny na wyświetlaczu LCD (moduł wyświetlacza sterowany przez UART) ] | |
$regfile = "m48pdef.dat" | |
$crystal = 4000000 | |
$hwstack = 40 | |
$swstack = 16 | |
$framesize = 32 | |
' Wsparcie dla sprzętowej implementacji I2C | |
$lib "i2c_twi.lbx" | |
' Plik z definicjami stałych używanych do sterowania modułem | |
' wyświetlacza szeregowego. | |
$include "sdrv-const.bas" | |
' Napięcie referencyjne dla ADC (miliwolty) | |
Const Vref = 5000 | |
' Współczynnik przeliczenia wartości odczytanej z przetwornika | |
' (z konwersją na stopnie Celsjusza) | |
Const Wspolczynnik = Vref / 10240 | |
' Adresy do zapisu dwóch ekspanderów I2C | |
Const Addr_0 = &B01110000 | |
Const Addr_1 = &B01111000 | |
' Konfiguracja pinów magistrali I2C | |
Config Scl = Portc.5 | |
Config Sda = Portc.4 | |
Config Adc = Single , Prescaler = Auto , Reference = Avcc | |
' Timer1 generuje przerwanie co ok. 1 sekundę | |
Config Timer1 = Timer , Prescale = 64 | |
On Timer1 On_timer_tick | |
' Timer0 generuje miganie diod ostrzegawczych (czerwona - zbyt wysoka temperatura, | |
' niebieska - za niska temperatura). | |
' Odrobina sztuki dla sztuki... ;) | |
Config Timer0 = Pwm , Prescale = 1024 , Compare A Pwm = Clear Down , Compare B Pwm = Clear Down | |
Enable Interrupts | |
Enable Adc | |
Enable Timer1 | |
Enable Timer0 | |
Dim Odczyt As Word | |
Dim Temp As Single | |
Dim Temp_select As Integer | |
Dim Timer_tick As Bit | |
Dim Temp_str As String * 3 | |
Pwm0a = 255 | |
Pwm0b = 255 | |
' Czas potrzebny na inicjalizację modułu wyświetlacza szeregowego | |
Waitms 500 | |
Printbin Clean | |
Printbin Coff | |
Do | |
If Timer_tick = 1 Then | |
Stop Timer0 | |
Printbin Clean | |
Temp = Odczyt * Wspolczynnik | |
Temp_str = Fusing(temp , "#.&") | |
Replacechars Temp_str , "." , "," | |
Print Temp_str ; "'C" | |
Temp = Round(temp) | |
Temp_select = Int(temp) | |
Select Case Temp_select | |
Case 0 To 3 : | |
' Efekt specjalny 1: | |
' - przy niskiej temperaturze pierwsza dioda miga | |
Pwm0a = Pwm0a - 60 | |
Pwm0b = 255 | |
Start Timer0 | |
' LED #1 | |
Case 4 To 6 : | |
I2csend Addr_0 , &B01111111 | |
I2csend Addr_1 , &B11111111 | |
' LED #2 | |
Case 7 To 9 : | |
I2csend Addr_0 , &B00111111 | |
I2csend Addr_1 , &B11111111 | |
' LED #3 | |
Case 10 To 12 : | |
I2csend Addr_0 , &B00011111 | |
I2csend Addr_1 , &B11111111 | |
' LED #4 | |
Case 13 To 15 : | |
I2csend Addr_0 , &B00001111 | |
I2csend Addr_1 , &B11111111 | |
' LED #5 | |
Case 16 To 18 : | |
I2csend Addr_0 , &B00000111 | |
I2csend Addr_1 , &B11111111 | |
' LED #6 | |
Case 19 To 21 : | |
I2csend Addr_0 , &B00000011 | |
I2csend Addr_1 , &B11111111 | |
' LED #7 | |
Case 22 To 24 : | |
I2csend Addr_0 , &B00000001 | |
I2csend Addr_1 , &B11111111 | |
' LED #8 | |
Case 25 To 27 | |
I2csend Addr_0 , &B00000000 | |
I2csend Addr_1 , &B11111111 | |
' LED #9 | |
Case 28 To 30 | |
I2csend Addr_0 , &B00000000 | |
I2csend Addr_1 , &B01111111 | |
' LED #10 | |
Case 31 To 33 : | |
I2csend Addr_0 , &B00000000 | |
I2csend Addr_1 , &B00111111 | |
' LED #11 | |
Case 34 To 36 : | |
I2csend Addr_0 , &B00000000 | |
I2csend Addr_1 , &B00011111 | |
' LED #12 | |
Case 37 To 39 : | |
I2csend Addr_0 , &B00000000 | |
I2csend Addr_1 , &B00001111 | |
' LED #13 | |
Case 40 To 42 : | |
I2csend Addr_0 , &B00000000 | |
I2csend Addr_1 , &B00000111 | |
' LED #14 | |
Case 43 To 45 : | |
I2csend Addr_0 , &B00000000 | |
I2csend Addr_1 , &B00000011 | |
' LED #15 | |
Case 46 To 48 : | |
I2csend Addr_0 , &B00000000 | |
I2csend Addr_1 , &B00000001 | |
' LED #16 | |
Case 49 To 51 : | |
I2csend Addr_0 , &B00000000 | |
I2csend Addr_1 , &B00000000 | |
Case Else: | |
' Efekt specjalny 2: | |
' - przy zbyt wysokiej temperaturze na przemian świecą wszystkie diody i dwie skrajne | |
Pwm0a = 255 | |
Pwm0b = Pwm0b - 60 | |
Start Timer0 | |
End Select | |
Timer_tick = 0 | |
End If | |
Loop | |
End | |
On_timer_tick: | |
Odczyt = Getadc(0) | |
Timer_tick = 1 | |
Return | |
- C1, C2: 22pF - 33pF
- C3, C4, C5: 100nF
- C6: 10μF
- L1: 10μH
- R1 - R18: 220Ω
- R19: 10kΩ
- R20, R21: 330Ω
- R22, R23: 4,7kΩ
- LED1 - LED18: zestaw dowolnych diod elektroluminescencyjnych
Przedstawione rozwiązanie z wielu względów nie jest optymalne - jakoś szczególnie go nie dopracowywałem - weźmy choćby pobór prądu przez szesnaście święcących diod...
Jednak efekt ciekawy, szczególnie, gdy dobierzemy różnokolorowe diody dla poszczególnych przedziałów temperatur.
Komentarze
Prześlij komentarz