C / C++ casten van struct naar een ander type struct

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
Ben aan het pielen met FreeRTOS op een ESP32 ucontroller. Ben aardig thuis in diverse programmeertalen maar nog een n00b in C... Ik wik een enkele message queue gebruiken om verschillende soorten berichten tussen de taken te versturen. Ik heb daarvoor een struct gedefineerd als basistype en andere structs voor de specifieke berichten:
code:
1
2
3
4
5
6
7
8
9
10
typedef struct {
  enum MessageType messageType;
  char buffer[64]; // maximum message payload size
} QueueMessage;

typedef struct {
  enum MessageType messageType;
  uint16_t x;
  uint16_t y;
} JoystickMessage;

Het versturen van de berichten geeft geen probleem. In de code voor het afhandelen gaat het wel mis. Ik probeer dit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void queueReader(void *pvParameters)
{
  QueueMessage msg; // de generieke struct voor berichten

  setupMessageQueue();

  while(1)
  {
    // wait, and block forever, for a message
    xQueueReceive(queue, &msg, portMAX_DELAY); 
    switch (msg.messageType)
    {
      case Joystick:
        uint16_t x = (JoystickMessage) msg.x; // <-- hier gaat het mis, deze cast pikt de compiler niet, ik krijg een melding dat struct QueueMessage geen member x heeft.
        uint16_t y = (JoystickMessage) msg.y;
        break;
    }
   }

  vTaskDelete( NULL );
}

Ik heb me al suf gegoogeld. Gok dat dit zo triviaal is dat ik meer niet relevante resultaten krijg dan iets waar ik mee geholpen ben of verder mee kom ;)

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap

Beste antwoord (via Zarathustra op 28-09-2019 11:31)


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
Zarathustra schreef op vrijdag 27 september 2019 @ 20:09:
Dit werkt wel:
code:
1
uint16_t x = ((JoystickMessage*) &msg)->x;
Het lijkt te werken, maar heb je potentieel een alignment probleem als je een dergelijke cast doet zonder de alignment van de buffer te forceren, maar daarmee heb je nog niet alle problemen gehad.
Het is namenlijk undefined behaviour wat je hier doet volgens de C11 standaard. Als het goed is krijg je een strict aliasing warning...

De oplossing met de union is ook niet zonder gevaren trouwens want dat werk alleen als je het over C hebt (in C++ is het ook undefined behavour) en je moet bovendien wel eerst een union hebben: Je kunt dus niet het adres van een willekeurige char[] buffer casten naar een pointer to union want dan heb je ook undefined behaviour.

Nu weet ik dat FreeRTOS onder water een memcpy doet in xQueueReceive() dus de union type punning is op die manier wel toegestaan.

Read it and weep hier
Oplossing met de union is wel een stuk netter dan casten. En de compiler zorgt er meteen voor dat er genoeg ruimte gereserveerd wordt voor alle data.
C:
1
queue = xQueueCreate(6, sizeof(QueueMessage));

Let wel dat deze erg belangrijk is om dat te kunnen garanderen.
C staat taal technisch stil
Na de correcties die de taal in 2018 heeft gehad is het ook een vrijwel perfecte taal. Alleen Rust is beter. _O-

[ Voor 10% gewijzigd door farlane op 27-09-2019 23:40 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Alle reacties


Acties:
  • +2 Henk 'm!

  • kluyze
  • Registratie: Augustus 2004
  • Niet online
Je hebt het over C en C++ wat eigenlijk twee compleet verschillende talen zijn. C++ kent namenlijk overerving en daarmee zou dit simpel om op te lossen zijn. C kent dit niet en is dus wat moeilijker.

Ik ben niet heel bekend met C, maar is dit iets?:
https://stackoverflow.com...9/struct-inheritance-in-c
Tweede antwoord?

Acties:
  • +1 Henk 'm!

  • Christiaan676
  • Registratie: December 2011
  • Laatst online: 10:38
Gaat zo te zien fout op de volgorde van operators: https://en.cppreference.com/w/c/language/operator_precedence

Probeer is: uint16_t x = ((JoystickMessage) msg).x;

Acties:
  • 0 Henk 'm!

  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
kluyze schreef op vrijdag 27 september 2019 @ 19:30:
Je hebt het over C en C++ wat eigenlijk twee compleet verschillende talen zijn. C++ kent namenlijk overerving en daarmee zou dit simpel om op te lossen zijn. C kent dit niet en is dus wat moeilijker.

Ik ben niet heel bekend met C, maar is dit iets?:
https://stackoverflow.com...9/struct-inheritance-in-c
Tweede antwoord?
Ik pas mijn opmerking over n00b aan: ik ben n00b op C en C++ vlak ;)

