# ATMEL mikrokontrolleri >  C++

## Jurkins

Sveiciens tiem nedaudzajiem, kas vēl šeit ganās!

Sekojošs kods:


```
#include <avr/io.h>

class ds
{
  public:
    uint8_t* ds_addr()
    {
      return addr;
    }
  protected:
    uint8_t addr[8];
};

int main(void)
{
  ds *sensor1;
  sensor1 = new ds;

  while(1)
  {
    
  }
  delete sensor1;
}
```

 Arduino 1.6.12 vidē kompilējas. Atmel Studio bļauj, ka: 

Error        undefined reference to `operator new(unsigned int)'    

Ja klases objektu taisa ne dinamiski, tad nebļauj. Vai kāda bibliotēka jāpievieno vai kas cits?

----------


## karloslv

Oi, šitais ir dziļi jārok. Pamēģini vienkārši rakstīt "new ds()". Iemesli ir te: http://stackoverflow.com/questions/6...rence-with-new

----------


## Jurkins

Jā, vakar vakarā jau izmēģināju visu ko...i ar iekavām , i bez, i konstruktorus, destruktorus rakstīt un nerakstīt. Gūgles tanti arī ņēmu priekšā ilgi un fundamentāli, bet skaidrībā netiku. Tiku līdz tam, ka funkciju new un delete nav bibliotēkā, jo avr c++ neesot paredzēti exception (par kuriem man nojausma gaužām miglaina pagaidām). Gan jau, ka es lienu par dziļu un par ātru, un mierīgi var iztikt bez tās dinamiskās objektu veidošanas. Bet jocīgi, ka arduino nebļauj.

----------


## karloslv

Ah, tikai tagad pieleca, tu taču grasies izmantot heap objektus. Stipri neiesaku. Heap un embedded sistēmas - ļoooooti uzmanīgi. Vai nu tad raksta paši savu, ļoti striktu atmiņas menedžera implementāciju, vai neizmanto nemaz. Vai tad tev tiešām viss tur tik dinamiski, ka nekādi neiztikt? Neparedzams skaits objektu?

----------


## Jurkins

Man gandrīz pirmie soļi c++, bet jau navarotus man vajag  :: . Zinu, ka 1wire bibleņu pilna pakaļa, bet gribējās pamēģināt kā ir, ja nerezervē atmiņu kaut kādam maksimālajam sensoru skaitam, bet cik atrod, tik objektus izveido.

Brīžiem jau pašam liekas, ka sāku kā "raimonds" uzvesties  ::  tikai tālāk no teorijas un tuvāk praksei.

edit: laikam ar malloc varētu mēģināt kaut ko darīt.

----------


## karloslv

Var ar malloc, bet tāpat tos 1024 baitus tik ātri aizd*sīsi, ka maz neliksies. Vēl var vienkārši pats nodefinēt new() un delete(), bet nu... ne jau bez iemesla viņi ir izņemti no avr-g++.



```
// replacement of a minimal set of functions: 
void* operator new(std::size_t sz) 
{
     return std::malloc(sz);
 } 
void operator delete(void* ptr) noexcept {
     std::free(ptr);
 }
```

----------


## Jurkins

Tā jau laikam ir. Ar to c++ iesāku dēļ tā, ka iepirkās e-līcī 32 bitu STM pa tādām pašām naudām kā arduino nano. 
Paldies par to kodu. Vakarā pie alus c++ būs pašā laikā  :: .

----------


## karloslv

Nevajag nemaz e-līci - tepat farnelī godīgi var labu STM Nucleo platīti dabūt par 10-15 naudiņām, kurai jau ir >40 MHz, padaudz RAM utml. Silti iesaku pamēģināt, pēc tam negribēsies vairs ķēpāties ar sīkumiem  ::  Programmējas eleganti vienkārši, kods ir lasāms, debugojams pa soļiem ar JTAG, platītes ir mehāniski savietojamas ar Arduino, n-tie seriāļi un citi periferāļi, utml gardumi.

----------


## Jurkins

Ja tomēr runājam par dinamisku objektu veidošanu. Kas slikts varētu notikt ar atmiņu, ja šie objekti pastāv visu laiku:


```
class OW
{
    ...
};

