1

C で「サービス ルーチンへのベクトル」を実装する際に問題があります。

現在、I2C インターフェイスの IRQ ハンドラーを次のように実装しています。

    void I2C0_IRQHandler(void)
    {
        statusCode = LPC_I2C->STAT;
        switch ( statusCode ) { (...) }
    }

多くのステータス コードを使用すると、すべてのケースを確認する必要があるため、これは遅くなります。

LPC12xx マイクロコントローラのユーザー マニュアルにヒントがあります。

ステータス コードがサービス ルーチンへのベクトルとして使用される場合、ルーチンは 8 つのアドレス ロケーションによって置き換えられます。ほとんどのサービス ルーチンでは、8 バイトのコードで十分です。

したがって、それぞれ正確に 8 バイトのコード サイズでいくつかのハンドラー関数を記述し、それらを正しいメモリ位置に配置する必要があります。

0xABCDEF00: handler_for_code_0x00
0xABCDEF08: handler_for_code_0x08
(...)
0xABCDEF90: handler_for_code_0x90

割り込みハンドラで私は

  1. ステータス コードをメモリ オフセット (ここでは 0xABCDEF00) に追加します。
  2. このアドレスを関数ポインタにキャストします
  3. そして関数を呼び出します。

私はこれの権利を持っていますか?これをCでどのように実装できますか?

4

2 に答える 2

1

これを C で行うのは難しいでしょう。2 つの解決策が考えられます。

(1) asm を使用して、8 バイトのハンドラー関数のテーブルを作成します。ここで、各ハンドラーは C 関数への単なるジャンプです。

org 0xABCDEF00
jmp handler_for_code_0x00
org 0xABCDEF08
jmp handler_for_code_0x01
...
org 0xABCDEF90
jmp handler_for_code_0x1f

(2)ベクトル化された割り込みを使用する代わりに、現在のスキームを維持しますが、関数ポインターを使用してジャンプテーブルを作成します(これは、コンパイラーがまだswitchステートメントからジャンプテーブルを生成するほど賢くないことを前提としています)。

typedef void (*handler_fn)(void);

static void handler_for_code_0x00(void) { ... }

static void handler_for_code_0x01(void) { ... }

...

static void handler_for_code_0x1f(void) { ... }

static void handler_for_code_unused(void) { ... }

const handler_fn handler_table[NUM_STATUS_CODE] = {
    handler_for_code_0x00,
    handler_for_code_0x01,
    handler_for_code_unused,
    handler_for_code_0x03,
    handler_for_code_0x04,
    ...
    handler_for_code_0x1d
    handler_for_code_unused,
    handler_for_code_0x1f
};

void I2C0_IRQHandler(void)
{
    statusCode = LPC_I2C->STAT >> 3 ;  // Get table index
    handler_table[statusCode]();
}
于 2012-08-31T16:11:15.637 に答える
1

switch追加のケースで速度が低下することをどのように判断しましたか?

ほとんどのコンパイラは、決定論的で、各ケースを確実に「訪問」しないジャンプ テーブルを生成します。ケースが数値的に連続しており、まばらに分離されていない (つまり、値の間にギャップがない) ことを確認することで、コンパイラが効率的なコードを生成できるようにすることができます。

考えられるステータス コードは 32 個しかないため、すべてのコードのケースを使用した徹底的な切り替え (ケースが空であっても) は、おそらく禁止されているわけではありません。の最初の 3 ビットSTATは使用されないため、最初にステータスを 3 ビット右にシフトしてステータス値が 0 から始まるようにすると、コンパイラはより効率的なコードを生成できるようになります。実際、@Dillの提案(私の提案になる予定だった)と同等のジャンプテーブルを生成することを期待しています。

以下は、ほとんどのコンパイラで簡単に最適化される慣用的な例です。明示的に処理したい場合にコードを追加するだけです。コンパイラがこれから確定的なコードを生成しない場合は、コンパイラの最適化レベルを試してみてください。ただし、それができない場合は、別のコンパイラを入手することを検討します!

void I2C0_IRQHandler(void)
{
    switch( LPC_I2C->STAT >> 3 ) 
    { 
        case 0x01 :
            break ;
        case 0x02 :
            break ;
        case 0x03 :
            break ;
        case 0x04 :
            break ;
        case 0x05 :
            break ;
        case 0x06 :
            break ;
        case 0x07 :
            break ;
        case 0x08 :
            break ;
        case 0x09 :
            break ;
        case 0x0a :
            break ;
        case 0x0b :
            break ;
        case 0x0c :
            break ;
        case 0x0d :
            break ;
        case 0x0e :
            break ;
        case 0x0f :
            break ;
        case 0x10:
            break ;
        case 0x11 :
            break ;
        case 0x12 :
            break ;
        case 0x13 :
            break ;
        case 0x14 :
            break ;
        case 0x15 :
            break ;
        case 0x16 :
            break ;
        case 0x17 :
            break ;
        case 0x18 :
            break ;
        case 0x19 :
            break ;
        case 0x1a :
            break ;
        case 0x1b :
            break ;
        case 0x1c :
            break ;
        case 0x1d :
            break ;
        case 0x1e :
            break ;
        case 0x1f :
            break ;
    }
}
于 2012-08-31T19:57:39.470 に答える