De message queue gebruikt een array van elementen (type void?) met een vaste grootte. De constructor is:
code:
1
queue = xQueueCreate(6, sizeof(QueueMessage));

Om een bericht te plaatsen in die queue gebruik ik deze code (niet meer dan een memcpy - vermoed ik):
code:
1
xQueueSendToBack(queue, &msg, 10)
Om te lezen dit:
code:
1
2
QueueMessage msg;
xQueueReceive(queue, &msg, portMAX_DELAY);

Je post verwijst naar het definieren van struct en inherited struct (mooie syntax!), volgens mij is de grootte van de verschillende structs dan ook anders. Ik wil nu juist een basis type met een grootte die groot genoeg is voor alle type berichten. En dan het basis type casten naar het specifieke type bericht.

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap


Acties:
  • 0 Henk 'm!

  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
Christiaan676 schreef op vrijdag 27 september 2019 @ 19:38:
Gaat zo te zien fout op de volgorde van operators: https://en.cppreference.com/w/c/language/operator_precedence

Probeer is: uint16_t x = ((JoystickMessage) msg).x;
Die geeft "no matching function for call to 'joystickMessage::JoystickMessage(QueueMessage&)" en "no suitable user-defined conversion from 'QueueMessage' to 'JoystickMessage; exists"

Heerlijk vage foutmeldingen 8)

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap


Acties:
  • +1 Henk 'm!

  • biomass
  • Registratie: Augustus 2004
  • Laatst online: 04-05 20:15
Knip

[ Voor 96% gewijzigd door biomass op 27-09-2019 19:54 . Reden: ik kijk nog een keer ]


Acties:
  • +2 Henk 'm!

  • Christiaan676
  • Registratie: December 2011
  • Laatst online: 10:38
C is al weer even geleden. Maar structs kan je blijkbaar niet direct casten: https://stackoverflow.com...-c-structure-into-another

Via een pointer kan het blijkbaar wel dan krijg je:
uint16_t x = ((JoystickMessage*) &msg)->x;

Acties:
  • 0 Henk 'm!

  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
Christiaan676 schreef op vrijdag 27 september 2019 @ 19:55:
C is al weer even geleden. Maar structs kan je blijkbaar niet direct casten: https://stackoverflow.com...-c-structure-into-another

Via een pointer kan het blijkbaar wel dan krijg je:
uint16_t x = ((JoystickMessage*) &msg)->x;
YES!

Dit werkt niet:
code:
1
uint16_t x = ((JoystickMessage*) &msg).x;
Dit werkt wel:
code:
1
uint16_t x = ((JoystickMessage*) &msg)->x;

Ik lees hier dat een dot of het pijltje niets uit zou mogen maken. Compiler xtensa denkt daar anders over :)

Dank @kluyze, @Christiaan676 en @biomass !

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap


Acties:
  • +1 Henk 'm!

  • DataGhost
  • Registratie: Augustus 2003
  • Laatst online: 20:49

DataGhost

iPL dev

Zarathustra schreef op vrijdag 27 september 2019 @ 20:09:
[...]

Ik lees hier dat een dot of het pijltje niets uit zou mogen maken.
Lees het nog maar eens, dat staat er niet. Er staat alleen dat iemand het fijn zou vinden als het (voor zijn beperkte use-case) niet uit zou maken.

Acties:
  • 0 Henk 'm!

  • Christiaan676
  • Registratie: December 2011
  • Laatst online: 10:38
De dot (.) operator of arrow (->) operator doen vrijwel het zelfde maar zijn niet het zelfde!

Zoals ook op stack overflow staat: bar->member is the same as (*bar).member in jou geval kom je dan op:
code:
1
 uint16_t x = ((JoystickMessage*) &msg)->x;

of
code:
1
 uint16_t x = (*((JoystickMessage*) &msg)).x;


Ofwel -> gebruik je als je een pointer naar een struct (of class in C++) hebt. En dot als het de struct (class) zelf is.


Dit is wel een gedoe in C zeg. In RUST gaat dat met enum's een stuk makkelijker: https://doc.rust-lang.org.../first-edition/enums.html

