AVR ATmega, pomiar częstotliwości przebiegu, prośba o sprawdzenie kodu

Do you have a question? Post it now! No Registration Necessary

Translate This Thread From Polish to

Threaded View
Witam,

Jakiś czas temu pytałem o sprawy związane z pomiarem częstotliwości
sinusoidy o częstotliwości od 2 do 150Hz oraz zmiennej amplitudzie (od kilku
V do 400V). Mam już układ elektroniczny przekształcający sinusoidę na
prostokąt (brana jest pod uwagę tylko dodatnia połówka, a ujemna jest
usuwana). Jest tu kilka rezystorów, mostek, tranzystor, dioda oraz
transformatorek. (Może działanie tego układu da się jeszcze ulepszyć, ale to
w sumie nie jest teraz tematem). Uzyskany z dodatniej połówki sinusoidy
prostokąt jest podawany na nóżkę PE7(INT7) mikrokontrolera ATmega128 (kwarc
16MHz). Dokładność pomiaru, którą chciałbym osiągnąć to 0,2Hz przy
częstotliwości 50Hz. Chciałem Was prosić o sprawdzenie mojego kodu, którego
zadaniem jest nieustanne mierzenie czasu trwania dodatniej połówki, a
następnie wypisywanie tej wartości na wyświetlacz. Kod, który napisałem
wygląda na działający, ale może ma jakiś ukryty błąd.

volatile unsigned long int timer = 100000;
volatile short actualTime;

./* procedura wywoływana 100000 razy na sekundę */
SIGNAL (SIG_OUTPUT_COMPARE2)
{
    cli();

    if (timer < 100000)
        timer++;

    sei();
}

/* wyzwalane zboczem narastającym i opadającym */
SIGNAL (SIG_INTERRUPT7)
{
    cli();

    if (PINE & PINE7) {
        timer = 0;
        TCCR2 = 0;    // stop timera
        TCNT2 = 0;    // zerowanie licznika
        TCCR2 = _BV(WGM21) |     // start timera
                         _BV(C21);            // (prescaler 8)
    } else {
        actualTime = timer;
    }

    sei();
}

int main(void)
{
   char s[16];

    initLCD();

    cli();

    EICRB |= _BV(ISC70);
    EIMSK |= _BV(INT7);

    TCCR2 = 0;
    OCR2 = 19;
    TIFR |= _BV(OCF2);
    TIMSK |= _BV(OCIE2);
    while (ASSR & _BV(OCR2UB))
        ;

    sei();

    while (1) {

        if (timer == 100000)
            LCDshowString("---");
        else {
            sprintf(s, "%d  ", actualTime);
            LCDshowString(s);
        }
    }

    return 0;
}

***
Jak to się sprawuje:
Jako źródło sygnału podłączyłem po prostu zasilanie sieciowe 230V/50Hz.
Wczoraj wieczorem przed godziną 22 liczba na wyświetlaczu wynosiła 996
(ostatnia cyfra niekiedy migała na "5"). Potem zaczęło to rosnąć... 997/8. O
godzinie 22 było jakieś 999. Potem zaczęło spadać do 996. Teraz (godzina
13:40) mam 996/7.
***

W szczególności chciałem zapytać o to, czy poprawnie zastosowałem cli() i
sei() w procedurach obsługi przerwań. Ponadto chciałem zapytać, czy kod
wewnątrz while(1) jest poprawny i czy nie lepiej byłoby zapisać to tak:

while (1) {
    unsigned long int timerLatch;
    short actualTimeLatch;

    cli();
    timerLatch = timer;
    actualTimeLatch = actualTime;
    sei();

    if (timerLatch == 100000)
        LCDshowString("---");
    else {
        sprintf(s, "%d  ", actualTimeLatch);
        LCDshowString(s);
    }
}

Chodzi o sytuację, w której w pętli głównej jest przetwarzana wartość
zmiennej timer albo actualTime (np. został odczytany pierwszy bajt) i w
trakcie nastąpi przerwanie, zmieni wartość zmiennej, obsługa przerwania się
skończy, zostanie dokończone przetwarzanie zmiennej timer albo actualTime --  
wartość chyba będzie błędna (jeden bajt z poprzedniej wartości, reszta
bajtów po aktualizacji w przerwaniu).

