Sprejem telegrama v interrupt rutini

Moderator: tilz0R

Sprejem telegrama v interrupt rutini

OdgovorNapisal/-a Proteus » 13 Feb 2015, 18:47

Tole je v bistvu vezano na tale post
https://www.s5tech.net/viewtopic.php?f=47&t=354,
kjer je Vilko predstavil svoj način komuniciranja s sužnji.

Tam sem mu predlagal poenostavljen protokol v naslednji sestavi:
Koda: Izberi vse
Byte 01: [Start] – start byte
Byte 02: [ID] – številka sužnja
Byte 03: [Komanda] – tukaj lahko v en bayte zakodiraš 255 komand
Byte 04: [podatki] – telegram lahko vsebuje podatke če je to potrebno. Preko tega lahko enostavno razširiš tudi število komand.
.
.
Byte n-1: [CRC hi]
Byte n: [CRC Lo]

Med drugim je dobra lastnost takčnega protokola, da se ga da enostavno obdelati že v sami prekinitveni rutini.

V nadaljevanju je primer kode za to obdelavo. Gre torej za fragment kode, ki je nameščena v prekinitvi serijskega porta, ki se izvrši ob vsakem sprejetem baytu. Prehod med posameznimi fazami dekodiranja poteka s pomočjo t.i. FSA (Finite State Automata). Variabla IntSer_Rx_State je torej kazalec med posameznimi stanji dekodiranja. C kodo sem malce spremenil, da je bolj čitljiva tudi tistim, ki C ne poznajo.

Koda: Izberi vse
      /* najprej prenesemo sprejeti podatek v variablo ComChr */
      ComChr = SBUF;
       
      /* Resetiraj FSA, ce je cas sprejema predolg */
        if (RTX_FSA_Rx_TimeOut == 0) IntSer_Rx_State = 0;

        switch (IntSer_Rx_State)
        {
            case 0:     // ---------- START Byte
                if (TG_Sprejet_F == 1) break;   // prejsnji TG se ni obdelan
               
                if (ComChr == _TG_START_BYTE)   // obdelavo zacni s START Bytom
                {
                    RTX_FSA_Rx_TimeOut = _RX_TIMEOUT;  // nastavi TimeOut cas
                    RTX_Buffer_Crc = 0;          // Inicializacija CRC
                    IntSer_Rx_State++;          // Naslednji je address byte
                }
            break;

            case 1:     // ---------- ADDRESS Byte
                if ((ComChr == DEVICE_ADDRESS)||(ComChr == 0xFF))
                {
                    RTX_Buffer_Ptr = 0;  // kazalec bufferja na zacetek
                   RTX_Buffer[RTX_Buffer_Ptr] = ComChr; // vpisi znak v buffer
                    RTX_Buffer_Ptr++;    // kazalec na naslednji znak v bufferju
                    RTX_Buffer_Crc = CRC16(RTX_Buffer_Crc, ComChr); // Izracun CRC
                    ++IntSer_Rx_State;  // Naslednji je command byte
                }
                else
                {
                    // Telegram ni namenjen tej napravi, zato pojdi na zacetek
                    IntSer_Rx_State = 0;
                }
            break;

            case 2:     // ---------- COMMAND Byte
                RTX_Buffer_Crc = CRC16(RTX_Buffer_Crc, ComChr);   // Izracun CRC
                RTX_Buffer[RTX_Buffer_Ptr] = ComChr;      // vpisi znak v buffer
                RTX_Buffer_Ptr++;        // kazalec na naslednji znak v bufferju
                IntSer_Rx_State++;      // Naslednji je data_size byte
            break;

            case 3:     // ---------- DATA_SIZE Byte
                if (ComChr > (_TG_BUFF_LEN-4))  // je dolzina podatkov OK ?
                {
                    IntSer_Rx_State = 0;        // NE, zato pojdi na zacetek
               break;
                }
               
                RTX_Buffer_Crc = CRC16(RTX_Buffer_Crc, ComChr);   // Izracun CRC
                RTX_Buffer[RTX_Buffer_Ptr] = ComChr;      // vpisi znak v buffer
                RTX_Buffer_Ptr++;        // kazalec na naslednji znak v bufferju
                RTX_Buffer_Len = ComChr; // Shrani dolzino telegrama
               
                IntSer_Rx_State++;  // Sledi data byte ...
                                    // ... oziroma CRC preverjanje, ce je LEN=0
                if (RTX_Buffer_Len == 0) IntSer_Rx_State++;
            break;

            case 4:     // ---------- DATA Byte
                RTX_Buffer_Crc = CRC16(RTX_Buffer_Crc, ComChr);   // Izracun CRC
                RTX_Buffer[RTX_Buffer_Ptr] = ComChr;      // vpisi znak v buffer
                RTX_Buffer_Ptr++;        // kazalec na naslednji znak v bufferju

                RTX_Buffer_Len--;    // v telegramu je en podatek manj, zato
                                    // zamanjsamo dolzino in zakljuci, ce LEN=0
                if (RTX_Buffer_Len == 0) ++IntSer_Rx_State;
            break;

            case 5:     // ---------- CRC_HI Byte
                if (ComChr == (RTX_Buffer_Crc >> 8)) ++IntSer_Rx_State;
            break;

            case 6:     // ---------- CRC_LO Byte
                if (ComChr == (RTX_Buffer_Crc & 0x00FF))
                {
                    TG_Sprejet_F = 1;       // Indikacija sprejetega telegrama
                }
            IntSer_Rx_State = 0;    // FSA na zacetek
            break;

            default:    // ---------- Nedovoljeno stanje / verjetno motnja ali napaka
            ResetCPU();
            break;
        }
    }