Acties:
  • 0 Henk 'm!

Anoniem: 590973

Volgens mij gaat het fout omdat msg.x and msg.y niet gedefinieerd zijn. En ga ik ervan uit dat je de struct van joystick wil vullen met data uit de msg.buffer[]. Dus iets van joystick.x = msg.buffer[0]?

Acties:
  • 0 Henk 'm!

  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
@DataGhost en @Christiaan676 ik denk dat ik wat verwend en/of lui geworden ben door jaren werken met scripttalen als Perl, PHP en Python ;)

Ik ga me verder verdiepen in pointers en derefencing, enzo.

@Christiaan676 oh, zonder meer 'gedoe'. Als hobby ucontrollers programmeren waar stacks / heaps / pointers en ander low-level 'gedoe' speelt is een leuke afwisseling voor me :D

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap


Acties:
  • +2 Henk 'm!

  • ThomasG
  • Registratie: Juni 2006
  • Laatst online: 18:42
Christiaan676 schreef op vrijdag 27 september 2019 @ 20:22:

Dit is wel een gedoe in C zeg. In RUST gaat dat met enum's een stuk makkelijker: https://doc.rust-lang.org.../first-edition/enums.html
Ik vroeg het mij al af waar het bleef, maar daar is 'ie hoor. Geen C/C++ topic zonder dat iemand Rust noemt :+

Zulke enum's in rust zijn in feite gewoon veredelde compile-time type-safe unions. Het kan in C ook gewoon, alleen is het minder safe dan in Rust. omdat je de data in de union kunt benaderen hoe je zelf wilt (en soms undefined behaviour oplevert).

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef enum {
    JOYSTICK,
    FOOBAR,
} MessageType;

typedef struct {
    MessageType type;
    union {
        struct {
            uint16_t x;
            uint16_t y;
        } joystick;
        
        struct {
            bool foo;
            char bar[100];
        } foobar;
    };
} Message;
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    Message msg = { .type = JOYSTICK, .joystick = { 16, 512 } };

    switch (msg.type) {
        case JOYSTICK:
            {
                int16_t x = msg.joystick.x;
                int16_t y = msg.joystick.y;
                // ...
            }
            break;
        case FOOBAR:
             // msg.foobar.foo ...
            break;
    }

Acties:
  • 0 Henk 'm!

  • Christiaan676
  • Registratie: December 2011
  • Laatst online: 10:38
ThomasG schreef op vrijdag 27 september 2019 @ 21:02:
[...]
Ik vroeg het mij al af waar het bleef, maar daar is 'ie hoor. Geen C/C++ topic zonder dat iemand Rust noemt :+
Kon het niet laten >:) C staat taal technisch stil. Wat op zich geen probleem is maar voor de meeste toepassingen zijn er betere alternatieven. Wordt wel gewerkt aan ESP32 ondersteuning voor LLVM en dus RUST, alleen geen idee hoever dat is.

Oplossing met de union is wel een stuk netter dan casten. En de compiler zorgt er meteen voor dat er genoeg ruimte gereserveerd wordt voor alle data.

Acties:
  • Beste antwoord
  • +3 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
Zarathustra schreef op vrijdag 27 september 2019 @ 20:09:
Dit werkt wel:
code:
1
uint16_t x = ((JoystickMessage*) &msg)->x;
Het lijkt te werken, maar heb je potentieel een alignment probleem als je een dergelijke cast doet zonder de alignment van de buffer te forceren, maar daarmee heb je nog niet alle problemen gehad.
Het is namenlijk undefined behaviour wat je hier doet volgens de C11 standaard. Als het goed is krijg je een strict aliasing warning...

De oplossing met de union is ook niet zonder gevaren trouwens want dat werk alleen als je het over C hebt (in C++ is het ook undefined behavour) en je moet bovendien wel eerst een union hebben: Je kunt dus niet het adres van een willekeurige char[] buffer casten naar een pointer to union want dan heb je ook undefined behaviour.

Nu weet ik dat FreeRTOS onder water een memcpy doet in xQueueReceive() dus de union type punning is op die manier wel toegestaan.

Read it and weep hier
Oplossing met de union is wel een stuk netter dan casten. En de compiler zorgt er meteen voor dat er genoeg ruimte gereserveerd wordt voor alle data.
C:
1
queue = xQueueCreate(6, sizeof(QueueMessage));