Z góry dziękuję za rady.

Robbo


Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o spra wdzenie kodu
Hello Robbo,


Quoted text here. Click to load it

Ja bym liczył czas pomiedzy dwoma narastającymi zboczami. Ale ja
herbatę cukrem...

[...]

--
Best regards,
 RoMan                            mailto:roman@pik-net.pl
We've slightly trimmed the long signature. Click to see the full one.
Re: AVR ATmega, pomiar częstotliwości przeb iegu, prośba o sprawdzenie kodu


Quoted text here. Click to load it

truskawki :P

... no chyba ze znamy inne dowcipy

c.


Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o spra wdzenie kodu
Hello Cezar,


[...]

Quoted text here. Click to load it

Truskawki to ja ze śmietaną, najlepiej bitą.

Quoted text here. Click to load it

To nie dowcip.

--
Best regards,
 RoMan                            mailto:roman@pik-net.pl
We've slightly trimmed the long signature. Click to see the full one.
Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu
Quoted text here. Click to load it

Pan policjant widzę chyba po służbie bo też pozwala sobie na NTG.
Wiem wiem. Jedzie pan na sygnale jako pojazd uprzywilejowany.

Marek


Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu
Quoted text here. Click to load it

Ja miodem...

Sprawdziłem. W tej chwili:
- mierzenie czasu między narastającym a opadającym zboczem dodatniej połówki
daje czas 998/7.
- mierzenie czasu między narastającymi zboczami kolejnych dodatnich połówek
daje czas 2000/1.

R


Re: AVR ATmega, pomiar częstotliwości przebiegu, prośba o spra wdzenie kodu
Hello Robbo,


Quoted text here. Click to load it

Za mocny smak...

Quoted text here. Click to load it

Jak Ci się wydaje - który wynik jest bardziej zbliżony do
rzeczywistości?

--
Best regards,
 RoMan                            mailto:roman@pik-net.pl
We've slightly trimmed the long signature. Click to see the full one.
Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu
Quoted text here. Click to load it

Wydaje mi się, że oczywiście ten drugi wynik :)

Ale, jeszcze trzeba będzie popracować nad elektroniką
i może uda się wykrzesać coś więcej z metody nr 1
Może też ujemną połówkę uda się użyć i wtedy miałbym
szybszy odczyt częstotliwości z niewielkim błędem.

R.


Re: AVR ATmega, pomiar częstotliwości przeb iegu, prośba o sprawdzenie kodu
W dniu 09.02.2011 13:54, Robbo pisze:
Quoted text here. Click to load it
Nie.

Jeżeli nie deklarujesz przerwań explicite jako nieblokujących to one
dbają o wyłączność za Ciebie. Pisząc sei na końcu przerwania umożliwiasz
odpalenie przerwania po 2 cyklach a prawdopodobnie wyjście z przerwania
trwa chwilę dłużej - przy dużym nagromadzeniu przerwań do obsługi
skończy się to przepełnieniem stosu.

--
Pozdrawiam
Michoo

Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu

Quoted text here. Click to load it

Widzisz, tak to jest jak się nigdy żadnego kodu w assemblerze na AVR-a nie
napisało.

Otóż procesor AVR wchodząc w obsługę przerwania sam wyłącza przerwania. Więc
stan jest taki, że do czasu zakończenia obsługi aktualnego przerwania,
obsługa kolejnych przerwań jest wyłączona.

Więc cli() po wejściu w obsługę przerwania nie ma sensu (bo przerwania już
są wyłączone), natomiast sei() dajesz wtedy jak chcesz włączyć ręcznie
obsługę przerwań, co oczywiście jest możliwe, tylko na stos trzeba uważać
(jeśli tych przerwań jest dużo w jednostce czasu).