Uporabniški avatar
Proteus
 
Prispevkov: 3363
Pridružen: 18 Jan 2015, 01:31
Kraj: Planet Zemlja
Zahvalil se je: 361 krat
Prejel zahvalo: 737 krat
Uporabnika povabil: s54mtb
Število neizkoriščenih povabil: 136

Re: Sprejem telegrama v interrupt rutini

OdgovorNapisal/-a mujo » 13 Feb 2015, 19:09

Imaš hrošča:
Koda: Izberi vse
case 5:     // ---------- CRC_HI Byte
     if (ComChr == (RTX_Buffer_Crc >> 8)) ++IntSer_Rx_State;
break;

Nimaš definirano, kaj se naj zgodi če se CRC_HI byte ne ujema. Predvidevam, da si na tem mestu želiš, da se FSM resetira.

+ v opisu protokola nimaš napisano, da se uporablja tudi zlog za dolžino.
Tudi default: v switch stavku mi ni najbolj všeč - kajti procesor resetirati ni ravno najboljša rešitev. Ker naslednjič pa boš imel težave, da se ti bo procesor "kar od sebe" resetiral. Sam imam prakso, da si dodam kakšem output v takih situacijah in/ali izvedem breakpoint preko ukaza (recimo na cortex m3 - BKPT ukaz v zbirniku).
mujo
 
Prispevkov: 735
Pridružen: 21 Jan 2015, 10:50
Kraj: MB
Zahvalil se je: 1 krat
Prejel zahvalo: 150 krat
Uporabnika povabil: VolkD
Število neizkoriščenih povabil: 18

Re: Sprejem telegrama v interrupt rutini

OdgovorNapisal/-a Sigi » 13 Feb 2015, 19:33

Podoben način uporabljam za preproste protokole; v primeru, da protokol uporablja ASCII znake in da je startni znak edinstven in se ne pojavlja drugje v telegramu, narediš protokol še bolj robusten, če ob sprejemu start znaka, ne glede na stanje sprejema, postaviš stanje na začetno vrednost (oz na stanje kjer je sprejet start znak). Podobno kot imaš rešeno za timeout. Protokol se tako brez zakasnitve "pobere" ob ponovitvi sporočila. Ta prehod lahko tudi zaznaš in po potrebi postaviš kako opozorilo ali napako.

lp Žiga
...
Sigi
 