Let wel dat deze erg belangrijk is om dat te kunnen garanderen.
C staat taal technisch stil
Na de correcties die de taal in 2018 heeft gehad is het ook een vrijwel perfecte taal. Alleen Rust is beter. _O-

[ Voor 10% gewijzigd door farlane op 27-09-2019 23:40 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • +2 Henk 'm!

  • biomass
  • Registratie: Augustus 2004
  • Laatst online: 04-05 20:15
Wat later terug bij de desktop, maar toen @Christiaan676 postte wist ik dat je dat zou gaan roepen dat het werkte.
De reden is voornamelijk alignment. De lengte van die structs is niet gelijk, en je wilde dat ook eigenlijk niet, maar de compiler wil weten waar die aan toe is als jij een rijtje QueueMessages achter elkaar in het geheugen hebt staan. Hoe moet je daar netjes JoystickMessages uit halen als de lengte niet gelijk is?
De enige manier waarop dat kan is expliciet maken waar de de data staat. En dat kan alleen als je met de lengte van een QueueMessage de beginposities van een JoystickMessage aanwijst.
Zo dus:
code:
1
 ((JoystickMessage*) &msg)->x;


En terwijl ik dit typ, stipt @farlane nog de belangrijkste subtiliteit aan: De indeling van de struct is 'implementation defined' gedrag. Ofwel, je hebt geen garantie op de plek in het geheugen van de members van je struct. Dat kan wat verrassingen geven als je verwacht memberlocaties te kunnen delen binnen je struct.. ;)

Tegenwoordig gaan we voor de simpele reden waarom het niet zou mogen: VC2019 weigert deze types ook te casten: (Lengte is gelijk).
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct {
    enum MessageType messageType;
} QueueMessage;

typedef struct {
    enum MessageType messageType;
} JoystickMessage;

int main()
{
    QueueMessage msg;
    JoystickMessage j= (JoystickMessage)msg;
    
}


Waarom zou je onderscheid mogen opheffen als het eerst gemaakt is? ;)

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
biomass schreef op vrijdag 27 september 2019 @ 23:47:
En terwijl ik dit typ, stipt @farlane nog de belangrijkste subtiliteit aan: De indeling van de struct is 'implementation defined' gedrag. Ofwel, je hebt geen garantie op de plek in het geheugen van de members van je struct.
Dat was niet de belangrijkste subtiliteit die ik aanstipte. -O-
Tegenwoordig gaan we voor de simpele reden waarom het niet zou mogen: VC2019 weigert deze types ook te casten: (Lengte is gelijk).
Ik weet niet of het al gezegd is, maar C ondersteunt helemaal niet het casten naar struct types.

[ Voor 23% gewijzigd door farlane op 27-09-2019 23:57 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
farlane schreef op vrijdag 27 september 2019 @ 23:35:
[...]
Het lijkt te werken, maar heb je potentieel een alignment probleem als je een dergelijke cast doet zonder de alignment van de buffer te forceren, maar daarmee heb je nog niet alle problemen gehad.
Ik zat te denken aan het alignen van de structs in de buffer... Tot ik je link ging lezen :)

Helder! Ik ga het niet zo oplossen (ook al geeft mijn compiler geen warning) Lees in het FreeRTOS boek een alternatieve methode (struct met message type en void pointer) voor berichten met verschillende grootte.

Hoop geleerd, dank!

Of het er leesbaarder op wordt :P
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
typedef struct
{
  enum MessageType messageType;
  void *message;
} QueueMessage;

typedef struct
{
  uint8_t button;
  unsigned long time;
} ButtonMessage;

/**
  Get a ButtonMessage struct from a pool of UI_MESSAGE_QUEUELENGTH items.
  The pool has the same size as the message queue. Since all UI tasks have lower
  priority than the consumer task of the queue (taskHandleUI) we can't run out / overrun
  the circular pool, ie. the source tasks will block untill items are removed from the 
  queue and thus the 'used' items in the pool are handled.
**/
ButtonMessage* getButtonMessage()
{
  static ButtonMessage buttonMessages[UI_MESSAGE_QUEUE_LENGTH];
  static int buttonMessagesIndex = 0;

  ButtonMessage *buttonMessage = &buttonMessages[buttonMessagesIndex++];
  if (buttonMessagesIndex == UI_MESSAGE_QUEUE_LENGTH)
  {
    buttonMessagesIndex = 0;
  }
  return buttonMessage;
}

