2

PHPでは、私は次のようなものを持っています

function doStuff($in, $value)  
{  
   $var = "V_" . $in;  
   $$var = $value;
}

Cで同様のことをする方法はありますか?

基本的に私は、AVRでのIOピンの操作を簡単にするための一種のライブラリを作成する方法を理解しようとしています。したがって、たとえば、特定のピンをOUTPUTに設定する関数があります。AVRのそのピンはPORTBの一部です。それを出力に設定して値を与えるには、定数と定数を参照してそれらの値を設定する必要がありDDRBますPORTB。そのすべてを実行するのではなく、などの関数を呼び出せるようにしたいと思いますSetMode(Pin #, Mode);。私はそれを行う方法を理解することができません。

4

8 に答える 8

4

あなたの質問はまだ少し不明確です(回答の解釈の品揃えによって示されているように)。物理的なピン番号でピンを参照したいとします。これが正しくない場合は、より良い回答を提供できるように、質問を明確にしてください。

誰かが私の頭に銃を持っていたら、これが大まかに私がそれをする方法です:

免責事項:私はこれをテストしておらず、ドキュメントのチェックにも特に注意を払っていません。コードはLinux上のavr-gcc/avr-libc用に記述されていますが、他の場所でも機能する可能性があります。

// Map from physical pin number to associated direction register.
volatile uint8_t *ddr_map[] = {
    NULL, // Vcc, GND, or some other non-IO pin.
    &DDRB,
    &DDRB,
    &DDRC,
    // etc...  Values will vary for different target chips.
};

// Map from physical pin number to port mask.
uint8_t mask_map[] = {
    0x00,
    _BV(0),
    _BV(1),
    _BV(0),
    // etc...  Values will vary for different target chips.
}

typedef enum {
    IN,
    OUT
} PinDir;

void setMode(int pin, PinDir dir) {
    if(dir == OUT) {
        *ddr_map[pin] |= mask_map[pin];
    } else {
        *ddr_map[pin] &= ~mask_map[pin];
    }
}

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_passを参照してください

そして、これが良い考えではない理由です:

  1. 意味のある動作を抽象化することはありません(実際には抽象化を削除します。物理ピン番号は論理ポート/ピンよりも低いレベルです)。さらに、物理ピン番号は、異なるパッケージ形式で必ずしも同じであるとは限りません。PORTBのピンは、QFPパッケージのPDIPパッケージと同じ物理ピン番号に割り当てられない場合があります。したがって、このコードは実際にはもっと混乱します。

  2. オーバーヘッドが追加されます。追加の関数呼び出し(サイクルとスタックが必要)とルックアップに使用される2つ(またはそれ以上)の配列があります(特別な措置を講じない限り、AVRのフラッシュとRAMが必要です。特別な措置を講じる場合は、追加のサイクルとフラッシュまたはEEPROMが必要です)すべての間接参照(配列ルックアップ、ポインター逆参照)と追加の比較および分岐は言うまでもありません。デスクトップとウェブの開発では、そのような小さなコストについての私の懸念を笑うのは正しいでしょうが、AVRでは無駄がかなり大きな影響を及ぼします。(注:コンパイラーにこれの一部を最適化するように説得できるかもしれませんが、-Osそれを使用している場合は困難になります。そして今、以前よりもさらに低いレベルの詳細について心配しています...)

  3. ピンを操作するために提供された手段は、このように隠す価値があるほど複雑ではありません。頭の中で16進数と2進数の間で変換することに慣れているはずです(難しくはありません)。16進数をいじりたくない場合でも、_BV()マクロを使用するとピンの操作が非常に簡単になります(または、(1 << x)より移植性が高く、より多くのプログラマーに認識されるようにするだけです)。

ちなみに、、PORTBなどDDRBは定数ではありません。これらは、特定のアドレスまたはレジスタに関連付けられている変数です。のようなもので定数を変更しようとするとCONST_THINGY |= 0x03、コンパイラエラーが発生します。

変数変数

Cには、説明した機能がありません。これは低水準言語(「高水準アセンブリ」と呼ばれることもあります)であり、(今日の基準では)多くの優れた機能を提供していません。これがAVRに最適な言語である理由です-あなたはハードウェアに近くなりたい、そしてあなたは多くの余分なオーバーヘッドを望まないのです。

Cが持っているのはポインタです。あなたの質問とコメントに基づいて、あなたはそれらにあまり精通していないと思いますので、ここに簡単な説明があります:

  • &演算子は変数へのポインターを返し、次のように使用されます。pointer = &variable;
  • *実際にはいくつかの用途があります。
    • 1つ目は、ポインター変数(つまり、int、char、またはfloatの代わりにポインターを保持する変数)を宣言するint *pointer; ことです。ポイントする変数のタイプを指定する必要があることに注意してください。
    • 2番目の使用法は、ポインターの間接参照と呼ばれるものです。基本的に、これはポインタを介して変数にアクセスすることを意味します。pointerを指す場合variable*pointer = 42;variable42に等しく設定され、の値にother_var = *pointer設定されます。other_varvariable
  • ポインタ演算もありますが、それはこの回答の範囲を超えています。

このすべてのポイントは、変数自体を値のように効果的に処理し、それらを格納して渡すことができるということです。それらの値を操作する以外に意味のある方法でそれらを実際に変更することはできませんが、どちらも行う必要はありません。

于 2010-07-28T04:36:44.057 に答える
3

要するに、いいえ、Cには変数変数はありません。あなたができることは、名前をキーとして、変数のある種のハッシュマップを作成し、それを使用することです。

于 2010-07-23T18:57:16.007 に答える
1

プリプロセッサ定義またはマクロは、Cで目的の目標を達成するための一般的な方法です。

于 2010-07-23T22:07:22.243 に答える
1

Cにはマクロ機能があり、このように使用できます

#define oof(a, b) a##b

int x1 = 5;
oof(x, 1) = 10;
printf("%d", x1); //prints 10
int oof(x, 2) = 2;
printf("%d", x2); //printf 2

関数にすることも、他の関数を使用することも、他のマクロを呼び出すこともできます。ここで、「##」は、その隣のオブジェクトを連結するプリプロセッサ演算子です。

于 2019-04-16T09:22:42.677 に答える
0

ピン番号と言うときは、物理チップの実際のピン番号を参照していますか?

もしそれが。あなたはこれを行うことができます。

1-ピン番号を受け取り、対応するポートとPINを返すマップ関数を作成します

元。

チップのピン#1にアクセスしたい

SetMode( int pinNumber, char mode ) {

    typedef struct  {
        int pin;
        int port;
    }pinValues;


    pinValues pinStruct;
    mapPin( &pinStruct, pinNumber );  // this resolves the pin # on the chip to a port            
    // and pin.
    GPIO_init( pinStruct, mode ); // this initializes the pin;
}

mapPin関数は、ピン番号を含む1つの配列を作成するだけで非常に単純なはずです。

元。

チップには4つのピンしかないという

const char GPIO_pin [5] = {1,2,3,4};

各ピンに対応するポートとピンの構造体を作成します#

typedef struct {
  int pin;
  int port;
}pinPort;


pinPort pinPortStruct[5] = { (PORTA,0), (PORTA,1), (PORTB,1), (PORTB,1) };

したがって、ピン#1はPORTA0に対応します

したがって、GPIO_pinを検索して、そのインデックスに対応する構造体を返すだけです。

for( int i = 0;i <4; i++)
{
 if( pin == GPIO_pin[i] )
     return pinPortStruct[i];
} 

これがあなたが必要としているものであることを願っています。

于 2010-07-23T21:49:20.990 に答える
0

すべてのAVRレジスタにはアドレスがあります。アドレスを使用して、ジェネリック関数を実装できます。

于 2010-07-23T22:03:01.330 に答える
0

話しているピン/ポートの数によっては、caseステートメントを使用するのが最も簡単な場合があります。

void SetMode(int pin, int mode) {
    switch (pin) {
        case PIN_A:
            DDRA = mode;
            PORTA = mode;
            break;

        case PIN_B:
            DDRB = mode;
            PORTB = mode;
            break;
        ...
    }
}

定数PIN_A、、などは、マクロまたは。PIN_Bを介して定義できます。このアプローチの利点の1つは、一部のポート/ピンを他のポート/ピンとは異なる方法で処理する必要がある場合でも、同様の表記を使用してすべてのポート/ピンを参照できることです(それぞれが異なる場合があります)。処理するピン/ポートが多数ある場合、これは最適なアプローチではない可能性があります。#defineenumcase

于 2010-07-23T22:14:05.887 に答える
0

一般的なケースでは、ポインターはできるだけ近くにあります。Cは、特にマイクロコントローラーでは、実行時に名前の概念を必ずしも持っていません(一部の名前は、通常、動的リンクを備えたOSに存在しますが、必須ではありません)。

ピン番号のシナリオでは、任意の番号のポートやビットインポートなどを把握するためのルックアップテーブルが機能します。これは、AVRでC++プログラミングを抽象化しようとするArduinoで採用されている手法です。たとえば、PWM信号を「analogWrite」、C ++を「配線」、プログラムを「スケッチ」と呼び、名前を変更するのが好きです。すべてのI / Oピンには、開発ボード上の位置の後に番号が付けられます。欠点は、最初のボード以外のものをプログラミングするとすぐに大きな混乱が生じ、低レベルの何かをしたいときにライブラリに埋め込まれている副作用を理解する必要があることです。

于 2010-08-25T16:33:27.317 に答える