1-Wire na AVR i nieco inne podejście niż

Witam!

Musze używać magistrali 1-Wire na AVR. Mam jednak następujące założenia:

1) Procedury 1-Wire nie mogą być aktywnie czekające w głównej pętli, bo ta pętla ma co robić i robi to w dodatku niestabilnie czasowo. 2) W systemie jest już sporo przerwań (UART,Timer,Ext) które są ważniejsze od 1-Wire (na którym jest mało ważny miernik temperatury, ale być musi). 3) 1-Wire jest nie dość, że strasznie czuły na czasy to w dodatku sa to czasy krótkie (15us to krócej niż 256 cykli zegara 14MHz - konkretnie 17us - więc timery się komplikują). 4) Wszystkie biblioteki jake znalazłem w necie sa aktywnie czekające w głównej pętli. 5) Stosowanie dodatkowych scalaków jest chwilowo bez sensu, nawet jeśli będa kosztowac 3zł/sztuka (ATTiny/etc).

No i co teraz ;) ?

Wykoncypowałem tak:

Wykorzystam jakiś timer, który jest skrócony (np. pi razy oko 5us) - TIMER2 ma taką funkcję w ATMega8. W przerwaniu zaimplementuje cały proces slotów Wite1/Read1 albo Write0/Read0 czy Reset. Główny program bedzie zlecał przerwaniom wykonanie tego pojedynczego elementu. Reszta algorytmu (wybieranie poszczególnych bitów) zrealizuje już z głownej pętli.

Najwazniejsze elementy do przeskoczenia to chyba stałośc czasów dla

1-Wire jednocześnie bez mocnego blokowania pozostałych przerwań. (przerwanie 1-Wire było by jedynym nieprzerywalnym, reszta może byc przerwana).

No i teraz pytanie:

a) Czy istnieje taka biblioteka już napisana - nie mam po co robić, jesli jest gotowe. b) Czy mogę pomiędzy poszczególnymi slotami dawać dowolnie długie przerwy (nie mam zasilania z lini danych dla miernika temp, normalnie przez Vcc) ? c) Czy mam słuszną koncepcję ;) ?

Reply to
Sebastian Bialy
Loading thread data ...

Ale 1-Drut nie musi czekać. Przecież to procesor decyduje kiedy odczytuje dane. Czujniki nie mają na to wpływu.

Temperatury nie ma sensu czytać zbyt często. Może co jakiś długi czas możesz wyłączyć pozostałe przerwania? Jeśli nie możesz to żaden problem. Odczyt temperatury zostanie przerwany, wykona się następnym razem.

No jeśli przerwanie timera który musi działać jest częstrze niż czas potrzebny na odczytanie temperatury to pora zmienić projekt układu.

Tyle, że to nie ma sensu.

Tylko czy jest sens czytać temperaturę co sekundę naprzykład?

Nie sprawdzałem, ale co pisze Dallas w swoich dokumentach?

Koncepcji słusznych może być dużo. Jeśli zadziała to jest słuszna :)

Reply to
Slawomir Sidor

Obawiam się, że to magistrala asynchroniczna i w przypadku odczytu bitów musze to zrobić w szczelinie czasowej 15us co oznacza koło 200 cykli zegarowych. Z drugiej strony użycie delay(200); i kręcenie się bez sensu w pętli jest głupotą bo bym musiał wyłączyć przerwania na ten czas (a wolałbym nie).

Nie mogę. Jedno z przerwań jest periodyczne i musi być obsługiwane z częstotliwością większą niż pełny cykl odczytu DS1820.

Niestety w tym przypadku zostanie przerwany za każdym razem. A takie przerwanie spowoduje zakłucenie "czasowosci" magistrali 1-Wire i utratę bitu.