int main(void)
{
    OW *owOne;
    owOne = (OW*)malloc(sizeof(OW));
    owOne->OW_init(&PORTB, PB4);
    sensor_count = owOne->OW_search_rom();
    for(uint8_t i = 0; i < sensor_count; i++)
    {
        owOne->OW_match_rom(sensor_id);
        ...
    }
    ...
    free((void*)owOne);
}
```

 Cits jautājums, vai to vajag, iztiekam ar statiski izveidotiem objektiem. Bet nu šoreiz par pašu principu gribētos saprast, kas slikts varētu notikt. Mans piemērs gan nav īpaši veiksmīgs. Man te tikai viena klase un viens šīs klases objekts.
Doma varētu būt tāda, ka ir OneWire klase un pēc tam objektu klases - temperatūras sensori, i-buttoni, elektroniskie potenciometri u.t.t. Tas OW_search_rom ir OneWire klases funkcija, kuru palaižot tad tiek izveidoti šie objekti tik, cik ir uz līnijas un kādi. Pēc tam šie objekti pastāv visu laiku līdz nākošajam resetam.

----------


## karloslv

Ok, ja alokācijas notiek tādā globālā līmenī un nevis kaut kādās eventu cilpās (un diespas hw pārtraukumos), tad tas ir nesāpīgs variants, protams. Jo galu galā neviens taču neliedz tev uzrakstīt jurkins_alloc un jurkins_free funkcijas, kas pašas izdomās, kur paņemt atmiņu un atgriezīs norādi uz to. Tas kopumā arī ir visieteicamākais variants - programmā statiski rezervēt baitu masīvu (memory pool), pa kuru tad tavi alloc/free dzīvosies un paši veiks grāmatvedību, kurš gabaliņš ir brīvs un kurš nav. It sevišķi, ja ir tik mierīgs atmiņas režīms, kā apraksti, nu neko jau tur nevajag, var iztikt ar vienu pointeri grāmatvedībai - tādu, kurš sākumā norāda uz pool sākumu, bet pēc katras alokācijas pavirzās uz priekšu. Free() savukārt neko nedara, un tā atmiņa vairs nekad nevarēs tikt otrreiz izmantota. Jā, muļķīgi, bet toties tupa vienkārši un prognozējama uzvedība. Viss jāskatās pēc vajadzībām. Labu memory manageri rakstīt ir liels darbs. 

Nedaudz advancētāks ir atmiņas pārvaldnieks, kurš uztur saistīto sarakstu ar atmiņas blokiem. Pašu sarakstu arī var glabāt tā paša memory pool ietvaros. Tad reiz atbrīvotiem gabaliem ir cerība tikt atkalizmantotiem, bet jāsāk domāt par fragmentācijas menedžēšanu. Gan jau pilns internets ar dažādiem risinājumiem, cilvēki par to ir domājuši kopš Tūringa laikiem  :: 

Standarta malloc() Arduino ir pasmags, bet lietojams, un tavā piemērā tiešām neko sliktu nedarīs.

----------


## Jurkins

OK, paldies par skaidrojumu. Priekšā ļoti plašs darbalauks, kur izvērsties. Ar to c++ kā ar kvantu fiziku... jo dziļāk lien, jo vairāk jautājumu.

----------


## Jurkins

Nu re, radās kārtējais jautājums. Pašlaik šķirstu gūgli, bet, nu, uzjautāšu. 
Kods:


```
class SPIClass
{
    public:
    SPIClass()
    {
        PORTB |= (1<<MISO)|(1<<MOSI)|(1<<SCK)|(1<<SS);
        DDRB |= (1<<MOSI)|(1<<SCK)|(1<<SS);
        SPCR = (1<<SPE)|(1<<MSTR);
        SPSR = (1<<SPI2X);
    }
    uint8_t transaction(uint8_t data)
    {
        SPDR = data;
        while(!(SPSR & (1<<SPIF))) continue;
        return SPDR;
    }
    inline void ss_low(void)
    {
        PORTB &= ~(1<<SS);
    }
    inline void ss_high(void)
    {
        PORTB |= (1<<SS);
    }
};
```

 divas pēdējās funkcijas gribu, lai būtu "inline", jo kāda vella pēc dēļ vienas assemblera komandas lēkt uz apakšprogrammu un tad atkal atpakaļ. Bet kompilatoram mīkstais uz manu vēlēšanos. Kāpēc?

----------


## karloslv

Šis ir bieži pārprasts un nevietā lietots gadījums. Kā raksta http://stackoverflow.com/questions/1...nction-method:




> It is said that inline hints to the compiler that you think  the function should be inlined.  That may have been true in 1998, but a  decade later the compiler needs no such hints.  Not to mention humans  are usually wrong when it comes to optimizing code, so most compilers  flat out ignore the 'hint'.


 Respektīvi - necepies  ::  Un lieto atbilstošus optimizācijas uzstādījumus - vai nu -Os vai nu -O2 vai -O3 (mikrokontrolieru kompilatoriem gan parasti ir nosacītas iespējas optimizēt un nav atšķirības starp O2/O3, cik esmu novērojis).

----------


## karloslv

Ja DIKTI vajag, lai kods tiešām vienmēr būtu inline, tad to var panākt ar makro definīciju, jo tā brutāli ieraksta kodu tajā vietā.



```
#define SS_LOW  PORTB &= ~(1<<SS);
```

----------


## Jurkins

Paldies! Vakar vēlu vakarā pārdomu rezultātā nonācu pie tā, ka ar -O1 šis ieliek kā apakšprogrammu, bet ar -O2 un -O3 viss notiekās.
Karoče, sasipos meistarībā  ::  Toč, tak ar makro šo lietu vienkāršāk izdarīt.

----------


## Jurkins

Labs rīts visiem! 



```
enum mType
 {     
    CONT,     
    MEMB,     
    APPL, 
};

