LPCXpressoとLPC800-MAXでマイコンを学ぶ(その3:LEDの動作(GPIO)を調べる)

サンプルアプリ(Blinky)のLEDの動作を調べてみる

これで、ひと通りデバッグ等ができるようになりました。実際のコードを解析しつつ、ARM Cortex-M0というかLPC800シリーズのアーキテクチャを見て行きたいと思います。まずは前半を飛ばして、わかりやすいLEDの制御部から見ていきます。以下の方法は、僕がこういったマイコンの動作とアーキテクチャを理解する上で確認していく過程をほぼそのまま書いている感じです。

LEDはGPIOで制御

LEDはLPC800-MAXのボード上に配置してあり、LPC812と接続してあります。接続を確認するためにLPC800-MAXの回路図を確認します。

p2にある下記の部分がLEDの回路になります。この回路図から

  • PIO0_16 : BLUE
  • PIO0_17 : GREEN
  • PIO0_7 : RED

ということがわかります。

f:id:tomo_watanabe:20131009215242p:plain

Blinky(main.c)のソースコードを見ると

  /* Set port p0.7 to output */
  GPIOSetDir( 0, 7, 1 );

  /* Set port p0.16 to output */
  GPIOSetDir( 0, 16, 1 );

  /* Set port p0.17 to output */
  GPIOSetDir( 0, 17, 1 );

となっていますので、正しいことがわかります。この関数を見てみると、きちんとAND, ORでレジスタ制御されています。dirはoutput=1と設定されています。

(lpc8xx_gpio.c)

void GPIOSetDir( uint32_t portNum, uint32_t bitPosi, uint32_t dir )
{
  if( dir )
  {
        LPC_GPIO_PORT->DIR0 |= (1<<bitPosi);
  }
  else
  {
        LPC_GPIO_PORT->DIR0 &= ~(1<<bitPosi);
  }
  return;
}

GPIOレジスタの確認

さて、この関数の「LPC_GPIO_PORT」が指している場所を探してみると、、、

(LPC8xx.h)

#define LPC_GPIO_PORT_BASE    (0xA0000000)
:
#define LPC_GPIO_PORT         ((LPC_GPIO_PORT_TypeDef  *) LPC_GPIO_PORT_BASE  )
:

「LPC_GPIO_PORT」は「LPC_GPIO_PORT_BASE」へのポインタになっていて、LPC_GPIO_PORT_BASE = 0xA0000000となっています。これを確認してみます。LPC800シリーズのページのLPC81xユーザーズマニュアルを調べます。p10にメモリマップが載っています。これによると0xA0000000〜0xA0004000までがGPIOに割り当てられています。

f:id:tomo_watanabe:20131009235224p:plain

LPC_GPIO_PORT_TypeDefをコード上で見ると下記のように定義されています。

(LPC8xx.h)

typedef struct {                            
  __IO uint8_t B0[18];                   /*!< (@ 0xA0000000) Byte pin registers port 0 */
  __I  uint16_t RESERVED0[2039];
  __IO uint32_t W0[18];                  /*!< (@ 0xA0001000) Word pin registers port 0 */
       uint32_t RESERVED1[1006];
  __IO uint32_t DIR0;                          /* 0x2000 */
       uint32_t RESERVED2[31];
  __IO uint32_t MASK0;                                  /* 0x2080 */
       uint32_t RESERVED3[31];
  __IO uint32_t PIN0;                          /* 0x2100 */
       uint32_t RESERVED4[31];
  __IO uint32_t MPIN0;                                   /* 0x2180 */
       uint32_t RESERVED5[31];
  __IO uint32_t SET0;                         /* 0x2200 */
       uint32_t RESERVED6[31];
  __O  uint32_t CLR0;                         /* 0x2280 */
       uint32_t RESERVED7[31];
  __O  uint32_t NOT0;                                    /* 0x2300 */

} LPC_GPIO_PORT_TypeDef;

GPIOのポートの定義はユーザーズマニュアルのp88から

f:id:tomo_watanabe:20131009235252p:plain

これに対応していることになります。この表からDIR0のReset Valueは0、つまり起動時の初期値はINPUTであることがわかります。BlinkyではDIR0の対応するbitに1(=OUTPUT)をORで書き込んでいることになります。

