Uzyskanie odczytu z pojedynczego/wielu
Transkrypt
Uzyskanie odczytu z pojedynczego/wielu
Uzyskanie odczytu z pojedynczego/wielu termometrów 1Wire DS18b20/ 1820
1.
Przykładowa komunikacja z pojedynczym urządzeniem DS1820/18b20 slave ze strony układu
master na magistrali 1-Wire przebiega wg schematu :
1.
2.
3.
4.
5.
6.
7.
Send: Reset pulse, Rec: Presence Pulse
Send: Skip ROM (0xCC)
Send: CONVERT_T (0x44)
Wait (750ms, dla 12bitów – tylko DS18b20)
Send: Reset pulse, Rec: Presence Pulse
Send: Skip ROM (0xCC)
Send: READ_SCRATCHPAD (0xBE)
Zostaje przeczytane kolejne 9 bajtów SCRATCHPADA. Nie trzeba słuchad wszystkich, można zakooczyd
komunikację po dwóch pierwszych, w których mieszczą się dane o temperaturze. Najpierw zostaje
przeczytany mniej znaczący bajt, następnie bardziej znaczący.
Czujnik DS1820 poza różnicami technicznymi, ma ustaloną 9 bitową rozdzielczośd, gdzie 8
bitów temperatury jest przechowywane w bajcie mniej znaczącym, a 1 bit znaku w bajcie bardziej
znaczącym. Podczas, gdy w DS18b20, 3 najbardziej znaczące bity temperatury są przechowywane w
bajcie bardziej znaczącym, a pozostałe 8 włączając częśd przecinkową w bajcie mniej znaczącym.
DS18b20 można programowad zmieniając rozdzielczośd pomiaru, przy czym zmniejszanie
rozdzielczości skraca czas konwersji, do skrajnych 9 bitów w czasie 100ms. DS1820 potrzebował na to
aż 500ms.
Jeśli kolejne zwrócone podczas odczytywania przez układ scratchpada bajty, umieścimy w
tablicy, to załadowanie ich do zmiennej co najmniej 16 bitowej załatwi sprawę. Czasem łatwiej częśd
całkowitą załadowad do int i częśd przecinkową do char, co ułatwia późniejsze formatowanie. Można
też od razu dokonad konwersji do float. Należy jednak pamiętad, że późniejsze operacje na float są
znacznie bardziej wymagające obliczeniowo, a nie zawsze częśd przecinkowa jest potrzebna.
2.
Komunikacja z kilkoma urządzeniami na magistrali 1-Wire opiera się na użyciu komendy
SearchROM, zaindeksowaniu wszystkich adresów 64 bitowych, a następnie wywoływaniu
poszczególnych układów komendą MatchROM według algorytmu.
1. Send: Reset pulse, Rec: Presence Pulse
2. Send: Search ROM (0xF0), w celu zdobycia adresów 64 bitowych urządzeo na magistrali
Sekwencja Reset → Presence → Search ROM jest powtarzana N razy, gdzie N stanowi liczbę
urządzeo. Dalej zostało to opisane w detalach. Następnie po uzyskaniu kodów ROM, N urządzeo, aby
komunikowad się z każdym z N urządzeo stosujemy algorytm.
1. Send: Reset pulse, Rec: Presence Pulse
2. Send: Match ROM (0x55)
3. Send: 64 bitowy kod, od bitów najmniej znaczących, do najbardziej znaczących
Po tej sekwencji wszystkie urządzenia poza wybranym, przechodzą w stan uśpienia do kolejnego
impulsu RESET. Z wybranym w ten sposób układem można się porozumiewad wysyłając wybrane
komendy. Na przykład:
1.
2.
3.
4.
5.
3.
Send: CONVERT_T (0x44)
Wait (750ms, dla 12bitów – tylko DS18b20)
Send: Reset pulse, Rec: Presence Pulse
Send: Match ROM (0x55) → 64 bitowy kod
Send: READ_SCRATCHPAD (0xBE)
Algorytm Search ROM
Kod:
///////////////////////////////////////////////////////
//Szuka urzadzen 1Wire/////////////////////////////////
// char buffer[N], gdzie N to liczba urządzeń,
// zainicjowany jest z zawartością {0,0,0...,0}
// tablicaRekordow[N][8] przechowuje ID układu;
///////////////////////////////////////////////////////
unsigned char searchROM1Wire(unsigned char tablicaRekordow[][8],char N) {
unsigned char bit,bit_complementary,pozycja,buffer[N];
for(unsigned char i=0;i<N;i++) {
buffer[i]=0;
}
for(unsigned char i=0;i<N;i++) {
pozycja=0;
if(~reset1Wire()&1) return 0;
sendByte1Wire(SEARCH_ROM);
for(unsigned char bitNo=0;bitNo<64;bitNo++) {
bit=readBit();
bit_complementary=readBit();
if ((~(bit|bit_complementary))&1) {
if(buffer[pozycja+1]==0) {
buffer[pozycja]++;
}
writeBit((buffer[pozycja]-1));
tablicaRekordow[i][bitNo/8]|=((buffer[pozycja]-1))<<(bitNo%8);
pozycja++;
} else {
writeBit(bit);
tablicaRekordow[i][bitNo/8]|=bit<<(bitNo%8);
}
}
while(buffer[pozycja-1]==2) {
buffer[pozycja]=0;
pozycja--;
}
}
return 1;
}
//////////////////////////////////////////////////////
Wyjaśnię o co chodzi w tym algorytmie. Zasadniczo, uzyskiwanie 64 bitowych kodów urządzeń 1Wire opiera się na przeszukiwaniu drzewa.
Załóżmy, że wykonamy obrys naszego drzewa urządzeń w/g rysunku powyżej. Wchodząc z jednej
strony i podążając wzdłuż czerwonej linii na pewno obejdziemy całe drzewo, wiedząc, że obrys jest
ciągły.
Obejście drzewa sukcesywnie składa się ze zmiany terminalnych bitów skrzyżowań i sprawdzanie,
czy po zmianie nie pojawiły się kolejne. Wektor bitów skrzyżowań przechowywany jest w
"buffer[N]". Wiemy, że istnieje N urządzeń, a więc N-1 skrzyżowań. N (a nie N-1) komórek jest
jednak potrzebne do poprawnego działania algorytmu. buffer[N] można zoptymalizować!
///// OPTYMALIZACJA
Tutaj składa się z N bajtów (N*256 stanów), a wymaga jedynie 3 stanów (N*3 stanów).
0 - nie użyty [0,0]
1 - stan 0 [1,0]
2 - stan 1 [1,1]
Najprościej zasocjować dwa wektory (np tablice char) : stan[int(N/8)+1], przechowujący bieżący
wybór bitów rozgałęzień i zajetosc[int(N/8)+1], przechowujący zajętość powyższych bitów.
Analogicznie [0,1] jest stanem zabronionym, oznaczałby, że istnieje bit 1 rozgałęzienia, ale nie ma
rozgałęzienia. Istnienie wektora zajetosc[int(N/8)+1] pozwala na odróżnienie "nie użytego" od
"stanu 0" jest kluczowe, żeby zorientować się w pozycji względem końca wektora.
/////
Obejście drzewa wiąże się ze zmianą jedynie ostatnich bitów. Należy "odwiedzić" sukcesywnie 0,
następnie 1 w każdym z rozgałęzień, rozpoczynając od terminalnych. Zmiana stanu proksymalnych
bitów jest możliwa dopiero po przeskanowaniu terminalnych!
Kod:
if(buffer[pozycja+1]==0) {
buffer[pozycja]++;
}
Jeśli kolejna pozycja w buforze jest wolna(0), to bieżący stan zwiększa się o 1.
Kod:
0 ( "stan:0","zajętość:0") - bit rozumiany jako nie użyty
|
V
1 ( "stan:0","zajętość:1") - wystawia stan 0 na magistralę
|
V
2 ( "stan:1","zajętość:1") - wystawia stan 1 na magistralę
Dlatego dla każdego skrzyżowania pojawi się najpierw stan 0, następnie 1. Po każdej zmianie
stanu, zachodzi kontynuacja zapisywania bitów i szukania skrzyżowań.
Przykład:
Kod:
s - wektor stanu
z - wektor zajętości
Zaczynam skanowanie
1. s:0,z:1; s:0,z:1; s:0,z:0; <- 2 skrzyżowania
2. s:0,z:1; s:1,z:1; s:0,z:1; s:0,z:0; <- po zmianie terminalnego bitu na 1
pojawiło się nowe skrzyżowanie.
3. s:0,z:1; s:1,z:1; s:1,z:1; s:0,z:0; <- zmiana terminalnego bitu na 1
4. wyczyszczenie obu końcowych stanów s:1,z:1; na s:0,z:0;
5. s:1,z:1; s:0,z:0; <- zmiana nowego końcowego bitu na 1.
6. wyczyszczenie terminalnego s:1,z:1;
7. s:0,z:0; <- całe drzewo przeskanowane
Rezultat :
4 urządzenia (N), 3 skrzyżowania(N-1)
Jeżeli ostatni bit w wektorze stanów, po przejrzeniu całej sekwencji 64 bitów, wynosi 2(s:1,z:1),
oznacza to, że obie gałęzie "0" i "1" zostały przeskanowane i ten bit można zamienić na stan:0 i
zajętość:0. Tę część realizuje :
Kod:
while(buffer[pozycja-1]==2) {
buffer[pozycja]=0;
pozycja--;
}
Sprawdza też, czy położony proksymalnie (bliżej początku) bit nie jest równy 1. Jeśli tak, oznacza
to, że ta część drzewa również została już przeskanowana.
Mam nadzieję, że wyjaśniłem trochę w konstrukcji tego algorytmu i zaproponowałem jego lekką i
uniwersalną wersję w C. Po więcej lektury zapraszam w kilka miejsc :
1. Bardzo przystępny opis 1-Wire ze strony układu master i jego realizacja sprzętowa na
ATMEGA : http://www.atmel.com/dyn/resources/prod_documents/doc2579.pdf
2. Opis algorytmu Search ROM na stronie twórcy 1Wire (Dallas Maxim) : http://www.maximic.com/app-notes/index.mvp/id/187