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

M2561 - (bascom) tracone dane z UART

20 Wrz 2016 15:26 1314 17
  • Poziom 24  
    Mam taki kłopot; korzystam w programie z transmisji z PC do mikrokontrolera vis RS232 (Atmega 2561, sprzętowy uart, 115200 b).

    W tym celu używam funkcji SERIALIN z opcją BYTEMATCH (znak CR).
    Bufor o długości 200 znaków.

    Kod: vbnet
    Zaloguj się, aby zobaczyć kod


    Dane odbierane z PC obsługiwane są standardowo ;

    Kod: vbnet
    Zaloguj się, aby zobaczyć kod


    Jak widać, odebrany string ląduje w zmiennej Bufor2.
    I niby wszystko jest OK - niestety, nie zawsze. Co któraś odbierana transmisja jest "przyciana". Tzn, w buforze jest tylko część odebranego stringu.

    Podejrzewam, że przyczyną może być to, że w czasie działania ogólnego programu wykorzystywane są timery. I być może przerwania z tych timerów wpływają na poprawny odbiór danych ?

    Jeżeli tak - to jak temu zapobiec ?
    Nie można wyłączać timerów, bo dane z PC przychodzą losowo, czyli nie wiadomo kiedy. Ot - w trakcie działania całości.

    Przerwania z timerów są generowane z częstotliwością 1Hz (timer3) i 1000Hz (timer1).

    Niestety priorytety przerwań są przypisane na sztywno i przerwanie UART jest poniżej przerwań timerów. Próbowałem na początku procedury Serial1charmatch wyłączać przerwania (disable interrupts) i włączać je na wyjściu, ale nie pomaga.
    Spowolnienie transmisji np. do 9600b też nie pomaga.


    A może nie mam racji i przyczyna może być inna ?

    (samo połączenie jest na pewno OK, bo ten sam port jest wykorzystywany przez bootloader, i przesyłanie nawet dużego programu działa poprawnie).
  • Użytkownik usunął konto  
  • Poziom 24  
    Hm, rzeczywiście obsługę "dekodowania" zawartości odebranego stringu można przenieść poza przerwanie. Choć - to akurat rzeczywiście działa :)

    Ale - to raczej nie zmieni problemu z "ginącą" częścią odebranego stringu, bo przecież zawartość bufora się nie zmienia od momentu "poskładania" go w serial1charmatch. Kiedy następuje wywołanie serialcharmatch to zawartość bufora kołowego jest już "ustalona".

    Czyli - jakby problem następował w czasie odbierania poszczególnych znaków chyba ?
    Jeżeli tak, to sposób , który zastosowałeś nie powinien nic zmienić jak sądzę.

    Ale zaraz sprawdzę.

    * co do przerwań - spodziewałbym się raczej, że problem powstaje w ten sposób, że
    kiedy przychodzi kolejny znak na UART, i następuje przerwanie z UARTa, a w tej chwili układ już akurat jest w przerwaniu timera, to może nastąpić utrata tego odebranego znaku ?

    Bo jak rozumiem faktyczne przerwanie uart generuje na każdy znak (i w tych przerwaniach właśnie Bascom sobie sam składa znaki do bufora kołowego, wywołując ewentualnie Serialcharmatch) ?
  • Użytkownik usunął konto  
  • Poziom 24  
    Niestety u mnie te przerwania są "zawalone". Nie było to do tej pory problemem, bo użycie uarta nie było przewidywane do "poważnych celów".
    Niestety na razie nadal jest źle...

    Popatrzę, czy jest możliwe przesunięcie "prac" z timera1 - bo to on pewnie jest źródłem nieszczęścia, jako że jest wywoływany 1000 razy na sek. A jest mocno obciążony;

    Oto jego obsługa ;

    Kod: vbnet
    Zaloguj się, aby zobaczyć kod


    Jak widać, jest tam prosta obróbka sygnału z ADC.
    Jest jednak pytanie - czy nawet maksymalnie "odciążony" timer, kiedy ma przerwania tak często nadal nie będzie blokował poprawnego odczytu UART'a ?

    Z drugiej strony - wydaje mi się, że żeby nastąpiła utrata danych z UARTA, to musiałoby "Zabraknąć" czasu na przetworzenie przerwania z portu szeregowego.
    Bo niezależnie od tego, ile czasu zużyje timer, to jeżeli po nim pozostanie wystarczająco dużo czasu, to przerwanie UARTA powinno zostać obsłużone.

    A choć jak widać timer1 ma co robić, to nadal przecież jest "wolny czas" - działa przecież w tle cała reszta programu, która na czas przerwania UART może zostać zawieszona ?

    Zegar jest wysoki (14Mhz) i wydawałoby się, że POWINNO SIĘ UDAĆ.
    A może problemem jest nie "brak czasu" a jakaś koncydencja ń chwili "wyzwalania" przerwań ? No ale z tego co wiem, to przerwanie do chwili obsłużenia po prostu czeka...

    Aaaa, coś mi teraz przyszło...
    Może sprawa jest zupełnie innego rodzaju - w "głównym" programie chwilami są wyłączane przerwania (disable interrupts). Jest to pozostałość po walce z zakłóceniami obsługi wyświetlacza LCD przez przerwania timerów.
    To w oczywisty sposób może zwyczajnie blokować pracę UARTA.

    Zaraz to sprawdzę...
    _________________
    sprawdziłem - nie pomogło :(
  • Pomocny post
    Użytkownik usunął konto  
  • Poziom 24  
    Wszystko to pięknie, ale zapomniałem o jednej rzeczy.
    Otóż teraz, kiedy testuję tą transmisję, to pokazana obsługa Timer1 nie jest wykonywana. Jak widać, w programie jest "case" i w zależności od sytuacji albo przerwanie wygląda jak w listingu (czyli z obsługą ADC), albo - i tak jest w tej chwili - przerwanie nie robi nic.

    Znaczy jest ;

    Kod: vbnet
    Zaloguj się, aby zobaczyć kod


    I to wszystko. Czyli to nie timer1 zawadza.
    No oczywiście problemem może też być Timer3. Co prawda on jest wykonywany "tylko" raz na sekundę, ale jeżeli transmisja trafi w niego, to też może być kłopot.

    Ale - chyba ta walka nie ma sensu. Nie będę mógł teraz pozmieniać wszystkiego, za dużo byłoby pracy...

    Może spróbuję zrobić taki "workaround" ; po odebraniu przez UART pierwszego znaku, wszystko inne zostanie zatrzymane, aż do chwili odebrania końca stringu.
    Teoretycznie nie powinno to przeszkadzać, bo w sumie "zamrożenie" całego interesu na czas odbioru nic nie robi złego.
    Żeby nie zablokować systemu jeżeli nie nadejdzie spodziewany koniec stringu, wprowadzę ograniczenie czasowe.

    To jest oczywiście zupełnie nieeleganckie rozwiązanie, i dość partackie, no ale może zadziała ?
    ____________________________

    Spróbowałem rozwiązania z blokowanie programu na czas odebrania "całości". Niestety - nadal są problemy. No i jakbym się zastanowił, to powinienem to przewidzieć. Jak wspomniałem, jest w programie parę (a nawet więcej) miejsc, gdzie są wyłączane przerwania.
    Choćby dla odczytu DS18B20 po 1wire. Ale nie tylko. No i w tej sytuacji - kiedy transmisja zacznie się w takim momencie, że przerwania są wyłączone - nic nie pomoże. Cudów nie ma.

    Z usunięciem tych wyłączeń pewnie będzie spory kłopot. Choć chyba trzeba spróbować.

    Jeżeli nie - to być może uratuje mnie zupełnie inne rozwiązania - w układzie jest drugi procesor, który prawie jest wolny. I można by tą transmisję skierować do niego, a z niego już główny MCU (czyli ten którego program omawiamy), mógłby sobie sam odebrać dane w stosownej chwili.

    Jednak wtedy niezbędna jest modyfikacja hardware, która jest właściwie "nie możliwa" logistycznie...

    No i jest ostateczne "obejście" - zbudowanie "interfejsu" RS232<>RS232 w postaci np. atmegi8, która przechwyci przychodzące dane, i na "zapytanie" wysłane przez moje urządzenie te dane zapoda. Oczywiście, to jest już chwytanie się brzytwy :)

    Teraz mam takie pytanie - czy jest w Bascomie możliwość sprawdzenia, czy dany timer jest włączony czy nie ?
    Pewnie można to zrobić sprawdzając rejestry - ale może można łatwiej ?
  • Użytkownik usunął konto  
  • Poziom 24  
    Oczywiście, że wiem jakich timerów używam.
    Chodziło mi o to, żeby nie szukać po dataszicie co i gdzie trzeba sprawdzić.
    No już poszukałem, i zrobiłem sprawdzania rejestrów TIMSK, ale myślałem, że może jest jakaś bascomowa funkcja w rodzaju :

    If timer1=enabled then...

    Z tą obsługą błędów też masz oczywiście rację, tyle, że pisząc dawno temu ten program nie pomyślałem o tym niestety...

    Cytat:
    Ten program po prostu mógłby być napisany lepiej :P


    sam bym tego lepiej nie ujął.
    Po prostu ten program powstawał przez 3 lata, rozwijany w ciągłym braku czasu, z nakładaniem nowych funkcji na stare błędy. Znaczy jak w windows ;)

    Żeby to miało sens, trzeba będzie go w końcu napisać od zera, i to raczej nie w bascomie. Tylko, że na to trzeba dużo czasu, a tego akurat nadal nie ma :D

    ____________________________________________

    Wynik badań jest dziwny - pomogło całkowite wyłączenie timerów.
    ale - nawet jeżeli włączony jest tylko jeden timer (np. timer1), i zasadniczo "nic nie robi", czyli jest tylko ponowne wpisanie wartości timera i powrót z procedury przerwania - to nadal są błędy transmisji.

    Co oznacza, że nie chodzi o brak czasu a jakąś formę konfliktu spowodowanego przez sam fakt powstawania przerwań timerów.
    Aż przejrzałem erraty datasheta, ale nic nie ma takiego...
  • Użytkownik usunął konto  
  • Poziom 24  
    Zmieniłem prędkość na 1200b. No to już jest naprawdę wolno.
    I co ? I nic - nadal gubi znaki.

    Mam taki pomysł. Pierwszy znak zawsze przychodzi poprawnie. Dlaczego ? Cholera wie... Więc - ustawiamy SERIALIN z opcją BYTEMATCH=ALL. Czyli już po pierwszym znaku wywołuje procedurę SERIAL1 BYTERECEIVED.

    Teraz -po pierwszym znaku wyłączamy wszystkie timery. I odbieramy "na piechotę" kolejne znaki, aż do odebrania ostatniego (CR).
    Po odebraniu ostatniego - włączamy timery z powrotem.
    To powinno "zamrażać" program główny na czas odbioru stringu.
    Czy to zadziała ?? Zaraz zobaczymy...

    _______________________________________________________________

    No i zobaczyliśmy, nie za dobrze jest...

    zrobiłem jak napisałem ;

    Kod: vbnet
    Zaloguj się, aby zobaczyć kod


    A obsługa:

    Kod: vbnet
    Zaloguj się, aby zobaczyć kod


    No przecież teoretycznie to NIE MOŻE nie działać.
    Pierwszy przychodzący znak powoduje wyłączenie przeszkadzających timerów.
    Zatem dalej nie powinno być żadnych przeszkód.
    Dopiero odebranie CR włącza timery ponownie.

    Niestety, efekt jest taki:

    W terminalu wysyłam "z ręki" string "1234567890<CR>

    A oto co ląduje w zmiennej BUFOR_PC ;
    Kod: vbnet
    Zaloguj się, aby zobaczyć kod


    Jak widać zdarzają się przekłamania, i to mimo prędkości 9600 !.
    Ale dla prędkości 1200b jest tak samo.

    No jakim cudem ??
  • Pomocny post
    Użytkownik usunął konto  
  • Poziom 24  
    Ha ! no to przynajmniej wiemy, że to nie jest bug Atmela.
    Co prawda, jak pisałem, to jest M2561, program jest "przeportowany" z M128.
    Właściwie bez zmian, tyle że w M2561 nieco inaczej jest z timerami właśnie.

    Ok, zmotywowałeś mnie to dalszego szukania... Co prawda, jak jutro tego nie rozwiążę, to będzie poważny kłopot. No ale nadzieja umiera ostatnia :)

    Bardzo Ci dziękuję. Będę raportował oczywiście...

    ________________________________________________________

    4.10 rano : dziennik pokładowy ;

    Po ciężkiej walce - wreszcie jest OK.
    Niestety, konieczne było przeniesienie wszystkich zadań z timerów
    do głównego programu i oczywiście wywoływanie flagami.

    Oznacza to jeszcze konieczność dalszego uporządkowania programu, gdyż oczywiście niektóre rzeczy przestały działać jak należy.
    Ale - chyba jestem już na dobrej drodze.

    Jak na razie próby przesyłania danych nie wykazała problemów.
    De facto , ta komunikacją via RS232 nie ma być z PC a z modemem GSM.
    Dlatego poprawność odbierania danych jest krytyczna.

    Przy okazji niejako, wyjaśniła się pewna rzecz.
    Mianowicie, oprócz opisanej komunikacji z PC/modem urządzenie cały czas
    korzysta z analogicznej komunikacji z innym procesorem. Sporadycznie następuje tam wymiana danych.

    I - co ciekawe, ta komunikacja działała poprawnie.

    Po sprawdzeniu w dokumentacji, priorytety przerwań są takie ( wybrane z listy, ale w poprawnej kolejności);

    TIMER2 (nie używany w omawianej części programu)
    TIMER1 - wywoływany 1000 na sek.
    TIMER0 - nie używany
    USART0 - do komunikacji z drugim MCU
    TIMER3 - wywoływany co 1 sek. ale z dużą ilością zadań
    USART1 - ten właśnie do komunikacji z PC/modemem

    Z tego wynika, że USART używany do komunikacji z drugim procesorem ma znacznie lepiej, bo nie jest zakłócany przez obsługę TIMER3, który co prawda przerywa rzadko, ale za to na długo.
    A USART "pecetowy" miał najbardziej przerąbane :)

    Z tego płynie kilka wniosków;

    1) jak pisze NIVEASOFT - przerwania jednak nie służą do tego, by w nich za dużo pracować.
    2) warto pomyśleć o kolejności przerwań nawet przy projektowaniu hardware
    3) warto wybierać MCU pozwalający na zmianę priorytetów przerwań

    Jeszcze raz dziękuję koledze za pomoc !
  • Użytkownik usunął konto  
  • Użytkownik usunął konto  
  • Poziom 24  
    Cytat:
    A było takie podejrzenie? Nono


    No w rozpaczy już zacząłem tak kombinować :)

    Z jednej strony zmiana filozofii obsługi timerów była w tym przypadku dość pracochłonna, bo trzeba było w wielu miejscach korygować program, ale szczęśliwie chyba się udało i ku mojemu zdziwieniu wszystko udało się uruchomić (odpukać).

    A radość z działania RS232 bez cudawianek - bezcenna.
  • Poziom 35  
    O jakiej zmianie mówisz ? Tu zawsze jest jedna zasada maksymalnie krótkie przerwania i wszystko w programie
  • Użytkownik usunął konto