uint8_t function_1(uint8_t val);
uint8_t function_2(uint8_t val);  

struct menuItem 
{    
    mType type;     
    //void (*function_1)(uint8_t val);   
    char* title;
     void* child;
     uint8_t count;
};  

const char mon[] = "Pirmdiena"; 
const char tue[] = "Otrdiena"; 

menuItem item1 = {MEMB, (char*)mon, (void*)&function_1, 5}; 
menuItem item2 = {MEMB, (char*)mon, (void*)&function_2, 3}; 

int main() 
{
    menuItem *currentItem = &item1;
    cout << (int) (*currentItem->child)(currentItem->count) << endl;
    cout << (int) (*function_1)(currentItem->count) << endl;
     currentItem = &item2;
    cout << (int) (*currentItem->child)(currentItem->count) << endl;
    cout << (int) (*function_2)(currentItem->count) << endl;
} 

uint8_t function_1(uint8_t val) { return --val; }
uint8_t function_2(uint8_t val) { return ++val; }
```

        Tātad situācija. Ir struktūra, kuras vienam elementam (void* child)  gribu iebarot pointeri uz fukciju. Vispārīgi, ko šim elementam iebarot,  ir atkarīgs no tā , kāds ir mType type. Vienā gadījumā man tur vajag  iebarot pointeri uz citu šādu pašu struktūru. Tur viss iet bez  problēmām. Bet šeit kompilators saka, ka; 
       45:32: error: 'void*' is not a pointer-to-object type 
       49:32: error: 'void*' is not a pointer-to-object type 
       Es pat saprotu  :: , ka šis ir malacis, ka tā saka, un kāpēc šis tā saka.  Bet kā šajā gadījumā "pievest" *currentItem->child tipu? Jeb es jau  no sākuma kaut ko daru nepareizi, un to "iebarošanu" vajag veikt kaut kā  citādi. Gūgles tantes dotajās pamācībās, struktūrā vienmēr ir pointeris  uz konkrētu funkciju, kā aizkomentētajā rindā. Bet tas neder  :: . 

       Iepriekš paldies, par Jūsu laiku un sapratni par manu c++ nevīžibu  :: 

edit: būtībā vajadzētu varēt to izdarīt, jo pointers tak, rupji sakot, ir norāde uz adresi, kura tādam AVR ir vārds, vienalga, vai tas pointers norāda uz baitu vai struktūru vai funkciju.

edit2: atradu variantu "vienādām" funkcijām - kam ir vienāda tipa un skaits parametru un kas atgriež vienādu tipu.


```
uint8_t func1(uint8_t val){return ++val;}
...
uint8_t func10(uint8_t val){return --val;}

