【问题标题】:C, "extern" type of variables in hardware abstraction layerC、硬件抽象层中的“extern”类型变量
【发布时间】:2021-01-27 19:39:25
【问题描述】:

我正在研究硬件抽象层。此 HAL 的目的是在 linux 驱动程序和 MCU 驱动程序之间轻松切换。

我正在研究 SPI 接口。下面是“打开”SPI接口的HAL函数的签名。

hal/spi.h

spi_handle_t spi_open(spi_port_t channel, spi_config_t config);

spi_port_t:

  • 在 Linux 上,它是一个基本类型:uint32_t
  • 在 MCU 上,它是一个结构体。

spi_config_t:

  • 在 Linux 和 MCU 上,它都是一个结构,但具有不同的字段。

所以在 mcu/spi.c 中,我有时会这样:

typedef spiBASE_t spi_channel_t;
typedef spiDAT1_t spi_config_t;

spi_handle_t spi_open(spi_channel_t channel, spi_config_t config) {
.
.
.
}

对于 linux/spi.c

typedef uint32_t spi_channel_t;
typedef ChannelConfig_t spi_config_t;

spi_handle_t spi_open(spi_channel_t channel, spi_config_t config) {
.
.
.
}

现在问题出在hal/spi.h,我需要定义什么是spi_channel_t和spi_config_t。

有没有办法制作类似的东西(我知道用 extern 是不可能的,但是为了解释很重要......):

extern spi_channel_t;
extern spi_config_t;

这会对编译器说:“好吧,这两种类型没有在头文件中定义,你仍然可以在我传递给工具链的文件之一上找到它们的存储大小”。

【问题讨论】:

  • 可以将 Linux 'native' 类型定义为具有单个成员的结构。这对你有用吗?然后,您的标头只需将这些类型声明为struct ...所谓的不透明结构。如果这听起来不错,也许我可以添加更多(甚至作为答案)。

标签: c types embedded extern hal


【解决方案1】:

您似乎正在寻找一种称为不透明类型的技巧。这是一种使用结构的前向声明以在C中实现私有封装和多态性的方法。它通常用于专业编写的嵌入式系统驱动程序,特别是可以这样实现:

hal/spi.h

// forward declaration of a struct, with typedef and struct tag:
typedef struct spi_handle_t spi_handle_t; 

// Require the caller to declare a spi_handle_t* pointer, not an object:
spi_handle_t* spi_init (...); 

mcu/spi.c

struct spi_handle_t
{
  // whatever you need here - these are now 100% private members
};

spi_handle_t* spi_init (...)
{
  spi_handle* result = address_of_some_static_memory_pool;

  /* init struct members here */

  return result;  
}

linux/spi.c

struct spi_handle_t
{
  uint32_t something;
  // whatever you need here - these are now 100% private members
};

spi_handle_t* spi_init (...)
{
  spi_handle* result = malloc(sizeof *result); // malloc is fine to use in Linux

  /* init struct members here */

  return result;  
}

现在调用者必须将spi_handle* 传递给驱动程序中的其他函数。这不仅对 OO 设计很方便,而且还可以使用多个实例运行相同的代码。例如,如果您在 MCU 上有 2 个不同的 SPI 硬件外设并希望以不同的方式使用它们,但使用相同的驱动程序代码。

【讨论】:

  • 我没有想到那个选项! +1
  • 您也可以使用typedef struct spi_handle_t* spi_handle_t; 来实现它,然后获得一个API,其中对象似乎是按值传递的,即使它们只是伪装的指针。我个人不喜欢将指针隐藏在 typedef 后面,所以我不建议这样做。我更愿意认为我的 lib 的用户是一个合格的 C 程序员,因此如果库告诉他们这样做,他们可以忍受在他们的代码中声明指针而不是对象。虽然这是风格问题,但每种风格都有其优点和缺点。
  • 好吧,我只是仔细看了一下,发现你指的不是spi_channel_tspi_config_t,它不适用于thos。这意味着无论您尝试定义要作为参数给出的类型的实例,它们的类型总是不完整的。
  • @eeucalyptus 您必须以相同的方式实现它们,但在这种情况下,我真的不明白为什么这些不应该是结构的私有成员。在正常的用例中,用户应该设置一次波特率、时钟设置和通道等内容,然后就不要再乱搞了。
  • 感谢您的回复@Lundin!事实上,我在不知情的情况下将您的提议应用于另一个项目。我也可以将此方法应用于 spi_channel_t 和 spi_confog_t 但这两个在 MCU 上有很多字段。所以不适用。
【解决方案2】:

执行此操作的典型方法是使用 hal/spi.h 中的预处理器:

#if defined(HAL_LINUX)
typedef uint32_t spi_port_t; /* int in linux */
#elif defined(HAL_MCU)
typedef struct {
    /* your struct in bare metal app */
} spi_port_t;
#else
#error "Platform not defined!"
#endif

您还可以将不同的类型放入不同的标头中,例如 hal/spi_linux.hhal/spi_mcu.h,并有条件地将其中一个包含在 hal/spi.h 中。

或者,您可以只获取指针并将它们转换为它们的实际类型。这会不太安全,因为您必须在运行时检查平台并决定指针背后的类型,但这个决定取决于许多其他因素。

【讨论】:

  • 我也想过这个主意。但它需要创建一个额外的标题。所以我不认为我会应用这个想法。
猜你喜欢
  • 2017-03-29
  • 1970-01-01
  • 2018-05-03
  • 1970-01-01
  • 2015-06-09
  • 2012-01-02
  • 1970-01-01
  • 2016-11-03
  • 1970-01-01
相关资源
最近更新 更多