• 1
  • 2

INIT Funkcia

. .

Určite sa každý jeden programátor stretol s otázkou INITu. K čomu je nám dobrý, ako funguje a či je vôbec nutné ho implementovať.

 


Predstavme si dva samostatné objekty. Môžme si na príklad predstaviť software, ktorý konfiguruje platformu Linux a platformu Windows 7. Jeden objekt konfiguruje Linux, druhý Win. 7 . Nie je možné software naprogramovať ako jeden celok, pretože pri štarte nie je jasné, pod akým jadrom platformy bude spustený. Pokial by bol spustený pod platformou Linux, zhavaroval by modul pre Win.7 . Naopak, pokial by sme program spustili pod Win. 7, zhavaroval by modul pre Linux. Nakoľko by nemali objekty delegovanie práce, software by nám bol k ničomu.
Je jasné, že v tomto prípade potrebujeme tretí objekt, "prostredníka", ktorý najprv zistí platformu nášho systému, a až potom zavedie jednotlivý modul. Je evidentné, že moduly pre Win. 7 a Linux o sebe nevedia. Tretí objekt zistí stav jadra a jednotlivý modul mu pridelí a nadeleguje mu prácu.
Pri objektových jazykoch sa dá krásne použiť návrhový vzor Mediátor.

Ako je to však pri MCUs ?
Vezmime v úvahu, že máme dva moduly. Jeden pracuje s displejom, zatial čo druhý príjma dáta a na displej ich vypisuje.

Ak by sme v module A využívali funkcie z modulu B a naopak, uvedomme si, čo by sa stalo, pokiaľ jeden z modulov zhavaroval pri oživovaní. Neoživil by sa, zatiaľ čo iný modul/moduly, by používali jeho funkcie. Dochádzalo by k hazardom nekonzistentných dát, prípadne k hazardu chýb. Pokiaľ píšeme MCUs, nemáme žiadne ďalšie periférium typu obrazovky, kde by sme ho mohli za behu chyby debugovať.

Ďalej si predstavme, ak by sme mali 25 takýchto modulov. Hľadať chybu by bolo veľmi obtiažne.
Ďalej sa zamyslime nad tým, ak by sme mali 25 modulov, ktoré by si vzájomne "natvrdo" využívali funkcie, neboli by z hľadiska univerzálnosti vôbec univerzálne. Pokiaľ by sme chceli niektorý z modulov implementovať v inom projekte, museli by sme všetky funkcie iných modulov, na ktoré sa odkazuje a používa, mazať. Čo by pri 3000 riadkov bolo dosť pracné.

A čo ak by sme chceli použiť zariadenie, o ktorom existencia sa nevie pri štarte procesora? Ide o takzvaný booting, napríklad, ak by sme chceli pomocou USB niečo preflashovať, alebo zaviesť MCU iné časti kódu. Napríklad, pri MCU ktorý sťahuje odniekiaľ celý súbor dát s ktorými neskôr pracuje.
Pri bootingu sa počíta na 50%, že dané zariadenie bude v danej chvíli pri štarte loadované, čo by pri statickom oživovaní bol problém.

Ďalej sa nám dostáva otázka rýchlosti. Pri chybách a neuniverzálnom písaní kódu, je čas omnoho väčší.

Z hľadiska otázok o INITe sme si odpovedali a dospeli k záverom:
Detekcia chýb
Stav inicializácie
Diagnostika problému
Predchádzanie hazardom
Univerzálnosť
Prenositeľnosť modulov
Rýchlosť


Ako ale navrhnúť "napodobeninu" mediátora, ak C nie je objektové?
Zameriame sa na pointu. Chceme navrhnúť funkciu, ktorá bude inicializovať jednotlivé funkcie modulov a zároveň čaká na odpovede týchto modulov. Jeden modul bude vždy ako master ku ktorému budú výsledky initov odosielané, napr.
LCD. Ostatné budú ako slave.
Pokiaľ zlyhá modul master, musíme zabezpečiť náhradnú signalizáciu, napr. pomocou Led. V tomto kroku, každé neúspešne-inicializované zariadenie identifikujeme počtom bliknutí, napr. pri zlom inite 3. zariadenia, LED zabliká 3 krát.
No a jeden režim booting - pokiaľ sa zariadenie očakáva, počas initu sa niekoľko sekúnd čaká, pričom sa toto zariadenie ožíví a snaží sa nabootovať. V prípade, že nabootuje, zavedie driver, prípadne oživí ďalšie moduly - zavisí na implementácií. Pri snahe bootovať, bude LED blikať cca 3x do sekundy.