typedef uint8_t(*ptr)(uint8_t);

ptr pf1 = &func1;
...
ptr pf10 = &func10;
```

 bet, vienalga, ja šos pointeros iebaro struktūrai kā (void*), tad atpakaļ ar (ptr*) nesanāk "pievest".

----------


## karloslv

Brr, vispirms mēs te īsti nezinām, kura rindiņa tev ir 45. un kura 49. Bet iekšējais kompilators aptuveni nojauš, kur pinies. 

Nekā tāda jau tur nav, mazas kļūdas. Pieeja ir lietojama, taču, lai strādātu, vajag drusku stingrāku _tipāžu_. Tātad, ja zini, ka tev tur tajā child reizēm var būt konkrēta tipa funkcija, kaut kur vajadzētu aprakstīt šo tipu, lai pēc tam var šo child konvertēt uz tādu pointeri. Tātad, vajag rindiņu 



```

typedef uint8_t(*MyCleverFunctionType)(uint8_t);
```

 Un tad, kad zini un esi pārliecināts, ka currentItem->child ir interpretējams kā funkcija, tad vienkārši nokāsto:



```

MyCleverFunctionType menuFunc = (MyCleverFunctionType) currentItem->child;
```

 Tālāk jau vienkārši var izsaukt menuFunc(), kompilators zinās, kas jādara. Protams, es te izvērsti uzrakstīju, var arī vienā rindiņā.

BET, ja gribi pro līmenī šo risināt, tad bārdainie vīri par to iedomājās jau sākumā un ieviesa tādu jēdzienu kā _union_ - palasi, baigais haks, bet šajā situācijā noder, jo nav lieki jāskaidro kompilatoram, ka jā, es zinu, ko daru, jā, tā ir funkcija, tagad interpretē to šādi.

----------


## karloslv

Cita lieta - drusku nesaprotami, ko tu tur veido ar šo nepareizo rindu:



```

cout << (int) (*function_2)(currentItem->count) << endl;
```

 ja pietiktu (un būtu pareizi) vienkārši ar


```