またGPIOへの書込みはSET0, CLR0, NOT0を利用することがわかります。SET0のReset Valueは0なので、BlinkyでGPIOSetDir関数をセットした直後の状態は

  • OUTPUT
  • 初期値: 0

となるので、該当するGPIOのLEDは「点灯」することになります。これを実機上で確認するにはGPIOSetDir関数までステップ実行させてみると確認できます。下記はPIO0_7(RED)のDIRをセットした直後の状態です。

f:id:tomo_watanabe:20131009223241j:plain

点灯する理由は回路図を確認することでわかります。回路図ではLEDはアノードコモンを使用しており、3.3vにプルアップされています。したがってOUTPUTで0が書き込まれると、電流が流れ込みLEDが点灯することになります。消灯する場合は1を書き込むことで3.3vで電位が同じになり、電流が流れないので消灯することになります。

GPIOへの書込み

設定が理解できた所で書込みですが、これは上記で書いたとおりになっています。main.cのコードを見るとここで行っています。

  while (1)                                /* Loop forever */
  {
        /* I/O configuration and LED setting pending. */
        if ( (mrt_counter > 0) && (mrt_counter <= 200) )
        {
            GPIOSetBitValue( 0, 7, 0 );
        }
        if ( (mrt_counter > 200) && (mrt_counter <= 400) )
        {
            GPIOSetBitValue( 0, 7, 1 );
        }

単純にGPIOSetBitValue関数で、点灯時は0, 消灯時は1をセットしています。セットする関数はGPIOSetDirと同じようになっています。ここではSET/CLRを使用していますね。

(lpc8xx_gpio.c)

void GPIOSetBitValue( uint32_t portNum, uint32_t bitPosi, uint32_t bitVal )
{
  if ( bitVal )
  {
        LPC_GPIO_PORT->SET0 = 1<<bitPosi;
  }
  else
  {
        LPC_GPIO_PORT->CLR0 = 1<<bitPosi;
  }
  return;
}

GPIOの初期設定

飛ばしてしまっていたのですが、GPIOの初期設定も見ておきます。

  /* Enable AHB clock to the GPIO domain. */
  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);

先ほどのGPIOで探したのと同じ要領で「LPC_SYSCON」と「SYSAHBCLKCTRL」を探します。これは「System configuration」の「System clock control」レジスタです。(ユーザーズマニュアルp26, p35)そして6bit目に1を書き込んでいます。

f:id:tomo_watanabe:20131009235347p:plain

この表からGPIOへのクロック供給をEnableにしてGPIOブロックを有効にしていることがわかります。