void sendButtonPressMessage()
{
  QueueMessage message { Button, getButtonMessage() };
  ButtonMessage *buttonMessage = (ButtonMessage *) message.message;
  buttonMessage->button = 1;
  buttonMessage->time = millis();

  xQueueSendToBack(queue, &message, 10);
}

[ Voor 59% gewijzigd door Zarathustra op 28-09-2019 15:50 ]

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
Tja....als het inderdaad zo is dat alle UI tasks een lagere prioriteit hebben dan de consumers van de events heb je ueberhaupt geen circular buffer nodig hier, dat maakt het wel weer wat overzichtelijker.

Als je je enums ook nog typedeffed, de uint8_t wegmoffelt (ti's geen AVR eh? :P) en de sendmessage test ook wat restructured ...
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
typedef enum
{
    MSG_BUTTON
}MessageType;

typedef struct
{
  MessageType messageType;
  void *message;
} QueueMessage;

typedef struct
{
  int button;
  unsigned long time;
} ButtonMessage;

static ButtonMessage msg;

void sendButtonPressMessage()
{
  msg.button = 1;
  msg.time = xTaskGetTickCount();
    
  QueueMessage message { Button, &msg };
  xQueueSendToBack(queue, &message, 10);
}


Let er wel op dat access op je ButtonMessage msg nu potentieel vanuit meerdere taken kan plaatsvinden!

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • +1 Henk 'm!

  • Rowwan
  • Registratie: November 2000
  • Laatst online: 21:04
Bovenstaande oplossing lijkt me ook tricky. Je werkt hier met pointers die naar het button object wijzen, en niet de content copieren. Het werkt dus alleen als je buttonmessage static is. Als je hem op de stack zet ben je hem kwijt. Ook kun je niet meerdere buttonnessages posten, ze zullen bij het retrieven allemaal dezelfde content bevatten.

Wil je dit netjes oplossen dan zal de queuemessage zelf storage moeten bevatten. Het is me nog steeds niet duidelijk of je nu C of C++ gebruikt, maar wat je volgens mij moet implementeren is een base class met storage, en derived classes (zie post van kluyze in geval van C) die het daarin serializen. In C++ is dit makkelijker dan in C, maar zeker niet onmogelijk. (Zie eerdere links naar de mogelijkheden van OO binnen C.). Verder kun je nog functies in je struct maken die de serializatie verzorgen. (Lukt in C ook niet rechtstreeks, maar zie https://stackoverflow.com.../c-function-inside-struct)

Acties:
  • 0 Henk 'm!

  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
@farlane begin te begrijpen waarom je gelijk hebt. Bij het toevoegen van een message wordt vermoedelijk een yield gedaan en dus het bericht meteen verwerkt. Als er (ui) messages in de queue zaten dan zouden deze eerst verwerkt worden voor er überhaupt een ‘button message’ geplaatst kan worden.

Had de button detectie in een interrupt staan. Vraag me af of het dan ook geldt - met meerdere knoppen. (Edit: ja, je hoort zelf een yield te doen) Heeft nu overigens een eigen task. Heerlijk zo’n RTOS kernel :) Fijn getimed inputs pollen. Netste en meest betrouwbare software debounce ooit :)

@Rowwan de code draait onder FreeRTOS. Een real-time OS met pre-emptive multitasking. Als mijn redenering klopt zal xQueueSendToBack pas ‘returnen’ (is daar een goed Nederlands woord voor?) als de andere taak het bericht al verwerkt heeft.

C of C++? Geen idee. Arduino met FreeRTOS op een ESP32. Compiler instellingen zijn niet zichtbaar.

Andere aanpassingen ga ik nog naar kijken. Bijna de helft van m’n code weg in een ‘review’ is altijd goed!

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap


Acties:
  • 0 Henk 'm!

  • Radiant
  • Registratie: Juli 2003
  • Niet online

Radiant

Certified MS Bob Administrator

Zarathustra schreef op zondag 29 september 2019 @ 08:39