cout << function_2(currentItem->count) << endl;
```

 function_1 taču ir joprojām vienkārši visparastākā funkcija, nekādu tur pointeru!

----------


## Jurkins

Velns, toč, rindu numuri tak neiekopējās. Atvainojos.

Jā, nu tā ir, ka šitajā rindiņā rāda kļūdu


```
cout << (int) (*currentItem->child)(currentItem->count) << endl;
```

 Ja uzrakstu


```
ptr2func *tmpFunc = (ptr2func*)currentItem->child;
cout << (int) (*tmpFunc)(currentItem->count) << endl;
```

 tad viss notiek. Vienā rindiņā gan nesanāk  ::  uzrakstīt. Gan jau iekavas ieliek ne tur vai neielieku, kur vajag.
Sliktums ir tas, ka ja tiek padotas dažādas funkcijas, nevar zināt, kura kurā gadījumā ir īstā.
Ar union mēģināju iepriekš. Acīmredzot, darīju kaut ko šķersām. Kamēr struktūrās ir parasti datu tipi, mani union strādā lieliski, līdzko sākas pointeri uz funkcijām un struktūrām, tā apmaldos kaut kur. Jā, bet nu union ir forša manta. Vispār, vēl nedēļu atpakaļ ieraugot izteiksmi ar daudzām iekavām, daudzām zvaigznītēm un ampersandiem, uznāca mazvērtības un bezcerības sajūtas - Visums tik liels, bet es tik mazs... :: .

Liels paldies, karloslv. Jāsaka, klusībā cerēju, ka Tu būsi tas, kas atbildēs.

p.s smieklīgi sanāca


```
cout << (int) sizeof(ptr2func) << endl;
```

 rezultāts 8. Kāda vella pēc??? Struktūrai (tā kas kodā) reultāts 32. Kas par...??? Kamēr pielēca, ka rakstu iekš webiskās IDEs un uzrakstīju


```
cout << (int) sizeof(size_t) << endl;
```

----------


## Jurkins

> Cita lieta - drusku nesaprotami, ko tu tur veido ar šo nepareizo rindu:
> 
> 
> 
> ```
> 
> cout << (int) (*function_2)(currentItem->count) << endl;
> ```


 Ai, tā rinda ir vienkārši pārbaudei, ka tas iet cauri, ja funkcijas vietā lieto pointeri uz funkciju. Tas lai pats saprastu.

----------


## sasasa

Lasu un klusībā ceru ka es arī kādreiz iebraukšu šajās lietās. Pagaidām ,gan vēl pa "smilšu kasti" vien rušinos. Kaut kas reizēm arī sanāk  ::

----------


## Jurkins

Zin, teikšu kā ir.Šitā lieta ievelk. Un, ja tas, diemžēl, nav maizesdarbs, tad reizēm process kļūst svarīgāks par rezultātu  :: . It kā ir daudz "meņušku" atmegām GitHubā un citur, bet man vajadzēja uzrakstīt savu. Ar enkoderu vai trim pogām. Un lai viss iespējamais glabājas iekš PROGMEM.


```
#include <avr/pgmspace.h>
#include "MenuTree.h"

enum button_t
{
  BTN_NEXT   = 1,
  BTN_PREV   = 2,
  BTN_ENTER  = 3,
};

#define MENU_LEVEL_COUNT  4
#define LCD_ROW_COUNT 4
 
uint8_t activeItem = 0;  
uint8_t Level = 0;

uint8_t LevelSelectedItem[MENU_LEVEL_COUNT] = {N, N, N, N};
uint8_t LevelCursorPos[MENU_LEVEL_COUNT] = {0, 0, 0, 0};
uint8_t LevelFirstItem[MENU_LEVEL_COUNT] = {0, 0, 0, 0};

uint8_t ScreenItems[LCD_ROW_COUNT];
 
uint8_t ScreenCursorPos = N;

uint8_t getNext(uint8_t item){return pgm_read_byte(&MenuItems[item].next);}
uint8_t getPrev(uint8_t item){return pgm_read_byte(&MenuItems[item].prev);}
uint8_t getChild(uint8_t Item){return pgm_read_byte(&MenuItems[Item].child);}
uint8_t getParent(uint8_t Item){return pgm_read_byte(&MenuItems[Item].parent);}

void printItemTitle(uint8_t Item)
{
  const char *iname = (const char*) pgm_read_word(&title[Item]);
  while (char cchar = pgm_read_byte(iname++)) Serial.print((char)cchar);
  if(getChild(Item) < N)
    Serial.println(" > ");
  else
    Serial.println("");
}

