4

アドレス say に書き込みたいとすると0xc000、C でマクロを次のように定義できます。

#define LCDCW1_ADDR       0xc000
#define READ_LCDCW1()     (*(volatile uint32_t *)LCDCW1_ADDR)
#define WRITE_LCDCW1(val) ((*(volatile uint32_t *)LCDCW1_ADDR) = (val))

私の質問は、マイクロコントローラーを使用する場合、MSP430 の例を考えてみてください。P1OUT レジスターのアドレスは 0x0021 です。

しかし、P1OUT=0xFFFF を使用すると、// P1OUT に値 0xFFFF を割り当てます。

私の質問は、この場合は0x0021など、そのアドレスにどのように書き込むかです。IDE は IAR です。定義の下のヘッダー msp430g2553.h で見つけました:

#define P1OUT_              (0x0021u)  /* Port 1 Output */
DEFC(   P1OUT             , P1OUT_)

アドレスを定義していると思いますが、書き込みまたは読み取りを行う他のマクロはどこにありますか。

その特定のアドレス位置で P1OUT がどのように書き込むかの流れを誰か説明してもらえますか? また、 0x0021u の意味を教えてください。

ありがとう


これまでのところ、私が見つけた詳細は次のとおりです。

msp430g2553.h で

#ifdef __IAR_SYSTEMS_ICC__
#include "in430.h"
#pragma language=extended

#define DEFC(name, address) __no_init volatile unsigned char name @ address;
#define DEFW(name, address) __no_init volatile unsigned short name @ address;
#define DEFXC  volatile unsigned char
#define DEFXW  volatile unsigned short

#endif  /* __IAR_SYSTEMS_ICC__  */


#ifdef __IAR_SYSTEMS_ASM__
#define DEFC(name, address) sfrb name = address;
#define DEFW(name, address) sfrw name = address;

#endif /* __IAR_SYSTEMS_ASM__*/



#define P1OUT_              (0x0021u)  /* Port 1 Output */
DEFC(   P1OUT             , P1OUT_)

io430g2553.h は言う

__no_init volatile union
{
  unsigned char P1OUT;   /* Port 1 Output */

  struct
  {
    unsigned char P0              : 1; /*  */
    unsigned char P1              : 1; /*  */
    unsigned char P2              : 1; /*  */
    unsigned char P3              : 1; /*  */
    unsigned char P4              : 1; /*  */
    unsigned char P5              : 1; /*  */
    unsigned char P6              : 1; /*  */
    unsigned char P7              : 1; /*  */
  }P1OUT_bit;
} @0x0021;

上記の定義が何をするのか説明できる人はいますか? MSP430 IAR C/C++ Compiler で見つけた詳細:

Example of using __write and __read
The code in the following examples use memory-mapped I/O to write to an LCD
display:
__no_init volatile unsigned char LCD_IO @ address;
size_t __write(int Handle, const unsigned char * Buf,
size_t Bufsize)
{
size_t nChars = 0;
/* Check for stdout and stderr
(only necessary if file descriptors are enabled.) */
if (Handle != 1 && Handle != 2)
{
return -1;
}
for (/*Empty */; Bufsize > 0; --Bufsize)
{
LCD_IO = * Buf++;
++nChars;
}
return nChars;
}
The code in the following example uses memory-mapped I/O to read from a keyboard:
__no_init volatile unsigned char KB_IO @ 0xD2;
size_t __read(int Handle, unsigned char *Buf, size_t BufSize)
{
size_t nChars = 0;
/* Check for stdin
(only necessary if FILE descriptors are enabled) */
if (Handle != 0)
{
return -1;
}
for (/*Empty*/; BufSize > 0; --BufSize)
{
unsigned char c = KB_IO;
if (c == 0)
break;
*Buf++ = c;
++nChars;
}
return nChars;
}

誰か知っていますか?

4

3 に答える 3

4

これは「私が書いたものからコンパイラがどのようにコードを生成するか」であり、コンパイラの作成者だけが実際にそれに答えることができます。

明らかに、上記のコードには __no_init や @ の使用など、非標準の C コンポーネントがいくつかあります。これを読んでみると、「これは HW ポートであり、unsigned char を提供し、そのアドレスは0xd2". コンパイラは、そのようなポートを読み書きするための適切な種類の命令を生成します。正確にどのように機能するかは、コンパイラ、コンパイラがコードを生成するプロセッサなどによって異なります。

P10out 構造体は、C 標準の一部であるビットフィールドを定義します。Google はここであなたの友達です。

于 2012-12-21T09:57:29.863 に答える
1

間接演算子 (単項*) は、ポインター アドレスの値に相当する左辺値を返します。

