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

Atmega8 - Implementacja przerwań dla DMX z UART i timerem RS485

maciofeles 20 Sie 2012 22:47 2622 14
REKLAMA
  • #1 11228927
    maciofeles
    Poziom 10  
    Witam buduje sterownik Dmx do sterowania oświetleniem scenicznym i mam problem w protokołem ponieważ DMX bazuje na RS485 i posiada dodatkowe ramki takie jak:
    2. Break.
    3. Mark After Break (MAB).
    4. Start Code (SC).
    5. Mark Time Between Frames (MTBF).
    6. Channel Data (CD).
    7. Mark Time Between Packets (MTBP).
    Problem jest taki że sama ramkę danych wysyłam po UART tak jak Rs232 i jest ok, ale żeby nadać resztę to muszę zrobić przerwanie i załączyć wtedy np timer. No i nie wiem jak to wykonać w praktyce by to się tak przełączało miedzy sobą.
    Proszę o jakieś rady:

    oto moj kod:
    Kod: text
    Zaloguj się, aby zobaczyć kod
    Proszę na przyszłość używać tagu [syntax=C] do wklejania kodu...[/syntax].
    Poprawiłem, LordBlick
  • REKLAMA
  • #2 11249229
    mickpr
    Poziom 39  
    maciofeles napisał:
    Problem jest taki że sama ramkę danych wysyłam po UART tak jak Rs232 i jest ok, ale żeby nadać resztę to muszę zrobić przerwanie i załączyć wtedy np timer. No i nie wiem jak to wykonać w praktyce by to się tak przełączało miedzy sobą.


    Przełączanie (przerwania) realizuje sprzętowo Atmega, nie wiem więc o co chodzi?
    Chcesz aby w momencie transmisji wywołać jakąś podprocedurę, aby ta coś zrobiła ekstra (włączyła timer)? Do tego przerwanie nie jest potrzebne.
    Więc co to za problem? Wyjaśnij dokładniej, wtedy będzie można ci pomóc.
  • #3 11255705
    maciofeles
    Poziom 10  
    Chodzi o to że protokół dmx ma takie ramki jak na obrazku Atmega8 - Implementacja przerwań dla DMX z UART i timerem RS485 na razie za pomocą UART zrealizowałem ramkę danego kanału, które to dane wysyłam za pomocą nóżki Tx, ale brakuje mi jeszcze mi początkowej ramki
    "Mark After Break (MAB)" MAB następuje natychmiast po zakończeniu Break i ma postać impulsu HI o czasie minimum 8 mikrosekund (2 bitów).
    i ramki
    Mark Time Between Packets (MTBP). Po nadaniu danych ostatniego kanału linia zostaje wprowadzona w stan HI. Czas ten to MTBP, może wynosić od 0 do 1 sekundy.

    Te ramki chciałem też wysyłać poprzez Tx ale dzięki zastosowaniu timera.
    Wiec jak dla mnie powinno wyglądać to wszystko tak: timer wysyła MAB potem przerwanie i uart wysyła ramki dla danych kanałów, po nadaniu danych ostatniego kanału znów timer wysyła (MTBP) potem uart znów kanały itd...

    I właśnie mam problem jak wysyłać te dwie ramki tym timerem miedzy nadawaniem po uarcie danych dla kanałów.
  • #4 11255753
    mickpr
    Poziom 39  
    A nie prościej będzie zrobić to całkowicie programowo, zamiast mieszać w to UART?
    Skoro masz to tak dobrze rozpisane - prościej będzie "machać pinami", niż rozkładać na czynniki pierwsze UART i w końcu stwierdzić, że się nie da.

    Jeśli uważasz, że za bardzo to obciąży Atmegę zrób tą funkcjonalność na dodatkowym uC (np. Attiny85). Za taki "uniwersalny mikro-sterownik DMX" myślę, że paru ludzi cię ozłoci. :)
  • #5 11256933
    maciofeles
    Poziom 10  
    Znajomy magister z który zachęcił mnie do budowy tego sterownika polecił mi uart bo łatwo w nim ustawić transmisje 250kHz i wysyłanie jest po rs232 więc bardzo zbliżone do rs485 na którym bazuje DMX512, więc jeśli teraz uporam się z dodaniem tych dwóch ramek to będzie już transmisja DMX. Ale jeśli można się z tym nadawaniem uporać programowo to z chęcią wysłucham i spróbuje. Bo ogólnie jak będę miał nadawanie to chce rozwijać ten sterownik o wyświetlacz itp. Więc jeśli możesz w jakiś sposób poratować lub coś doradzić itp to czekam. Bo do roku studenckiego jeszcze miesiąc to może uda mi się mocniej przysiąść nad tym...
  • REKLAMA
  • Pomocny post
    #6 11256974
    mickpr
    Poziom 39  
    Nadajnik możesz to zrobić w oparciu o timer, na którym ustawisz sobie określony zegar - np. te porządane 250 kHz.
    Być może musisz ustawić 500kHz - aby wyłapywać nie tylko czas cyklu, ale i jego zbocze narastające i opadające.
    Potem budujesz sobie kolejkę danych (bufor fifo) - która stanowi podstawę dla timera - do "machania określonym pinem".
    Wysłanie określonej ramki polega potem na wpisaniu określonej kolejki do bufora fifo.
    Tak bym zrobił tylko jeśli potrzebujesz sam nadajnik.

    Drugi sposób (na odbiornik i nadajnik) - to realizacja z odliczaniem przerw między impulsami.
    W podobny sposób są realizowane programowe SPI i I2C, których przykłady bez problemu znajdziesz na Internecie.
    Rozwiązanie takie pochłania 100% czasu procesora w przeciwieństwie do poprzedniej metody. Ma jednak tą zaletę nad poprzednią, że możesz (jako odbiornik) synchronizować się do nadajnika.
    Przykładowo przychodzi ci sygnał z twojego DMX'a - czy jakiegokolwiek urządzenia o częstotliwości nie 250, a 252 kHz. Wyłapujesz zbocze i czekasz na następne. Możesz uwzględnić tolerancję że impuls ma być w czasie od...do, a jak go nie ma to jest błąd.

    Jeśli potrzebujesz tylko wysyłać dane - pierwszy sposób jest wystarczający.
    Jeśli odczytujesz - polecam drugi sposób (również dla nadajnika).

    Nie wiem czy przybliżyłem moje (nie moje w sumie) pomysły.
  • #7 11257612
    Andrzej__S
    Poziom 28  
    Odniosę się tylko do nadajnika, bo mam wrażenie, że autor wątku w tej chwili właśnie nadajnik próbuje wykonać.
    mickpr napisał:
    Nadajnik możesz to zrobić w oparciu o timer, na którym ustawisz sobie określony zegar - np. te porządane 250 kHz.
    Być może musisz ustawić 500kHz - aby wyłapywać nie tylko czas cyklu, ale i jego zbocze narastające i opadające.
    Potem budujesz sobie kolejkę danych (bufor fifo) - która stanowi podstawę dla timera - do "machania określonym pinem".
    Wysłanie określonej ramki polega potem na wpisaniu określonej kolejki do bufora fifo.

    Zakładając maksymalną częstotliwość taktowania 16MHz i częstotliwość przerwań timera 250kHz, przerwanie będzie występować co 64 takty. Z kolei zakładając, że nic nie trzeba odkładać na stos, minimum 9 taktów zajmuje wejście i wyjście z obsługi przerwania. Zostaje tylko 55 taktów, a przecież niektóre instrukcje zajmują więcej, niż 1 takt. Wbrew pozorom, w tej procedurze obsługi przerwania parę rzeczy trzeba zrobić, żeby "machnąć" tym bitem. Jesteś pewien, że te 55 taktów wystarczy?

    Pisząc procedurę w C, szczególnie bez użycia atrybutu ISR_NAKED, raczej nie ma szans na sukces. Może jest szansa w przypadku napisania procedury w assemblerze i przypisanie niektórych zmiennych (takich jak aktualny wskaźnik bajtu w buforze czy maska bitowa) na sztywno do rejestrów, żeby nie trzeba było ich odczytywać i zapisywać do RAM. Rozmiar bufora i jego adres w pamięci RAM też mogą być istotne z punktu widzenia optymalizacji kodu.

    Ja myślę jednak, że można to zrobić za pomocą USART'a i timera odpowiednio konfigurując timer i manipulując jego taktowaniem (włącz/wyłącz) oraz maskując w odpowiednich momentach przerwania. Algorytm byłby wprawdzie nieco zawiły, ale przerwania rzadsze i procedury obsługi przerwań niezbyt długie, więc korzyści spore. Miałbym nawet na to pomysł, tylko w obecnej chwili nie mam czasu, żeby go przedstawić.
  • #8 11258884
    Andrzej__S
    Poziom 28  
    Zakładam, że można pominąć czasy Mark Time Between Frames (MTBF) oraz Mark Time Between Packets (MTBP), ponieważ zgodnie ze standardem mogą one być równe 0.
    Gdyby przyjąć, że zmiany w poszczególnych kanałach nie są od siebie zależne, czyli nie trzeba zmienić najpierw wszystkich (lub wybranej grupy) wartości kanałów, a dopiero później przesłać je w jednym pakiecie, można uruchomić na początku funkcji main() niekończącą się pętlę przerwań, która będzie w kółko wysyłać zawartość bufora zawierającego dane, a później w pętli nieskończonej umieścić kod, który odpowiednio będzie zmieniał wartości poszczególnych elementów tablicy wysyłanego bufora (no i ewentualnie inne funkcje).

    Spróbowałbym rozwiązać to następująco:

    Konfigurację USART znasz. Bit TXEN w UCSRB równy 1, co włącza funkcjonalność pinu TXD w stanie Idle (stan wysoki). Zmieniłbym tylko wysyłanie danych - zamiast funkcji USARTWriteChar() użyć przerwania UDRE, tylko w tym momencie nie włączaj jeszcze zezwolenia na to przerwanie.

    Konfiguracja timera (niestety tylko Timer1 nadaje się dla mojego rozwiązania):
    - tryb fast PWM z cyklem zliczania równym Break + Mark After Break (MAB) zapisanym w ICR1 i taką wartością OCR1A, aby przerwanie od OCF1A pojawiło się po czasie Break,
    - włączona obsługa przerwań OCIE1A i TOIE1 w TIMSK,
    - tymczasem wyłączone taktowanie (bity CS12:CS11:CS10 = 0 w TCCR1B).

    Konfiguracja pinu PD1 (TXD):
    - wyjście w stanie niskim.

    Zasada działania.

    Start transmisji (we funkcji main() przed pętlą nieskończoną):
    - wyzerowanie TCNT1,
    - ustawienie TXEN w UCSRB na 0 - wyłączy TXD a włączy PD1 (stan niski),
    - włączenie taktowania timera 1 (bit CS10 = 1 w TCCR1B),
    - włączenie globalnej flagi zezwalającej na przerwania.

    Po czasie Break następuje przerwanie od OCF1A w którym następuje:
    - ustawienie TXEN w UCSRB na 1 - wyłączy PD1 a włączy TXD w stanie Idle (stan wysoki).

    Po czasie Mark After Break (MAB) nastąpi przerwanie TOV1 w którym należy:
    - zatrzymać taktowanie timera (bity CS12:CS11:CS10 = 0 w TCCR1B),
    - wyzerować TCNT1, bo coś tam na pewno zdążył zliczyć podczas wejścia w procedurę obsługi,
    - zezwolić na przerwania od UDRE (flaga UDRIE w UCSRB) - USART zaczyna wysyłać dane.

    Przerwanie UDRE:
    - zadeklarować zmienną statyczną i (typ zależy od rozmiaru bufora) i nadać początkową wartość 0,
    - jeśli i jest mniejsze od rozmiaru bufora, pobrać z bufora element o indeksie i, a następnie wpisać do UDR, wyzerować flagę TXC w UCSRA (patrz datasheet, jak to zrobić) i zwiększyć zmienną i o 1,
    - jeśli nie jest mniejsze, wyzerować zmienną i, wyłączyć zezwolenie na przerwanie UDRE (UDRIE) oraz włączyć zezwolenie na przerwanie TXC (TXCIE).

    Przerwanie TXC:
    - wyłączyć zezwolenie na przerwanie TXC (TXCIE) (koniec transmisji, można zacząć od nowa),
    - wyzerować TXEN w UCSRB - wyłączy TXD a włączy PD1 (stan niski) - rozpoczynamy Break,
    - włączyć taktowanie timera 1 (bit CS10 = 1 w TCCR1B) i pętla zaczyna się od nowa.

    Algorytm nie sprawdzony, ale powinien działać. Może ewentualnie być konieczne dopracowanie szczegółów. Pomiędzy procedurami obsługi przerwań powinno być wystarczająco dużo czasu na wykonanie dodatkowych funkcji, np. obsługę wyświetlacza.
  • #9 11262522
    Konto nie istnieje
    Konto nie istnieje  
  • #10 11263088
    Andrzej__S
    Poziom 28  
    albertb napisał:
    To to samo co 15 zer a potem ramka danej o odpowiedniej wartości, którą przecież już umiesz wysłać.
    Czyli zaczynając transmisję zerujesz wyjście, czekasz 15 taktów, wysyłasz magiczny znak, a potem już Twoje dane kanałów.

    Proste rozwiązania są dobre, tylko że:
    > 15 taktów z częstotliwością 250kHz = (przy taktowaniu 16MHz) 960 taktów zmarnowanych na czekanie.
    > Obawiam się też, że pomiędzy tymi 15 zerami a rozpoczęciem transmisji może pojawić się krótki impuls poziomu wysokiego spowodowany włączeniem transmitera USART. Włączenie bitu TXEN powoduje włączenie TxD w stanie Idle (czyli wysokim), po którym dopiero następuje bit startu (stan niski). Nie jestem pewien, czy uda się tego uniknąć.
    > Kolega już umie wysłać ramkę, ale robi to używając funkcji oczekujących na zwolnienie rejestru UDR. Jeśli chce ten program w dalszej perspektywie rozbudować (co deklarował) to raczej czarno to widzę, bo będzie miał czas na inne funkcje tylko pomiędzy wysyłaniem poszczególnych pakietów.

    Moje rozwiązanie jest może nieco zawiłe, ale wykorzystuje zasoby sprzętowe i podejrzewam, że nie zajmie mikorkontrolerowi więcej niż 10% czasu. Gdyby problemem było to, że transmisja idzie w pętli, to po małych modyfikacjach algorytmu można zrobić tak, żeby każdy pakiet inicjować osobno. Oczywiście nie mogę nic nikomu narzucić, każdy robi jak woli. Ja staram się tylko przedstawić zalety mojego rozwiązania.
  • REKLAMA
  • Pomocny post
    #11 11263405
    Konto nie istnieje
    Konto nie istnieje  
  • Pomocny post
    #12 11263431
    Dar.El
    Poziom 41  
    Dla BREAK ustawiasz 125kHz, 9 bitów danych i jeden bit stopu. Wystarczy tylko UART.
  • Pomocny post
    #13 11263735
    Andrzej__S
    Poziom 28  
    @Albert B.:
    albertb napisał:
    Zwróć uwagę, że ja także podaję to jako alternatywę nie krytykując Twojego rozwiązania.

    Przepraszam, nie chciałem nikogo urazić czy krytykować. Sam początkowo pomyślałem: przecież to banalnie proste, więc w czym problem. Zacząłem kombinować podobnie jak Ty, ale właśnie napotkałem problemy, o których napisałem powyżej.

    albertb napisał:
    Te 960 taktów nie musi być zmarnowane. Równie dobrze możesz ustawić timer i dalszą transmisję inicjuje jego przerwanie.

    No właśnie tak to próbowałem rozwiązać, tylko skoro już uruchomimy ten timer, to co stoi na przeszkodzie odmierzyć dwa czasy Break i Mark After Break (MAB) wykorzystując przerwanie od porównania do zmiany stanu pinu w międzyczasie, a później rozpocząć normalną transmisję UART.

    albertb napisał:
    Trzeciego zarzutu nie rozumiem. On nie ma nic wspólnego z proponowaną metodą

    Chodziło mi o to, że oparłeś swoje rozwiązanie o umiejętność wysyłania ramek przez autora wątku. Jednak funkcja wysyłająca dane, z której korzysta, oczekuje na zwolnienie rejestru UDR. Jeśli będzie wysyłał dane w pętli, to procesor będzie cały czas trwania transmisji w stanie oczekiwania. Moim zdaniem lepiej podawać dane w przerwaniu UDRE, a flagi TXC użyć do określenia końca transmisji. Autor wątku pisał o chęci rozbudowy tego programu w przyszłości, więc dobrze byłoby gdyby transmisja nie konsumowała zbyt wiele czasu procesora.

    Dar.El napisał:
    Dla BREAK ustawiasz 125kHz, 9 bitów danych i jeden bit stopu. Wystarczy tylko UART.

    Faktycznie to powinno zdać egzamin. Wprawdzie daje to (w przeliczeniu na 250kHz) 20 zer i 2 jedynki (Break powinien chyba mieć min. 22 zera), ale gdyby odpowiednio dobrać częstotliwość transmisji UART'a (trochę mniejszą od 125kHz) to może się udać.
    Ja próbowałem rozwiązać to za pomocą timera, ponieważ daje on możliwość dokładnego i łatwego odmierzenia praktycznie dowolnego czasu. Nie wziąłem pod uwagę, że dokładność nie jest tu de facto krytyczna. "Minimum 22 takty" to nie "dokładnie 22 takty" itd. Rozwiązanie Dar.El zapewne zda egzamin i pozwoli zaoszczędzić timer, który może przydać się do czegoś innego.

    Nigdy nie ośmieliłbym się twierdzić, że moje rozwiązanie jest najlepsze czy jedynie słuszne. Jeśli ktoś w ten sposób odebrał moje wypowiedzi, to przepraszam jeszcze raz. Starałem się tylko znaleźć sposób, który będzie najmniej obciążał procesor podczas transmisji, aby można było powierzyć mu jeszcze inne zadania.
  • #14 11285791
    maciofeles
    Poziom 10  
    Czyli do wysyłania całych tych pakietów zastosować tylko uart? Tylko z inna konfiguracja przy ramce break?
  • REKLAMA
  • #15 11320460
    maciofeles
    Poziom 10  
    Dobra juz sobie poradziłem z tym wysyłaniem po uarcie i ładnie to smiga teraz mam problem jak gromadzic i odczytywac programy napisane dla urządzeń DMX. Chyba najlepiej mieć programy w Epromie jako tabela liczb hex następnie pobierać cześć tabeli wysyłać do SRAM i przypisywać do zmiennej jaka będzie wysyłana. Dobrze myślę czy ktoś ma inny pomysł?
REKLAMA