Programowanie uC - wspoldzialanie przerwan i algorytmu - zasady sztuki

Feb 21, 2007 6 Replies

Witam,



Chciałem spytać Was, nieco bardziej doświadczonych programistów o sposób programowania uC, gdy program główny jest dość skomplikowany i zależy od wielu sygnałów. Dla ustalenia uwagi załóżmy, że uC ma współpracować m.in. z modułem radiowym CC1100 (podłączonym przez SPI) oraz niekiedy z modułem Ethernet (podłączony do USART) Główne zadania programu to dostęp do medium radiowego (a'la CSMA/CA), analiza ramek radiowych (różnych-różnistych - zgłoszenia, wiadomości sygnalizacyjne, wiadomości tekstowe, głos) i reakcja na nie.



PODEJŚCIE 1. Do tej pory (w innych projektach) robiłem tak:



  1. Procedury obsługi przerwań możliwie najkrótsze (odczyt danych z peryferiów, zapis do bufora w pamięci, pobieżna analiza i ustawianie flag - np. FRAME_COMPLETE dla programu głównego).
  2. Program główny po stwierdzeniu flagi FRAME_COMPLETE wywoływał funkcję Analiza, która ustalała protokół, konkretne znaczenie ramki, ustalając kolejne flagi "wyższej warstwy" (np. RECIEVED_CTS), które to flagi brane były pod uwagę w głównym programie. Coś w stylu (pseudokod dla wysyłania ramki w a'la CSMA/CA):


[IRQ] ... Zapisuj bajty do bufora if (ostatni bajt) then FRAME_COMPLETE ... reti


[MAIN] ... if (FRAME_COMPLETE) then FrameType = Analiza(bufor);

if (Stan == RTS_SENT and FrameType == CTS_RECIEVED and ChannelFree) then Send(data); Stan = DATA_SENT; else if (Stan == DATA_SENT and FrameType == ACK_RECIEVED and ChannelFree) then Stan = IDLE; i tu inne skomplikowane przejścia, zwłaszcza gdy np. wystąpią timeouty ...


[Analiza] ... if (cośtam) then FRAME_TYPE = CTS_RECIEVED; ...

Zaletą jest fakt, że program główny zawsze może zareagować niemal na wszystko. Wady - komplikacja warunków (kilka nałożonych na siebie diagramów stanów: protokołów będzie kilka, typów ramek ok. 20-30) i ogólnie wymięknąłem rozpisując program główny.



PODEJŚCIE 2. Czy nie byłoby grzechem pisanie procedury nadawania ramki po prostu jako funkcji: [SendFrame] Send(RTS); WaitFor(CTS_RECIEVED); if (Timeout) then return ERROR; Send(data); WaitFor(ACK_RECIEVED); if (Timeout) then return ERROR; RETURN OK;



Zaletą jest klarowny oczekiwany przebieg programu i uproszczenie głównego programu. Problemem na pewno są funkcje WaitFor(*), podczas których program jest obojętny na wiele innych sygnałów (np. pojawienie się ramek innych typów, flagi ustawiane przez procedury innych przerwań np. USART). Moim zdaniem można rozważyć w ciele WaitFor sprawdzanie niektórych warunków(flag). Jakie macie sposoby na programowanie uC obsługujących protokoły, muszących brać pod uwagę wiele flag? Dziękuję za wszystkie odpowiedzi, wskazówki, uwagi. Mam nadzieję, że nie zamąciłem zbytnio.


Stefcio Z. napisał(a):

I to jest najlepsze podejście. Zminimalizuj czas przebywania procesora w przerwaniach. Zastosowanie automatu fsm albo nawet kilku do obsługi wszystkich protokołów będzie bardzo dobrym i wygodnym rozwiązaniem. Pewnie gotowych implementacji jest kilka i masz z czego wybierać, ja napisałem po swojemu.

formatting link

Niekoniecznie - w wiêkszych systemach op³aci siê zastosowanie jakiego¶ systemu operacyjnego, lub choæby kernela prze³±czaj±cego taski i napisanie ³±dnych driverów dla RS232, SPI a program g³ówny w kilku zsynchronizowanych muteksami w±tkach, itp.

semafory.

if semafor= 0 then return if semafor= 1 then begin SendFrame semafor=2 return end

if semafor = 2 then if cts_received then begin send(data) semafor:= 3 end if semafor= 3 then if ack_recieved itd wywolujesz non stop taka funkcje - kiedy wpiszesz jeden w semafor to ruszy. nieraz budowalem w ten sposob calkiem pokazne algorytmy

wojtek

formatting link
CMMS Maszyna Golem OEE Produkt- Baza Wiedzy

Pszemol napisał(a):

Oczywiście - co nie zmienia faktu, że w przerwaniach najlepiej przebywać jak najkrócej. Jeżeli robisz system czasu rzeczywistego to ważne także jest, ile trwa najdłuższe przerwanie o wysokim priorytecie i na jaki maksymalny czas blokujesz przerwania w głównym programie.

Adam Dybkowski napisał(a):

Może nie wyraziłem się wystarczająco jasno - jestem całkowicie świadom potrzeby maksymalnego skracania obsług przerwań i stosuję tę zasadę zawsze. Główne pytanie polega na tym, czy ktoś stosuje zastępowanie skomplikowanego diagramu stanów (który w kodzie bez dokumentacji graficznej jest w zasadzie nieczytelny) funkcjami działającymi w sposób "sekwencyjny" ("czekaj na konkretny sygnał, reaguj na inne najistotniejsze sygnały) wg ustalonej w diagramie stanów kolejności - tak jak to przedstawiłem w podejściu 2.

Gdybys chcial cos takiego napisac w jednym watku to bylby jeden wielki balagan w programie obslugujacym kilka rownoleglych funkcji. Wez pod uwage ze zdarzenia ze swiata zewnetrznego przychodza w stosunku do siebie asynchronicznie.

Taki sposob pisania kodu jest wlasnie tylko mozliwy dzieki zastosowaniu wielowatkowosci. System operacyjny udostepnia Ci bowiem mechanizmy synchronizacji miedzywatkami i miedzy funkcjami obslugi przerwan (muteksy, semafory, kolejki, flagi zdarzen itp). W efekcie mozesz napisac sterownik np. do obslugi portu szeregowego i napisac w kodzie glownym:

-wyslijpaczke(paczka);

-czekajNaOdpowiedz(2sekundy);

-przetwarzajodpowiedz();

A wewnatrz funkcji "wyslij paczke" Twoj watek bedzie spokojnie czekal na zasygnalizowanie flagi w obsludze przerwania i to bez marnowania czasu procesora gdy wazniejszy watek ma cos do roboty.

Polecam bardzo dobra ksiazke Labrosse "MicroC/OS-II". W zasadzie ksiazka jest o konkretnym mikro-systemie operacyjnym ale ma w bardzo latwy/przystepny sposob wyjasniona wielowatkowosc i zasady wspolpracy watkow i obslugi przerwan w wiekszych programach.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required