void renderMenu(button_t button)
{
    uint8_t X = 0;
    uint8_t i;
    if(LevelSelectedItem[Level] == activeItem)
    {
        switch (button)
        {
          case BTN_NEXT:
              if(getChild(activeItem) == N)
              {
                  //pagaidām nedaram neko
              }
              else
              {
                  if(Level < MENU_LEVEL_COUNT) Level++;
                  activeItem = getChild(activeItem);

                  ScreenCursorPos++;
              }
              break;
          case BTN_PREV:
              if(getPrev(activeItem) == N)
              {
                  //pagaidām nedaram neko
              }
              else
              {
                  //pagaidām nedaram neko
              }
              break;
          case BTN_ENTER:
              LevelSelectedItem[Level] = N;

              if(activeItem == 0)
                  ScreenCursorPos = N;
              else
                ScreenCursorPos = LevelCursorPos[Level];
            
              ScreenItems[0] = LevelFirstItem[Level];
              if(activeItem == 0)
                  ScreenItems[1] = getNext(ScreenItems[0]);
              else
                  ScreenItems[1] = getChild(ScreenItems[0]);
              for(i = 2; i < LCD_ROW_COUNT; i++)
              {
                  ScreenItems[i] = getNext(ScreenItems[i-1]);
              }
              break;
          default: break;
        }
    }
    else
    {
        switch (button)
        {
          case BTN_NEXT:
              if(getNext(activeItem) == N)
              {
                  //pagaidām nedaram neko
              }
              else
              {
                  activeItem = getNext(activeItem);
                  if(ScreenCursorPos < LCD_ROW_COUNT - 1)
                    ScreenCursorPos++;
                  else
                  {     
                      if(ScreenItems[0] == LevelSelectedItem[Level - 1])
                      {

                        ScreenItems[0] = getChild(ScreenItems[0]);
                        for(i = 1; i < LCD_ROW_COUNT; i++)
                        {
                            ScreenItems[i] = getNext(ScreenItems[i-1]);
                        }
                    }
                    else
                    {
                        ScreenItems[0] = getNext(ScreenItems[0]);
                        for(i = 1; i < LCD_ROW_COUNT; i++)
                        {
                            ScreenItems[i] = getNext(ScreenItems[i-1]);
                        }
                    }
                  }
              }
              break;
          case BTN_PREV:
              if(getPrev(activeItem) == N)
              {
                  if(getParent(activeItem) == N)
                  {
                      //Pagaidām nedaram neko;
                  }
                  else
                  {
                      if(Level > 0) Level--;
                      activeItem = getParent(activeItem);
 
                      ScreenCursorPos = 0;
 
                      ScreenItems[0] = activeItem;
                      ScreenItems[1] = getChild(ScreenItems[0]);
                      for(i = 2; i < LCD_ROW_COUNT; i++)
                      {
                          ScreenItems[i] = getNext(ScreenItems[i-1]);
                      }
                  }
              }
              else
              {
                  activeItem = getPrev(activeItem);

                  if(ScreenCursorPos > 0)
                    ScreenCursorPos--;
                  else
                  {                    
                    ScreenItems[0] = activeItem;
                    for(i = 1; i < LCD_ROW_COUNT; i++)
                    {
                        ScreenItems[i] = getNext(ScreenItems[i-1]);
                    }
                  }
              }
              break;
          case BTN_ENTER:
              if(getChild(activeItem) < N)
              {
                  LevelSelectedItem[Level] = activeItem;
                  LevelCursorPos[Level] = ScreenCursorPos;
                  LevelFirstItem[Level] = ScreenItems[0]; //ScreenFirstItem;
    
                  ScreenCursorPos = 0;
                  ScreenItems[0] = activeItem;
                      ScreenItems[1] = getChild(ScreenItems[0]);
                  for(i = 2; i < LCD_ROW_COUNT; i++)
                  {
                  ScreenItems[i] = getNext(ScreenItems[i-1]);
                  }
              }
              else
              {
                    //Pagaidām nedaram neko
              }
              break;
          default: break;
        }
    }

    for(i = 0; i < LCD_ROW_COUNT; i++)
    {
      if(i == ScreenCursorPos)
      {
         Serial.print(" > ");
         printItemTitle(activeItem);
      }
      else
      {
         Serial.print("   ");
         printItemTitle(ScreenItems[i]);
      }
    }
    Serial.println("");
}

 

void setup() {
  Serial.begin(115200);
  uint8_t i;
  ScreenItems[0] = 0;
  for(i = 1; i < LCD_ROW_COUNT; i++)
  {
    ScreenItems[i] = N;
  }
  for(i = 0; i < LCD_ROW_COUNT; i++)
  {
    if(i == ScreenCursorPos)
      Serial.print(" > ");
    else
      Serial.print("   ");
    printItemTitle(ScreenItems[i]);
  }
  Serial.println("");
}

