[PIC18F] Niekompilujący się __delay_ms() / __delay_us()

Nasze polecane książki, kursy, strony internetowe, fora itp. pomocne przy przyswajaniu wiedzy związanej z PIC i pisaniem programów dla nich.
Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1117
Rejestracja: czwartek 03 wrz 2015, 22:02

[PIC18F] Niekompilujący się __delay_ms() / __delay_us()

Postautor: Antystatyczny » środa 16 lis 2016, 15:35

Witam serdecznie.

Dzisiaj opowiem o problemach związanych z wykorzystaniem funkcji opóźniających __delay_us() oraz __delay_ms(). Na czym polegają te problemy?

Wyobraźmy sobie, że właśnie poskładaliśmy sobie pierwszą płytkę z wymarzonym PIC18F4550, kwarcem 4MHz i jakimś podstawowym osprzętem typu dioda led oraz mikroprzełącznik. Zazwyczaj pierwszy program, jaki chcemy uruchomić na nowym układzie, jest zwyczajne zamiganie diodą. Aby skorzystać z zaimplementowanej funkcji __delay_ms() (o __delay_us() nie wspominam, bo przy miganiu diodą interesują nas milisekundowe opóźnienia) należy zdefiniować makro określające aktualną prędkość pracy rdzenia naszego mikrokontrolera. Makro to można zdefiniować bezpośrednio w pliku programu:

Kod: Zaznacz cały

#define _XTAL_FREQ 4000000UL


...albo w ustawieniach projektu:

_XTAL_FREQ=4000000UL

Po konfiguracji pinu sterującego diodą świecącą program wpada w pętlę nieskończoną, by radośnie migać z częstotliwością kilku/kilkunastu Herców.

Kod: Zaznacz cały

while(true)
    {
        LATD ^= _LATD_LATD0_MASK;
        __delay_ms(200);
    }
   


Ot, klasyczny led blink na pinie D0. Zmiany mają następować z częstotliwością 5Hz. Niestety ten program nigdy się nie skompiluje... W wyniku kompilacji otrzymamy następujący błąd:

error: (1355) in-line delay argument too large

Dlaczego? Jak widzicie kompilator doczepił się funkcji _delay() oraz tego, że przekroczyliśmy wartość argumentu, który do niej przekazaliśmy. Funkcja ta wywoływana jest w wyniku rozwinięcia jednego z makr, czyli __delay_us() lub __delay_ms():

Kod: Zaznacz cały

#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))


Funkcja _delay() przyjmuje argument typu unsigned long, więc mogłoby się wydawać, że z opóźnieniem możemy szaleć niczym pijane zające... Niestety nie, nie możemy. Funkcja nie przyjmuje wartości większych od 197120. Nie wiem dlaczego, bo nie mam źródeł tej funkcji, więc mogę jedynie zgadywać, że chłopaki z Microchip celowo ograniczyli możliwość tworzenia tych betonowych opóźnień, by zachęcić do korzystania z timerów. Może to i pokrętne wyjaśnienie, ale kto wie...Może akurat zgadłem.

Spróbujmy obliczyć, jaką wartość przekazaliśmy do _delay() uruchamiając makro __delay_ms() z wartością 200.

200 * _XTAL_REQ / 4000.0 = 200 * 4000000 /4000.0 = 200000 (Dozwolona wartość maksymalna to 197120)

No i co? Wartość została przekroczona... Jaki z tego wniosek? Jeśli potrzebujemy skorzystać z większego opóźnienia, możemy skorzystać z pętli i kilka razy wywołać małe opóźnienie, a można skorzystać z timera ;)

Pozdrawiam!

PS. W powyższym przypadku do __delay_ms() możemy wpisać co najwyżej 197, a gdyby skorzystać z funkcji __delay_us(), możemy wpisać najwyżej 197120.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Wróć do „Źródła wiedzy na temat PIC”

Kto jest online

Użytkownicy przeglądający to forum: Google [Bot] i 1 gość