2011-08-23 62 views
1

我正在用C.获得嵌入式软件

嵌入式程序

有吨的硬件宏象

#ifdef HardwareA 
do A 
#endif 

这不是可读的,很难摆脱硬件宏覆盖所有不同带有单元测试的路径。

因此,我决定将与硬件相关的代码移至arch文件夹,并使用makefile中的宏来决定链接哪个arch文件夹。就像在Linux内核代码中一样。

但是当我看到Linux内核时,我注意到arch文件夹中有很多重复项。

当在一个硬件中发现错误时,他们如何更改所有相关硬件,但可能会影响所有其他硬件?

我认为这样做将不可避免地带来重复的代码库。

有没有人有这种类型的问题的经验?

如何对包含大量硬件宏的代码进行单元测试?

重构代码以移除源代码中的硬件宏?

+0

有没有人有不同的硬件有不同分支的程序的单元测试经验,如何覆盖不同的路径?谢谢。 –

回答

3

这听起来像您要更换这样的功能:

somefunc() 
{ 
    /* generic code ... */ 

    #ifdef HardwareA 
    do A 
    #endif 

    /* more generic code ... */ 
} 

有多种实现,一个在每个拱文件夹,例如:

somefunc() 
{ 
    /* generic code ... */ 

    /* more generic code ... */ 
} 

somefunc() 
{ 
    /* generic code ... */ 

    do A 

    /* more generic code ... */ 
} 

的通用代码的重复是什么你担心。不要做:代替,有一个实现的功能是这样的:

somefunc() 
{ 
    /* generic code ... */ 

    do_A(); 

    /* more generic code ... */ 
} 

..和然后实现在拱文件夹do_A():在硬件上它具有硬件的代码,并在其他硬件,它是一个空的功能。

不要害怕空功能 - 如果你使它们在拱头文件中定义的功能inline,它们将被完全优化。

+0

是的,这就是我所关心的。 –

2

Linux试图避免在多个arch目录之间重复的代码。你会看到相同的功能实施,但实施不同。毕竟,所有体系结构都需要用于管理页表的代码,但细节有所不同。所以它们都具有相同的功能,但具有不同的定义。

对于某些功能,有构建系统定义的CONFIG_GENERIC_*,这些构建系统也会使用通用版本替换不必要的体系结构钩子(通常是no-ops)。例如,没有FPU的拱形不需要挂钩来保存/恢复上下文切换时的FPU状态。

+0

我面对的问题与Linux内核有点不同,通用代码占主导地位,与硬件相关的代码更少,在硬件A中,仅添加一行代码,用于另一硬件B,两行代码,因此,它真的我很难从中提取一些常见的界面或功能,并将它们放在拱形文件夹中,您觉得如何? –

1

这种#ifdef地狱肯定是要避免的,但自然你也想避免代码重复。我不认为这将解决你所有的问题,但我认为最大的一步可以让你将#ifdef#ifdef HardwareX改为#ifdef HAVE_FeatureY#ifdef USE_FeatureZ。这可以让你做什么是因素的知识,哪些硬件/操作系统/等。目标有哪些功能/界面你的所有源文件和成一个头,从而避免了诸如:

#if defined(HardwareA) || (defined(HardwareB) && HardwareB_VersionMajor>4 || ... 

渲染你的源代码不可读。

+0

我不明白,使用功能而不是硬件,仍然需要ifdef来决定打开或关闭哪些代码,你的意思是我们可以将它们移动到某个头文件?怎么做?谢谢。 –

1

我倾向于将特定于硬件的#define移动到每个平台的一个头文件中,然后在所有源文件包含的“platform.h”文件中选择它。

platform.h:

#if defined PLATFORM_X86_32BIT 
#include "Platform_X86_32Bit.h" 
#elsif defined PLATFORM_TI_2812 
#include "Platform_TI_2812.h" 
#else 
#error "Project File must define a platform" 
#endif 

架构特定的头文件将包含两件事情。

1)所有常见整数大小的Typedef,如typedef short int16_t;请注意,c99指定了具有这些预定义的'stdint.h'。 (切勿在便携式代码中使用原始的int)。

2)所有硬件特定行为的函数头或宏。通过提取所有的依赖关系的功能,代码的主体保持清洁:

//example data receive function 
    HW_ReceiverPrepare(); 
    HW_ReceiveBytes(buffer, bytesToFetch); 
    isGood = (Checksum(buffer+1, bytesToFetch-1) == buffer[0]) 
    HW_ReceiverReset(); 

然后一个平台特定的报头可以提供原型复杂HW_ReceiverPrepare()功能,而另一种简单地与#define HW_ReceiverPrepare()

定义它远在您的评论中描述的情况下,这种情况非常有效,其中平台之间的差异通常为一行或两行。只需将这些行封装为函数/宏调用,并且可以保持代码可读性,同时最大限度地减少重复。