#define LCDCW1_ADDR       0xc000

void f()
{
     uint32_t a = *(volatile uint32_t *)LCDCW1_ADDR; //reading from LCDCW1_ADDR
     *(volatile uint32_t *)LCDCW1_ADDR = 0xffff;     //writing to LCDCW1_ADDR
     /*...*/
}

基本的に、コンパイラは十分に賢く、その式は「アドレスa = *addr;から値を読み取り、それを に配置します。同時に、 「アドレスに 0xffff を配置する」のように解釈されます。addra*addr = 0xffffaddr

あなたの場合READ_LCDCW1()、代入演算子の左側と右側の両方でマクロを使用できます。WRITE_LCDCW1(val)別のマクロは必要ありません。前のコードを次のように書き直すことができます。

#define LCDCW1_ADDR       0xc000
#define LCDCW1     (*(volatile uint32_t *)LCDCW1_ADDR)

void g()
{
     uint32_t a = LCDCW1; //reading from LCDCW1_ADDR
     LCDCW1 = 0xffff;      //writing to LCDCW1_ADDR
     /*...*/
}

P1OUTIAR のマクロは、おそらくLCDCW1上記と同じように定義されています (定義に従えば、DEFC()最終的にそれに似たものを見つけることができます)。

于 2012-12-21T06:49:27.760 に答える
0

私の質問は、マイクロコントローラーを使用する場合、MSP430 の例を検討することです。

あなたはマイクロコントローラーを使用しておらず、MSP430 を使用しています。メモリ マップド IO を備えています (これは、プログラマにとって非常に便利です)。メモリ マッピングは、デバイスによって異なります。アドレス関連の質問に対する回答は、特定のデバイスのユーザー ガイドに記載されています。TI は非常に優れたユーザー ガイドを作成しています。特定のデバイス用のものを見つけて、よく読んでください。

私の質問は、この場合は0x0021など、そのアドレスにどのように書き込むかです。IDE は IAR です。

コンパイラのグルー コード。コンパイラ ベンダーは、デバイス アドレスに書き込むために必要なヘッダー、マクロ、および関数を提供します。自分のケースでは機能しないことを完全に証明できない限り、コンパイラ ベンダーのコードを使用してください (IAR では、99.9% は機能し、料金に見合ったものを手に入れることができると想定しています。おそらく、真新しいデバイスでは、実装にバグがある可能性があります。しかし、それを証明できない限り、おそらくそうではありません)。

また、 0x0021u の意味を教えてください。

あなたが投稿したものから、それはポート 1 のベース アドレスです。ポート 1 には 8 つのピンがあり、制御できるようです。

#pragma language=extended

この時点から、あらゆる種類の「魔法の」(別名非標準 C) ことが起こると想定する必要があります。コンパイラが何をしているのかを推測することはできますが (ほとんどの場合、かなり明確です)、これは実装定義です。つまり、IAR コンパイラだけが次に何が起こるかをサポートします。特定のコマンドと意味については、コンパイラのドキュメントを参照してください。最も顕著なのは、__no_init と @ 記号が非標準であることです。__no_init は、C の起動時 (つまり、main() の実行前) に変数を初期化しません。@ は、リンカに与えられる絶対アドレス命令のように見えます (ここは間違っているかもしれません)。

__no_init volatile union
{
  unsigned char P1OUT;   /* Port 1 Output */

  struct
  {
    unsigned char P0              : 1; /*  */
    unsigned char P1              : 1; /*  */
    unsigned char P2              : 1; /*  */
    unsigned char P3              : 1; /*  */
    unsigned char P4              : 1; /*  */
    unsigned char P5              : 1; /*  */
    unsigned char P6              : 1; /*  */
    unsigned char P7              : 1; /*  */
  }P1OUT_bit;
} @0x0021;

これは、ポート 1 のバイトの特定のビットを取得する方法を定義します。これにより、IO ピンを操作できます。OMG ビットフィールドは移植可能であり、実装が定義されていると言う人もいます。はい、彼らは正しいですが、IAR が実装者であるため、この場合は、彼らが正しいことを行うと信頼してください。

最後に、IAR マクロを定義どおりに使用したいだけかもしれません。あなたはそれらに多額のお金を払いました (無料のキックスタート版を使用していない場合)。このようにビットを操作することなく、アプリの作成に集中できます。IAR はそれらの名前を標準化するのに優れているため、関連する部分で同じ (または非常に類似した) コードを使用することもできます。別のコンパイラに切り替えると、これはすべて窓の外に出てしまい、新しいコンパイラの方法でそれを行う必要があります. このアプローチの良い点と悪い点、おそらく「正しい」答えはありません。

于 2013-02-24T07:36:49.903 に答える