Question

J'ai un peu de code C je cible pour un AVR. Le code est compilé avec avr-gcc, essentiellement le compilateur gnu avec le back-end droit.

Ce que je suis en train de faire est de créer un mécanisme de rappel dans un de mes événements / interrompre les bibliothèques motrices, mais je semblent avoir du mal à garder un peu la valeur du pointeur de fonction.

Pour commencer, j'ai une bibliothèque statique. Il a un fichier d'en-tête (twi_master_driver.h) qui ressemble à ceci:

#ifndef TWI_MASTER_DRIVER_H_
#define TWI_MASTER_DRIVER_H_

#define TWI_INPUT_QUEUE_SIZE 256

// define callback function pointer signature
typedef void (*twi_slave_callback_t)(uint8_t*, uint16_t);

typedef struct {
    uint8_t buffer[TWI_INPUT_QUEUE_SIZE];
    volatile uint16_t length; // currently used bytes in the buffer
    twi_slave_callback_t slave_callback;
} twi_global_slave_t;

typedef struct {
    uint8_t slave_address;
    volatile twi_global_slave_t slave;
} twi_global_t;

void twi_init(uint8_t slave_address, twi_global_t *twi, twi_slave_callback_t slave_callback);

#endif

Maintenant, le fichier C (twi_driver.c):

#include <stdint.h>
#include "twi_master_driver.h"

void twi_init(uint8_t slave_address, twi_global_t *twi, twi_slave_callback_t slave_callback)
{
    twi->slave.length = 0;
    twi->slave.slave_callback = slave_callback;

    twi->slave_address = slave_address;

    // temporary workaround <- why does this work??
    twi->slave.slave_callback = twi->slave.slave_callback;
}

void twi_slave_interrupt_handler(twi_global_t *twi)
{
    (twi->slave.slave_callback)(twi->slave.buffer, twi->slave.length);

    // some other stuff (nothing touches twi->slave.slave_callback)
}

Alors je construis ces deux fichiers dans une bibliothèque statique (.a) et construire mon programme principal (main.c)     #comprendre     #comprendre     #comprendre     #comprendre     #include "twi_master_driver.h"

//  ...define microcontroller safe way for mystdout ...

twi_global_t bus_a;

ISR(TWIC_TWIS_vect, ISR_NOBLOCK)
{
    twi_slave_interrupt_handler(&bus_a);
}

void my_callback(uint8_t *buf, uint16_t len)
{
    uint8_t i;

    fprintf(&mystdout, "C: ");
    for(i = 0; i < length; i++)
    {
        fprintf(&mystdout, "%d,", buf[i]);
    }
    fprintf(&mystdout, "\n"); 
}

int main(int argc, char **argv)
{
    twi_init(2, &bus_a, &my_callback);

    // ...PMIC setup...

    // enable interrupts.
    sei();

    // (code that causes interrupt to fire)

    // spin while the rest of the application runs...
    while(1){
        _delay_ms(1000);
    }
    return 0;
}

Je TRIGGER attentivement les événements qui provoquent l'interruption de feu et d'appeler le gestionnaire approprié. En utilisant quelques fprintfs je suis en mesure de dire que l'emplacement attribué à twi->slave.slave_callback dans la fonction twi_init est différent de celui dans la fonction twi_slave_interrupt_handler.

Bien que les chiffres sont de sens, en twi_init la valeur est 0x13b, et en twi_slave_interrupt_handler lors de l'impression la valeur est 0x100.

En ajoutant la ligne de contournement a commenté dans twi_driver.c:

twi->slave.slave_callback = twi->slave.slave_callback;

Le problème disparaît, mais cela est clairement une solution magique et indésirable. Qu'est-ce que je fais mal?

Pour autant que je peux dire, je l'ai marqué des variables appropriées volatile, et je l'ai essayé de marquer d'autres parties volatiles et enlever les marques volatiles. Je suis venu avec la solution de contournement lorsque j'ai remarqué la suppression des déclarations de fprintf après la cession à la valeur twi_init causé à lire différemment plus tard.

Le problème semble être la façon dont je passe autour du pointeur de fonction -. Et notamment la partie du programme qui accède à la valeur du pointeur (? La fonction elle-même) est techniquement dans un autre thread

Toutes les idées?

Edits:

  • fautes de frappe résolus dans le code.

  • liens vers des fichiers réels: http://straymark.com/code/ [Test .c | twi_driver.c | twi_driver.h]

  • FWIW: options du compilateur: -Wall -Os -fpack-struct -fshort-enums -funsigned-char -funsigned-bitfields -mmcu=atxmega128a1 -DF_CPU=2000000UL

  • J'ai essayé le même code inclus directement (plutôt que par une bibliothèque) et j'ai la même question.

Edits (tour 2):

  • J'ai supprimé toutes les optimisations, sans mon « contournement » le code fonctionne comme prévu. Ajout de retour -Os provoque une erreur. Pourquoi -Os corrompent mon code?
Était-ce utile?

La solution

Juste une intuition, mais ce qui se passe si vous changez ces deux lignes autour de:

twi->slave.slave_callback = slave_callback;
twi->slave.length = 0;

Ne retirer le drapeau de gcc -fpack-struct résoudre le problème? Je me demande si vous ne l'avez pas trébuché sur un bug écrit champ length est une partie de la écrasant valeur de rappel.


Il me semble que les Optimisations de -Os (vous pouvez essayer des combinaisons des Optimisations individuelles activées par -Os pour voir exactement ce qui est la cause de lui), le compilateur n'émet pas le bon code pour manipuler le champ de longueur de uint16_t lorsque son pas aligné sur une limite de 2 octets. Cela se produit lorsque vous incluez un twi_global_slave_t dans un twi_global_t qui est emballé, parce que le membre de la uint8_t initiale de twi_global_t provoque la struct twi_global_slave_t à être placé à une adresse impaire.

Si vous faites ce champ initial de twi_global_t un uint16_t il sera probablement le fixer (ou vous pouvez désactiver l'emballage struct). Essayez la dernière version de gcc et de voir si elle arrive encore -. Si elle le fait, vous devriez être en mesure de créer un test minimal qui montre le problème, vous pouvez soumettre un rapport de bogue au projet gcc

Autres conseils

Cela ressemble vraiment à un problème de corruption pile / mémoire. Si vous exécutez taille sur AVR votre fichier elfe, qu'est-ce que vous obtenez? Assurez-vous que (données + bss)

Remplacer "& my_callback" avec "my_callback" en fonction main ().

Parce que différents threads accéder à l'adresse de rappel, essayez le protéger avec un mutex ou d'un lock-écriture.

Si le pointeur de la fonction de rappel n'est pas accessible par un gestionnaire de signal, le qualificatif « volatile » est inutile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top