Elektroda.pl
Elektroda.pl
X
Arrow Multisolution Day
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

ATM8/C - częste wykonywanie się przerwań

Modecom601 26 Lip 2012 23:02 2651 30
  • #1 26 Lip 2012 23:02
    Modecom601
    Poziom 13  

    Witam!
    Dwa dni temu zadałem sobie takie oto pytanie:
    Czy multipleksowanie np. 4 wyświetlaczy przez Timer tak szybko, by nie było widać efektu mrugania nie przeszkodzi wykonywaniu się głównego programu, np. mruganiu diody co 1s?

    Chciałem to sprawdzić i zacząłem działać: uruchomiłem Timer1 na ATM8 w trybie CTC. W przerwaniu COMPA_vect sterowałem 4-ema wyświetlaczami zapalałem je kolejno by oko ludzkie nie dostrzegło mrugania, a w programie głównym tylko zapalałem i gasiłem jedną diodę co 1 sekundę (PORTD^=0X01). Niestety efekt nie był taki jaki chciałem uzyskać, dioda w głównym programie zapalała i gasiła się ale w znacznie dłuższym równomiernym czasie.

    Program jest krótki, ale pytanie kieruję do Was w jaki sposób wykorzystać timer tak, by swoim wywoływaniem nie "przeszkadzać" w prawidłowym działaniu głównego programu?

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Proszę o podsunięcie mi pomysłów ;)

    Zdrawiam

    0 29
  • Arrow Multisolution Day
  • Pomocny post
    #2 26 Lip 2012 23:43
    perlon
    Poziom 19  

    Pokaż procedurę obsługi przerwania i pętlę główną bo samo ustawienie timer1 niewiele mówi.
    Obsługę diody LED można zrobić na Timer0, lub nawet w procedurze obsługi Timer1 stawiać flagę zmiany stany diody a w pętli głównej sprawdzać stan flagi i odpowiednio reagować. Generalnie procedury obsługi przerwań powinny być jak najkrótsze ale zawsze przerywają pracę pętli głównej. Sam multipleks 100Hz na 4 wyświetlaczach to Mega8 robi w przerwie między kanapkami.

    0
  • #3 26 Lip 2012 23:54
    Modecom601
    Poziom 13  

    Mrugająca dioda co 1s była tylko sprawdzeniem czy program będzie wykonywał się prawidłowo, czy będą interwały 1 sekundowe tak jak założyłem sobie zadając to pytanie. Dobrze wiemy, że w programie głównym różne rzeczy się będą w projektach działy ;)

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Co do tych kanapek, rozumiem, że takie wyświetlanie to dla niego bułka z masłem?:) W tym przykładie preskaler i częstotliwośc proca 8 MHZ to wartości testowe nie oparte jeszcze żadnymi obliczeniami. Może tu tkwi problem, a może w delayach w procedurze przerwania?

    0
  • Pomocny post
    #4 27 Lip 2012 00:20
    perlon
    Poziom 19  

    Nie no w ten w ten sposób obsługa przerwania wywoływanego co 16ms twa 12ms z czego 11.99 to czekanie na nie wiadomo na co i dopiero powrót do pętli głównej. 80% pracy procesora to czekanie. Poz tym multipleks tak zrealizowany nie będzie równy, ponieważ w przerwie między przerwaniami nic się nie pali.
    W żadnym razie delaye w obsługach przerwań !!!
    Zamiast tego ustawiaj flagę cyklicznie zmienianą w przerwaniu i na jej podstawie w petli głównej przerzucaj świecenie segmentów.

    0
  • Arrow Multisolution Day
  • #5 27 Lip 2012 00:26
    Modecom601
    Poziom 13  

    Jeśli skasuję delaye z przerwania to wszystkie wyświetlacze pokazują tą samą liczbę, kiedyś już to sprawdzałem.
    W pętli głównej nie chciałem zajmować się multipleksowaniem wyświetlaczy, bo chciałem np. zająć się odbiorem TSOP'a, gdyby podczas odbierania nagle wywołało mi się przerwanie to tsop nie odczyta rozkaru prawidłowo, nie mówiąc o jakimś LCD'ku.

    0
  • Pomocny post
    #6 27 Lip 2012 00:38
    LordBlick
    VIP Zasłużony dla elektroda

    Modecom601 napisał:
    Jeśli skasuję delaye z przerwania to wszystkie wyświetlacze pokazują tą samą liczbę, kiedyś już to sprawdzałem.
    To kombinuj inaczej - zrób wewnętrzną zmienną statyczną,. W zależności od wartości ustawiaj odpowiednią cyfrę.

    0
  • #7 27 Lip 2012 00:47
    Modecom601
    Poziom 13  

    Nie wiem czy o to samo chodzi, każdy wyświetlacz miał swoją zmienną, która po tablicy skakała, jeśli >9 wyzeruj zmienna jedność a zmienną dziesiątek ++, ta sama sytuacja z godziną. Tylko jeśli była cyfra 01 na wyświetlaczach to musiałem jednak to 0 ciągle zapalać. Sposób jakiś tam mam, ale nie chcę zaprzęgać proca, który może znacznie więcej do takiej błahostki, by tylko tym się zajmował, jeszcze mam dwa przerwania INT0/1.

    Dziś już jest trochę późno by coś kombinować jeszcze, natomiast jutro pobawię się z F_CPU i preskalerem a czas trwania przerwania postaram się skrócić, usuwając opóźnienia.

    0
  • Pomocny post
    #8 27 Lip 2012 01:11
    perlon
    Poziom 19  

    Zmień myślenie o przerwaniach

    Kod: c
    Zaloguj się, aby zobaczyć kod


    No i masz multipleks bez delayów. W pętli głównej wpisujesz do bufora co tam chcesz i kiedy chcesz a przerwanie w odpowiednim czasie i kolejności pobierze sobie odpowiednią daną i ją wyświetli. Obsługa przerwania zajmie kilkanaście instrukcji maszynowych co 16ms a reszta na obsługę pętli głównej.

    0
  • #9 27 Lip 2012 08:45
    Modecom601
    Poziom 13  

    Dzięki za kod, wrzucę go zaraz do megi, natomiast analizując go teoretycznie nie rozumiem jednej rzeczy: w procedurze przerwania mam "zaladowac do portu cyfrę wyświetlacza daną z bufora BuforLED[BiezacyLED]" ale skąd ma proc wiedzieć który wyświetlacz co ma wyświetlić? Chodziło Ci o to, że po każdym sprawdzeniu wartości BiezacyLED ma wyświetlić cyfrę??

    Nie rozumiem też dlaczego co wywołanie przerwania ma być obsłużony tylko jeden wyświetlacz, przecież to wtedy wymaga jeszcze częstszego wywoływania przerwania by pozostałe wyświetlacze świeciły ?? Chyba, że w takim sposobie nie ma 80% czasu czekania procesora.

    0
  • Pomocny post
    #10 27 Lip 2012 09:22
    dondu
    Moderator Mikrokontrolery Projektowanie

    Modecom601 napisał:
    Nie rozumiem też dlaczego co wywołanie przerwania ma być obsłużony tylko jeden wyświetlacz, przecież to wtedy wymaga jeszcze częstszego wywoływania przerwania by pozostałe wyświetlacze świeciły ?? Chyba, że w takim sposobie nie ma 80% czasu czekania procesora.


    Przykład:
    - masz 3 cyfry sterowane multipleksowo.
    - chcesz mieć pewność, by nie migały więc powiedzmy ustalasz, że każdy ma być wyświetlony 50 razy na sekundę.

    50Hz * 3 = 150Hz

    Ustawiasz timer na częstotliwość przerwań około 150Hz i w każdym przerwaniu wyświetlasz kolejną cyfrę wyświetlacza. Przerwanie działa tak szybko, że pozostaje Ci powiedzmy 98% czasu wolnego na wykonanie innych zadań w main() lub innych przerwaniach.

    0
  • Pomocny post
    #11 27 Lip 2012 09:27
    LordBlick
    VIP Zasłużony dla elektroda

    Obsługa przerwania ma być najkrótsza. Najlepsza analogia do życia, to gdy dzwoni telefon, to tylko go odbierzesz, porozmawiasz i koniec, nie będziesz nieruchomo stał, aż zadzwoni następny raz. Podobnie w procesorze są przerwania uzależnione od różnych zdarzeń, zmiany stanu zewnętrznego pinu, opróżnienia bufora transmisji wyjściowej, otrzymania bajtu transmisji wejściowej, przepełnienia licznika/timera, koniec konwersji przetwornika itp itd.
    Wracając do kodu, to Ty ustalasz reguły, jak często następuje przerwanie CTC, a w nim tylko zmieniasz wyświetlaną cyfrę. Cyfta będzie się cały czas świecić aź do następnej zmiany. Niedawno kolega Antystatyczny trenował ten sam problem:
    https://www.elektroda.pl/rtvforum/topic2342398.html

    0
  • Pomocny post
    #12 27 Lip 2012 10:25
    tmf
    Moderator Mikrokontrolery Projektowanie

    Ja dorzucę jeszcze jedno - jakby nie napisał ISR to odmierzanie przerw przez delay w pętli głównej już nigdy nie będzie działało prawidłowo - odliczany czas powiększy się o czas realizacji wszystkich przerwań. To można ominąć w prosty sposób wywalając te delaye i robiąc opóźnienia na timerze.

    0
  • #13 27 Lip 2012 11:39
    Modecom601
    Poziom 13  

    LordBlick napisał:
    Ty ustalasz reguły, jak często następuje przerwanie CTC, a w nim tylko zmieniasz wyświetlaną cyfrę. Cyfta będzie się cały czas świecić aź do następnej zmiany.


    Ok, ustawię by wywoływał się co 150Hz, natomiast skąd Wy wiecie, że 98% czasu procesora to będzie czas dla main? Przez to, ze pozbędę się delayów w przerwaniu? Moje założenie było takie, by w CTC wyświetlacze się multipleksowały, a ich inkrementacja/jakakolwiek zmiana w innych przerwaniach.

    Czyli największą korzyść daje wyświetlenie/zaktualizowanie w przerwaniu tylko jednego wyświetlacza, potem w nast. przerwaniu kolejnego segmentu tak jak pokazał to w kodzie kolega perlon?

    To chyba ze mną jest coś nie tak, że się nie mogę z Wami dogadać :cry: :cry:

    0
  • Pomocny post
    #14 27 Lip 2012 11:49
    LordBlick
    VIP Zasłużony dla elektroda

    Przejrzyj dokładnie temat, do którego linka podawałem wyżej, praktycznie pod koniec to masz gotowca - zmiana segmentów też następuje w tym samym przerwaniu...

    0
  • Pomocny post
    #15 27 Lip 2012 11:52
    tmf
    Moderator Mikrokontrolery Projektowanie

    Liczymy to prosto - (liczba instrukcji poświęcona na obsługę jednego przerwania * liczba przerwań na sekundę)/liczba instrukcji realizowanych przez MCU na sekundę.
    Przy multipleksowaniu najwygodniej jest aby przy kolejnych przerwaniach wyświetlać kolejne wyświetlacze - dlatego, że intensywność świecenia to w przybliżeniu czas świecenia/czas całkowity. Jeśli zapalisz LED na 10us co 1/150 s, to będziesz miał wypełnienie 0,00001/(1/150)=0,015% - idę o zakład, że przy takim wypełnieniu LED będzie sprawiał wrażenie jakby w ogóle się nie świecił. Natomiast jeśli rozbijesz wyświetlanie na kolejne przerwania, to dla 4 wyświetlaczy wypełnienie wyniesie 1/4=25% - jest różnica, prawda?
    BTW, multipleksuje się całe wyświetlacze 7-segmentowe, a nie pojedyncze segmenty. Jakbyś multipleksował pojedyncze segmenty, to znowu masz kłopot z wypełnieniem (duty time) - 7 segmentó*4 wyświetlacze=28 segmentów. Duty time =1/28, czyli 3,6% - świecenie marne.

    0
  • #16 27 Lip 2012 12:07
    Modecom601
    Poziom 13  

    Przepraszam za źle użyte słowo, segment miałem na myśli cały jeden wyświetlacz. Czyli niedość, że sterując każdym wyświetlaczem osobno w przerwaniu zyskuję na jasności to i procesor nie "czeka".

    Obliczyłem coś takiego.
    W przerwaniu mam załóżmy 10 instrukcji(poleceń) * 150Hz bo co tyle wywołuję przerwanie i dzielę to przez liczbę instrukcji wykonywanych przez MCU na sekunde czyli częstotliwosć taktowania 1000000 i wychodzi mi 0,0015 - co z tym dalej?
    Ta liczba, przez którą dziele mi coś nie pasuje tutaj, z drugiej strony skąd mam wiedzieć ile instrukcji na sekunde wykonuje MCU :|


    Przeczytam jeszcze raz "na chłodno" Wasze pomocne komentarze, ustawię przerwanie co 150Hz i zdam relacje, dopóki sam czegoś nie sprawdze nie dowiem się jak to naprawde w praktyce działa

    0
  • #17 27 Lip 2012 12:25
    LordBlick
    VIP Zasłużony dla elektroda

    Modecom601 napisał:
    wychodzi mi 0,0015 - co z tym dalej?
    W promilach to będzie 1,5‰, czyli 0,15%. ;)
    Ale należy zauważyć, że liczy się cykle procesora, bo niektóre instrukcje są wykonywane dłużej. W każdej nocie katalogowej AVR jest lista instrukcji wraz z czasem ich trwania. Kompilator avr-gcc potrafi pokazać jakich instrukcji użył w pliku *.lss.

    0
  • #18 27 Lip 2012 12:31
    tmf
    Moderator Mikrokontrolery Projektowanie

    Kolega wyżej już ci to obliczył :) Co do liczby instrukcji na sekundę - to zależy oczywiście jakich instrukcji, dla AVR typowo w jednym takcie wykonywana jest jedna instrukcja, stąd też można przyjąć, że ta liczba to po ptostu taktowanie procka. Oczywiście takie założenie jest obarczone pewnym błędem, ale czy czas MCU wyniesie 0,15%, czy 0,5% to jest w praktyce bez znaczenia. Jeszcze co do tych 150 Hz - weź pod uwagę, że jeśli masz 4 cyfry, to ich odświeżanie będzie następowało co 150/4 Hz. Niecałe 40Hz to niby dużo, ale lubi to interferować z innymi typami oświetlenia, np. lampami fluorescencyjnymi starej daty (100Hz). W efekcie przy ruchu głową i pewnych kątach patrzenia widać "falowanie". Ponieważ jak widzisz MCU się praktycznie nudzi, warto podnieść odświeżanie, ja zwykle stosuję 1 kHz, co daje mi też możliwość sterowania jasnością LED.

    0
  • #19 27 Lip 2012 12:38
    Modecom601
    Poziom 13  

    Czyli wynik to procent jaki zajmuje procesorowi wykonanie przerwania podczas jednej sekundy czy dobrze rozumuję?

    Z tymi promilami to serio? :D

    Albo ruch głową albo machać urządzeniem ;) Te 150 Hz to jak mówię wartośc do sprawdzenia, zapewne ją podniosę skoro mówisz jak jest, to są narazie dla mnie ćwiczenia by w przyszłości operować tymi zagadnieniami chociaż w 70% jak Wy :)

    Posklejam jakiś kod i wrzucę.

    Dzięki za dotychczas napisane odpowiedzi!! :))

    0
  • Pomocny post
    #20 27 Lip 2012 12:46
    tmf
    Moderator Mikrokontrolery Projektowanie

    Posklejaj kod, a potem go porównaj do mojego kodu - ftp://ftp.helion.pl/przyklady/jcmikr.zip.. Masz tam przykłady takiego multipleksowania. Ponieważ problem jest w sumie banalny, więc nie warto tracić czasu na wynajdywanie koła, lepiej przeanalizują tamte kody i zastanów się dlaczego tak, a nie inaczej :)

    Dodano po 1 [minuty]:

    BTW, co do promili i procentów - pro centum - łac. na sto, pro mille - łac. na tysiąc.

    0
  • #21 27 Lip 2012 12:52
    LordBlick
    VIP Zasłużony dla elektroda

    tmf napisał:
    BTW, co do promili i procentów - pro centum - łac. na sto, pro mille - łac. na tysiąc.
    A na dziesięć tysięcy ? Jest taki fajny znaczek basepoint "‱" i w tej mierze wychodzi 15‱... ;)

    0
  • #22 27 Lip 2012 12:56
    tmf
    Moderator Mikrokontrolery Projektowanie

    Nie spotkałem się z taką miarą, ale można wyrazić w ppm (parts per million), wtedy wyjdzie 1500 :)

    0
  • #23 27 Lip 2012 17:13
    Modecom601
    Poziom 13  

    A więc tak, po Waszych odpowiedziach napisałem kod, który steruje 4 wyświetlaczami w 8-bitowym T0, oraz inkrementacja liczb w 16-bitowym T1.

    1. Chciałem uzyskać częstotliwość wykonywania przerwania dla T0 co 500 Hz, więc częstotliwość taktowania procesora 8Mhz podzieliłem przez preskaler 256 co dało mi 31250 - tą liczbę podzieliłem przez 500(bo tyle chce uzyskać) i uzyskałem 62.5 a ten wynik odjąłem od 255 i wpisałem 192.5 to TCNT0.

    2. Licznik inkrementuje liczby z częstotliwością wykonywania procedury przerwania 1Hz.
    8Mhz/256=31250 - tą wartość wklepałem to OCR1A.

    W trakcie pisania programu nie napotkałem żadnych znaczących przeszkód, trzeba było tylko to napisać, natomiast zrodziły mi się kolejne pytania:

    a)Jak rozumieć ten zapis?

    Kod: c
    Zaloguj się, aby zobaczyć kod

    W warunku jest sprawdzenie jest czy ta zmienna istnieje?, czy ona ma prawidłową wartość z zakresu jakie może przyjąć? czy może ona jest po to by zdekrementować zmienną?
    Ja zrobiłem to inaczej i też działa:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    b) zarówno dla Timera 0 i 1 musiałem ustawić preskaler w dwóch różnych rejestrach a w nocie na stronie 74 jest rysunek, że preskaler jest wspólny dla obu Timerów. Konieczne jest ustawianie dla obu preskalerów wartości? Wpisałem dwa różne preskalery do rejestrów i też działa, jak to naprawde jest z tym?

    (ATM8/8MHZ)
    Wklejam kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    No i na końcu w glównej pętli umieściłem tą diodę zmieniającą stan co 1 s, przez pierwsze 10 sekund nawet równo tykała z wyświetlaczami, potem wewnętrzny rezonator i niedokładność pętli _delay_ms zrobiła swoje :D
    Program działa, lecz chciałbym się od Was dowiedzieć, czy zastosowałem się do Waszych wszystkich porad.

    Z góry dziękuję :)

    Pozdrawiam

    0
  • Pomocny post
    #24 27 Lip 2012 17:24
    LordBlick
    VIP Zasłużony dla elektroda

    Modecom601 napisał:
    Program działa, lecz chciałbym się od Was dowiedzieć, czy zastosowałem się do Waszych wszystkich porad.
    Jak dla mnie , to diodę podepnij pod Timer, to będzie normalnie, a <util/delay.h> powiedz dobranoc... ;)
    Modecom601 napisał:
    Ja zrobiłem to inaczej i też działa:
    Kod: c
    Zaloguj się, aby zobaczyć kod
    Moja wersja:
    Kod: c
    Zaloguj się, aby zobaczyć kod
    ;)

    0
  • Pomocny post
    #25 27 Lip 2012 17:25
    tmf
    Moderator Mikrokontrolery Projektowanie

    Zapis:
    if(BiezacyLED) BiezacyLED--; else BiezacyLED = 3;
    sprawdza czy zmienna BiezacyLED ma wartość różną od zera. W C wartość true to coś różnego od zera, a false to zero. Twój zapis nie jest równoważny - zakłada, że zmienna jest ze znakiem (int), w większości przypadków niepotrzebnie komplikuje to obliczenia, stąd warto używać typów bez znaku (unsigned). Zauważ też, że cały warunek można zapisać prościej: BieżącyLED++ & 0b11;

    0
  • #26 27 Lip 2012 17:35
    Modecom601
    Poziom 13  

    Nie chciałem "podpinać" diody pod Timer, bo chciałem zobaczyć, czy dwa już nawet Timery nie przeszkodzą w prawdiłowym wykonywaniu się głównego bloku programu.

    Teraz zauważyłem, że nie dałem typu dany_wysw, też uint8_t powinien być.


    tmf, nawet w Twojej książce czasem mam problemy ze zrozumieniem zapisów do rejestrów czy do zmiennych, dopiero po rozpisce na kartce,
    np.
    x=(x | 0x0f)^(1<<y) mnie ostatnio rozłożyło :D

    prościej dla programisty czy prościej dla procesora? bo jeśli tylko dla kodoklepcy to ja wolę zostać przy tej wersji, którą rozumiem.

    Lord
    Twoja wersja z dany_wysw działa, jednak po rozpisce jej nie łapie.
    Początkowo ta zmienna ma wartośc 0b00000000
    a bramka AND 0b00000000 z 0b00000011 nadal da 0b0000000000 przy pierwszym wykonaniu przerwania :|

    EDIT: dopisałem uint8_t, ale działa poprawnie tylko z Waszymi sposobami :(

    0
  • #27 27 Lip 2012 18:10
    tmf
    Moderator Mikrokontrolery Projektowanie

    Działa poprawnie tylko z naszymi, po typ uint nie może być <0, więc warunek zawsze daje false i kompilator ten fragment w ogóle wyrzuca - zobacz jak wygląda skompilowany kod.
    Co do and - prześledź jak to się zachowa w kolejnych krokach:
    0 - 0
    1 - 1
    2 - 2
    3 - 3
    4 - 0 - rozpisz to binarnie to sytuacja stanie się jasna.
    A za zapis: x=(x | 0x0f)^(1<<y) jeśli takowy wystąpił to z góry przepraszam :) Ale po jakimś czasie zobaczysz, że sam mimowolnie stosujesz takie skróty :)

    0
  • #28 27 Lip 2012 18:26
    Modecom601
    Poziom 13  

    Z int nie musze mowić, że zadzialało, ale nie o to tu chodzi.

    EDIT: wszystko już jasne :)

    0
  • #29 27 Lip 2012 20:15
    tmf
    Moderator Mikrokontrolery Projektowanie

    Operacja &0b11 maskuje najmłodsze dwa bity - wszystkie bity starsze będą po niej wyzerowanie. Stąd też dla wartości zmiennej w zakresie 0-3 operacja & 0b11 operacja ta nic nie powoduje. Jednak dodanie do 3 jedynki, powoduje że masz 0b100, co &0b11 daje 0 - w efekcie liczenie rozpoczyna się od zera, bez żadnych warunków. Analogicznie jest jeśli od 0 odejmiesz 1 - w wyniku otrzymasz 0b11111111, co & 0b11 da ci 0b11, czyli 3.

    0
  • Pomocny post
    #30 28 Lip 2012 01:15
    perlon
    Poziom 19  

    Patent kolegi tmf z maskowaniem przy inkrementcji bardzo mi się podoba. Oczywiście posiadam książkę ale nie wpadł mi w oko taki smaczek. Warto czytać takich forumowiczów jak tmf jak również LordBlick. Człowiek uczy się całe życie.
    Chciałem się dopytać w innej sprawie. Czy rejestry TCNTx mogą przechowywać inne typy niż całkowite? Czy może zapis

    Kod: c
    Zaloguj się, aby zobaczyć kod

    będzie poddany niejawnej konwersji do typu całkowitego?
    Taka konwersja spowoduje zwiększenie inkrementcji licznika TCNT0 o 1 i w rezultacie wprowadzi bądź co bądź minimalny ale jednak błąd odliczania w stosunku do założeń autora wątku.
    Tak jeszcze dla uzupełnienia 2 linki do opisu zasady działania timerów. Wydały mi się pomocne a być może nie są sprzeczne z regulaminem naszego forum.
    http://maxembedded.wordpress.com/2011/06/22/introduction-to-avr-timers/
    http://maxembedded.wordpress.com/2011/06/24/avr-timers-timer0-2/

    0