Nie rozumiesz - 15us to minimalna jednostka czsowa 1-Wire - w przypadku ektremalnym musze własnie w tym czasie odczytać bit z magistrali inaczej go stracę. MUSZĘ. To jest priorytet. Ale na szczęscie dotyczy to odczytu pojedynczego bitu. Dlatego za marnotrawstwo uznaje zabieranie głównej pętli progamu na odczyt 64 bitów. Wolałbym żeby krytyczna czasowo operacja odczytu/zapisu bitu była na przerwaniach. Reszta już lajtowo w głównej pętli na zasadzie procedury onewire_next_step() która może to robić bit po bicie przez godzinę. Nie zależy mi na szybkości.

Te biblioteki nie mają sensu ? Czy mój pomysł nie ma sensu ?

Nie - jak napisałem wyżej mogę co godzinę. Nie mogę jednak zablokowac głównej pętli na czas odczytu oraz nie mogę zablokowac przerwań na czas odczytu. Chciałbym jednak zablokować przerwania na czas odczytu _pojedynczego_ bitu (też nie do końca, teoretycznie inne przerwania mogły by być przerywalne przez timer 1-Wire a on sam już nie - zapewnia mi to namiastkę priorytetów) a resztę logiki zrobić na zasadzie maszynki stanów z głównej pętli.

Wydaje mi sie, że tak, jednak w prost nie znalazłem stwierdzenia. Gdyby się nie dało (napisane by było w prost) w ogóle bym ten pomysł porzucił.

Reply to
Sebastian Bialy

Użytkownik Sebastian Bialy napisał:

Witam Istotny jest tylko czas trwania stanu niskiego na magistrali 1-Wire. Stan wysoki w przerwie miedzy slotami nie jest krytyczny (a z doświadczenia - może trwac dowolnie długo). Robię to w ten sposób, że mam procedury wysyłające bity 0, 1, reset i one blokują przerwania. Pętla wysyłająca poszczególne bity w bajcie może juz być spokojnie przerywana (zwiększy się tylko całkowity czas przesłania bajtu). Zabawa z timerami nie przyniosła mi spodziewanego rezultatu ze względu na brak wielopoziomowych przerwań w AVR-ach. Jeśli zaraz przed sygnałem przerwania z timera dostaniesz inne przerwanie, którego obsługa będzie dość długa to i tak procek nie obsłuży przerwania timera dopóki nie zakończy bieżącej obsługi co zaowocuje "odmierzeniem" znacznie dłuższego czasu niż wynikałoby to z ustawień timera.

Pozdrawiam Grzegorz

Reply to
Grzegorz Kurczyk

Owszem, ale w AVR (chyba ;) można mieć przerwanie przerywalne i nie przerywalne (SIGNAL albo INTERRUPT w gcc).

Konkretnie: Jesli przerwanie zrobi CLI na początku, to nikt go nie przerwie. Jeśli nie zrobi - to może być przerwane przez inne.

Mozna by wszystkie przerwania mieć bez CLI za wyjątkiem tego od timera

1-Wire. W ten sposób on bedzie zawsze obsłużony, ale jego już nikt nie przerwie.
Reply to
Sebastian Bialy

Odwrotnie, przerwanie blokuje inne przerwania, o ile procedura obslugi jawnie ich nie odblokuje.

No to juz wiemy, ze odwrotnie:) Ale nie wazne - po pierwsze zdecyduj sie co chcesz miec - mastera czy slave na AVR? Jesli mastera to sprawa nie jest trudna, zasadnioczo nawet b. latwa, szczegolnie jesli masz do dyspozycji jakis hardwarowy UART, lub chociazby timer (wyjscie timera mozesz podpiac do pinu IO, a timer wykorzystywac do generacji impulsu ujemnego o zaprogramowanym czasie trwania). Jesli chcesz oprogramowac slave - to jest to nieco trudniejsze, ale mozliwe, tu moge sie z toba podzielic kodem, ktory wlasnie skonczylem. Zla wiadomosc jest taka, ze w przypadku slave masz max 15us na wystawienie odpowiedniego bitu, a w praktyce mniej (pewne egzemplasze DS18B20 chcialy, zeby poprawny bit byl max. po 8us i nie dalo im sie wytlumaczyc inaczej). Jednakze w kazdym przypadku jesli inne procedury obslugi przerwan nie sa krytyczne czasowo i moga byc przerywane to problem jest raczej prosty, w przeciwnym wypadku jego rozwiazanie graniczy z niemoznoscia:)

