logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[Atmega16] [C] Badanie warunku w przerwaniu

Krokus22 21 Mar 2011 22:36 1821 18
  • #1 9306546
    Krokus22
    Poziom 19  
    Witam, piszę ostatnio program na Atmega16, odtwarzający melodie midi, przy wolniejszych utworach wszystko działa wspaniale, przesyłałem nawet informację na terminal i bajty wspaniale się zgadzają. Generalnie przychodzą 3 bajty, z których pierwszy jeśli jest równy 90, gram nutkę występującą po nim, jak 80 to ją wyłączam, jak 0xC0 i wyżej do 0xC9 to znaczy że zmienia się program i jak 0xB0 do 0xB9 to bajt kontrolny, który jest do niczego nie potrzebny w mojej aplikacji, przykładowa informacja midi:

    C0 51 90 46 78 80 46 40 90 46 5A

    Oznacza zmień program na 55 (jedyna dwubajtowa informacja w ciągu danych), dalej włącz nutkę (90) o numerze 46 (jakis tam dzwiek, mam to w tablicy) i 78 to siła naciśnięcia klawisza. To jest koniec kompletnej informacji. Dalej po 80 mamy 46 co nie oznacza nic innego, jak wyłącz dźwięk o numerze 46, całość powtarza się aż do końca piosenki, dla zainteresowanych kody midi są tutaj: http://www.onicos.com/staff/iz/formats/midi-event.html Mój problem jest następujący:

    Dane są zadeklarowane ( w mainie zainicjalizowane zmienne typu volatile ):

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod



    Fragment kodu przerwania:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    W mainie pętla nic nie robi, timer pracuje w trybie CTC, zegar 16Mhz, jak wspomniałem wszystkie dźwięki odtwarzane są płynnie, póki nie ma szybkozmiennych szesnastek itd, wtedy jakby gubił dźwięki i niektóre przedłużał. Pytanie moje dotyczy tego, jak szybko i bezpiecznie sprawdzać warunki na przychodzący bajt w przerwaniu? Czy instrukcja switch/case rozpisana na kilkadziesiąt przypadków jest szybsza niż zastosowane przeze mnie ify? Ogólnie zależy mi na tym, żeby po nadejściu bajtów z serii 0xB0..B9 wyłączało granie, 0xC0 ładowało tylko dwa bajty i żeby zawsze bajty kolejne (z serii 3-bajtowych wartościowych) były ładowane tak, że środkowy z bufora jest jednocześnie indeksem do tablicy.
  • #2 9306632
    dondu
    Moderator na urlopie...
    Tak na raty:

    SWITCH vs IF
    Możesz podglądnąć kod wygenerowany przez kompiler i da Ci to jakieś pojęcie o szybkości tych dwóch wariantów.
    Ale patrząc na Twój kod to raczej IF-y będą szybsze niż jak to piszesz kilkadziesiąt CASE.

    Dodano po 3 [minuty]:

    Na jakim zegarze pracuje Twoja ATMEGA?

    Dodano po 3 [minuty]:

    Szczerze mówiąc nie bardzo widzę miejsc, w których coś może spowalniać Twoje przerwanie . Jedynie LCD, ale masz je zakomentowane więc także nie.

    Dodano po 4 [minuty]:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #3 9306705
    Krokus22
    Poziom 19  
    Jak napisałem, pracuje na częstotliwości 16MHz, dodam że wolne odtwarzanie gra jak należy, dźwięki generowane przez CTC mierzone częstościomierzem pasują, dodam, że baud jest nietypowy bo 31250, ale napisałem program wysyłający po RSie dane z midi do komputera i wszystko jest w porządku, problem jest właśnie z optymalizacją tego kody i obsługi przerwania? Gdzie może tkwić to opóźnienie? Zegar jest przecież naprawdę szybki. Pozdrawiam
  • #4 9306726
    michalko12
    Specjalista - Mikrokontrolery
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    I nie oszczędzaj miejsca na kod to to nie terminal 80x40.
    Przez te oszczędzanie program jest nieczytelny
  • #5 9306735
    dondu
    Moderator na urlopie...
    16MHz - faktycznie, umknęło mi.

    Hmmm, ....
    - jaką masz optymalizację włączoną?
    - czy warunki się zazębiają dokładnie (nie ma żadnych luk albo podwójnych wystąpień) - nie sprawdzałem.

    Dodano po 1 [minuty]:

    michalko12 napisał:
    Przez te oszczędzanie program jest nieczytelny

    Bardzo słuszna uwaga.
  • #6 9306764
    tmf
    VIP Zasłużony dla elektroda
    michalko12: case nie przyjmuje zakresów, to nie Pascal.
    Krokus22 - te zmienne, które wykorzystujesz w przerwaniu są volatile, co sugeruje, że jeszcze gdzieś z nich korzystasz. I zapewne pojawia się problem atomowości dostępu do nich, co z kolei generuje kolejne problemy...
    switch może być szybsze niż wielokrotne if'y jeśli możliwe jest stworzenie look up table, dodatkowo kolejne wartości najlepiej jeśli będą kolejnymi liczbami całkowitymi. U ciebie prawdopodobnie niewiele to poprawi sytuację.
  • #7 9306781
    michalko12
    Specjalista - Mikrokontrolery
    tmf napisał:
    michalko12: case nie przyjmuje zakresów, to nie Pascal.

    No patrz, a GCC przyjmuje, a to nie pascal.
    Case-Ranges
  • #8 9306799
    dondu
    Moderator na urlopie...
    Krokus22 napisał:
    Ogólnie zależy mi na tym, żeby po nadejściu bajtów z serii 0xB0..B9 wyłączało granie, 0xC0 ładowało tylko dwa bajty i żeby zawsze bajty kolejne (z serii 3-bajtowych wartościowych) były ładowane tak, że środkowy z bufora jest jednocześnie indeksem do tablicy.

    Patrząc na to co piszesz powyżej sądzę, że problem leży w niewłaściwym doborze warunków i/lub ich kolejności. Sprawdź je dokładnie uporządkuj kod wywal zakomentowane linie i wklej ponownie używając [ syntax=c] .... [ /syntax]
    bez spacji po znaku [

    Będzie nam łatwiej analizować kod
  • #9 9306815
    Krokus22
    Poziom 19  
    Popracuję nad tym i jutro powiadomię o rezultatach, nie spodziewałem się takiego zainteresowania, bardzo dziękuję :)
  • #10 9306832
    michalko12
    Specjalista - Mikrokontrolery
    dondu napisał:
    Będzie nam łatwiej analizować kod


    Stanowczo łatwiej
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #11 9306894
    tmf
    VIP Zasłużony dla elektroda
    michalko12 napisał:
    tmf napisał:
    michalko12: case nie przyjmuje zakresów, to nie Pascal.

    No patrz, a GCC przyjmuje, a to nie pascal.
    Case-Ranges


    Ale to jest rozszerzenie specyficzne dla gcc, język C nie dysponuje takim mechanizmem!
    W efekcie kod staje się nieprzenośny, a nic na tym nie zyskujemy, bo z takiego zakresu nie da się zbudować sensownej look up table, w efekcie niczym się to nie różni od serii ifów.
  • Pomocny post
    #13 9306959
    michalko12
    Specjalista - Mikrokontrolery
    tmf napisał:

    Ale to jest rozszerzenie specyficzne dla gcc, język C nie dysponuje takim mechanizmem!
    W efekcie kod staje się nieprzenośny, a nic na tym nie zyskujemy, bo z takiego zakresu nie da się zbudować sensownej look up table, w efekcie niczym się to nie różni od serii ifów.


    Człowiek nic nie napisał o kompilatorze więc przyjąłem że standardowo korzysta z GCC które ma jeszcze więcej fajnych rozszerzeń z których cały czas korzystam.
    A co do przenośności, nawet JAVA nie zawsze jest nieprzenośna.

    Wracając do tematu...
    Ciężko zrozumieć o co chodzi w tym kodzie, tym bardziej, że z MIDI nie miałem do czynienia, ale szedłbym w takim kierunku:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • Pomocny post
    #14 9306960
    tmf
    VIP Zasłużony dla elektroda
    ISR to tylko makro, w szczególności jedno define załatwia sprawę. Aczkolwiek masz rację, przerwania to też rozszerzenie standardu, tyle, że bez tego rozszerzenia nie można się obejść, bez przedziałów da się. Oczywiście każdy pisze jak uważa za stosowne, a ja nie zamierzam nikogo nawracać. Po prostu stwierdzam pewien fakt.
  • #15 9306988
    Krokus22
    Poziom 19  
    Witam, teraz pokażę jak tymczasowo uprościłem sprawę (chociaż niekoniecznie dobrze, ale teraz teoretycznie zniknęły problemy z atomizacją itd.). Martwi mnie jednak, że korzystając ze swojej tablicy, jak od przychodzącej nutki zamiast 24 odejmę 27 (trochę inna generowana częstotliwość), wówczas wolnozmienne gra dobrze, a szybkie gubi nuty, i w drugą stronę analogicznie, szybkie grane dobrze są na 24, a wolne na 26 dają dziwne dźwięki, co ciekawe nie zależy to od tempa, jak nawet ustawie 50 (co jest bardzo wolne), efekt też występuje, oto kod:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod



    Inicjalizacja UARTA:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod



    I inicjalizacja CTC:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #16 9307152
    dondu
    Moderator na urlopie...
    Po tej zmianie zrobiłeś bardzo niebezpieczny algorytm polegający na czekaniu w przerwaniu na odbiór znaku z USART.
    Zdefiniowałeś funkcję USART_Receive() z while() w środku i używasz jej nawet 2 razy pod rząd w przerwaniu.
    To zaprzeczenie istoty przerwań i potencjalne miejsce powstawania błędów.

    Mam propozycję. Narysuj algorytm tego co chcesz osiągnąć, bo według mnie tutaj leży problem.
  • #17 9307624
    Krokus22
    Poziom 19  
    Wiem o tym, natomiast usunąłem w ten sposób zmienne volatile, docelowo tak nie będzie, to tylko w celach testów, jak tylko wrócę, narysuję algorytm.
  • Pomocny post
    #18 9307978
    dondu
    Moderator na urlopie...
    Krokus22 napisał:
    Wiem o tym, natomiast usunąłem w ten sposób zmienne volatile, docelowo tak nie będzie, to tylko w celach testów, jak tylko wrócę, narysuję algorytm.

    :D:D:D
    A w czym Ci przeszkadzały? Dla testów także nieodpowiednie.
    TMF pisząc:

    tmf napisał:
    ... zmienne, które wykorzystujesz w przerwaniu są volatile, co sugeruje, że jeszcze gdzieś z nich korzystasz. ...

    ...miał na myśli (tak sądzę), że być może zmieniasz je także w innym miejscu, a nie że nie można ich wykorzystywać w przerwaniu.

    Wróć więc do pierwotnego rozwiązania ze zmiennymi volatile i bez while() w przerwaniu.
    Możesz także kombinować tak, aby tylko niezbędne zmienne były volatile, a reszta lokalne w przerwaniu.
  • #19 9627478
    Krokus22
    Poziom 19  
    Witam, generalnie po dużych przeróbkach w programie mogę powiedzieć że całość nie działała z powodu... przekłamań w transmisji szeregowej. Dziękuję wszystkim za pomoc, pozdrawiam.
REKLAMA