有没有办法在普通计算机上为嵌入式微控制器设计 运行 C 代码?

is there a way to run C code designed for an embedded micro-controller on a normal computer?

我有一个为 ATmega16 芯片编写的 C 代码,它充满了关键字,例如:

flash, eeprom, bit

和宏 (?) 如

interrupt [TIM1_OVF] void timer1_ovf_isr(void)

在函数签名之前。

现在我想做的是编写 运行 单元测试来验证控制器单元逻辑的正确性,我希望能够 运行 在任何计算机上进行这些测试,并且不需要代码表示的 "device"。

我搜索了很多并遇到了 "abstracting the hardware" 和 "replacing them with stubs" 种解决方案,但我不确定如何抽象出类似“中断 [TIM1_OVF]”在代码中!

我想知道是否有任何特殊工具可以为 运行 编写此类代码提供环境?

而且,如果我做错了,有人能指出我正确的方向吗?考虑到更改或重写 (!) 微控制器的代码可能不是一种选择?

非常感谢。

根据您的工具链,您可能已经有了可用的 AVR 模拟器,这将允许您在任何 PC 上 运行 进行单元测试。例如,IAR 提供 "C-SPY",一个支持终端 window 的 AVR 模拟器,可以显示显示寄存器值,可以支持中断生成等。假设您保持合理的单元大小,则不需要使这项工作有效的重要基础设施或存根接口。

运行在目标平台(使用目标编译器)上进行单元测试的一大好处是,您可以考虑由平台(字节序、字长、编译器)引起的任何特定行为或库特性等),与 运行 PC 环境中的 ning 相比。

您的示例不是 ISO C 代码,它们是特定于编译器的扩展,它们在 AVR 编译器中并不常见,更不用说架构了。在许多情况下,它们可以通过定义需要很少或不需要修改代码的宏来解决。为了使您的代码在任何情况下都可移植,甚至跨不同供应商的 AVR 编译器,在任何情况下都这样做是个好主意,尽管可能需要结合使用多种技术。

大多数编译器支持 "always include" 选项,允许从命令行包含 header 文件,并在源代码中使用显式 #include 指令。使用您的兼容性宏创建 header 并将其包含在代码中,如所描述的那样隐式地或显式地包含在代码中是一种有用的技术。例如对于您提到的问题,您可能有:

// compatability.h
#if !defined COMPATABILITY_INCLUDE
#define COMPATABILITY_INCLUDE

  #if defined __IAR_SYSTEMS_ICC__

    #define INTERRUPT( irq, handler ) __interrupt [irq] void handler(void)

  #elif defined _WIN32

    #define INTERRUPT( irq, handler ) void handler(void)

    #define __flash const
    #define __eeprom const
    #define __bit char
  #else
    #error Unknown toolchain/environment
  #endif 

#endif

这将从 Win32 代码中删除内存位置限定符,并将 __bit 定义为字符。中断处理程序宏会将处理程序转换为 Win32 上的常规函数​​,但确实需要修改您的代码,但由于每个工具链都以不同的方式执行此操作,所以这也许不是坏事。

例如在这种情况下你会改变:

interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
    ...
}

INTERRUPT( TIM_OVF, timer1_ovf_isr )
{
    ...
}

请注意,您应该在兼容性文件中使用适当的目标宏 - 例如,我已经猜到了 IAR;您可能正在使用不同的编译器。您的编译器文档应该指定可用的预定义宏,或者 Sourceforge 上的 Pre-defined Compiler Macros "project" 是一个有用的资源。

一些转换可能会在语义上改变代码,例如在某些情况下将 __bit 交换为 char 例如如果该位被分配一个大于 1 的值,然后与 1 进行比较,嵌入式目标可能会产生 true,而在 PC 构建上则不会。它可能更好地转换为 _Bool 但您的编译器可能会发出有关隐式转换的警告。我的建议也不一定是最好的转换 - 请查阅您的编译器手册以了解精确的语义,并决定如何最好地将它们转换为标准 C 以进行测试构建。

另一种保留专有语义的方法是 运行 在 instruction-set 模拟器中使用调试器脚本进行单元测试(如果可以实现硬件交互存根),但是该方法无法使用 off-the-shelf unit-testing 框架,例如 CUnit。