Zagwozdka w C Keil.

Pytanie do znawców C. Czy zapis taki:

volatile unsigned int DEL_STEP;

void DELAY(unsigned int Czas) { DEL_STEP = Czas; while(DEL_STEP); }

może różnić się wykonaniem od zapisu takiego?

void DELAY(unsigned int Czas) { unsigned char DEL_MSB,DEL_LSB; DEL_STEP = Czas; while(1) { DEL_LSB = DEL_STEP/0x100; DEL_MSB = DEL_STEP^0x100; if(DEL_MSB == 0 & DEL_LSB == 0) return; } }

Znalazłem błąd w starym kodzie. Ze zdziwieniem odkryłem, że w komendzie while(DEL_STEP); kompilator sprawdza tylko LSB zmiennej. Oczywiście generuje to kłopoty, gdy DEL_STEP przekracza wartość 255. Przepisanie jak niżej rozwiązuje problem, ale nie kumam powodu jego wystąpienia. Sprawdziłem optymalizacje, to nie to, po prostu kod jest generowany źle. Ktoś ma pomysł dlaczego kompilator uprościł, a może ja czegoś nie zauważam?

Miłego. Irek.N. ps. DEL_STEP jest modyfikowana w przerwaniu, inaczej procedura nie miała by sensu.

Reply to
Irek.N.
Loading thread data ...

Irek.N. wrote on 10.02.2019 22:32:

Pokaż cały kod wynikowy funkcji DELAY. Oczywiście kompilator może mieć błąd, ale dużo bardziej prawdopodobne jest że coś źle interpretujesz.

Reply to
Zbych

O "Keil" nigdy nie słyszałem, ale C obiło mi się już o uszy, wrzucę moje

3 grosze, być może jakkolwiek pomocne :)

Pierwszy wariant wydaje się bardzo sensowny. Kompilator zepsuty? A może typ int nie jest atomiczny, i kompilator sprawdza najpierw jednego nibbla, i dopiero jeśli ten okaże się zerowy to zajmie się drugim? Próbowałeś może zastąpić jakimś sig_atomic_t?

Ten drugi przykład to w ogóle lichy jest, bo pętla ma teoretyczne prawo zostać przerwana za każdym razem kiedy wartość DEL_STEP zostaje zmieniona z wartości o zerowym MSB do wartości o zerowym LSB (np. 0x0034 -> 0x1200)

- a to dlatego, że los może złośliwie zechcieć wykonać twoje przerwanie akurat pomiędzy liniami:

DEL_LSB = DEL_STEP/0x100; DEL_MSB = DEL_STEP^0x100;

Nazewnictwo jest tu nieco pomieszane, bo DEL_LSB będzie faktycznie zawierał 8 "ważniejszych" bitów, tj. MSB. A DEL_MSB będzie zawierał 8 "mniej ważnych" bitów - z resztą celowość operacji XOR całkiem mi tu umyka, tym bardziej że operuje na bitach których DEL_MSB i tak nie zobaczy (bo jest charem). Może zamiast '^' (xor) miałeś na myśli '%' (mod)? Też byłoby niepotrzebnie, ale byłbym wtedy w stanie zrozumieć co autor miał na myśli.

Swoją drogą, trochę dziwnie patrzeć na dzielenie w takiej operacji. Jeśli kompilator mądry, to zoptymalizuje twoje "/ 0x100" do ">> 8", ale jeśli na to nie wpadnie... to będzie mało ekologicznie.

Mateusz

Reply to
Mateusz Viste

Dnia Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. napisał(a):

A moze na tym procesorze int jest 8-bitowy ?

J.

Reply to
J.F.

Dnia Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. napisał(a):

ewentualnie ... kompilator potraktowal to jako wartosc logiczna, i uznal ze mu LSB wystarczy, albo wrecz ma niejawny typ logiczny,

8-bit, dokonal konwersji i sprawdzenia ... i mu sie MSB zoptymalzowal.

Sprobuj while(DEL_STEP != 0);

J.

Reply to
J.F.
Reply to
Grzegorz Niemirowski
Reply to
Grzegorz Niemirowski

Nie, na razie nic nie próbowałem.

To prawda, ale przynajmniej pozwoliło mi potwierdzić przyczynę problemów. W ogóle kiepsko to przepisałem, znaczek modulo mi się omsknął i jak sam zauważyłeś - zamieniłem połówki. Sorki. Zamysł był taki jak podejrzewałeś.

Radzi sobie z takimi zapisami sensownie.

THX, postaram się wrzucić jak tylko będę mógł kod wynikowy tego fragmentu.

Miłego. Irek.N.

Reply to
Irek.N.

J.F. pisze:

Sorki, źle napisałem na grupie podział na połówki, jak Mateusz zauważył. Powinno być:

DEL_MSB = DEL_STEP/0x100; DEL_LSB = DEL_STEP%0x100;

Choć intencja jest oczywista i niczego to raczej nie zmienia, wypada poprawić.

Sprawdzę Twoją propozycję jutro. Może naprowadzi nas na powód.

Miłego. Irek.N.

Reply to
Irek.N.

Tak nazwane są zmienne i może niefortunnie napisałem LSB.