de code draait onder FreeRTOS. Een real-time OS met pre-emptive multitasking. Als mijn redenering klopt zal xQueueSendToBack pas ‘returnen’ (is daar een goed Nederlands woord voor?) als de andere taak het bericht al verwerkt heeft.
Nee hoor, dat hoeft niet. Als dat zo zou zijn kon je net zo goed een function call doen en je OS achterwege laten in dit geval :P
De message wordt op de queue gezet en die taak blijft gewoon doorlopen tot de scheduler besluit dat zijn tijd op is of er een andere reden is om de taak te suspenden.
Er is één uitzondering: als de queue vol is zal de task in een blocked state terecht komen tot er weer ruimte is (als een andere taak de queue heeft geleegd).

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
Had de button detectie in een interrupt staan. Vraag me af of het dan ook geldt - met meerdere knoppen.
In principe als je zelf netjes een task switch doet vanuit de interrupt zoals je zelf al aangeeft niet, ook dan wordt de taak meteen geactiveerd.
Als er meerdere interrupts binnen kunnen komen voordat de queue wordt afgehandeld wordt het verhaal anders, een interrupt heeft altijd prioriteit boven een normale taak .... :)
Radiant schreef op zondag 29 september 2019 @ 11:48:
[...]

Nee hoor, dat hoeft niet. Als dat zo zou zijn kon je net zo goed een function call doen en je OS achterwege laten in dit geval :P
De message wordt op de queue gezet en die taak blijft gewoon doorlopen tot de scheduler besluit dat zijn tijd op is of er een andere reden is om de taak te suspenden.
Eeehm, nee :/ .

Als er een hogere prioriteit taak staat te wachten op de queue wordt de lagere prioriteit taak suspended en komt de hogere aan de beurt.
Task switching latency zou anders nooit beter zijn (en waarschijnlijk een stuk slechter) dan je timer tick die standaard 10ms op de ESP volgens mij, wat een eeuwigheid is.

Het hele idee achter een RTOS is dat je je latency zo klein mogelijk kunt houden doordat taken die een hogere prioriteit hebben zo snel mogelijk kunnen reageren op events.
Als je een "superloop" applicatie hebt met statemachines bijvoorbeeld zal de latency afhankelijk zijn van de "looptijd".

[ Voor 17% gewijzigd door farlane op 29-09-2019 18:08 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
[b]farlane schreef op zaterdag 28 september 2019 @ 22:43:
Let er wel op dat access op je ButtonMessage msg nu potentieel vanuit meerdere taken kan plaatsvinden!
De sendButtonPressMessage() is geen test maar een functie die ik aanroep vanuit een taak die inputs polled. Als ik ButtonMessage binnen de functie static declareer is dat 'probleem' opgelost. Functie krijgt nog een parameter voor welke button ingedrukt is trouwens.

Arduino is overigens C++. Lees dat de typedef voor de enum dan niet nodig is. Ik heb dit:
code:
1
2
3
4
5
6
7
enum MessageType { Joystick = 0 , Button };

typedef struct
{
  MessageType messageType; // <- hier hoeft dan dus inderdaad geen 'enum' voor te staan
  void *message;
} QueueMessage;
Je aanpassingen aan sendButtonPressMessage zijn mooi / elegant. Minder 'leestekens' is ook altijd goed :)

@Radiant mijn spielerij projectje is een soort kruising tussen Space Invaders en een 'scrolling platform' spelletje op een ESP32, Noka 5110 scherm, analoge joystick, twee knoppen en een piezo speakertje. Ik denk dat dat zonder meer te doen is zonder RTOS en een functie aanroep zou volstaan ;)

(Alhoewel, ik las iets over PWM en D-Class versterking / audio genereren / DAC :D )

Naast de duimpjes: dank! Ook voor de reacties die, omdat ik niet helemaal duidelijk was, achteraf niet de bedoelde hulp gaven :)

[ Voor 5% gewijzigd door Zarathustra op 01-10-2019 16:04 ]

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap


Acties:
  • +1 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
Zarathustra schreef op dinsdag 1 oktober 2019 @ 15:59:
[...]
De sendButtonPressMessage() is geen test maar een functie die ik aanroep vanuit een taak die inputs polled. Als ik ButtonMessage binnen de functie static declareer is dat 'probleem' opgelost. Functie krijgt nog een parameter voor welke button ingedrukt is trouwens.
Als je je maar bewust bent van het feit dat als je dezelfde buffer/messag hergebruikt in meerdere button preses (zeker als deze sendButtonPressMessage vanuit interrupts wordt aangeroepen) dat je problemen kunt krijgen.
Arduino is overigens C++.
Het was me niet duidelijk dat je Arduino gebruikt en je code was nogal C-ish, vandaar de typedef. Als het C++ is is ook de struct/class typedef overbodig trouwens.
@Radiant mijn spielerij projectje is een soort kruising tussen Space Invaders en een 'scrolling platform' spelletje op een ESP32, Noka 5110 scherm, analoge joystick, twee knoppen en een piezo speakertje. Ik denk dat dat zonder meer te doen is zonder RTOS en een functie aanroep zou volstaan ;)
FWIW, de uitspraak van Radiant is niet correct.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Radiant
  • Registratie: Juli 2003
  • Niet online