void loop() {
  button_t button;
  if (Serial.available())
  {
    switch (Serial.read())
    {
      case 's':
        //Serial.println("NEXT");
        button = BTN_NEXT; break;
      case 'a':
        //Serial.println("PREV");
        button = BTN_PREV; break;
      case 'z':
        //Serial.println("ENTER");
        button = BTN_ENTER; break;
      default: break;
    }
    renderMenu(button);
  }
}
```

 Izskatās diezgan briesmīgi, bet strādā. Nu vot tagad vajag piemuhļīt attiecīgajiem punktiem funkcijas klāt. Kaut kas sāk sanākt. Nu i ķeru blusas.

p.s. velns nevar kā pievienoto failu ielikt. Rāda "invalid file".

----------


## Jurkins

un pati meņuška MenuTree.h


```
struct menuItem_t
{
  /*const char *name;*/
  uint8_t next;
  uint8_t prev;
  uint8_t child;
  uint8_t parent;
};

#define N 35

const char root[] PROGMEM = "LEVEL_ROOT";
const char lv10[] PROGMEM = "Level_1.0";
const char lv11[] PROGMEM = "Level_1.1";
const char lv12[] PROGMEM = "Level_1.2";
const char lv13[] PROGMEM = "Level_1.3";
const char lv14[] PROGMEM = "Level_1.4";
const char lv15[] PROGMEM = "Level_1.5";
const char lv16[] PROGMEM = "Level_1.6";
const char lv17[] PROGMEM = "Level_1.7";
const char lv100[] PROGMEM = "Level_1.0.0";
const char lv101[] PROGMEM = "Level_1.0.1";
const char lv102[] PROGMEM = "Level_1.0.2";
const char lv103[] PROGMEM = "Level_1.0.3";
const char lv104[] PROGMEM = "Level_1.0.4";
const char lv105[] PROGMEM = "Level_1.0.5";
const char lv106[] PROGMEM = "Level_1.0.6";
const char lv110[] PROGMEM = "Level_1.1.0";
const char lv111[] PROGMEM = "Level_1.1.1";
const char lv112[] PROGMEM = "Level_1.1.2";
const char lv113[] PROGMEM = "Level_1.1.3";
const char lv114[] PROGMEM = "Level_1.1.4";
const char lv115[] PROGMEM = "Level_1.1.5";
const char lv116[] PROGMEM = "Level_1.1.6";
const char lv120[] PROGMEM = "Level_1.2.0";
const char lv121[] PROGMEM = "Level_1.2.1";
const char lv122[] PROGMEM = "Level_1.2.2";
const char lv123[] PROGMEM = "Level_1.2.3";
const char lv124[] PROGMEM = "Level_1.2.4";
const char lv125[] PROGMEM = "Level_1.2.5";
const char lv1210[] PROGMEM = "Level_1.2.1.0";
const char lv1211[] PROGMEM = "Level_1.2.1.1";
const char lv1212[] PROGMEM = "Level_1.2.1.2";
const char lv1213[] PROGMEM = "Level_1.2.1.3";
const char lv1214[] PROGMEM = "Level_1.2.1.4";
const char lv1215[] PROGMEM = "Level_1.2.1.5";
const char empty[] PROGMEM = "";

const char* const title[] PROGMEM = {root,
                                     lv10,
                                     lv100, lv101, lv102, lv103, lv104, lv105, lv106,
                                     lv11,
                                     lv110, lv111, lv112, lv113, lv114, lv115, lv116,
                                     lv12,
                                     lv120, lv121,
                                     lv1210, lv1211, lv1212, lv1213, lv1214, lv1215,
                                     lv122, lv123, lv124, lv125,
                                     lv13, lv14, lv15, lv16, lv17,
                                     empty
                                    };


