1

我在 C 中实现“服务例程的向量”时遇到了一些问题。

现在我实现了 I2C 接口的 IRQ-Handler,如下所示:

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

对于许多状态代码,这很慢,因为必须访问所有案例。

LPC12xx 微控制器的用户手册给出了提示:

如果状态码用作服务例程的向量,则例程会被八个地址位置所取代。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 中执行此操作会很困难。我可以想到两种解决方案:

(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 种可能的状态代码,所以一个包含所有代码的案例的详尽开关(即使案例是空的)可能并不令人望而却步。的前三位STAT未使用,因此如果您首先将状态右移三位以使状态值从零开始,这也可以使编译器生成更高效的代码。事实上,我希望它生成一个与@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 回答