-
21. Data: 2011-02-10 16:00:43
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: Michoo <m...@v...pl>
W dniu 10.02.2011 16:16, Zbych pisze:
> Nie używaj cli i sei do robienia sekcji atomowych, te makra nie są
> zabezpieczone przed optymalizacją i kompilator może zmienić kolejność
> instrukcji (choć oczywiście nie musi).
Chyba nie - definicja wyglądają tak:
# define sei() __asm__ __volatile__ ("sei" ::)
# define cli() __asm__ __volatile__ ("cli" ::)
Co zabrania kompilatorowi zamiany kolejności ewaluacji wyrażeń.
Dziwne swoją drogą, że nie jest to zapisane na wszelki wypadek jako
# define sei() __asm__ __volatile__ ("sei" :::"memory")
ale może to wynika z tego, że ATOMIC_BLOCK robi barierę.
> Zamiast tego posłuż się
> ATOMIC_BLOCK
Zwłaszcza, że przy ATOMIC_BLOCK trudniej o pomyłkę i od razu widać gdzie
jest synchronizacja.
--
Pozdrawiam
Michoo
-
22. Data: 2011-02-10 18:15:18
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: "Robbo" <y...@m...com>
Dziękuję za odpowiedź.
> Po pierwsze jak obydwa przerwania (przechwytywanie i przepełnienie)
> zostaną zgłoszone jednocześnie, to będziesz miał nieprawidłowy wynik.
Racja.
> Zmienna prev powinno pamiętać nie tylko stan licznika sprzętowego, ale i
> software'owego.
Tego trochę nie rozumiem. Czy nie masz może kodu, który
realizuje taki 24-, 32-bitowy licznik? Niestety, ale nie mogę
nic znaleźć, a szukam już kilka godzin :(
R.
-
23. Data: 2011-02-10 18:35:49
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: Zbych <a...@o...pl>
W dniu 2011-02-10 17:00, Michoo pisze:
> W dniu 10.02.2011 16:16, Zbych pisze:
>> Nie używaj cli i sei do robienia sekcji atomowych, te makra nie są
>> zabezpieczone przed optymalizacją i kompilator może zmienić kolejność
>> instrukcji (choć oczywiście nie musi).
> Chyba nie - definicja wyglądają tak:
> # define sei() __asm__ __volatile__ ("sei" ::)
> # define cli() __asm__ __volatile__ ("cli" ::)
>
> Co zabrania kompilatorowi zamiany kolejności ewaluacji wyrażeń.
volatile zabrania usunięcia, optymalizacji, ale nie zabroni przesunięcia
czegoś co jest pomiędzy sei i cli, czyli z kodu
cli();[coś];sei();
może wyjść:
cli();sei(); [coś];
Dodatkowo taka konstrukcja nie zmusza kompilatora do zapisania wartości
tymczasowych trzymanych w rejestrach do pamięci, więc może się okazać,
że zapis wielobajtowej zmiennej nastąpi już przy włączonych przerwaniach.
Przykłady można znaleźć na liście dyskusyjnej avr-gcc.
> Dziwne swoją drogą, że nie jest to zapisane na wszelki wypadek jako
> # define sei() __asm__ __volatile__ ("sei" :::"memory")
No właśnie o tę barierę na pamięci chodzi.
-
24. Data: 2011-02-10 18:56:42
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: Zbych <a...@o...pl>
W dniu 2011-02-10 19:15, Robbo pisze:
> Dziękuję za odpowiedź.
>
>> Po pierwsze jak obydwa przerwania (przechwytywanie i przepełnienie)
>> zostaną zgłoszone jednocześnie, to będziesz miał nieprawidłowy wynik.
>
> Racja.
>
>> Zmienna prev powinno pamiętać nie tylko stan licznika sprzętowego, ale
>> i software'owego.
>
> Tego trochę nie rozumiem. Czy nie masz może kodu, który
> realizuje taki 24-, 32-bitowy licznik? Niestety, ale nie mogę
> nic znaleźć, a szukam już kilka godzin :(
Mniej więcej (raczej mniej niż więcej) widziałbym to tak:
uint32_t period;
uint8_t overflow;
SIGNAL (SIG_INPUT_CAPTURE3)
{
static uint32_t prev = 0;
union{
struct{
uint16_t byte01;
uint8_t byte2;
uint8_t byte3
}s;
uint32_t dword;
}now;
uint32_t now.s.byte01 = ICR3;
if (przepełnienie){
overflow++;
skasuj_przepełnienie;
}
now.s.byte2 = overflow;
now.s.byte3 = 0;
period = now.dword - prev;
prev = now.dword;
}
SIGNAL (SIG_INPUT_OVERFLOW3)
{
overflow++;
}
Nie chce mi się teraz szukać w dokumentacji jak się nazywają rejestry z
flagami od przepełnienia, więc musisz fragment ze sprawdzaniem
przepełnienia odpowiednio uzupełnić.
-
25. Data: 2011-02-10 20:56:55
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: "Robbo" <y...@m...com>
Dziękuję bardzo za ten kod. Przykład z wykorzystaniem union oraz struct
również bardzo cenny!
Twój kod, tak na szybko, bez union i struct (chodzi o samą ideę):
SIGNAL (SIG_INPUT_CAPTURE3)
{
icr = ICR3;
if (ETIFR & _BV(TOV3)) {
overflow++;
ETIFR |= _BV(TOV3);
ff++; // licznik zdarzeń tego typu
}
actualDuration = ((unsigned long)icr + overflow * 65536UL) - pp;
pp = (unsigned long)icr + overflow * 65536UL;
}
SIGNAL (SIG_OVERFLOW3)
{
overflow++;
}
Wyświetlam sobie na LCD wartość ff. Na ogół ma wartość "1" i tak już
zostaje. Wynika z tego, że bardzo rzadko mam sytuację z jednoczesnymi
przerwaniami.
Wydaje mi się, że możliwe jest jeszcze pewne uproszczenie -- w praktyce mi
ono zdaje egzamin (a przynajmniej tak mi się wydaje na podstawie
obserwacji). Chodzi o to, że u Ciebie "overflow" inkrementowany jest
nieustannie. To nie jest błąd -- wszystko działa jak należy. Niemniej ja
chciałem mieć "overflow" zerowany, gdyż zależnie od jego wartości chciałem
zrobić odcinanie niskich częstotliwości mierzonego sygnału. Oto przeróbka:
*** KROK 1
SIGNAL (SIG_INPUT_CAPTURE3)
{
icr = ICR3;
if (ETIFR & _BV(TOV3)) {
overflow++;
ETIFR |= _BV(TOV3);
}
actualDuration = ((unsigned long)icr + overflow * 65536UL) - pp;
pp = (unsigned long)icr + overflow * 65536UL;
pp -= overflow * 65536UL;
overflow = 0;
}
powyższe można uprościć do takiej postaci:
*** KROK 2
SIGNAL (SIG_INPUT_CAPTURE3)
{
icr = ICR3;
if (ETIFR & _BV(TOV3)) {
overflow++;
ETIFR |= _BV(TOV3);
}
actualDuration = ((unsigned long)icr + overflow * 65536UL) - pp;
pp = (unsigned long)icr;
overflow = 0;
}
Zatem wydaje mi się (może się mylę, ale w praktyce działa i wygląda OK), że
w "pp" nie musimy trzymać wartości licznika software'owego, a wystarczy
trzymać ICR.
Co o tym myślisz?
A oto kompletny kod -- odcinamy poniżej ok. 2Hz (w szczególności, gdy sygnał
w ogóle jest zerowy).
SIGNAL (SIG_INPUT_CAPTURE3)
{
icr = ICR3;
if (ETIFR & _BV(TOV3)) {
overflow++;
ETIFR |= _BV(TOV3);
}
actualDuration = ((unsigned long)icr + overflow * 65536UL) - pp;
pp = (unsigned long)icr;
overflow = 0;
}
SIGNAL (SIG_OVERFLOW3)
{
overflow++;
}
int main(void)
{
char buf[16];
unsigned long actualDurationLatch;
TCCR3A = 0;
TCCR3B |= _BV(ICES3) | _BV(ICNC3) |
_BV(CS32) | _BV(30); // prescaler 64
ETIMSK |= _BV(TICIE3) | _BV(TOIE3);
sei();
while (1) {
cli();
actualDurationLatch = actualDuration;
sei();
if ((overflow >= 5) || (actualDurationLatch > 150000)) {
LCDwriteString("-------------");
} else {
sprintf(s, "%ld ", actualDurationLatch);
LCDwriteString(s);
}
}
}
W moim pierwotnym liście podałem kod z wykorzystaniem SIG_OUTPUT_COMPARE2
oraz SIG_INTERRUPT7. Kod tamten miał (w porównaniu do tego z wykorzystaniem
input capture) tę wadę, że zwiększanie zmiennej "timer" 100000 razy na
sekundę obciążąło procesor. Niemniej miał on też pewną zaletę -- chodzi o
to, że dla niskich częstotliwości "odcinanie" miałem zawsze w określonym
czasie (np. gdy timer osiągnął wartość 100000; timer był resetowany po
każdym zboczu narastającym -- zatem wartość 100000 mówiła, że minęło 100000
ticków zegara od ostatniego zbocza narastającego sygnału mierzonego). Tu
(chodzi o kod z input capture) niestety jest problem. Gdy sygnał zaniknie
nagle, to "overflow" musi doliczyć do 5 (taką wartość sobie przyjąłem), ale
przecież ICR może mieć różną wartość i czas, w którym "overflow" doliczy do
5 może być różny (gdy ICR było bliskie 0, to będzie to czas prawie pięciu
przepełnień ICR; gdy ICR było prawie 65000, to będzie to czas nieco ponad
czterech przepełnień ICR). Aby mieć dokładne odcinanie (po upływie dokładnie
określonego czasu), musiałbym zrobić dodatkowy timer (np. dający impuls co 1
sekundę), resetowany w SIG_INPUT_CAPTURE3. Jeśli timer da impuls (czyli nie
został zresetowany w ciągu ostatniej sekundy), to znaczy, że sygnał jest
poniżej 1Hz i robimy odcinanie.
R.
-
26. Data: 2011-02-10 21:31:01
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: Michoo <m...@v...pl>
W dniu 10.02.2011 19:35, Zbych pisze:
> W dniu 2011-02-10 17:00, Michoo pisze:
>> W dniu 10.02.2011 16:16, Zbych pisze:
>>> Nie używaj cli i sei do robienia sekcji atomowych, te makra nie są
>>> zabezpieczone przed optymalizacją i kompilator może zmienić kolejność
>>> instrukcji (choć oczywiście nie musi).
>> Chyba nie - definicja wyglądają tak:
>> # define sei() __asm__ __volatile__ ("sei" ::)
>> # define cli() __asm__ __volatile__ ("cli" ::)
>>
>> Co zabrania kompilatorowi zamiany kolejności ewaluacji wyrażeń.
>
> volatile zabrania usunięcia, optymalizacji, ale nie zabroni przesunięcia
> czegoś co jest pomiędzy sei i cli, czyli z kodu
> cli();[coś];sei();
> może wyjść:
> cli();sei(); [coś];
Tylko jeżeli [coś] jest kiepskim kodem ;) - Używanie w sekcji krytycznej
czegoś co nie jest volatile i nie zawiera bariery na przesyłanej
zmiennej jest sprzeczne zarówno ze standardem jak i ze zdrowym rozsądkiem.
Gdy [coś] operuje na zmiennych volatile wszystko musi działać a
jednocześnie nie ma konieczności synchronizacji wszystkich rejestrów jak
przy klasycznej barierze.
>
> Dodatkowo taka konstrukcja nie zmusza kompilatora do zapisania wartości
> tymczasowych trzymanych w rejestrach do pamięci, więc może się okazać,
> że zapis wielobajtowej zmiennej nastąpi już przy włączonych przerwaniach.
>
> Przykłady można znaleźć na liście dyskusyjnej avr-gcc.
Masz może linkę? Bo na szybko nie mogłem znaleźć.
>
>> Dziwne swoją drogą, że nie jest to zapisane na wszelki wypadek jako
>> # define sei() __asm__ __volatile__ ("sei" :::"memory")
>
> No właśnie o tę barierę na pamięci chodzi.
Tylko bariera jest wolna i nieoptymalna. W sumie jak się głębiej
zastanowić to to jest sensowne:
cli();[operacje na volatile]sei();
- działa jak należy
cli();[dowolne operacje i synchronizacja zmiennej współdzielonej]sei();
- też działa
cli();[operacje bez synchronizacji]sei();
- robi to co programista napisał a nie to co chciał, ale C jest znane z
tego że pozwala się postrzelić w stopę
--
Pozdrawiam
Michoo
-
27. Data: 2011-02-11 07:36:54
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: Zbych <a...@o...pl>
W dniu 2011-02-10 22:31, Michoo pisze:
> Tylko jeżeli [coś] jest kiepskim kodem ;) - Używanie w sekcji krytycznej
> czegoś co nie jest volatile i nie zawiera bariery na przesyłanej
> zmiennej jest sprzeczne zarówno ze standardem jak i ze zdrowym rozsądkiem.
Pokaż mi gdzie jest to napisane w standardzie. Ja wtykanie volatile
wszędzie gdzie się da uważam co najmniej za lenistwo. Jak już muszę użyć
to często robię unię zmiennej volatile i bez volatile. Dzięki czemu nie
mam nadmiarowego kodu np. w przerwaniach.
>> Dodatkowo taka konstrukcja nie zmusza kompilatora do zapisania wartości
>> tymczasowych trzymanych w rejestrach do pamięci, więc może się okazać,
>> że zapis wielobajtowej zmiennej nastąpi już przy włączonych przerwaniach.
>>
>> Przykłady można znaleźć na liście dyskusyjnej avr-gcc.
> Masz może linkę? Bo na szybko nie mogłem znaleźć.
Nie chce mi się szukać. Musisz przejrzeć daty w okolicach dodania makr
ATOMIC do biblioteki. Te makra powstały właśnie po tym jak ludzie
zaczęli zgłaszać błędy związane przenoszeniem operacji poza blok
cli()-sei().
>>> Dziwne swoją drogą, że nie jest to zapisane na wszelki wypadek jako
>>> # define sei() __asm__ __volatile__ ("sei" :::"memory")
>>
>> No właśnie o tę barierę na pamięci chodzi.
> Tylko bariera jest wolna i nieoptymalna.
Ta bariera nie jest wolniejsza niż używanie volatile. Wymusza tylko
zrzut i pobranie zmiennych.
> cli();[operacje bez synchronizacji]sei();
> - robi to co programista napisał a nie to co chciał, ale C jest znane z
> tego że pozwala się postrzelić w stopę
Tia, temat na kolejne bicie piany :-)
-
28. Data: 2011-02-11 11:05:15
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: "Robbo" <y...@m...com>
> Jak już muszę użyć to często robię unię zmiennej volatile i bez volatile.
> Dzięki czemu nie mam nadmiarowego kodu np. w przerwaniach.
Gdybyś mógł wyjaśnić, co daje taka konstrukcja...?
R.
-
29. Data: 2011-02-11 11:43:23
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: Michoo <m...@v...pl>
W dniu 11.02.2011 08:36, Zbych pisze:
> W dniu 2011-02-10 22:31, Michoo pisze:
>
>> Tylko jeżeli [coś] jest kiepskim kodem ;) - Używanie w sekcji krytycznej
>> czegoś co nie jest volatile i nie zawiera bariery na przesyłanej
>> zmiennej jest sprzeczne zarówno ze standardem jak i ze zdrowym
>> rozsądkiem.
>
> Pokaż mi gdzie jest to napisane w standardzie.
C++:
When the processing of the abstract machine is interrupted by receipt of
a signal, the values of objects with type other than volatile
sig_atomic_t are unspecified, and the value of any object not of
volatile sig_atomic_t that is modified by the handler becomes undefined.
Czyli przerzucanie program<->przerwanie czegoś innego niż "volatile
sig_atomic_t" w myśl standardu nie jest zdefiniowane. Ale jednocześnie:
Accessing an object designated by a volatile lvalue (3.10), modifying an
object, calling a library I/O function, or calling a function that does
any of those operations are all side effects, which are changes in the
state of the execution environment. Evaluation of an expression might
produce side effects. At certain specified points in the execution
sequence called sequence points, all side effects of previous
evaluations shall be complete and no side effects of subsequent
evaluations shall have taken place.)
W związku z tym gcc/g++ daje gwarancję na prawidłowe przekazanie
obiektów volatile.
http://gcc.gnu.org/onlinedocs/gcc/Volatiles.html
> Ja wtykanie volatile
> wszędzie gdzie się da uważam co najmniej za lenistwo.
Ja za błąd w sztuce - nie dość, że optymalizacje psuje to jeszcze
wprowadza bardzo trudne do wykrycia błędy (przesyłanie wielobajtowych
obiektów volatile bez synchronizacji a jedynie licząc, że samo volatile
wystarczy) Niemniej *nie oznaczenie* obiektu przekazywanego jako
volatile to już lampka ostrzegawcza, bo trzeba dodatkowo pamiętać o
barierach.
> Jak już muszę użyć
> to często robię unię zmiennej volatile i bez volatile. Dzięki czemu nie
> mam nadmiarowego kodu np. w przerwaniach.
Ja zazwyczaj używam volatile do samego przekazania i osobne zmienne "po
obu stronach" albo nie daję volatile i pamiętam o synchronizacji ;)
> Musisz przejrzeć daty w okolicach dodania makr
> ATOMIC do biblioteki. Te makra powstały właśnie po tym jak ludzie
> zaczęli zgłaszać błędy związane przenoszeniem operacji poza blok
> cli()-sei().
Ok, będę miał chwilę to poszukam.
>>>> Dziwne swoją drogą, że nie jest to zapisane na wszelki wypadek jako
>>>> # define sei() __asm__ __volatile__ ("sei" :::"memory")
>>>
>>> No właśnie o tę barierę na pamięci chodzi.
>> Tylko bariera jest wolna i nieoptymalna.
>
> Ta bariera nie jest wolniejsza niż używanie volatile. Wymusza tylko
> zrzut i pobranie zmiennych.
Wszystkich zmiennych. Używanie volatile jest wolne tylko w odniesieniu
do tego jednego obiektu.
Napisałem kiedyś makro, które robiło barierę na podanej zmiennej -
dziwię się, że nie ma czegoś takiego w bibliotece:
#define SYNC_VARIABLE(var) __asm__ __volatile__ ("" :"=m"(var):"m"(var))
>
>> cli();[operacje bez synchronizacji]sei();
>> - robi to co programista napisał a nie to co chciał, ale C jest znane z
>> tego że pozwala się postrzelić w stopę
>
> Tia, temat na kolejne bicie piany :-)
Po coś te grupy dyskusyjne powstały ;)
--
Pozdrawiam
Michoo
-
30. Data: 2011-02-11 12:15:56
Temat: Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu
Od: Zbych <a...@o...pl>
W dniu 2011-02-11 12:43, Michoo pisze:
> W dniu 11.02.2011 08:36, Zbych pisze:
>> W dniu 2011-02-10 22:31, Michoo pisze:
>>
>>> Tylko jeżeli [coś] jest kiepskim kodem ;) - Używanie w sekcji krytycznej
>>> czegoś co nie jest volatile i nie zawiera bariery na przesyłanej
>>> zmiennej jest sprzeczne zarówno ze standardem jak i ze zdrowym
>>> rozsądkiem.
>>
>> Pokaż mi gdzie jest to napisane w standardzie.
> C++:
> When the processing of the abstract machine is interrupted by receipt of
> a signal, the values of objects with type other than volatile
> sig_atomic_t are unspecified, and the value of any object not of
> volatile sig_atomic_t that is modified by the handler becomes undefined.
>
> Czyli przerzucanie program<->przerwanie czegoś innego niż "volatile
> sig_atomic_t" w myśl standardu nie jest zdefiniowane. Ale jednocześnie:
>
> Accessing an object designated by a volatile lvalue (3.10), modifying an
> object, calling a library I/O function, or calling a function that does
> any of those operations are all side effects, which are changes in the
> state of the execution environment. Evaluation of an expression might
> produce side effects. At certain specified points in the execution
> sequence called sequence points, all side effects of previous
> evaluations shall be complete and no side effects of subsequent
> evaluations shall have taken place.)
Co prawda c++, a nie c, ale dzięki za podesłanie tego. Miło wiedzieć, że
jest to w ogóle ustandaryzowane.
> Napisałem kiedyś makro, które robiło barierę na podanej zmiennej -
> dziwię się, że nie ma czegoś takiego w bibliotece:
> #define SYNC_VARIABLE(var) __asm__ __volatile__ ("" :"=m"(var):"m"(var))
Fajne :-)