C、 ";“外部”;硬件抽象层中的变量类型
我正在研究硬件抽象层。此HAL的目的是在linux驱动程序和MCU驱动程序之间轻松切换 我正在研究SPI接口。下面是“打开”SPI接口的HAL函数的签名 hal/spi.hC、 ";“外部”;硬件抽象层中的变量类型,c,types,embedded,extern,hal,C,Types,Embedded,Extern,Hal,我正在研究硬件抽象层。此HAL的目的是在linux驱动程序和MCU驱动程序之间轻松切换 我正在研究SPI接口。下面是“打开”SPI接口的HAL函数的签名 hal/spi.h spi_handle_t spi_open(spi_port_t channel, spi_config_t config); // forward declaration of a struct, with typedef and struct tag: typedef struct spi_handle_t spi_
spi_handle_t spi_open(spi_port_t channel, spi_config_t config);
// 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 (...);
spi_端口:
- 在Linux上,它是一种基本类型:uint32\u t
- 在MCU上,它是一个结构
- 在Linux和MCU上,它都是一个结构,但具有不同的字段
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\u channel\u t和spi\u config\t
有没有一种方法可以做出这样的事情(我知道使用extern是不可能的,但是对于解释问题…)
这会对编译器说:“好的,这两种类型没有在头文件中定义,但是您仍然可以在我传递给工具链的一个文件中找到它们的存储大小。”这样做的典型方式是使用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
中
或者,您可以只获取指针并将其转换为实际类型。这将不太安全,因为您必须在运行时检查平台并决定指针后面的类型,但这一决定取决于许多其他因素。您似乎在寻找一种称为不透明类型的技巧。这是一种使用结构的前向声明的方法,以实现C中的私有封装和多态性。它通常用于专业编写的嵌入式系统驱动程序,具体实现方式如下: hal/spi.h
spi_handle_t spi_open(spi_port_t channel, spi_config_t config);
// 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;
}
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;
}
linux/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;
}
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\u句柄*
传递给驱动程序中的其他函数。这不仅对于OO设计很方便,而且还可以在多个实例中运行相同的代码。例如,如果MCU上有两个不同的SPI硬件外设,并且希望以不同的方式使用它们,但使用相同的驱动程序代码。您可以将Linux“本机”类型定义为具有单个成员的结构。那对你有用吗?然后,您的头只需将这些类型声明为struct
。。。所谓不透明结构。如果这听起来不错的话,也许我可以添加更多(甚至作为一个答案)。我没有想到这个选项+1您也可以使用typedef struct spi_handle_t*spi_handle_t实现此功能然后获得一个API,其中对象似乎是按值传递的,即使它们只是伪装的指针。我个人不喜欢在typedef后面隐藏指针,所以我不建议这样做。我更倾向于认为我的库的用户是某种程度上合格的C程序员,因此如果库告诉他们这样做的话,他们可以在代码中声明指针而不是对象。不过,这是一个风格问题,每种风格都有其优缺点。好吧,我只是更深入地研究了一下,发现您所指的不是spi\u channel\u t
和spi\u config\u t
,它不适用于THO。这意味着,如果您试图定义作为参数提供的类型的实例,则它们的类型总是不完整的。@eeucalyptus您必须以相同的方式实现它们,但在这种情况下,我真的不明白为什么这些不应该是结构的私有成员。在正常使用情况下,用户应一次性设置波特率、时钟设置和通道等内容,并且从此不再对它们进行干扰。感谢您的回复@Lundin!事实上,我在不知情的情况下将你的建议应用于另一个项目。我也可以将此方法应用于spi_channel_t和spi_Confug_t,但这两种方法在MCU上有很多字段。所以它不适用。我也考虑过这个想法。但它需要创建一个附加头。所以我不认为我会应用这个想法。