Prispevkov: 477
Pridružen: 23 Jan 2015, 01:57
Kraj: Kamnik
Zahvalil se je: 370 krat
Prejel zahvalo: 287 krat
Uporabnika povabil: s54mtb
Število neizkoriščenih povabil: 60

Re: Sprejem telegrama v interrupt rutini

OdgovorNapisal/-a Proteus » 13 Feb 2015, 21:57

Mujo hvala za pripombe. Tukaj so odgovori:
mujo je napisal/-a:Imaš hrošča .... Nimaš definirano, kaj se naj zgodi če se CRC_HI byte ne ujema. Predvidevam, da si na tem mestu želiš, da se FSM resetira.

Zelo dobra pripomba; "It's not a bug, it's a feature" :mrgreen:
Zadeva ni kritična, ker naslednji byte postavi stvari na svoje mesto. Poleg tega je tu še dodatna varovalka preko timeout-a.
Je pa zato zadeva zelo uporabna za testiranje, če je knjižnica pravilno nameščena. Zaporedoma pošljem dva telegrama in sicer prvega z manjkajočim zadnjim baytom in drugega, ki je O.K. Prvega ne sme sprejeti, drugega pa mora.
mujo je napisal/-a:v opisu protokola nimaš napisano, da se uporablja tudi zlog za dolžino.

V bistvu imam pod: Byte 04: [podatki]
Res pa je, da ni dodatno razdelano. Prvi byte je v bistvu dolžina podatkov in če je nič teh pač ni.
mujo je napisal/-a:Tudi default: v switch stavku mi ni najbolj všeč - kajti procesor resetirati ni ravno najboljša rešitev. Ker naslednjič pa boš imel težave, da se ti bo procesor "kar od sebe" resetiral. Sam imam prakso, da si dodam kakšem output v takih situacijah in/ali izvedem breakpoint preko ukaza (recimo na cortex m3 - BKPT ukaz v zbirniku).

To uporabljam pri vseh FSA. V default: stanje lahko pridem samo, če je nekaj resno narobe (tukaj ni mišljen bug v kodi). V tem primeru lahko dvomim v vse in je posledično reset najboljša rešitev. V bistvu gre samo za dodaten varnostni ventil.
BKPT in signalizacija na izhodu ti v primerih, ko na naprave ne gledaš ves čas ne pomaga kaj dosti. V takšnih primerih si vsaj jaz želim samo varen "recovery".
Uporabniški avatar
Proteus
 
Prispevkov: 3363
Pridružen: 18 Jan 2015, 01:31
Kraj: Planet Zemlja
Zahvalil se je: 361 krat
Prejel zahvalo: 737 krat
Uporabnika povabil: s54mtb
Število neizkoriščenih povabil: 136

Re: Sprejem telegrama v interrupt rutini

OdgovorNapisal/-a Proteus » 13 Feb 2015, 22:00

Sigi je napisal/-a:Podoben način uporabljam za preproste protokole

Pozdravljen Žiga, tak pristop je uporaben tudi za kaj hujšega.
Na ta način obdelujem n.pr. tudi mBus protokol. Težko si predstavljam kaj še bolj zajeb....
Uporabniški avatar
Proteus
 
Prispevkov: 3363
Pridružen: 18 Jan 2015, 01:31
Kraj: Planet Zemlja
Zahvalil se je: 361 krat
Prejel zahvalo: 737 krat
Uporabnika povabil: s54mtb
Število neizkoriščenih povabil: 136

Re: Sprejem telegrama v interrupt rutini

OdgovorNapisal/-a mujo » 15 Feb 2015, 13:52

Proteus je napisal/-a:Mujo hvala za pripombe. Tukaj so odgovori:
mujo je napisal/-a:Imaš hrošča .... Nimaš definirano, kaj se naj zgodi če se CRC_HI byte ne ujema. Predvidevam, da si na tem mestu želiš, da se FSM resetira.

Zadeva ni kritična, ker naslednji byte postavi stvari na svoje mesto.