Implementáciu funkcie urobíme pomocou passing variables, kde budeme prenášať do funkcie ako parametre "názvy funkcií", respektíve, referencie - pointre na funkcie modulov. Každá funkcia nám bude vraciať sťandardizovaný výsledok, ktorý odchytíme a detekujeme.
Môžme ho dynamicky ukladať do poľa, a iné moduly neskôr odkázať na toto pole, kde si zistia, s akým výsledkom sa končila inicializácia modulu, ktorý chcú použiť. Ak sa inicializácia skončila zle, jej časti používať nebudú.

Celý priebeh funkcie by sa dal zjednodušene a bez bootingu prezentovať na diagrame aktivit:




Diagramy ďalšieho fungovania:







Návrh funkcie záložného detekovania LED:

config_diall.h


#ifndef __Status_Led__
  #define __Status_Led__
#endif

struct __attribute__ ((packed)) {
       volatile unsigned int *Port;
       volatile unsigned int *Tris;
           int Led;

           unsigned int _s_;
                         int _loop_;
                         long _x_;
                         long _y_;
                         long _z_;
                         long _u_;

           int ok_status;
           int bad_status;
           int init_ok;
struct __attribute__ ((packed)) {
           int time;
                  }Tmp;
}LedStatus;


void Detect_Satus(int _status_)
{
   Init_Port(*LedStatus.Port,LedStatus.Led,"digital");
   Config_OUT(*LedStatus.Tris,LedStatus.Led);
   Bit_Clr(*LedStatus.Port,LedStatus.Led);

  if (_status_ > 0 && _status_ < 22)
    {
      if (_status_ == 1) // ok_status
        {
           LedStatus._loop_ = 3;
           LedStatus._x_ = 20;
           LedStatus._y_ = 50;
           LedStatus._z_ = 32;
           LedStatus._u_ = 0;
        }

      if (_status_ == 2) // init_ok
        {
           LedStatus._loop_ = 1;
           LedStatus._x_ = 2000;
           LedStatus._y_ = 1000;
           LedStatus._z_ = 500;
           LedStatus._u_ = 0;
        }

     if (_status_ == 3) // bad_status _master_
       {
           LedStatus._loop_ = 1;
           LedStatus._x_ = 1000;
           LedStatus._y_ = 400;
           LedStatus._z_ = 4000;
           LedStatus._u_ = 0;
       }

     if (_status_ == 21) // booting
        {
           LedStatus._loop_ = 1;
           LedStatus._x_ = 100;
           LedStatus._y_ = 200;
           LedStatus._z_ = 100;
           LedStatus._u_ = 0;
        }

   Delay_ms(LedStatus._x_);

   for(LedStatus._s_= 0; LedStatus._s_<=LedStatus._loop_; LedStatus._s_++)
       {
          Delay_ms(LedStatus._y_);
          Bit_Set(*LedStatus.Port,LedStatus.Led);
          Delay_ms(LedStatus._z_);
          Bit_Clr(*LedStatus.Port,LedStatus.Led);
          Delay_ms(LedStatus._u_);
       };
   }
}



Pomocou :
LedStatus._loop_
LedStatus._x_
LedStatus._y_
LedStatus._z_
LedStatus._u_

sa nastavuje počet bliknutí a doby časovania zhasnutia a svietenia LED.
Konfiguráciu portu, na ktorom bude zapojená LED, nastavíme vo funkci char *PageSys(void); (nižšie).



Návrh funkcií pre Bootloader a INIT:

config_diall.h


struct _loader_ {
   struct __attribute__ ((packed)) {
             char* __init__;
             char* __master__;
             char* __boot__;
             char* __slave__;
             char* _mode_done_;
             char* _status_;
             int _swap_;
              }Load;