Radiant

Certified MS Bob Administrator

farlane schreef op zondag 29 september 2019 @ 12:33:
[...]

Eeehm, nee :/ .

Als er een hogere prioriteit taak staat te wachten op de queue wordt de lagere prioriteit taak suspended en komt de hogere aan de beurt.
Task switching latency zou anders nooit beter zijn (en waarschijnlijk een stuk slechter) dan je timer tick die standaard 10ms op de ESP volgens mij, wat een eeuwigheid is.

Het hele idee achter een RTOS is dat je je latency zo klein mogelijk kunt houden doordat taken die een hogere prioriteit hebben zo snel mogelijk kunnen reageren op events.
Als je een "superloop" applicatie hebt met statemachines bijvoorbeeld zal de latency afhankelijk zijn van de "looptijd".
True. Als de consumer een hogere prioriteit heeft dan zal die runnable worden omdat hij uit de queue kan lezen en onmiddellijk tijd krijgen, dat klopt helemaal.

Acties:
  • 0 Henk 'm!

  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
farlane schreef op woensdag 2 oktober 2019 @ 12:51:
[...]
Het was me niet duidelijk dat je Arduino gebruikt en je code was nogal C-ish, vandaar de typedef. Als het C++ is is ook de struct/class typedef overbodig trouwens.
Die opmerking was dan ook meer richting Rowwan zijn vraag of ik C / C++ gebruik. Dat mijn code nogal C-ish is komt vast omdat ik (nog) overal code bij elkaar hark en die met kennis van andere talen en tutorials aanpas aan wat ik wil.

Vind embedded en C++ wel uitdagend. Heb 'the C++ programming language' van Stroustrup dus net binnen gekregen. Heb je globale tips om op te letten voor wat betreft embedded? Technieken of taalstructuren die op embedded juist niet handig zijn, of totaal niet ondersteund? Globaal.

Pure hobby om lol mee te hebben :)

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap


Acties:
  • 0 Henk 'm!

  • Rowwan
  • Registratie: November 2000
  • Laatst online: 21:04
Embedded van tegenwoordig heeft veel mogelijkheden. Mijn streven is om zoveel mogelijk (liefst alles) zonder heap te programmeren, dus alleen de stack the gebruiken. Dus geen stl etc. Nu gaat dat meestal wel goed als je Arduino gebruikt, dat is allemaal redelijk basic

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
Zarathustra schreef op dinsdag 8 oktober 2019 @ 19:07:
[...]
Vind embedded en C++ wel uitdagend. Heb 'the C++ programming language' van Stroustrup dus net binnen gekregen. Heb je globale tips om op te letten voor wat betreft embedded? Technieken of taalstructuren die op embedded juist niet handig zijn, of totaal niet ondersteund? Globaal.

Pure hobby om lol mee te hebben :)
RTTI en exceptions zijn vaak uitgeschakeld omdat deze features resources kosten dus die kun je over het algemeen niet gebruiken.

Daarnaast is het aan te raden om goed in de gaten te houden of je constructies gebruikt die je realtime gedrag negatief beinvloeden zoals busy waits etc. Vaak neem je daar dan liever een timer voor (software danwel hardware als het een nauwkeurige wait moet zijn)

Het gebruiken van de heap is vaak een dingetje, alhoewel je in moderne projecten bijna niet zonder kunt. Persoonlijk ontwikkel ik nog maar weinig projecten die zonder RTOS ( "bare metal" ) draaien, en een RTOS gebruikt 9 van de 10 keer een heap of vergelijkbaar concept.

Verder lekker plezier hebben met prutsen, dat is het belangrijkste als het een hobby is :)
Embedded STL

