您的解决方案虽然正确,但不是一个好的解决方案。我知道这可能看起来不错,因为您对它的使用非常满意,但这最终会产生大量宏定义、每个板具有不同名称/参数的函数,而且,最有问题的一个,所有应用程序都会知道关于通过包含的实际硬件。更糟糕。对任何板的任何更改都将强制重新编译所有不同板的所有项目(因为所有项目都包含所有板,尽管没有使用它)。
正确的方法是使用共享 API 的单个 .hfile。这是一组定义行为的函数,而不是硬件。像 void setUartSpeed(uart_t *uart, size_t speed) 之类的函数,其中 uart_t 是一个不透明的指针,每个板的定义不同。是的,对 API 的任何更改都会导致所有板的编译错误,但这很好,因为您会注意到它并且您只需要更改板 .c 文件,而不是所有项目文件。所以,更正确的方法是@Lundin 的回答。
按照你的cmets,如何避免这样的实现?
https://github.com/opentx/opentx/blob/2.3/radio/src/targets/taranis/hal.h
只考虑行为,而不是硬件:
即谈论宏KEYS_GPIO_REG_MENU:你应该完全删除它,因为它是一个硬件实现细节,对于应用程序的其余部分不是必需的。取而代之的是GPIOStruct* getPinout()setHigh(GPIOStruct *)、setLow(GPIOStruct *) 等函数,其中GPIOStruct 是.c 文件中定义的不透明指针。
因此,在每个板上,您将有一个 .c 文件定义:
- getPinout 函数,它将返回一个包含所有可用引脚的数组。
- setHigh 和 setLow 函数,将引脚设置设置为逻辑电平。也许,这个功能可以放在相同的 uC 架构中,因为它取决于 uC 而不是板上。
因此,特别是在您的示例中,您将拥有:
//hal.h
typedef struct GPIOStruct GPIOStruct;
GPIOStruct* getPinout();
void setHigh(GPIOStruct* pin);
void setLow(GPIOStruct* pin);
//board PCBX9E.c
struct GPIOStruct {
void *idr; //Set to the correct type, probably volatile uint8_t
int idx;
};
static const GPIOStruct gpio_reg_menu={.idr=&GPIOD->IDR, .idx=7};
static const GPIOStruct gpio_reg_exit={.idr=&GPIOD->IDR, .idx=2};
//And so on...
GPIOStruct* getPinout()
{
GPIOStruct gpios[]={
&gpio_reg_menu,
&gpio_reg_exit,
//And so on...
}
}
//board PCBXLITE.c
//Note: this structs are the same. It could be moved to a file called taranis_gpio.h, since the architecture of the uC is the same for both boards,
//among with the functions to setHigh/setLow a pin.
struct GPIOStruct {
void *idr; //Set to the correct type, probably volatile uint8_t
int idx;
};
//But the pinout is different, so this should be kept in each board file.
static const GPIOStruct gpio_reg_menu={.idr=&GPIOE->IDR, .idx=8};
static const GPIOStruct gpio_reg_exit={.idr=&GPIOE->IDR, .idx=7};
//And so on...
GPIOStruct* getPinout()
{
GPIOStruct gpios[]={
&gpio_reg_menu,
&gpio_reg_exit,
//And so on...
}
}
在您的项目中,您只需添加一个或另一个 .c 文件,具体取决于您要编译到的板。
希望不要从这里压倒你......
处理硬件时功能的深入分离可以在以下论文中找到:
https://atomicobject.com/uploads/archive/files/EIT2006EmbeddedTDD.pdf
总结起来,他们为每个硬件模块创建三个文件:
Hardware:此文件包含硬件访问、寄存器等。这主要是我们要说的。
型号:此文件将包含硬件应该做什么的高级表示。 IE。它将包含以下功能:openMenu、isMenuOpened 等...
导体:此文件包含胶水。它监视和更新硬件,并根据另一端的变化监视和更新模型。
通过这种分离,很容易将硬件从一种实现更改为另一种,只需更改.c
在示例中,所有这些文件都是三元组的 hardware 元素。
您可以按照我向您展示的方式保留它,直接处理 GPIO,或者您可以遵循此方案并通过提供 openMenu、closeMenu 等函数来进一步抽象它。这些函数将是三元组的model 元素。最后,在 GPIO 上循环的主要代码将是 conductor 元素。