Reply to
T.M.F.

Użytkownik Sebastian Bialy napisał:

Tzn. chyba odwrotnie, aby przerwanie mogło zostać przerwane to trzeba na początku dać sei(). Bo AVR standardowo po przyjeciu przerwania zeruje flagę przerwań i odtwarza ją po rozkazie reti.

Pozdrawiam Grzegorz

Reply to
Grzegorz Kurczyk

Ok, pomyłka, ale idea taka sama.

Założenie jest takie, ze nie ma UART'a (walczy na RS485), mam timer, ale wolałbym sterować tym pinem jednak programowo. I oczywiście mam mastera.

Nie twierdze, że to trudne, widze sam, że trywialne, jednak zastanawia mnie sens pisania tego od zera, bo może jest akurat gotowiec na sieci. jesli nie ma, to zabieram się za robotę.

Dzięki, ale Slave nie jest mi potrzebny do czegokolwiek.

Nie są aż tak krytyczne, chodzi mi tylko o to, że wszystkie gotowce to oczekiwanie w pustej pętli z wyłaczonymi przerwaniami co jest głupotą i mi przeszkadza. Dlatego kombinuje inaczej.

Reply to
Sebastian Bialy

Ok, moja pomyłka, racja oczywiście.

Reply to
Sebastian Bialy

To moze procek z 2 UARTami?

No tak, ale zauwaz, ze jesli masz jakas krytyczna procedure, ktora blokuje przerwania, i jesli dlugosc zablokowanej sekcji jest porownywalna do owych 15us (lacznie z czasem wejscia do twojej procedury obslugi 1-wire) to masz problem. W losowych momentach bedziesz gubil bity, cholernie trudne do diagnostyki, kiedys cos zmienisz w programie i nagle zacznie sie zachowywac magicznie. Oczywiscie sam czas mozesz liczyc za pomoca timera, czyli wykonujesz skok do procedury np. Read-1-Wire-Bit, ona odczytuje licznik timera, odblokowywuje przerwania i czeka sobie az licznik przekroczy owe 15us, nastepnie odczytuje stan lini IO i konczy dzialanie. Dzieki temu procedura moze byc przerywana, ale przez procedury odslugi przerwan, ktore szybko koncza swoja dzialalnosc. Inne podejscie - w procedurze programujesz timer, tak, zeby po okreslonym czasie zglosil przerwanie, w proc. obslugi przerwania zczytujesz stan pinu IO. Tylko tu znowu problem sekcji krytycznych - jesli sa i do tego dluzsze niz pojedyncze mikrosekundy to lezysz. Jeszcze inne rozwiazanie, ale wrazliwe na zaklocenia - robisz timer taktowany z pinu IO. Dajesz readslot, nastepnie czekasz dowolny czas i kiedy ci wygodnie sprawdzasz stan timera. Jesli nic nie zliczyl to znaczy, ze nie bylo zadnego impulsu ujemnego (urzadzenie wyslalo 1), jesli zmienil stan to urzadzenie w miedzyczasie wyslalo 0. Ta koncepcja mi sie najbardziej podoba:)

Reply to
T.M.F.

Odpada, w prototypie będzie DIP a DIPy sa ogromne, jak mają 2xUART (chyba ze jest coś w gabarytach ATMega8 DIP ?)

Pozostałe przerwania mogą być przerywalne i same nie są bardzo krytyczne czasowo za wyjątkiem sytuacji wyłączenia przerwań na całą operację odczytu rejestrów DS1820. To już by było przegięcie i zaczęły by się problemy poważne.

