我在EFM32 Cortex M3处理器上开发嵌入式C代码,几个月后代码开始变得疯狂......我的意思是我们改变了硬件,因此我们得到了不同的版本,其中我们改变了一些组件,移动了一些IO,在启动时有不同的状态......
所以我试着稍微清理一下:
我在这里组织了一些非常大的文件:
/*============================================================================*/
/* File : BSP_gpio.h */
/* Processor : EFM32GG980F1024 */
/*----------------------------------------------------------------------------*/
/* Description : gpio driver */
/*----------------------------------------------------------------------------*/
#ifndef BSP_GPIO_H
#define BSP_GPIO_H
#ifdef EXTERN
#undef EXTERN
#endif
#ifdef BSP_GPIO_C
#define EXTERN
#else
#define EXTERN extern
#endif
typedef enum
{
GPIO_INIT = 0,
GPIO_WAKEUP = 1,
GPIO_SLEEP = 2,
} GPIO_MODE;
/* Definition / conts */
#define PIO_PIN_HIGH 1
#define PIO_PIN_LOW 0
#ifdef HW_v1
... hundreds of lines...
#elif defined HW_v2
... hundreds of lines...
#endif
#endif
我尝试将分隔文件中的不同版本分开并尝试这样的事情:
#ifdef HW_v1
#include "BSP_gpio_HW1.h"
#elif defined HW_v2
#include "BSP_gpio_HW2.h"
#endif
每个"子文件的标题类型相同" (直到枚举)。目标是在其他所有的" .c"文件我们包括" BSP_gpio.h"它会自动包含与所用硬件相对应的文件。
第一个问题是编译取决于我包含子文件的位置。例如,我有一个功能" void BSP_GPIO_set(GPIO_MODE模式)"它使用enum" GPIO_MODE"并且两个硬件版本不同(因为两个硬件上的IO状态不同)。如果我在声明之前包含子文件,它就不知道类型" GPIO_MODE"并且编译错误,即使我#include" BSP_gpio.h"在子文件中。 所以我只是将它放在文件的末尾并且它可以工作,即使我不是真的喜欢它......
第二个问题出现在我将变量声明为extern时,我想在子文件和其他C文件中使用它。假设我把这条线放在" #ifdef HW_v1":
之前EXTERN int numberOfToggles;
" EXTERN"在我的" BSP_gpio.c"我在文件开头定义了BSP_GPIO_C,并且在我包含" BSP_gpio.h"的每个其他文件中都是关键字 extern 。当我构建我的项目时,它会编译但我有一个链接器错误:" BSP_gpio.o和BSP_gpio_HW2.o"中的numberOfToggles的重复定义;而我无法找到解决方案。
如果有人有适当的解决方案,我已经准备好改变项目的架构了!
答案 0 :(得分:1)
第一个问题是编译取决于我包含子文件的位置。例如,我有一个函数“void BSP_GPIO_set(GPIO_MODE mode)”,它使用枚举“GPIO_MODE”,并且在两个硬件版本中有所不同[...]。如果我在声明之前包含子文件,它就不知道类型“GPIO_MODE”并产生编译错误,即使我在子文件中#include“BSP_gpio.h”。所以我把它放在文件的末尾并且它可以工作,即使我不喜欢它......
我不确定“第一个问题是[...]”和“它是否有效”。我猜您的抱怨是关于编码风格的重构。
无论如何,是的,C对声明出现的顺序很敏感,因此在你放置#include
指令的地方很重要。就个人而言,我更倾向于采用每个头文件H
应该#include
所有其他头文件提供H
需要并且本身不提供的宏和声明的方法。通过标准防范多重包含,可以缓解标题顺序和位置周围的许多(但不是全部)问题。
可能需要将更多或更精细的标题分解出来才能将#include
指令仅放在每个标题的顶部。
第二个问题出现在我将变量声明为extern时,我想在子文件和其他c文件中使用它。 [...]当我构建我的项目时,它编译但我有一个链接器错误:“BSP_gpio.o和BSP_gpio_HW2.o中的numberOfToggles的重复定义”,我找不到解决方案。
全局变量可以在任意数量的编译单元中具有任意数量的兼容声明,但它必须具有正好一个 定义。只要没有全局变量的初始值设定项,标题中的extern
声明就完全可以了。然后,每个全局应在一个.c
源文件中具有非外部声明,以用作定义。在定义中使用初始化程序的加分点。
答案 1 :(得分:1)
对我而言,您似乎没有明确区分界面和实现。是否有充分的理由在头文件中具有硬件依赖性?是否有充分的理由拥有GPIO
驱动程序而不是功能特定的驱动程序?
在我看来,硬件依赖应隐藏在实现中(即.c
文件),而不是通过头文件公开。例如,用于LED的Signalisation
驱动程序。在那里,头文件看起来像这样:
#ifndef DRIVER_SIGNAL_H
#define DRIVER_SIGNAL_H
typedef enum
{
SIGNAL_STARTED,
SIGNAL_ERROR,
SIGNAL_WARNING,
} signal_id_t;
void signal_show(signal_id_t signal_id);
#endif
如果您遵循这种方法,您通常只需要更改驱动程序代码的实现,而不是更改不同硬件版本之间的头文件。
除此之外,我同意Ludin和John Bollinger的答案。
答案 2 :(得分:0)
这个问题似乎100%与缺乏版本控制有关,而不是真正的编程。
不要忘记使用编译器开关,只需更改最新版本硬件的代码即可。随你去,创建一个"标签"在您的版本控制系统中为每个硬件版本更改。如果您需要使用旧硬件,只需返回您当时使用的版本即可。
或者创建当前最新文件的分支,采取"硬件路由"来自旧标记的文件,并将其转储到最新文件中。
答案 3 :(得分:0)
我根据你的回答修改了我的代码,使其更加清晰。我现在只有一个BSP_gpio.h文件,我有我的定义和所有原型:
/*============================================================================*/
/* File : BSP_gpio.h */
/* Processor : EFM32GG980F1024 */
/*----------------------------------------------------------------------------*/
/* Description : gpio driver */
/*----------------------------------------------------------------------------*/
#ifndef BSP_GPIO_H
#define BSP_GPIO_H
#ifdef EXTERN
#undef EXTERN
#endif
#ifdef BSP_GPIO_C
#define EXTERN
#else
#define EXTERN extern
#endif
typedef enum
{
GPIO_INIT = 0,
GPIO_WAKEUP = 1,
GPIO_SLEEP = 2,
} GPIO_MODE;
/* Definition / conts */
#define PIO_PIN_HIGH 1
#define PIO_PIN_LOW 0
/* Variables */
EXTERN UINT8 numberOfToggles;
/* Functions */
void BSP_GPIO_set_interupt(UINT8 port, UINT8 pin, BOOL falling);
void BSP_GPIO_set(GPIO_MODE mode)
#endif
我现在有三个.c文件,BSP_gpio.c,我实现了两个硬件共有的所有内容:
/*============================================================================*/
/* File : BSP_gpio.c */
/* Processor : EFM32GG980F1024 */
/*----------------------------------------------------------------------------*/
#define BSP_GPIO_C
/*----------------------------------------------------------------------------*/
/* Includes */
/*----------------------------------------------------------------------------*/
#include "BSP_type.h"
#include "BSP_gpio.h"
void BSP_GPIO_set_interupt(UINT8 port, UINT8 pin, BOOL falling)
{
//implementation
}
和BSP_gpio_HW1(/ 2).c文件,其中我实现了由预处理程序指令包围的void BSP_GPIO_set(GPIO_MODE mode)
函数,因此它只实现了一次:
#ifdef HW_v1(/2)
#include "BSP_gpio.h"
/*============================================================================*/
/* BSP_gpio_set */
/*============================================================================*/
void BSP_GPIO_set(GPIO_MODE mode)
{
//Implementation which depends on the hardware
}
#endif
所以这回答了我的第一个评论/问题,我对我的代码并不满意,但我仍然有我不理解的重复定义问题,因为BSP_GPIO_C
仅在BSP_gpio.c中定义文件而不是BSP_gpio_HW1(/ 2).c文件。
有什么想法吗? 再次感谢您的帮助!