sei() na końcu też nie ma sensu, ponieważ procedury obsługi przerwania nie
opuszcza się poprzez ret, tylko poprzez reti, będące niczym innym jak
połączeniem: ret + sei.

 


Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu
Nie bawiłem się jeszcze przerwaniami w atmedze i muszę zaraz sprawdzić
w nocie jak jest ale zapytam wybiegiem a czy aby nie ma przypadkiem
zatrzasku przerwań przychodzących podczas obsługi przerwań i
priorytetów przerwań?

Marek


Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu
Quoted text here. Click to load it

Dziękuję.
A chciałem jeszcze prosić o odpowiedź na pytanie dotyczące użycia
sei i cli w pętli głównej.

R.


Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu

Quoted text here. Click to load it

W wielkim skrócie - cli/sei używasz wtedy gdy dokonujesz zapisu/odczytu
zmiennych, które: wykorzystujesz też w przerwaniach i cała operacja zajmuje
więcej niż 1 operację procesora (zmienne dłuższe niż 1 bajt i operacje
bitowe). No i oczywiście wtedy gdy z pewnych przyczyn (głównie czasowych)
dana operacja nie powinna zostać przerwana obsługą przerwania.






Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu

A może wykorzystac dwa liczniki.
Jeden zliczający uformowane impulsy a drugi z autoresetem wyzwalający
przerwanie co określony czas. W przerwaniu sprawdzałbym stan licznika
zliczającego, następnie kasował go i opuszczał przerwanie.

Marek


Re: AVR ATmega, pomiar częstotliwości przeb iegu, prośba o sprawdzenie kodu
W dniu 09.02.2011 13:54, Robbo pisze:
Quoted text here. Click to load it

Witam
Na wstępie to się zapytam, na czym Koledze tak naprawdę zależy ? Na
pomiarze czasu (okresu) czy częstotliwości ? Bo pisze Kolega o
"dokładności 0,2Hz" i o "wyświetlaniu czasu trwania dodatniej połówki".
Ponieważ zakres częstotliwości jest stosunkowo niewielki, można to
zrobić na dwa sposoby i kilka wariacji ;-).
1. Pomiar ilości impulsów w stałym przedziale czasu. Przy wymaganej
rozdzielczości 0,2Hz pomiar musiałby trwać co najmniej 5 sekund. Metod
realizacji jest wiele. Przy tak małej częstotliwości zbocze sygnału
mierzonego można sobie wykrywać programowo w pętli (pod warunkiem, że w
systemie nie istnieją przerwania, których obsługa zajmuje ponad 6ms).
Jako bramkę czasu najprościej wykorzystać któryś z timerów. Bardziej
elegancko liczyć zbocza w obsłudze zewnętrznego przerwania. Można
zliczać impulsy sprzętowo za pomocą licznika 16-bitowego, a 8-bitowym
realizować bramkę czasu. Można również całkowicie sprzętowo z
wykorzystaniem rejestru ICR licznika 16-bitowego i licznika 8-bitowego
generującego na wyjściu OCx impulsy o czasie bramkowania.
2. Pomiar czasu między impulsami (okresu). Tu aż się prosi wykorzystać
Input Capture Register.

Pozdrawiam
Grzegorz


Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu
Quoted text here. Click to load it

Owszem, nieprecyzyjnie napisałem. Mierzyć będę czas i na tej podstawie
będzie działać układ. Użytkownikowi będę jednak prezentował częstotliwość
wyznaczoną na podstawie zmierzonego czasu.


Quoted text here. Click to load it

I to chyba będzie eleganckie rozwiązanie. Właśnie się tym zająłem i mam
już efekty. Jutro pozwolę sobie wkleić kod do oceny.

R.


Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu
Oto kod:

volatile unsigned short icr, prevIcr;

SIGNAL (SIG_INPUT_CAPTURE3)
{
    icr = ICR3;
    acutalDuration = icr - prevIcr;
    prevIcr = icr;
}


int main(void)
{
    char buf[16];
    unsigned short actualDurationLatch;

    TCCR3A = 0;
    TCCR3B |= _BV(ICES3) | _BV(ICNC3) |
                        _BV(CS32) | _BV(30);  // prescaler 64
    ETIMSK |= _BV(TICIE3);

    sei();

    while (1) {
        cli();
        actualDurationLatch = actualDuration;
        sei();

        sprintf(s, "%u  ", actualDurationLatch);
        LCDwriteString(s);
    }
}