const menuItem_t MenuItems[] PROGMEM =
{
  {/*root,*/    N, N, 1, N},        //0
  {/*lv10,*/      9, N, 2, 0},      //1
  {/*{lv100,*/      3, N, N, 1},    //2
  {/*lv101,*/       4, 2, N, N},    //3
  {/*lv102,*/       5, 3, N, N},    //4
  {/*lv103,*/       6, 4, N, N},    //5
  {/*lv104,*/       7, 5, N, N},    //6
  {/*lv105,*/       8, 6, N, N},    //7
  {/*lv106,*/       N, 7, N, N},    //8
  {/*lv11,*/      17, 1, 10, 0},    //9
  {/*lv110,*/       11, N, N, 9},   //10
  {/*lv111,*/       12, 10, N, N},  //11
  {/*lv112,*/       13, 11, N, N},  //12
  {/*lv113,*/       14, 12, N, N},  //13
  {/*lv114,*/       15, 13, N, N},  //14
  {/*lv115,*/       16, 14, N, N},  //15
  {/*lv116,*/       N, 15, N, N},   //16
  {/*lv12,*/      30, 9, 18, 0},    //17
  {/*lv120,*/       19, N, N, 17},  //18
  {/*lv121,*/       26, 18, 20, N}, //19
  {/*lv1210,*/        21, N, N, 19},//20
  {/*lv1211,*/        22, 20, N, N},//21
  {/*lv1212,*/        23, 21, N, N},//22
  {/*lv1213,*/        24, 22, N, N},//23
  {/*lv1214,*/        25, 23, N, N},//24
  {/*lv1215,*/        N, 24, N, N}, //25
  {/*lv122,*/       27, 19, N, N},  //26
  {/*lv123,*/       28, 26, N, N},  //27
  {/*lv124,*/       29, 27, N, N},  //28
  {/*lv125,*/       N, 28, N, N},   //29
  {/*lv13,*/      31, 17, N, N},    //30
  {/*lv14,*/      32, 30, N, N},    //31
  {/*lv15,*/      33, 31, N, N},    //32
  {/*lv16,*/      34, 32, N, N},    //33
  {/*lv17,*/      N, 33, N, N},     //34
  {/*empty,*/     N, N, N, N},    //35
};
```

----------


## Jurkins

```
#include <stdio.h>
#include <stdint.h>

typedef uint8_t(*ptr2func1)(uint8_t);
typedef uint8_t(*ptr2func2)(uint8_t, uint8_t);

uint8_t func1(uint8_t val){return ++val;}
uint8_t func3(uint8_t val){return --val;}
uint8_t func2(uint8_t val1, uint8_t val2){return val1+val2;}

typedef struct{
    uint8_t id;
    union{
        ptr2func1 pf1;
        ptr2func2 pf2;
    };
    uint8_t value;
}Item;

int main(int argc, char **argv)
{
    Item item0 = {0, {&func1}, 10};
    //Item item0 = {1, {&func2}, 15}; /*nestrādā*/
    Item item1 = {1, {.pf2 = &func2}, 15};
    Item item2 = {1, {&func3}, 15};
    
    printf("%d\n", (*item0.pf1)(10));
    printf("%d\n", (*item1.pf2)(12, 6));
    printf("%d\n", (*item2.pf1)(14));
    
    //printf("hello world\n");
    return 0;
}
```

 Jā, nu it kā izoperēju mandeles caur pakaļu, bet tas nekā nedod.
Pirmkārt jau nesaprotu, kāpēc negrib strādāt rinda:


```
Item item0 = {1, {&func2}, 15}; /*nestrādā*/
```

 It kā pēc pamācībām vajadzēja strādāt. Nu bet ok, to var apiet. Sliktais stāsts ir tas, ka lai gan es iebaroju funkcijas ar dažādiem prototipiem, tad kad vēlāk vajadzēs rakstīt kaut kādu funkciju, kura ņems vērtības no kaut kāda


```
 Item *currentItem...
```

  būs jāzina, kura prototipa funkcija kurā brīdī būs "currentItem"sastāvā. Bet tas galīgi neder. Viss ir slikti  :: 
Lai gan atmeļprojektos, jau tā izvēlne nav nekāda briesmīgā , un, varbūt, ka pietiek, ja piem. vienā "submenu" zarā punktos izpildāmās funkcijas ir ar vienādiem prototipiem.

----------