さすがにArduinoとはひと味もふた味も違う難解さですね(^^; GPIO一つ動かすだけでもこれだけの調査と理解が必要になります。次回はMRT(Multi rate timer)とかクロック周りを見ていこうかと思います。

LPCXpressoとLPC800-MAXでマイコンを学ぶ(その2:デバッグでprintf文を使う)

普通にオンラインデバッグができますが、一点だけprintf文を使おうとしてハマりました。

デバッグでprintf文を使う

前回のBlinkyプロジェクトにprintf文を書いてビルドすると以下のようにリンカでエラーになってしまいます。

f:id:tomo_watanabe:20131009211938p:plain

調べたところ解決策がこちらの「趣味エンジニアリング」に書いてありました。

プロジェクトウィンドウから「Blinky」のプロジェクトを右クリック→「Properties」→「C/C++ Build」→「Settings」で右のビューの「MCU Linker」→「Target」を選択します。

「Use C Library」が「Redlib(none)」となっていますので、これを「Redlib(semihost)」に変更します(下図参照)

f:id:tomo_watanabe:20131009213149p:plain

これでビルドすると・・・

f:id:tomo_watanabe:20131009213319p:plain

無事ビルドが通るようになりました。ただしこの設定はプロジェクトごとに必要みたいなので注意が必要です。

実際に実行すると「Console」ウィンドウにprintfの内容が表示されるようになります。

f:id:tomo_watanabe:20131009213814p:plain

BlinkyのGPIOレジスタの設定についても書こうと思いますが、別途エントリを分けます。

LPCXpressoとLPC800-MAXでマイコンを学ぶ(その1:サンプルアプリを動かす)

ARMマイコンの勉強を始めました。とりあえず、LPC800-MAXというArduino互換(ぽい)のが出たようなので、スイッチサイエンスさんで購入。このボードはmbedにもなるボードなのですがそっちは置いておいて、ARMマイコンを理解するためにもLPCXpressoで動作するとこまで確認しました。

LPC800-MAXについてはこちら LPC800-MAX

LPCXpressoの導入

なにはともあれ、開発環境をインストールします。LPCXpressoの情報は「LPCマイコン情報」の公式にあります。ここを参考にしながら進めるといいでしょう。

NXPのユーザ登録とLPCXpressoダウンロード

ツールのダウンロードにはユーザ登録が必要になります。ここからユーザ登録を行い、ダウンロードページからLPCXpresso 6をダウンロードしてインストールします。

LPCXpressoのアクティベート

インストールが完了して起動すると、eclipseベースのIDEが起動します。最初の起動時にはメッセージが表示されるのでアクティベートを行う必要があります。なおアクティベートはアカウント単位ではなく、インストールベース単位なので、複数台使う場合もそれぞれでアクティベートが必要です。

f:id:tomo_watanabe:20131008231203p:plain

アクティベートはメッセージにあるように「Help」→「Activate LPCXpresso (Free Edition)」→「Create serial number and register...」でシリアルナンバーを取得します。

f:id:tomo_watanabe:20131008232331p:plain

シリアルナンバーをコピーしておき、OKを押すとIDE内もしくは外部ブラウザでサイトが開くので、登録したユーザでログインして、ペーストすることでアクティベートキーを取得できます。アクティベートキーを取得できたら、LCPXpressoのIDEでHelp」→「Activate LPCXpresso (Free Edition)」→「Enter Activation code...」に入力して完了です。アクティベートページがわからなくなってしまった場合はこちら

LPC800-MAXのサンプルプログラムを動かす

それでは、LPC800-MAXをUSB経由で接続して、サンプルプログラムを動作させてみましょう。このボードはmbedにもなるので、接続すると「MBED」というストレージがマウントされますが、今回は気にしません。

LPC800関連のライブラリのインポート

まずは関連するライブラリをワークスペースにインポートしておきます。左下のメニューから「import project(s)」を選択します。

f:id:tomo_watanabe:20131008233417p:plain

「Archive」で「Browse...」を選択し、「Exmaple」→「NXP」→「LPC800」で「LPC8xx_Libraries.zip」を選択してインポートします。すると下記のように3つのプロジェクトがインポートされます。これらはライブラリとして使うことになります。追記※CMSIS_DSPLIB_CM0は必ずしも必要ではないようです

f:id:tomo_watanabe:20131008233612p:plain

サンプルアプリのインポート

次にサンプルアプリをインポートします。同じように「import project(s)」から「NXP_LPC8xx_SampleCodeBundle.zip」を選択してインポートします。この時に「Blinky」のみを選択してインポートします。ワークスペースは以下のようになります。

f:id:tomo_watanabe:20131008234028p:plain

なにはともあれ動かす

とりあえず動かしてみましょう。左下のメニューから「Debug 'Blinky'」を選択するとビルドが行われ、ビルドが成功するとLPC800-MAXにビルドバイナリをダウンロード書込みにいきます。初回起動時には書き込み先の選択画面が出るので、選択してやるとボードへの書込みが行われます。

f:id:tomo_watanabe:20131008234727p:plain

無事書込みが完了するとデバッガが起動して、main()の入り口付近で止まります。

f:id:tomo_watanabe:20131008235014p:plain

この状態でコードデバッグできるのですが、とりあえず動かしてみたいので「Resume(F8)」でプログラムを走らせてしまいましょう。

f:id:tomo_watanabe:20131009000417p:plain

これで実機のLPC800-MAXではフルカラーのLEDが「R」 → 「B」 → 「G」と点滅を繰り返すサンプルプログラムが動作しているはずです。

f:id:tomo_watanabe:20131008224841j:plain

次回は、コードの中身を追いながらARM Cortext-M0+のアーキテクチャを理解してみたいと思います。