   struct __attribute__ ((packed)) {
                  /* frtm - 0x34 boot device has to boot one time, no more
                      exp - 0x11 boot device was load succesfully
                      jmp - 0x21 boot device was disload succesfully
                      status - 0x1A/0xFF boot device was/was not init succesfully */
             int status;
             byte frtm;
             byte exp;
             byte jmp;
             char* load;
             int i;
              }Boot;

   struct __attribute__ ((packed)) {
             int i;
             char * _tmp_;
              }Temp;
}Loader;


void Init(char*(*_master_)(), char*(*_slave_A)(), char*(*_slave_B)(), char*(*_slave_C)(), char*(*_slave_D)(), int(*_boot_A)())
   {
      Loader.Load._status_ = "0xFF";
      byte _status_2 = 0xFF;
      Loader.Load._mode_done_ = "done";
      Loader.Load._swap_ = 1;
      Loader.Boot.exp = 0x11;
      Loader.Boot.jmp = 0x21;

      Loader.Load.__master__ = "master";
      Loader.Load.__slave__ = "slave";
      Loader.Load.__init__ = "init";
      Loader.Load.__boot__ = "boot";


      char * _init_ok_ = "Init - ok boot";
      char * _init_bad_ = "Init - xx boot";
      char * _pagesys_ = "Page - ok load";
      char * _device_1 = "DevA - xx boot";
      char * _device_2 = "DevB - xx boot";
      char * _device_3 = "DevC - xx boot";
      char * _device_4 = "DevD - xx Load";

      PageSys();

      if(_master_ != null)
       {
          Loader.Temp._tmp_ = (*_master_)(_pagesys_,Loader.Load.__init__);
          if(Loader.Temp._tmp_ == Loader.Load._status_)
           {
              Detect_Satus(3);
           }
       }

     if(_boot_A != null)
      {
         Booting(*_boot_A,null,Loader.Load.__init__);
      }

    if(_slave_A != null)
     {
        Loader.Temp._tmp_ = (*_slave_A)(Loader.Load.__init__);
        if( _master_ != null)
         {
           if(Loader.Temp._tmp_ == Loader.Load._status_)
            {
               Loader.Temp._tmp_ = _device_1;
               Loader.Load._swap_ = 0;
         }
           (*_master_)(Loader.Temp._tmp_,Loader.Load.__master__);
         } else
             {
                if(Loader.Temp._tmp_ == Loader.Load._status_)
                 {
                    for(Loader.Temp.i = 1; Loader.Temp.i<=2; Loader.Temp.i++)
                       {
                          Detect_Satus(3);
                       };
                 }
                return;
             }
   }

    if(Loader.Boot.status == 1)
     {
        Booting(*_boot_A,*_master_,Loader.Load.__boot__);
     }

    if(_slave_B != null)
     {
        Loader.Temp._tmp_ = (*_slave_B)(Loader.Load.__init__);
        if( _master_ != null)
         {
            if(Loader.Temp._tmp_ == Loader.Load._status_)
             {
                Loader.Temp._tmp_ = _device_2;
                Loader.Load._swap_ = 0;
             }
                (*_master_)(Loader.Temp._tmp_,Loader.Load.__master__);
         } else
            {
               if(Loader.Temp._tmp_ == Loader.Load._status_)
                {
                   for(Loader.Temp.i = 1; Loader.Temp.i<=3; Loader.Temp.i++)
                       {
                          Detect_Satus(3);
                       };
                }
               return;
            }
      }

    if(Loader.Boot.status == 1 && Loader.Boot.frtm != 0x34)
      {
         Booting(*_boot_A,*_master_,Loader.Load.__boot__);
      }

    if(_slave_C != null)
     {
        Loader.Temp._tmp_ = (*_slave_C)(Loader.Load.__init__);
        if( _master_ != null)
         {
            if(Loader.Temp._tmp_ == Loader.Load._status_)
             {
                Loader.Temp._tmp_ = _device_3;
                Loader.Load._swap_ = 0;
             }
            (*_master_)(Loader.Temp._tmp_,Loader.Load.__master__);
        } else
          {
             if(Loader.Temp._tmp_ == Loader.Load._status_)
              {
                 for(Loader.Temp.i = 1; Loader.Temp.i<=4; Loader.Temp.i++)
                     {
                        Detect_Satus(3);
                     };
              }
            return;
          }
     }

    if(Loader.Boot.status == 1 && Loader.Boot.frtm != 0x34)
     {
        Booting(*_boot_A,*_master_,Loader.Load.__boot__);
     }

    if(_slave_D != null)
     {
        Loader.Temp._tmp_ = (*_slave_D)(Loader.Load.__init__);
        if( _master_ != null)
          {
             if(Loader.Temp._tmp_ == Loader.Load._status_)
              {
                 Loader.Temp._tmp_ = _device_4;
                 Loader.Load._swap_ = 0;
              }
             (*_master_)(Loader.Temp._tmp_,Loader.Load.__master__);
          } else
             {
                if(Loader.Temp._tmp_ == Loader.Load._status_)
                 {
                    for(Loader.Temp.i = 1; Loader.Temp.i<=5; Loader.Temp.i++)
                       {
                          Detect_Satus(3);
                       };
                 }
                return;
             }
     }

    if(Loader.Load._swap_ == 1)
     {
        (*_master_)(_init_ok_,Loader.Load.__master__);
        (*_master_)(Loader.Load._mode_done_,null);
     } else
        {
           (*_master_)(_init_bad_,Loader.Load.__master__);
        }
        return;
   }



