Goto page 1, 2, 3, 4, 5, 6 Next
Irek.N.
Guest
Sun Feb 10, 2019 10:32 pm
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.
Zbych
Guest
Sun Feb 10, 2019 11:01 pm
Irek.N. wrote on 10.02.2019 22:32:
Quote:
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.
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.
Mateusz Viste
Guest
Sun Feb 10, 2019 11:41 pm
On Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. wrote:
Quote:
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;
}
}
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
J.F.
Guest
Sun Feb 10, 2019 11:49 pm
Dnia Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. napisał(a):
Quote:
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?
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?
A moze na tym procesorze int jest 8-bitowy ?
J.
J.F.
Guest
Sun Feb 10, 2019 11:55 pm
Dnia Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. napisał(a):
Quote:
Pytanie do znawców C. Czy zapis taki:
volatile unsigned int DEL_STEP;
void DELAY(unsigned int Czas)
{
DEL_STEP = Czas;
while(DEL_STEP);
}
Znalazłem błąd w starym kodzie. Ze zdziwieniem odkryłem, że w komendzie
while(DEL_STEP); kompilator sprawdza tylko LSB zmiennej.
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.
Grzegorz Niemirowski
Guest
Sun Feb 10, 2019 11:59 pm
J.F. <jfox_xnospamx@poczta.onet.pl> napisał(a):
Quote:
A moze na tym procesorze int jest 8-bitowy ?
J.
int musi mieć conajmniej 16 bitów, nawet na 8-bitowcu. Natomiast nie musi
być obsługiwany atomowo.
--
Grzegorz Niemirowski
https://www.grzegorz.net/
Grzegorz Niemirowski
Guest
Mon Feb 11, 2019 12:10 am
Irek.N. <tajny@jakis.taki.jest.pl> napisał(a):
Quote:
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.
LSB to least significant bit. Wątpię aby sprawdzany był najmniej znaczący
bit, może chodziło o bajt. Szkoda, że nie ma kodu asemblerowego. W każdy
razie to też wątpliwe, raczej sprawdzane jest wszystko, tylko pewnie nie
naraz. Jeśli procesor jest 8-bitowy, to int ma 16 bitów i obsługiwany jest
po bajcie a nie atomowo. Czyli de facto są dwa testy, dla młodszego i
starszego bajtu. Jeśli przerwanie nastąpi między testami to mamy problem.
Np. jeśli mamy wartość 256, to test młodszego bajtu da 0. Następnie
przerwanie zmieni wartość na 255. Wówczas wykonany zostanie test starszego
bajtu i też da zero. A więc zostanie uznane, że cała zmienna ma wartość
zero, skoro testy obu bajtów wykazały zera. W takich sytuacjach stosuje się
sekcje krytyczne.
--
Grzegorz Niemirowski
https://www.grzegorz.net/
Irek.N.
Guest
Mon Feb 11, 2019 12:11 am
Quote:
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?
Nie, na razie nic nie próbowałem.
Quote:
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;
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ś.
Quote:
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.
Radzi sobie z takimi zapisami sensownie.
THX, postaram się wrzucić jak tylko będę mógł kod wynikowy tego fragmentu.
Miłego.
Irek.N.
Irek.N.
Guest
Mon Feb 11, 2019 12:14 am
J.F. pisze:
Quote:
Dnia Sun, 10 Feb 2019 22:32:33 +0100, Irek.N. napisał(a):
Pytanie do znawców C. Czy zapis taki:
volatile unsigned int DEL_STEP;
void DELAY(unsigned int Czas)
{
DEL_STEP = Czas;
while(DEL_STEP);
}
Znalazłem błąd w starym kodzie. Ze zdziwieniem odkryłem, że w komendzie
while(DEL_STEP); kompilator sprawdza tylko LSB zmiennej.
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.
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.
Irek.N.
Guest
Mon Feb 11, 2019 12:28 am
Quote:
LSB to least significant bit.
Tak nazwane są zmienne i może niefortunnie napisałem LSB.
Wątpię aby sprawdzany był najmniej
Quote:
znaczący bit, może chodziło o bajt.
Sprawdzana jest zmienna DEL_STEP ale tylko w zakresie 8 mniej znaczących
bitów.
Quote:
Szkoda, że nie ma kodu asemblerowego.
Postaram się jutro podać.
Quote:
W każdy razie to też wątpliwe, raczej sprawdzane jest
wszystko, tylko pewnie nie naraz. Jeśli procesor jest 8-bitowy, to int
ma 16 bitów i obsługiwany jest po bajcie a nie atomowo. Czyli de facto
są dwa testy, dla młodszego i starszego bajtu. Jeśli przerwanie nastąpi
między testami to mamy problem. Np. jeśli mamy wartość 256, to test
młodszego bajtu da 0. Następnie przerwanie zmieni wartość na 255.
Wówczas wykonany zostanie test starszego bajtu i też da zero. A więc
zostanie uznane, że cała zmienna ma wartość zero, skoro testy obu bajtów
wykazały zera. W takich sytuacjach stosuje się sekcje krytyczne.
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.
Mateusz Viste
Guest
Mon Feb 11, 2019 9:27 am
On Mon, 11 Feb 2019 00:11:08 +0100, Irek.N. wrote:
Quote:
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?
Nie, na razie nic nie próbowałem.
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
Mateusz Viste
Guest
Mon Feb 11, 2019 9:32 am
On Mon, 11 Feb 2019 00:28:20 +0100, Irek.N. wrote:
Quote:
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.
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
Grzegorz Niemirowski
Guest
Mon Feb 11, 2019 9:43 am
Mateusz Viste <mateusz@nie.pamietam> napisał(a):
Quote:
Musiałby
być albo procesor 8-bitowy, albo szyna pamięci. W obecnych czasach wydaje
się to (chyba?) mało prawdopodobne.
AVR-y nadal trzymają się mocno, ale tu pewnie coś innego skoro Keil.
--
Grzegorz Niemirowski
https://www.grzegorz.net/
Grzegorz Niemirowski
Guest
Mon Feb 11, 2019 10:02 am
Irek.N. <tajny@jakis.taki.jest.pl> napisał(a):
Quote:
Warunek jest sprawdzany poza przerwaniami, a zmienna jest modyfikowana w
przerwaniu.
No właśnie. Zaczyna się sprawdzenie warunku, bach przerwanie, kończy się
sprawdzanie warunku.
Quote:
Może należało by sprawdzić najpierw jedną połówkę, później drugą i
ponownie pierwszą, dla pewności.
Można. Można też zastosować sekcję krytyczną czyli wyłączyć przerwania na
moment testu.
https://serwis.avt.pl/files/kurs_c/29_KursAVR_cz08.pdf - ostatnia strona
Lub też tak jak pisze Mateusz, sprawdzanie wartości licznika przenieść do
obsługi przerwania a w głównym kodzie sprawdzać tylko flagę.
Co to w ogóle za procesor programujesz? 8-bitowy?
--
Grzegorz Niemirowski
https://www.grzegorz.net/
Mateusz Viste
Guest
Mon Feb 11, 2019 11:10 am
On Mon, 11 Feb 2019 10:02:24 +0100, Grzegorz Niemirowski wrote:
Quote:
Może należało by sprawdzić najpierw jedną połówkę, później drugą i
ponownie pierwszą, dla pewności.
Można.
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.
Quote:
Można też zastosować sekcję krytyczną czyli wyłączyć przerwania
na moment testu.
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
Goto page 1, 2, 3, 4, 5, 6 Next