Nie ma sekcji krytycznych (prawie nie ma, mają po pare instrukcji, konkretnie aktualizacja licznika 32 bit).

Nie jest to zła orange, tfu, idea i zastanowie się nad tym jeszcze, choć dalej pozostaje problem zrobienia odpowieniej długości bitu '0' przez mastera. Ale zastanwie się i dziękuje za pomysł.

Reply to
Sebastian Bialy

Nie znam, ale dlaczego uparles sie na DILe?

No to nie ma problemu. Pomysl z programowym generowaniem impulsu niskiego przez timer wydaje sie byc najlepszy.

To co napisalem dobre jest przy odczycie, do zapisu z kolei wykorzystaj mozliwosc generacji impulsu o zadanym czasie trwania przez timer. Dzieki temu programujesz timer i na tym rola procedury sie konczy. W obsludze przerwania timera zmienias zpoziom ponownie na wysoki i juz. Zeby calosc zrobic juz w ogole bajerancko to moze jakis maly bufor FIFO i w procedurze obslugi przerwania sprawdzac czy jest cos do wyslania i ew. wyslac.

Reply to
T.M.F.

Prototyp przechodzi ciągłą ewolucję i stąd montaż przewlekany jest zbawienny.

Dokładnie tak zamierzam, choć do tej pory kombinowałem nico głebiej: w procedurze timera zaimplementować prostą maszynkę stanów, która krok po kroku wykona wszystkie czynności związane ze slotem i odczyta bit.

Fifo zbedne, jak powiedziałem procedura w głównym programie może być wywoływana ręcznie, taki sposób programowania jest "w moim stylu" i raczej zrobie to tak właśnie.

Reply to
Sebastian Bialy

Owszem, ale wszystkie gotowce używają aktywnego czekania w przypadku sygnału RESET który sam z siebie trwa już dość długo. Stąd napisanie własnego zestawu procedur mnie nie minie, a skoro tak, to może lepiej nie czekać aktywnie nawet na pojedyncze bity. W końcu narzut programistyczny duży nie bedzie.

Reply to
Sebastian Bialy

Użytkownik Sebastian Bialy napisał:

Ale dlaczego chcesz blokowac przerwania na czas przesyłania/odczytu całych bajtów z DS-a ? Jak wspominałem wystarczy zablokować przerwania na czas "odmierzania" jednego bitu. W momencie gdy na magistrali wraca stan wysoki możesz już obsłużyć co innego.

Pozdrawiam Grzegorz

Reply to
Grzegorz Kurczyk

Sebastian Bialy napisał(a):

po co w ogóle czekać na odpowiedź resetu? jeśli układ nie odpowie i tak nic nie poradzisz, a równie dobrze możesz polegać na sumie CRC odczytanych danych.

w.

Reply to
Wojtek Kaniewski

Sebastian Bialy napisał(a):

Ja to kiedyś na picu zrobiłem swego rodzaju interpreter. Chodziło toto w przerwaniu od timera, odczytywało rozkaz ze stosu, daną i wykonywaną. Zresztą sam zerknij. Proszę nie krytykować... to stare jest już :) i powycinane są rzeczy zbędne. Aha można korzystać do woli, jak się komuś przyda.

Pozdrawiam Krzysztof Szmurło

#define TRUE 1 #define FALSE 0 #define W1_RESET 1 #define W1_READ 2 #define W1_WRITE 3 #define W1_DELAY 4 #define W1_END 5 #define W1_DONE 6

