4

AVRをターゲットにしているCコードがいくつかあります。コードはavr-gcc、基本的には適切なバックエンドを備えたgnuコンパイラでコンパイルされています。

私がやろうとしているのは、イベント/割り込み駆動型ライブラリの1つにコールバックメカニズムを作成することですが、関数ポインタの値を維持するのに問題があるようです。

まず、静的ライブラリがあります。twi_master_driver.h次のようなヘッダーファイル( )があります。

#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

ここで、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)
}

次に、これら2つのファイルを静的ライブラリ(.a)にビルドし、メインプログラムを作成します(main.c)#include #include #include #include #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;
}

割り込みを発生させるイベントを注意深くトリガーし、適切なハンドラーを呼び出します。いくつかのfprintfsを使用すると、関数で割り当てられた場所twi->slave.slave_callbacktwi_init関数内の場所とは異なることがわかりtwi_slave_interrupt_handlerます。

数字は無意味ですがtwi_init、値は0x13bであり、twi_slave_interrupt_handler印刷時の値は0x100です。

コメント付きの回避策を次の行に追加しtwi_driver.cます。

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

問題はなくなりますが、これは明らかに魔法で望ましくない解決策です。私は何が間違っているのですか?

私が知る限り、適切な変数volatileにマークを付け、他の部分を揮発性にマークし、揮発性のマークを削除してみました。fprintf割り当て後にステートメントを削除するtwi_initと、後で値の読み取りが異なることに気付いたときに、回避策を思いつきました。

問題は、関数ポインターをどのように渡すかにあるようです。特に、ポインターの値にアクセスしているプログラムの部分(関数自体?)は、技術的には別のスレッドにあります。

何か案は?

編集:

  • コードのタイプミスを解決しました。

  • 実際のファイルへのリンク:http : //straymark.com/code/ [test.c | twi_driver.c | twi_driver.h]

  • fwiw:コンパイラオプション:-Wall -Os -fpack-struct -fshort-enums -funsigned-char -funsigned-bitfields -mmcu=atxmega128a1 -DF_CPU=2000000UL

  • (ライブラリ経由ではなく)直接含まれている同じコードを試しましたが、同じ問題が発生しました。

編集(ラウンド2):

  • 「回避策」なしですべての最適化を削除しましたが、コードは期待どおりに機能します。-Osを追加すると、エラーが発生します。-Osが私のコードを破壊しているのはなぜですか?
4

3 に答える 3

2

直感ですが、この 2 行を入れ替えるとどうなるでしょうか。

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

gcc フラグを削除-fpack-structすると問題は解決しますか? lengthそのフィールドを書き込むとコールバック値の一部が上書きされるというバグに遭遇していないのだろうか。


最適化がオンになっているように見え-Osます(個々の最適化を組み合わせて、どの最適化が原因であるかを正確に確認できます)、コンパイラは、長さフィールドが整列されていない場合、長さフィールド-Osを操作するための正しいコードを出力していませんuint16_t2 バイト境界。これは、パックされた a のtwi_global_slave_t中に aを含めると発生します。これは、の最初のメンバーによって構造体が奇数アドレスに配置されるためです。twi_global_tuint8_ttwi_global_ttwi_global_slave_t

a の初期フィールドを作成するtwi_global_tと、uint16_tおそらく修正されます (または、構造体のパッキングをオフにすることができます)。最新の gcc ビルドを試して、まだ発生するかどうかを確認してください。発生する場合は、問題を示す最小限のテスト ケースを作成できるはずです。そのため、バグ レポートを gcc プロジェクトに送信できます。

于 2009-12-23T04:55:47.303 に答える
1

これは本当にスタック/メモリの破損の問題のように聞こえます。elfファイルでavr-sizeを実行すると、何が得られますか?(data + bss)<パーツにあるRAMであることを確認してください。これらのタイプの問題を追跡することは非常に困難です。無関係なコードを削除/移動すると動作が変わるという事実は、大きな危険信号です。

于 2009-12-24T03:53:18.910 に答える
0

関数 main() の "&my_callback" を "my_callback" に置き換えます。

さまざまなスレッドがコールバック アドレスにアクセスするため、ミューテックスまたは読み取り/書き込みロックで保護してみてください。

コールバック関数ポインターがシグナル ハンドラーによってアクセスされない場合、"volatile" 修飾子は不要です。

于 2009-12-23T03:10:12.527 に答える