Wątpię aby sprawdzany był najmniej

Sprawdzana jest zmienna DEL_STEP ale tylko w zakresie 8 mniej znaczących bitów.

Zgoda, Mateusz też to zauważył. Ale to nie jest istotne w tym przypadku. Warunek jest sprawdzany poza przerwaniami, a zmienna jest modyfikowana w przerwaniu. Zastanawiam się, czy może to mieć jakikolwiek znaczenie. Może należało by sprawdzić najpierw jedną połówkę, później drugą i ponownie pierwszą, dla pewności.

Miłego. Irek.N.

Reply to
Irek.N.

Dodam, że jeśli teoria z nie-atomicznym intem miałaby się potwierdzić, to mam wątpliwości czy jakikolwiek sig_atomic_t (jeśli w ogóle taki istnieje na tym kompilatorze) będzie w stanie przechować więcej jak 8 bitów. Z drugiej strony, nie-atomiczny int to byłby trochę dziwoląg... Musiałby być albo procesor 8-bitowy, albo szyna pamięci. W obecnych czasach wydaje się to (chyba?) mało prawdopodobne.

Mateusz

Reply to
Mateusz Viste

Brzmi trochę jak drutowanie. :) A czy - strzelam - kod wykonujący się w przerwaniu nie mógłby, zamiast pastwić się nad 16-bitowym DEL_STEP, ustawiać jakiegoś globalnego, jednobajtowego 'breakloop = 1'? Wtedy pętla ma łatwo, a sprawdzanie kiedy dokładnie ustawić breakloop leżeć będzie w obowiązkach kodu obsługi przerwania który to kod, niejako z definicji, przerwany być nie może.

Mateusz

Reply to
Mateusz Viste

Mateusz Viste snipped-for-privacy@nie.pamietam napisał(a):

AVR-y nadal trzymają się mocno, ale tu pewnie coś innego skoro Keil.

Reply to
Grzegorz Niemirowski
Reply to
Grzegorz Niemirowski

Niby można, ale to dalej będzie lichota, bo nic nie gwarantuje, że przy trzecim sprawdzaniu BAM! znów interrupt nie strzeli. Ktoś mógłby powiedzieć "no tak, ale to przerwanie wyzwala się raz na jakiś czas, kilka cykli CPU to za krótko by dwa razy zdążyło się wyzwolić" - ale to nie do końca słuszne założenie. Może być tak, że uruchomi się nasze przerwanie, zaraz za nim jakieś obce przerwanie które robi coś dłuuuugo i zaraz po nim znów wraca to nasze, z nowym (złośliwie ustawionym) DEL_STEP.

To tak. Ale moje skromne i niezobowiązujące zdanie jest takie, że z _disable() należy obchodzić się tak jak z goto. Są przypadki gdzie można się tym pokusić bo coś uprości i będzie wszystkim żyło się lepiej, ale jeśli tylko można - lepiej unikać, bo potem człowiek się uzależni i zacznie produkować potworki. No i oczywiście wyłączenie przerwań poskutkuje tym, że ich wykonywanie obarczone będzie jitterem (bo przy dłuższych sprawach przerwania nam się zakolejkują) - a to może być, w niektórych zastosowaniach/warunkach, jakimś problemem. Do tego takie ręczne wyłączanie przerwań wprowadza stan, o którym należy pamiętać (tj. nie zapomnieć o włączeniu przerwań z powrotem) - przy większym codeflow który może różnymi ścieżkami pobiec łatwo robi się wtedy mętlik.

Mateusz

Reply to
Mateusz Viste

Nie mógł tak zrobić. Przecież 0x0100 to prawda a nie fałsz. Tylko zero jest fałszem.

To tożsame z `while(DEL_STEP);`.

Reply to
Queequeg

W jaki sposób to odkryłeś i potwierdziłeś?

Masz wygenerowany kod assemblerowy?

Czy jeżeli na sztywno przypiszesz do DEL_STEP wartość np. 0x0100, i nigdzie nie będziesz jej zmieniał, to pętla z `while` się nie "odetka", a odetka się, gdy przypiszesz 0x0101?

(zakładam że to '51, czyli 8-bitowiec)

Reply to
Queequeg

W dniu poniedziałek, 11 lutego 2019 12:18:54 UTC+1 użytkownik J.F. napisał:

Hej, standard C99 będzie niedługo obchodził swoje dwudzieste urodziny:) Ja wiem, że embedded to skansen ale idąc drogą "C to tylko C90" nie można by było uznać za program w C nawet takiego, który deklaruje zmienną gdzie indziej niż na początku programu/funkcji.

Pozdrawiam,

Reply to
kropelka

Bo to zaiste nie (ANSI) C. Na co komu deklarowanie zmiennych w środku kodu? Jeśli odczuwasz taką potrzebę, to zapewne twój kod wymaga refaktoryzacji. Prawdziwe C to C89, wszystko inne to wymysły młodzieży, której się nudziło.

No ok - uczciwie muszę przyznać, że zdarza mi się korzystać z 'long long', ale to jedyna rzecz której mi czasem braknie w C89. :)

Mateusz

Reply to
Mateusz Viste

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.