logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

At90can128 - jak dostosować czasy czujnika po zmianie zegara na 16MHz?

9xxx 17 Mar 2012 21:41 2435 19
  • #1 10689697
    9xxx
    Poziom 10  
    Witam,

    Mam (zapewne głupie) pytanie i będę wdzięczny za pomoc. A więc mam napisany program do obsługi czujnika temperatury (odczyt wyświetlany jest na LCD). Program został napisany na procesor taktowany wewnętrznym zegarem, ale musiałem dodać zewnętrzny oscylator i nagle przestało mi wyświetlać dane. Wyświetlacz działa poprawnie, ale czujnik temperatury już nie. Może mi ktoś napisać jak mam zmienić czasy w obsłudze czujnika jeśli wcześniej procesor był taktowany 1Mhz a obecnie jest 16Mhz? Czy wystarczy podzielić poprzednie czasy przez 16 czy należy zastosować jakieś inne rozwiązanie (Prescaller?)?
  • #2 10690122
    tmf
    VIP Zasłużony dla elektroda
    Jak zastosujesz preskaler to będzie to tak, jakbyś wcale nie zmienił zegara. Oczywiście możesz czasy po prostu podzielić przez 16 - jeśli są one generowane przez pętle opóźniające. Jeśli korzystasz z funkcji opóźniających zdefiniowanych w delay.h to wystarczy zdefiniować poprawnie symbol F_CPU, tak, aby zawierał nową częstotliwość taktowania i przekompilować program.
  • #3 10690795
    Ostry23
    Poziom 18  
    tmf napisał:
    Oczywiście możesz czasy po prostu podzielić przez 16

    A nie pomnożyć? Tzn. wiadomo, że czasy trzeba zapewnić takie same, a więc pomnożyć ewentualne stałe na bazie których odmierza się opóźnienie. Albo na przykład pomnożyć wartości dla sprzętowych liczników, jeśli na bazie ich odmierzasz sobie czas.
    Jeśli korzystasz z delay.h z avr-libc, to tak jak pisze tmf, wystarczy poprawnie zdefiniować F_CPU (albo we własnym makefile'u lub w ustawieniach projektu dodać -DF_CPU=16000000UL albo gdzieś w jakimś pliku nagłówkowym zdefiniować #define'm).

    Zwróc tylko uwagę na to, że makra _delay_us i _delay_ms z avr-libc mają swoje ograniczenia - nie możesz jako parametr wywołania podać dowolnych wartości, bo te makra mają swój zakres poprawnego działania, opisany w dokumentacji.
    I właśnie przy zwiększeniu częstotliwości F_CPU może to sprawić problem, bo po wywołaniu _delay_ms z pożądanym czasem opóźnienia preprocesor przelicza to po prostu na liczbę pętli NOP'ów. Liczba tych pętli jest ograniczona i po zwiekszeniu F_CPU 16 razy wywołanie _delay z tą samą wartością może odmierzać inny czas.

    A jeśli korzystasz ze sprzętowych timer'ów to pomnożenie starej wartości np. rejestru COMPA czy COMPB przez 16 może Ci spowodować przepełnienie zakresu tego rejestru. Może być wówczas konieczna zmiana preskalera danego timera. Albo możesz nie ruszać rejestru a zmniejszyć preskaler timera o 16 (jeśli dla wybranego przez Ciebie uprzednio
    preskalera tak się da). Generalnie, jeśli nie miałeś tego wcześniej dobrze sparametryzowanego względem F_CPU, to pewnie będziesz musiał parę rzeczy zdefiniować ręcznie. A potem po prostu przesymuluj program i ustaw breakpoint w przerwaniu, żeby sprawdzić, czy "tyka" w takim tempie, w jakim chciałeś.

    Ja u siebie robię tak, że mam makra (albo funkcje inline) i #ifdef'y (dlatego tak, żeby pewne duże wartości były obliczane przez preprocesor na etapie kompilacji a nie wyznaczane potem na żywo w trakcie działania programu) do konfiguracji liczników w dane tryby. Najczęstszym używanym przeze mnie trybem jest CTC (czasem jeszcze jakiś tryb PWM, ale zdecydowanie rzadziej). W każdym razie do mojego makra przekazuję tylko pożądaną częstotliwość generowania przerwań w Hz a w kodzie mam parę #ifdef'ów, w których na bazie F_CPU i mojej stałej, np. TO_CTC_FREQ, wyznaczany jest odpowiedni preskaler i odpowiednia wartość COMP dla danego timer'ka. Wyznaczana jest maksymalna rozdzielczość timera (maksymalna wartość preskalera), dla której da się wyznaczyć wartość COMPa - dzięki temu odmierzanie czasu jest b. dokładne. Zreszą, i tak wywołuję swoje makra z takimi wartościami, żeby dokładnie takie dało się osiągnąć.

    Proste, zgrabne, i nie muszę się o nic martwić przy zmianie F_CPU.
    A jeśli wywołam dla danego timerka makro z częstotliwością, której dla danego F_CPU nie da się w tym timerku zrealizować, to program się nie skompiluje, bo za wszystkimi #ifdef'ami (tymi w których wybierane są preskaler i wartość do porównania) mam #elif #error "Takiej częsotliwości nie da się ustalić".
    Jeśli chcesz, mogę tu wrzucić przykładowy kod takiego makra dla jednego licznika.

    Najlepiej napisz jaki masz czujnik temperatury, jakim protokołem się z nim komunikujesz i jak to robisz
  • #4 10690886
    tmf
    VIP Zasłużony dla elektroda
    Tak istotnie, należy pomnożyć. Niemniej co do delay.h to kompletnie się mylisz - po przekroczeniu pewnych zakresów zmienia się jedynie rozdzieczość tych makr, niemniej odmierzane odcinki czasu ciągle będą poprawne. Przeczytaj dokładniej dokumentację, bo to często powielany błąd.
  • #5 10690936
    9xxx
    Poziom 10  
    Dzięki za podpowiedzi.
    Używałem delay.h i dla 1MHz wszystko było OK ale po zastosowaniu kwarcu 16MHz otrzymuje błędne informacje z czujnika (właśnie takie jakie były w momencie, źle ustawionych czasów). Korzystam z czujnika DS18B20 (czujnik temperatury) i komunikacja następuje poprzez 1-Wire, ale zasilam czujnik z zewnętrznego źródła, a nie poprzez 1-Wire.
    Odnośnie tego co piszecie. Czasem muszę mieć czasu rzędu us i nie wiem czy kiedy zastosuję kwarc 16MHz da się to osiągnąć za pomocą delay.h

    Jeśli możesz Ostry to bym prosił o taki kod. Może uda się dzięki temu rozwiązać problem.
  • #6 10691240
    Ostry23
    Poziom 18  
    tmf napisał:
    po przekroczeniu pewnych zakresów zmienia się jedynie rozdzieczość tych makr, niemniej odmierzane odcinki czasu ciągle będą poprawne

    Nie mylę się "kompletnie" :). Wiedziałem o tym, że zmienia się rozdzielczość, ale nie chciałem naraz za dużo mieszać w wyjaśnieniach. Dlatego napisałem
    Cytat:
    po zwiekszeniu F_CPU 16 razy wywołanie _delay z tą samą wartością może odmierzać inny czas.
    .
    Bo wartość będzie poprawna, jeśli nowa rozdzielczość ci na to pozwoli.
    No ale masz rację, że nie napisałem wcześniej precyzyjnie.
  • #7 10691965
    tmf
    VIP Zasłużony dla elektroda
    9xxx napisał:
    Czasem muszę mieć czasu rzędu us i nie wiem czy kiedy zastosuję kwarc 16MHz da się to osiągnąć za pomocą delay.h


    Oczywiście się da. Jeśli coś po zmianie kwarcu się kaszani to znaczy, że funkcje obsługi OW od początku były złe, tylko działały przez przypadek. Pokaż kod i fusebity.
  • #8 10694164
    9xxx
    Poziom 10  
    Funkcje są na pewno dobrze bo na atmega16 bez kwarcu działa idealnie. Wrzucę jutro wieczorem kod bo mam go na innym komputerze, może jednak się mylę i coś mam w nim nie tak, a nie sądzę, żeby zmiana procka miała wpływ na działanie programu (ale może się mylę - oczywiście programując wybrałem odpowiedni procesor a AVRStudio).
  • #9 10695623
    9xxx
    Poziom 10  
    Wklejam kod z jakiego korzystam (jest on w większości wzięty z różnych źródeł znalezionych w internecie i ewentualnie troche zmieniony na moje potrzeby). Na atmega 16 z wewnętrzym oscylatorem chodzi świetnie.
    Kod: text
    Zaloguj się, aby zobaczyć kod
  • #10 10695918
    Ostry23
    Poziom 18  
    Czy zrobiłeś podstawową rzecz, o której pisaliśmy, czyli:

    Ostry23 napisał:
    Jeśli korzystasz z delay.h z avr-libc, to tak jak pisze tmf, wystarczy poprawnie zdefiniować F_CPU (albo we własnym makefile'u lub w ustawieniach projektu dodać -DF_CPU=16000000UL, albo gdzieś w jakimś pliku nagłówkowym zdefiniować #define'm).


    Jeśli nie zrobiłeś, nie ma prawa działać dobrze.

    Skoro napisałeś, że kod działał dobrze na 1MHz, to nie zagłębiałem się w sama obsługę 1-wire, chociaż wydaje się, że masz postawiane poprawne opóźnienia wszędzie, gdzie to potrzebne.

    Przesymuluj kod - poustawiaj breakpointy tuż przed i tuż po wywołaniach _delay_ms i _delay_us i zobacz jakie rzeczywiście czasy odmierzają (pamiętaj, żeby w symulatorze też poprawnie ustalić częstotliwość zegara - jest to niezależne od ustawienia F_CPU).

    Mam też spore zastrzeżenia co do stylu programowania (zwłaszcza sposobu w jaki definiujesz niektóre makra), przez co trudniej się twój kod analizuje. Może w wolnej chwili napiszę o co chodzi.
  • #11 10696134
    9xxx
    Poziom 10  
    Oczywiście zdefiniowałem częstotliwość CPU (zapomniałem dopisać to w kodzie bo mam programy przygotowane w pliku txt i w zależności od procesora i środowiska programowania dodaje lub nie częstotliwość taktowania). Zdefiniowałem ją na samym początku przed #include.
    Kod możliwe, że jest chaotyczny, ale prawdę mówiąc staram się robić wszystko na razie na bazie programów napisanych przez inne osoby i w miarę możliwości dostosowywać do swoich potrzeb. Ciągle się uczę programowania mikrokontrolerów i elektroniki.
    Postaram się uruchomić symulację i zobaczymy co wyjdzie.

    Edit:
    Zrobiłem symulację i przekłamanie w niektórych miejscach jest na poziomie około 1us (nawet mniej). Takie błędy nie powinny spowodować niepoprawnej pracy programu bo mimo wszystko czasy zawierają się w tych wymaganych do poprawnej pracy czujnika (podanych w nocie katalogowej).
  • #12 10696406
    Ostry23
    Poziom 18  
    No dobra, szukamy dalej.

    9xxx napisał:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Widzę tu potencjalny błąd. Wg noty katalogowej DS18B20:
    Cytat:
    Output data from the DS18S20 is valid for 15 µs after the falling edge that initiated the read time slot. Therefore, the master must release the bus and then sample the bus state within 15 µs from the start of the slot
    .

    U ciebie jest 17us zamiast maksymalnie 15. Ale to nie to powinno powodować błędne działanie programu, bo po zmianie z 1MHz na 16 sama zmiana kierunku portu oraz odczyt z portu odbywa się szybciej, więc wynikowe opóźnienie jest mniejsze (niż poprzednio, nie niż 17us).

    Czy zmieniłeś uC nie zmieniając samego czujnika?
    A może masz tę samą aplikację odtworzoną na innej płytce, z innym egzemplarzem DS18B120.
    Jakie są w obu przypadkach pull-up'y?
    Raczej nie szukałbym przyczyny po stronie różnic pomiędzy 2 egzemplarzami czujnika, ale jeśli masz różne pull-upy, to może to jest przyczyną a nie zmiana częstotliwości zegara?
  • #13 10696489
    tmf
    VIP Zasłużony dla elektroda
    9xxx napisał:
    Oczywiście zdefiniowałem częstotliwość CPU (zapomniałem dopisać to w kodzie bo mam programy przygotowane w pliku txt i w zależności od procesora i środowiska programowania dodaje lub nie częstotliwość taktowania). Zdefiniowałem ją na samym początku przed #include.


    To pierwsza rzecz jaką zrób to wywal definicję F_CPU z kodu i umieść ją tam gdzie być powinna, czyli w makefile.
  • #14 10696527
    Ostry23
    Poziom 18  
    Kolejna sprawa:

    Wg noty DS18B20:
    Cytat:
    All read time slots must be a minimum of 60 µs in duration with a minimum of a 1 µs recovery time
    between slots


    A u ciebie w funkcji czytającej bit masz czas trwania slotu niewiele większy niż 17us.
    Co prawda w funkcji czytającej bajt, masz po wysłaniu każdego bitu dodatkowe opóźnienie 15us + opóźnienie wynikające z wchodzenia i wychodzenia (zachowanie kontekstu) z funkcji czytającej bit oraz wykonania reszty instrukcji i powrotu do początku pętli for, ale to wciąż za mało, żeby osiągnąć slot trwający >60us.

    I może dlatego dla 1MHz działało poprawnie a teraz, z szybszą obsługą pętli już nie.
    W pierwszej kolejności zwiększ ten delay w funkcji uv1Wire_ReadByte() do 45us.
    Jeśli nie pomoże przesymuluj działanie tego dla 2 różnych częstotliwości i podaj jakie czasy ci wyszły. Chodzi mi o czasy trwania slotów, czyli od wystawienia na magistralę 0 (w funkcji czytającej bit) w 1 cyklu pętli for, do wystawienia 0 w następnym.

    Zakładam też, że w międzyczasie nie zmieniałeś poziomu optymalizacji kompilatora (żeby funkcje z delay.h działały i tak musi być co najmniej -O1) i że jedyna różnica pomiędzy kodem działającym a nie działającym to F_CPU, że nie ruszałeś żadnych flag dla gcc.
    Teraz już trochę gdybam... w każdym razie w pierwszej kolejności sprawdź te sloty.

    Dodano po 17 [minuty]:

    tmf napisał:
    To pierwsza rzecz jaką zrób to wywal definicję F_CPU z kodu i umieść ją tam gdzie być powinna, czyli w makefile.


    To oczywiście nie spowoduje, że twój program zacznie działać poprawnie.
    Zresztą, kto powiedział, że musi być zdefiniowana w makefile'u?
    Ważne, żeby na etapie kompilacji F_CPU było "widoczne" przez wszystkie funkcje/makra które z niego korzystają, inaczej projekt, się nie skompiluje. A autorowi wątku się kompiluje, skąd wniosek, że zdefiniował w dobrym miejscu, z odpowiednim "scope".

    Nawet w manualu do avr-libc jest mnóstwo przykładów, w których F_CPU jest zdefiniowane gdzieś na samym początku przykładu. Ma to sens dla małych projektów, które składają się z 1 lub kilku plików. Nigdzie nie jest powiedziane, że F_CPU musi być definiowane już w makefile'u przy użyciu -D.

    Jako dodatkowe uzasadnienie podaję fragment manuala do gcc:
    Cytat:
    -D name=definition
    The contents of defnition are tokenized and processed as if they appeared dur-
    ing translation phase three ina‘#define’directive.
  • #15 10696656
    tmf
    VIP Zasłużony dla elektroda
    Ostry23 napisał:

    tmf napisał:
    To pierwsza rzecz jaką zrób to wywal definicję F_CPU z kodu i umieść ją tam gdzie być powinna, czyli w makefile.


    To oczywiście nie spowoduje, że twój program zacznie działać poprawnie.
    Zresztą, kto powiedział, że musi być zdefiniowana w makefile'u?
    Ważne, żeby na etapie kompilacji F_CPU było "widoczne" przez wszystkie funkcje/makra które z niego korzystają, inaczej projekt, się nie skompiluje. A autorowi wątku się kompiluje, skąd wniosek, że zdefiniował w dobrym miejscu, z odpowiednim "scope".


    Kompilować się będzie zawsze, bo funkcje z delay.h jeśli nie ma F_CPU dają tylko warning i przyjmują domyślną wartość tego symbolu, co IMHO jest błędem. F_CPU można definiować w pliku wyłącznie jeśli cały projekt składa się z jednego pliku (co z kolei w C jest nonsensem dla każdego rozsądnego projektu). Pomijam nieliczne wyjątki od tej reguły. Dlaczego tak jest to odsyłam do bloga kolegi dondu:
    http://mikrokontrolery.blogspot.com/2011/03/fcpu-gcc-gdzie-definiowac.html

    W manualu do AVR-libc jest przykład, gdzie F_CPU jest definiowane w kodzie, ale tylko po to, żeby zobrazować jak to działa.
    Oczywiście nie jest to zapewne powód niedziałania powyższych funkcji, ale jest to jeden z błędów jakie autor popełnił. Samego kodu nie analizuję, bo przecież autor jest pewny, że jego kod jest świetny, no to co się będę spierać :)
  • Pomocny post
    #16 10696720
    Ostry23
    Poziom 18  
    tmf napisał:
    Kompilować się będzie zawsze, bo funkcje z delay.h jeśli nie ma F_CPU dają tylko warning i przyjmują domyślną wartość tego symbolu, co IMHO jest błędem.


    O kurczę, rzeczywiście. Nie widziałem tego wcześniej, ale faktycznie, w pliku delay.h mamy:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Nie wiedziałem, że coś takiego tam siedzi.
    W manualu do avr-libc widnieje co prawda ten #define (chociaż w samym pdf'ie nigdzie nie jest napisane, że ta definicja obowiązuje, gdy jeszcze nie ma innej.
    Za to takim zdaniem, z rozdziału dot. przykładów, można się zasugerować:
    Cytat:
    The main reason is that this file defines the value of F_CPU which needs to be known before including <utils/delay.h>.


    I uznać, że trzeba bezwzględnie zdefiniować F_CPU, że bez tego się nie skompiluje. W każdym razie ja to tak rozumiałem.
    Sam zawsze miałem to zdefiniowane poprawnie i nawet nie sprawdzałem, czy program się bez tego skompiluje. Na pewno wszystkie moje własne funkcje bazujące na F_CPU nie.
    No to mnie zaskoczyłeś... też uważam, że to błąd. Wolałbym od razu dostać #error. W każdym razie zwracam honor.


    Cytat:
    W manualu do AVR-libc jest przykład, gdzie F_CPU jest definiowane w kodzie, ale tylko po to, żeby zobrazować jak to działa.

    Oczywiście. Natomiast program kolegi 9xxx specjalnie duży nie jest, no i zawiera się jednym pliku.
    W każdym razie wynik symulacji wskazuje na to, że poprawna wartość F_CPU została umieszczona w odpowiednim miejscu.
    No ale zgadzam się, definiowanie F_CPU już w makefile'u, nadrzędnie do wszystkiego, dobrym nawykiem jest.
  • Pomocny post
    #17 10696902
    tmf
    VIP Zasłużony dla elektroda
    Ostry23 napisał:

    Oczywiście. Natomiast program kolegi 9xxx specjalnie duży nie jest, no i zawiera się jednym pliku.
    W każdym razie wynik symulacji wskazuje na to, że poprawna wartość F_CPU została umieszczona w odpowiednim miejscu.
    No ale zgadzam się, definiowanie F_CPU już w makefile'u, nadrzędnie do wszystkiego, dobrym nawykiem jest.


    Oczywiście, jeszcze duży nie jest więc miejsce definicji nie jest kluczowe (ale refactoring by się już przydał, albo chociażby podzielenie tego na moduły). Zapewne błędy wynikają z przyczyn o których pisałeś wcześniej, ale to by wymagało założenia, że program jest błędny. A przecież autor stwierdził wyraźnie, że nie jest :) No bo przecież zlepek kodów znalezionych w Internecie musi działać :) Trochę mi się złośliwość włączyła, ale nader często pytający na elektrodzie szukają błędów wszędzie, łącznie z kompilatorem (!), zamiast wykazać trochę pokory i dopuścić możliwość, że jednak większość błędów leży po stronie programu (tako napisałem, jednocześnie będąc prawie pewnym, że znalazłem przed chwilą błąd w avr-gcc z Atmela).
    Ale, żeby nie było, błędne są timingi odczytu (15us to maks, w praktyce należy czytać bit koło 7-10us), błędny jest timing zapisu - impuls ujemny przy starcie bitu powinien trwać ok. 1 us, a nie 5, przy wolniejszej magistrali urządzenie może zsamplować linię jak będzie jeszcze w stanie 0 (trzeba pamiętać, że powrót do 1 jest pasywny), żle jest czytany presence pulse, znaczy na granicy jest. A przede wszystkim nie ma odstępów pomiędzy odbieraniem i nadawaniem bitów. Na wolniejszym procesorze samo wykonywanie programu wprowadza pewien odstęp, na szybszym się on redukuje i są efekty takie jakie są. To tylko kilka uwag, jak na z pewnością dobry program :)
  • #18 10696938
    9xxx
    Poziom 10  
    No wiesz, stwierdziłem, że program jest dobry bo dobrze działał na innym procesorze i w związku z tym uważałem, że jest napisany poprawnie. Oczywiście czasy po zmianie procesora i dodanie oscylatora mogą ulec zmianie i bardzo dziękuję, że wytknąłeś mi ten błąd.
    Poza tym od samego początku chodziło mi własnie o czasy, chociaż myślałem, że należy je jakoś odpowiednio zmienić przy modyfikacji procesora, a nie, że zostały wcześniej źle dobrane. Jeszcze raz dzięki za konstruktywną krytykę. Biorę się za przeglądanie kodu z datasheetem i wprowadzanie korekt w czasach.
  • #19 10697269
    Ostry23
    Poziom 18  
    tmf napisał:
    A przecież autor stwierdził wyraźnie, że nie jest :) No bo przecież zlepek kodów znalezionych w Internecie musi działać :) Trochę mi się złośliwość włączyła, ale nader często pytający na elektrodzie szukają błędów wszędzie, łącznie z kompilatorem (!), zamiast wykazać trochę pokory i dopuścić możliwość, że jednak większość błędów leży po stronie programu

    Ja to zrozumiałem trochę inaczej - autor, nie chwytając niunansów programowania mikrokontrolerów, założył, że skoro działa na innym uC, a w kodzie wszystko jest (teoretycznie) niezależne od sprzętu czy częstotliwości taktowania, to znaczy, że program jest bezwzględnie dobry. Gdyby autor znał te niuanse, pewnie nie zakładałby wątku na elektrodzie. Ja bym go nie winił :).

    Dodano po 8 [minuty]:

    tmf napisał:
    Ale, żeby nie było, błędne są timingi odczytu (15us to maks, w praktyce należy czytać bit koło 7-10us), błędny jest timing zapisu - impuls ujemny przy starcie bitu powinien trwać ok. 1 us, a nie 5, przy wolniejszej magistrali urządzenie może zsamplować linię jak będzie jeszcze w stanie 0 (trzeba pamiętać, że powrót do 1 jest pasywny), żle jest czytany presence pulse, znaczy na granicy jest. A przede wszystkim nie ma odstępów pomiędzy odbieraniem i nadawaniem bitów. Na wolniejszym procesorze samo wykonywanie programu wprowadza pewien odstęp, na szybszym się on redukuje i są efekty takie jakie są. To tylko kilka uwag, jak na z pewnością dobry program :)


    9xxx, słuchaj kolegi tmf. Ma raczej większe doświadczenie z tymi czujnikami ode mnie. Ja musiałem zaglądać do noty. Zresztą, polecam ci jej przestudiowanie. Gdy w końcu program zacznie ci działać, będziesz przynajmniej świadomy, czemu działa :)

    Dodano po 55 [minuty]:

    tmf napisał:
    nader często pytający na elektrodzie szukają błędów wszędzie, łącznie z kompilatorem (!), zamiast wykazać trochę pokory i dopuścić możliwość, że jednak większość błędów leży po stronie programu (tako napisałem, jednocześnie będąc prawie pewnym, że znalazłem przed chwilą błąd w avr-gcc z Atmela).


    No i jak? Jest błąd kompilatora?
  • #20 10710447
    9xxx
    Poziom 10  
    Jeszcze raz dzięki za pomoc. Punkty przyznane, temat zamykam.
REKLAMA