[ Voor 7% gewijzigd door farlane op 09-10-2019 10:11 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Rowwan
  • Registratie: November 2000
  • Laatst online: 21:04
Het gebruiken van de heap is vaak een dingetje, alhoewel je in moderne projecten bijna niet zonder kunt. Persoonlijk ontwikkel ik nog maar weinig projecten die zonder RTOS ( "bare metal" ) draaien, en een RTOS gebruikt 9 van de 10 keer een heap of vergelijkbaar concept.
Dat hangt natuurlijk af van de grootte van je project. Zelf programmeer ik het merendeel bare metal (ook met peripherals als BLE etc), maar soms ontkom je interdaad niet aan een RTOS.

Dan nog neig ik er vooral naar om mijn eigen code single theaded (maar asynchroon) te maken. Dit soort issues zijn vaak vele malen makkelijk op te lossen dan threading issues in een OS. Daarnaast veroorzaakt het gebruik van heap vaak fragmentatie, wat in een embedded systeem ook tot onverwachte problemen kan leiden.

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
Rowwan schreef op woensdag 9 oktober 2019 @ 13:55:
[...]
Dat hangt natuurlijk af van de grootte van je project.
Steeds minder. Als je eenmaal gewend bent aan een RTOS neem je ook in kleinere projecten sneller een RTOS is mijn ervaring simpelweg omdat het veel voordelen heeft mbt het verdelen van taken. Een "simpel" IoT product bijvoorbeeld kan eigenlijk al niet meer zonder.
Zelf programmeer ik het merendeel bare metal (ook met peripherals als BLE etc), maar soms ontkom je interdaad niet aan een RTOS.
Ik probeer er ook niet meer aan te ontkomen, dus dat probleem heb ik niet.
Dan nog neig ik er vooral naar om mijn eigen code single theaded (maar asynchroon) te maken. Dit soort issues zijn vaak vele malen makkelijk op te lossen dan threading issues in een OS. Daarnaast veroorzaakt het gebruik van heap vaak fragmentatie, wat in een embedded systeem ook tot onverwachte problemen kan leiden.
Als je je taken goed opdeelt, zijn threading issues minimaal en niet veel anders dan overwegingen die je zou maken bij bijvoorbeeld het verdelen van verantwoordelijkhedden van interrupt handlers en normale taken.

De problemen die je met een RTOS kunt oplossen zijn bijvoorbeeld latency problemen, of het verminderen van het aantal statemachines in je superloop applicatie.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Rowwan
  • Registratie: November 2000
  • Laatst online: 21:04
De problemen die je met een RTOS kunt oplossen zijn bijvoorbeeld latency problemen, of het verminderen van het aantal statemachines in je superloop applicatie.
Ik gebruik dan weliswaar geen RTOS, maar maak wel gebruik van een scheduler

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
Rowwan schreef op woensdag 9 oktober 2019 @ 18:11:
[...]
Ik gebruik dan weliswaar geen RTOS, maar maak wel gebruik van een scheduler
Dus je bent je eigen RTOS aan het maken..?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Rowwan
  • Registratie: November 2000
  • Laatst online: 21:04
Als je het zo wil noemen :), maar dan een zonder heap en met maar 1 taak...

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 04-05 17:23
Rowwan schreef op woensdag 9 oktober 2019 @ 20:30:
Als je het zo wil noemen :), maar dan een zonder heap en met maar 1 taak...
Als je maar 1 taak hebt wat doet die scheduler dan?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Rowwan
  • Registratie: November 2000
  • Laatst online: 21:04
Oké, 1 taak is misschien wat kort door de bocht: er is één taak die events afhandelt, events komen voort uit interrupts, of worden gescheduled door de taak zelf. . Zonder events gaat de core in sleep. (Er gaan overigens behoorlijk offtopic hier)

Acties:
  • 0 Henk 'm!

  • Zarathustra
  • Registratie: Januari 2008
  • Laatst online: 28-04-2020
Dank heren! Ik begon ooit met programmeren, onder andere assembly, op een ZX-81 en MSX met weinig geheugen. Details van heaps en fragmentatie zijn bekend :) Zie dat FreeRTOS diverse implementaties voor 'heap management' heeft. Komt vast goed :) (is ook spielerij dus een reset van tijd tot tijd is geen probleem)

Exceptions is wel een dingetje. Maar heb al gelezen dat die op de ESP32 ingeschakeld kunnen worden. Ook hier ben ik bekend met de overhead die dat geeft. Programmeert wel fijn en het is geen AVR ;)

Lekker 'old-skool' voor mij :)

Veni, vidi, vici - ik kwam, zag en overwon de drempels van het leven - denk ik dan maar, en vond vriendschap

Pagina: 1