Ko se sprejme naslednji zlog pride v isto stanje avtomata - saj nisi naredil prehoda v drugo stanje. Torej če je prvi zlog CRC napačen, potem vsi zlogi, ki mu sledijo pridejo v stanje avtomata za preverjanje prvega zloga CRC-ja. Razen ko pride do timeouta - oziroma se slučajno kateri zlog ujame.
Če sta dva paketka zaporedoma zelo skupaj lahko na ta način izgubiš pakete (sicer odvisno od zahtev protokola - čas med sporočili).
S tega stališča je napaka kritična.

Jaz uporabljam podobne metode za sprejem podatkov. Načeloma se to izvede zelo hitro in se lahko vse opravi v prekinitveni rutini. Večinoma uporabljam še RTOS in potem samo prejeta sporočila mečem v vrsto operacijskega sistema (queue) - s tem se pridobi še dodana plast kode.

Ne mislim nič kritizirati - prav nasprotno - pohvala, da objaviš kaj takega. Jaz bi tudi, ampak trenutno vse kaj delam je za komericalne namene in ne smem. Ampak napake pa je potrebno odpraviti.
mujo
 
Prispevkov: 735
Pridružen: 21 Jan 2015, 10:50
Kraj: MB
Zahvalil se je: 1 krat
Prejel zahvalo: 150 krat
Uporabnika povabil: VolkD
Število neizkoriščenih povabil: 18

Re: Sprejem telegrama v interrupt rutini

OdgovorNapisal/-a Proteus » 16 Feb 2015, 11:39

mujo je napisal/-a:Ko se sprejme naslednji zlog pride v isto stanje avtomata - saj nisi naredil prehoda v drugo stanje. Torej če je prvi zlog CRC napačen, potem vsi zlogi, ki mu sledijo pridejo v stanje avtomata za preverjanje prvega zloga CRC-ja. Razen ko pride do timeouta - oziroma se slučajno kateri zlog ujame.

Tole drži ni pa kritično, ker sem knjižnico skopiral iz master/slave radijske komunikacije, kjer čakaš kakšnih 200ms samo na to, da se radijska postaja postavi na oddajo.
Timeout je torej dovolj dober varnostni ključ.

V principu pa ima ta pristop eno hujšo napako in sicer v primeru neuspešnega dekodiranja so vsi zgodovinski podatki v bistvu izgubljeni. Med njimi je lahko n.pr. tudi telegram, ki je čisto o.k. samo kakšen znak kasneje bi moral začeti dekodirati. V tem primeru je n.pr. možna rešitev s krožnim baferjem, ki omogoča tudi elegantno razdelitev aplikacije na dva nivoja. Drugi je skorajda neodvisen od HW.

Vendar kot sem na začetku napisal, pri master/slave aplikacijah to ni problem, ker gospodar vedno čaka, kaj mu bo povedal suženj in po potrebi ponovi zahtevo. Med čakanjem gospodarja pa se lahko suženj brez problema ponastavi.
Uporabniški avatar
Proteus
 
Prispevkov: 3363
Pridružen: 18 Jan 2015, 01:31
Kraj: Planet Zemlja
Zahvalil se je: 361 krat
Prejel zahvalo: 737 krat
Uporabnika povabil: s54mtb
Število neizkoriščenih povabil: 136

Re: Sprejem telegrama v interrupt rutini

OdgovorNapisal/-a Kroko » 26 Mar 2015, 00:47

Jaz sem si naredil algoritem na osnovi COBS:
http://en.wikipedia.org/wiki/Consistent ... e_Stuffing
http://www.planet-cnc.com Kroko was here!
Uporabniški avatar
Kroko
 
Prispevkov: 6153
Pridružen: 14 Jan 2015, 12:12
Kraj: Ljubljana
Zahvalil se je: 770 krat
Prejel zahvalo: 2445 krat
Uporabnika povabil: Vrtni palček
Število neizkoriščenih povabil: 255


Vrni se na C in sorodni jeziki

Kdo je na strani

Po forumu brska: 0 registriranih uporabnikov in 1 gost