PRIVATE byte Booting(byte(*_boot_device_)(),char*(*_master_)(), char* _mode_)
     {
                Loader.Temp.i = 0;

          if(_mode_ == "init")
           {
              Loader.Boot.status = (*_boot_device_)(Loader.Load.__init__);

           } else
              if(_mode_ == "boot")
               {
                  Detect_Satus(21);
                  while((Loader.Boot.status != 2) && (Loader.Temp.i != 5))
                         {
                       Loader.Boot.status = (*_boot_device_)(Loader.Load.__boot__);
                            Detect_Satus(21);
                            Loader.Temp.i++;
                         }
                   Loader.Temp.i = 0;
                   if(Loader.Boot.status == Loader.Boot.exp)
                    {
                       if(_master_ != null)
                        {
                           (*_master_)(Loader.Boot.load, Loader.Load.__master__);
                        }

                       Loader.Boot.frtm = 0x34;

                       while(Loader.Boot.status != Loader.Boot.jmp)
                              {
                                 Loader.Boot.status = (*_boot_device_)
                                 (Loader.Load.__boot__);
                              }

                       return Loader.Boot.status;
                    }
                }
              return Loader.Boot.status;
     }

Bootloader je niekoľko krát volaný z funkcie INIT. Ako vstupné parametre berie init metódu boot zariadenia a metódu master zariadenia, ktorá v prípade použitia spracuje úspešné nabootovanie. Spracovanie závisí na konktrétnej implementácií funkcie.
Ostatná funkcionalita by mala byť z kódov jasná.


Funkcia INIT implementuje inicializačné prostredie PageSys().
Ide o funkciu, ktorá je volaná ako úplne prvá a slúži ako prvotné nastavenie modulov pred samotným inicializovaním. Príklad použitia:

main.c


#define __NoTypeDef__

#include "config_diall.h"

char *PageSys(void)
   {
      OSCTUN = 0;
      RCONbits.SWDTEN = 0;
      LedStatus.Port = uintptr &PORTB;
      LedStatus.Led = 0;

      return "0x21";
   }

int main(void)
   {
      Init(LCD_Init,Robotic_Arm,USB_Interface,null,null,Hdd);

      ...code...
   }



Pomocou LedStatus.Port = uintptr &PORTB; nastavíme port pre detekciu stavu ako PORTB a s LedStatus.Led = 0; nastavíme číslo portu RB0


!Upozornenie!
Funkcia *PageSys() je prototypne definovaná v konfiguračnom headery, ale je volaná z funkcie Init(..)!. Funkcia *PageSys() musí byť vždy deklarovaná v súbore main.c aj v prípade, že funkciu Init() nepoužívame! Pri použití funkcie Init() sa *PageSys() spustí (aj pokiaľ je prázdna), no v prípade nepoužitia Init() sa nevyvolá.
Deklarovaná však musí byť vždy.


Kompletný súbor config_diall.h a jeho celky sú popísané tu: >>config_diall.h<<