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_callback
がtwi_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が私のコードを破壊しているのはなぜですか?