Na kwarcu 16MHz i prescalerze 64, ICR3 zwiększa się z częstotliwością
1/250000. Dla 50Hz na wyświetlaczu mam liczbę 5000. Mogę zejść do ok. 2Hz --  
poniżej już się nie da, bo licznik 16-bitowy ICR3 się przepełnia. Jednak 2Hz
jest dla mnie akceptowalnym wynikiem i nie potrzebuję już zwiększać
prescalera, kosztem obniżenia dokładności pomiaru.

Chciałem zapytać
1. Czy kod jest poprawny, a jeśli nie, to co poprawić?
2. Jak poradzić sobie teraz z "odcinaniem" dla częstotliwości mniejszej niż
2Hz?
    Poniżej 2Hz licznik się przekręci i wskazany czas nie będzie poprawny;
może być też sytuacja, gdy sygnał zaniknie (częstotliwość 0Hz). Muszę mieć o
tym informację.
    W moim programie z timerem nie było problemu (pokazywały się "---" na
wyświetlaczu dla zbyt niskich częstotliwości) -- tu nie wiem, jak to dobrze
rozwiązać.

R.


Re: AVR ATmega, pomiar częstotliwości przeb iegu, prośba o sprawdzenie kodu
W dniu 2011-02-10 16:09, Robbo pisze:

Quoted text here. Click to load it


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). Zamiast tego posłuż się
ATOMIC_BLOCK
http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html

Quoted text here. Click to load it

Albo zmniejsz taktowanie timera, albo wykorzystaj przerwanie od
przepełnienia do "dorobienia" trzeciego bajtu do licznika.

Re: AVR ATmega, pomiar częstotliwości przebiegu, p rośba o sprawdzenie kodu
Quoted text here. Click to load it

Czy chodzi o coś takiego (pisane z pamięci)?

volatile unsigned long icr, prevIcr;
volatile char overflow;

SIGNAL (SIG_INPUT_CAPTURE3)
{
    icr = ICR3;
    acutalDuration = icr - prevIcr + overflow * 0xffffUL;
    overflow = 0;
    prevIcr = icr;
}

SIGNAL (SIG_INPUT_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();

        sprintf(s, "%ld  ", actualDurationLatch);
        LCDwriteString(s);
    }
}

Jeśli chodzi o coś takiego, to mi nie działa. "overflow" impulsuje czasem na
wartość "1", bo ICR3 nieustannie się przepełnia.
Czy ktoś może wskazać, jak poprawnie zastosować rozwiązanie z
przepełnieniem?
To mogłoby być dobre rozwiązanie "odcięcia" niskich częstotliwości, tylko
nie wiem, jak poprawnie to zrealizować.

R.


Re: AVR ATmega, pomiar częstotliwości przeb iegu, prośba o sprawdzenie kodu
W dniu 2011-02-10 16:27, Robbo pisze:
Quoted text here. Click to load it

Po pierwsze jak obydwa przerwania (przechwytywanie i przepełnienie)
zostaną zgłoszone jednocześnie, to będziesz miał nieprawidłowy wynik.
Trzeba w przerwaniu od przechwytywania sprawdzać obecność nieobsłużonego
przepełnienia. Zmienna prev powinno pamiętać nie tylko stan licznika
sprzętowego, ale i software'owego. Zamiast mnożyć 65535 powinieneś
mnożyć przez 65536, a zdecydowanie lepiej przesunąć liczbę o 16-bitów
(albo jeszcze lepiej użyć unii liczby 32-bitowej z tablicą 4 bajtów, bo
gcc ma słabą optymalizację przy obsłudze długich liczb na avr). Do tego
trzeba pamiętać o ręcznym rzutowaniu na 32-bity przed przesunięciem, bo
kompilator tego za ciebie nie zrobi (co najwyżej zrobi promocję do
16-bitów, bo tyle ma int).


Site Timeline