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)とかクロック周りを見ていこうかと思います。