18

メモリ保護のない組み込みシステム (ARM Cortex-M1、gcc 4.3 でコンパイル) のシステムレベル コードを書いており、メモリ マップド レジスタを直接読み書きする必要があります。これまでのところ、私のコードは次のようになります。

#define UART0     0x4000C000
#define UART0CTL  (UART0 + 0x30)

volatile unsigned int *p;
p = UART0CTL;
*p &= ~1;

ポインターを使用しない短い方法 (つまり、コードが短い) はありますか? 実際の割り当てコードをこれと同じくらい短く書く方法を探しています (もっと #define を使用しなければならない場合でも問題ありません)。

*(UART0CTL) &= ~1;

私がこれまでに試したことは、左辺値に何かを割り当てることができないと不平を言うgccで終わりました...

4

6 に答える 6

21
#define UART0CTL ((volatile unsigned int *) (UART0 + 0x30))

:-P

追加するために編集: ああ、質問に C++ と C のタグが付けられている方法についてのすべてのコメントに応えて、ここに C++ ソリューションがあります。:-P

inline unsigned volatile& uart0ctl() {
    return *reinterpret_cast<unsigned volatile*>(UART0 + 0x30);
}

これは、C スタイル マクロと同様に、ヘッダー ファイルに直接スタックできますが、呼び出すには関数呼び出し構文を使用する必要があります。

于 2010-03-10T13:34:39.553 に答える
17

私はちょっとしたことをしたいと思います: 私たちは C または C++ について話しているのですか?

C の場合は、Chris の回答に喜んで従います (そして、C++ タグを削除したいと思います)。

C++ の場合、これらの厄介な C-Cast を#defineまったく使用しないことをお勧めします。

慣用的な C++ の方法は、グローバル変数を使用することです。

volatile unsigned int& UART0 = *((volatile unsigned int*)0x4000C000);
volatile unsigned int& UART0CTL = *(&UART0 + 0x0C);

型付きグローバル変数を宣言します。これはスコープ規則に従います (マクロとは異なります)。

簡単に使用でき ( を使用する必要はありません*())、さらに短くなります!

UART0CTL &= ~1; // no need to dereference, it's already a reference

ポインターにしたい場合は、次のようになります。

volatile unsigned int* const UART0 = 0x4000C000; // Note the const to prevent rebinding

しかし、constnull になれないポインターを使用するポイントは何ですか? これが意味的に参照が作成された理由です。

于 2010-03-10T14:21:48.383 に答える
2

ハードウェアレジスタを単純な古い変数のように見せたい場合は、クリスの答えよりもさらに一歩進めることができます:

#define UART0     0x4000C000
#define UART0CTL (*((volatile unsigned int *) (UART0 + 0x30)))

UART0CTL &= ~1;

どちらが好ましいかは好みの問題です。私は、チームがレジスターを変数のように見せたいという状況で働いてきました。また、追加された逆参照が「隠しすぎている」と見なされるコードに取り組んできたので、レジスターのマクロはポインターとして残されなければなりませんでした。明示的に逆参照する必要があります(クリスの回答のように)。

于 2010-03-10T16:04:27.817 に答える
1

構造体で実際の制御ビットを指定し、それを制御アドレスに割り当てるのが好きです。何かのようなもの:

typedef struct uart_ctl_t {
    unsigned other_bits : 31;
    unsigned disable : 1;
};
uart_ctl_t *uart_ctl = 0x4000C030;
uart_ctl->disable = 1;

(構文が正しくない場合は申し訳ありません。実際に C でコーディングしたことはかなりありません...)

于 2010-03-10T16:51:04.927 に答える
1

組み込みアプリケーションで私が好きなもう 1 つのオプションは、リンカーを使用してハードウェア デバイスのセクションを定義し、変数をそれらのセクションにマップすることです。これには、複数のデバイスをターゲットにしている場合、TI などの同じベンダーのものであっても、通常はデバイスごとにリンカー ファイルを変更する必要があるという利点があります。つまり、同じファミリー内の異なるデバイスは異なる量の内部ダイレクト マップ メモリを持ち、ボード間では異なる量の RAM と異なる場所にあるハードウェアを持つ場合があります。GCC ドキュメントの例を次に示します。

通常、コンパイラは生成したオブジェクトを data や bss などのセクションに配置します。ただし、追加のセクションが必要な場合や、特別なハードウェアにマップする場合など、特定の変数を特別なセクションに表示する必要がある場合があります。section 属性は、変数 (または関数) が特定のセクションに存在することを指定します。たとえば、次の小さなプログラムでは、いくつかの特定のセクション名を使用しています。

      struct duart a __attribute__ ((section ("DUART_A"))) = { 0 };
      struct duart b __attribute__ ((section ("DUART_B"))) = { 0 };
      char stack[10000] __attribute__ ((section ("STACK"))) = { 0 };
      int init_data __attribute__ ((section ("INITDATA")));

      main()
      {
        /* Initialize stack pointer */
        init_sp (stack + sizeof (stack));

        /* Initialize initialized data */
        memcpy (&init_data, &data, &edata - &data);

        /* Turn on the serial ports */
        init_duart (&a);
        init_duart (&b);
      }

例に示すように、ローカル変数ではなくグローバル変数で section 属性を使用します。

初期化済みまたは初期化されていないグローバル変数で section 属性を使用できますが、初期化されていない変数が一時的に共通 (または bss) セクションに入り、複数の「定義」が可能であることを除いて、リンカは各オブジェクトを 1 回定義する必要があります。セクション属性を使用すると、変数が入るセクションが変更され、初期化されていない変数に複数の定義がある場合、リンカーがエラーを発行する可能性があります。-fno-common フラグまたは nocommon 属性を使用して、変数を強制的に初期化できます。

于 2010-03-10T18:50:10.197 に答える
1
#define UART0  ((volatile unsigned int*)0x4000C000)
#define UART0CTL (UART0 + 0x0C)
于 2010-03-10T13:40:08.763 に答える