2

我正在编写一些包含各种功能的代码,例如 PID 控制器、信号发生器等。

我的硬件提供各种输入和输出。刚才我有一个 SWITCH 语句来确定我的计算的来源和目的地。

例如对于 PID 控制器,每 100 毫秒有一个开关命令决定将哪个输入传递给 pid_calculate 函数,然后是另一个开关来决定如何处理返回值。因为我有 32 个模拟输入,以及可能的输入,can、lin 和 serial,所以 switch 语句是巨大的!

我想参考或一个物理示例,说明如何将某些东西编码为独立于硬件的(在图片的范围内)功能。我确信答案在于指针,但我是 C 的新手,并不确定从哪里开始指针。

我设想函数原型类似于 int pid_init(*source,*destination) 其中源是指向输入的指针,例如 ADC 缓冲区,而目标可以是例如 pwm 占空比寄存器。刚才我必须切换每个可能的条件,然后将数据加载到寄存器中。

所以为了澄清,我将如何实现一个允许它独立输入和输出的函数,以及我将如何取消引用指针(假设指针是正确的方法)

我希望这是有道理的,在此先感谢。

4

2 回答 2

3

只要您稍加注意,您就可以在 C 中实现一种面向对象的编程。在我所做的方式中,我使用了函数指针以及以函数指针作为成员的结构。

例如,为了具有一种数据独立性,您可能有一个类似于快速排序 (qsort) 函数的函数,它为 data long 提供了一个指向对数据进行操作的函数的指针。通常使用这种方法,您最终会使用各种类型的 void 指针来允许使用指向任何数据类型的指针。

我使用的另一种方法是为各种类型的设备支持的常见操作提供一个抽象接口,然后创建一个为这些操作提供函数指针模板的结构。然后,我将创建一个由这些结构组成的数组,并通过使用指向为特定设备实现该操作的特定函数的函数指针填充数组元素。

例如,像这个非常简单的例子是我用来为几种不同设备提供标准接口的东西。该接口通过提供标准接口隐藏了设备差异。

// I create a struct that specifies what the function
// interface looks like for each of these operations for the
// devices that we are supporting.
typedef struct {
    int  (*pInput)(int iValue);
    char *(*pName) (void);
    int  (*pDance) (int iTune, char *aszName);
} HardwareInterface;

// next I provide the function prototypes for the various devices.
// we have two different devices, Device_01 and Device_02 which
// have a common set of operations with the same interface.
int  Device_01_Input (int iValue);
char *Device_01_Name (void);
int  Device_01_Dance (int iTune, char *aszName);

int  Device_02_Input (int iValue);
char *Device_02_Name (void);
int  Device_02_Dance (int iTune, char *aszName);

// now I define my array of the device operation interfaces.
// this is where I provide the link between a specific operation
// on a specific device.  I will number my devices beginning with
// zero to the number of devices supported minus one.
HardwareInterface HardwareList [] = {
    {Device_01_Input, Device_01_Name, Device_01_Dance},
    {Device_02_Input, Device_02_Name, Device_02_Dance},
};

在这一点上,我可以调用一个函数来获取我真正想要使用的设备。此函数返回硬件列表的索引。所以它可能会变成这样。

int DeviceStdInput (int iValue)
{
    int  i = GetDeviceType ();
    return HardwareList[i].pInput (iValue);
}

或者我可以使用句柄方法,以便调用一个函数,该函数为传递给函数的某些描述中指定的设备提供句柄。然后对我的标准接口的任何调用都会指定句柄。在下面,句柄只是不同设备数组的索引。

{
    int iHandle = GetDeviceHandle ("Device_01");
    int iXvalue = DeviceStdInput (iHandle, iValue);
}

函数 DeviceStdInput() 看起来像:

int DeviceStdInput (int iHandle, int iValue)
{
    return HardwareList[iHandle].pInput (iValue);
}

您仍然需要实现用于每个设备的实际功能,但是这提供了一种方法,可以为具有通用操作的多个设备提供标准接口,然后您可以在其余代码中使用标准接口。

我用它来提供一个标准的输出接口,其中输出设备是文件、打印机和 Web 服务。实际的接收器或输出设备由用户选择。使用接口的代码没有改变。只要界面没有改变,添加更多设备就很容易了。在某些情况下,我会拥有不支持特定操作的设备,并且该功能只会返回而无需执行任何操作。

于 2012-08-07T03:59:49.427 回答
2

我个人最喜欢的是使用宏。它们很简单,并且始终适用于所有编译器。

例如,假设您有一些功能可以做一些通用的事情(在您的情况下是 PID),但在硬件特定的寄存器和/或内存位置上运行。只需将硬件特定部分定义为您放入硬件特定头文件的宏。然后,当您编译代码时,您可以选择使用哪个宏(如果您注意到,这就是 PIC 编译器对硬件特定寄存器地址所做的事情)。


例子:

do_something.c

void do_something () {
    MY_OUTPUT = complex_operation(MY_INPUT);
}

dev_board_16f877.h

#define MY_INPUT  PORTB
#define MY_OUTPUT PORTD

production_board_version_1_0.h(替代标题)

#define MY_INPUT  PORTC
#define MY_OUTPUT PORTD

更好的是,您甚至应该将 I/O 操作抽象为函数而不是变量赋值,因为大多数其他 CPU 无法做到这一点。同样,这可以简单地在您在编译阶段选择的硬件特定头文件或硬件特定 C 文件中完成。

顺便说一句,这也是 Linux 内核的做法——将算法与硬件特定的东西分开。

于 2012-08-07T05:05:21.223 回答