bank1 unsigned char W1_Buffer[4]; const unsigned char stosProgram[20] = {W1_RESET, // wysyła sygnał resetu W1_WRITE,0xCC, // rozkaz SkipRom W1_WRITE,0x44, W1_DELAY,75, W1_RESET, W1_WRITE,0xCC, W1_WRITE,0xBE, W1_READ,0, W1_READ,1, W1_DONE, W1_DELAY,25, W1_END}; const unsigned char * ptrProgram = stosProgram; bank1 unsigned char DeviceConfigured = 0; bank1 bit jesttemp = FALSE; bank1 bit zezwolenie = FALSE; bank1 bit suspended = FALSE;

void main (void) { while(1); }

//********************************************************************************* void interrupt isr(void) { static bank1 unsigned char T1Counter; static bit DelayProgress = FALSE;

// obsluga przerwania od T1 if (TMR1IF) {

// obsluga urzadzen 1-wire - procedura interpretera switch (*ptrProgram++) { // odczytywany jest rozkaz i zwiększany licznik programu case W1_RESET: // około 1ms D_Reset(); break; case W1_READ: // około 0,6ms W1_Buffer[*ptrProgram]= D_Read(); ptrProgram++; break; case W1_WRITE: // około 0,6ms D_Write(*ptrProgram); ptrProgram++; break; case W1_DELAY: // pare us // ustawiany jest znacznik DelayProgress // licznik programu ptrProgram nie zostaje zwiększony // do momentu kiedy licznik opóźnienia T1Counter się nie wyzeruje // da to w przybliżeniu opóźnienie równe 10ms*T1Counter if (!DelayProgress) { T1Counter = *ptrProgram; DelayProgress = TRUE; } if (!T1Counter--) { ptrProgram++; DelayProgress = FALSE; } else ptrProgram--; break; case W1_END: ptrProgram = stosProgram; break; case W1_DONE: jesttemp = TRUE; default: break; } // koniec obslugi 1-wire TMR1H = 231; // przerwanie co 10ms TMR1L = 15; // freqcnt3 = 0; // zerujemy do następnego pomiaru TRISA5 = 1; // odblokowujemy tmr0 TMR1IE = 1; TMR1IF = 0; // odblokowanie przerwan } }

Reply to
Krzysztof Szmurło

Użytkownik Sebastian Bialy napisał:

Możnaby zrobić drobną hybrydę :) Czas impulsu RESET nie jest krytyczny byleby więcej niż 480us. Możnaby tylko jego generować timerem. Jeśli w międzyczasie wlezie jakieś inne przerwanie, to impuls się conajwyżej przedłuży.

Wysyłanie bajtu zrobiłem w taki sposób (gotowce też mi się nie podobały i naskrobałem to po swojemu)

#define tREC 1 #define tLOW0 70 #define tLOW1 10

// generowanie impulsu tREC void onewire_trec(int us) { // ustaw magistralę na wyjście i ustaw stan wysoki sbi(onewire_port, onewire_bit); sbi(onewire_ddr, onewire_bit); // tREC; udelay(tREC); // stan aktywny master cbi(onewire_port, onewire_bit); // odczekaj zadany czas udelay(us); }

// wysłanie bitu void onewire_write_bit(char bit) { push_sfr(SREG); cli(); if (bit) onewire_trec(tLOW1); // generuj impuls "1" else onewire_trec(tLOW0); // generuj impuls "0" // ustaw magistralę na STRONG PULLUP sbi(onewire_port, onewire_bit); // wyrównaj czas szczeliny dla impulsu "1" if (bit) udelay(tLOW0 - tLOW1); pop_sfr(SREG); }

// wysłanie bajtu void onewire_write(char bajt) { char i; for (i = 1; i; i <<= 1) onewire_write_bit(bajt & i); }

Pozdrawiam Grzegorz

Reply to
Grzegorz Kurczyk

Akurat dokładnie tak samo pisze sobie maszynkę stanów w przerwaniu w tej chwili ;) Też ze stosem ;)

Reply to
Sebastian Bialy

No to życzę miłego programowania :)

EOT Pozdrawiam Krzysztof Szmurło

Reply to
Krzysztof Szmurło

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.