问题描述
我正在研究硬件抽象层。此 HAL 的目的是在 Linux 驱动程序和 MCU 驱动程序之间轻松切换。
我正在研究 SPI 接口。下面是HAL“打开”SPI接口的函数签名。
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;
这会对编译器说:“好吧,这两种类型没有在头文件中定义,您仍然可以在我传递给工具链的文件之一中找到它们的存储大小”。
解决方法
您似乎正在寻找一种称为不透明类型的技巧。这是一种使用结构的前向声明来实现 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 硬件外设,并希望以不同的方式使用它们,但使用相同的驱动程序代码。
执行此操作的典型方法是使用 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.h
和 hal/spi_mcu.h
,并有条件地将其中之一包含在 hal/spi.h
中。
或者,您可以只获取指针并将它们转换为实际类型。这会不太安全,因为您必须在运行时检查平台并决定指针背后的类型,但此决定取决于许多其他因素。