2

PID コントローラー、信号発生器などのさまざまな機能を実装するコードに取り組んでいます。

私のハードウェアは、さまざまな入力と出力を提供します。ちょうど今、計算のソースと宛先を決定するための SWITCH ステートメントが大量にあります。

たとえば、PID コントローラーの場合、pid_calculate 関数に渡す入力を決定するスイッチ コマンドが 100 ミリ秒ごとにあり、その後に戻り値をどう処理するかを決定する別のスイッチが続きます。私は 32 のアナログ入力と、can、lin、serial の可能な入力を持っているので、switch ステートメントは巨大です!

ハードウェアに依存しない(写真の範囲内で)機能として何かをコーディングする方法の物理的な例を参照したいと思います。答えはポインターにあると確信していますが、私は C が初めてで、ポインターをどこから始めればよいかよくわかりません。

関数プロトタイプは int pid_init(*source,*destination) のようなものだと思います。ここで、ソースは入力へのポインターであり、たとえば ADC バッファーであり、宛先はたとえば pwm デューティ サイクル レジスタである可能性があります。ちょうど今、考えられる各条件を切り替えてから、データをレジスタにロードする必要があります。

明確にするために、入力と出力に依存しないようにする関数をどのように実装し、どのようにポインターを逆参照しますか(ポインターがこれを行う正しい方法であると仮定します)

これが理にかなっていることを願っています、事前に感謝します。

4

2 に答える 2

3

少し注意すれば、Cで一種のオブジェクト指向プログラミングを実装できます。私が行った方法では、関数ポインターと、関数ポインターをメンバーとして持つ構造体を使用しました。

たとえば、ある種のデータに依存しないようにするために、クイックソート(qsort)関数のように、データを操作する関数へのポインターを備えた長いデータが提供される関数がある場合があります。通常、このアプローチでは、さまざまな種類の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

さらに良いことに、他のほとんどの CPU ではそれができないため、I/O 操作を変数の代入ではなく関数として抽象化する必要があります。繰り返しますが、これは、コンパイル段階で選択したハードウェア固有のヘッダー ファイルまたはハードウェア固有の C ファイルで簡単に実行できます。

ところで、これは Linux カーネルが行う方法でもあります - アルゴリズムをハードウェア固有のものから分離します。

于 2012-08-07T05:05:21.223 に答える