LPCXpressoとLPC800-MAXでマイコンを学ぶ(その9:I2Cでスイッチコントロール(GPIO))

おそらく今回でLPCXpressoは最後になりますが、I2Cを使ってみます。LPC812は少ピンなのでGPIOが少ないのですが、Arduinoと同じようにGPIOやADCを載せるために、I2Cバスにデバイスを接続して拡張しています。

I2Cのサンプルプログラムを動かす

まずはサンプルプログラムを動かしてみます。サンプルは例のごとく、import project(s)からNXP_LPC8xx_SampleCodeBundle.zipを選択して「I2C」をインポートします。メインはi2ctest.cです。しかしこのサンプルコードは、このLPC800-MAXでは動かないのでLPC800-MAXのGPIO制御用のI2Cのコードを作成しました。

"i2c.c"のコード

このコードをi2ctest.cを削除して使用します。このコードを動かすと、SW2のスイッチを押すと赤色のLEDが光るようになっています。 LEDを光らせているのは、最初の方にやったGPIO制御ですが、SW2のスイッチ制御にI2Cを使用しています。

LPC800-MAXのI2C

I2Cデバイス

LPC800-MAXでは2つのデバイスがI2Cバスに接続されています

  • PCA9672PW : GPIOエキスパンダ
  • PCF8591T : ADC / DAC

今回はこのうちのGPIOエキスパンダを使用しています。PCA9672PWの資料はこちらにあります。

I2Cアドレス

GPIOエキスパンダのI2Cアドレスを調べてみます。回路図に書いてあります。

f:id:tomo_watanabe:20131026223929p:plain

I2C Addrs = 0b01000(A1)(A0); Default Addrs = 0b0100011

ということなので、0x23になります。I2Cバスは7bitアドレスを採用していて、下位1bitでRead/Write制御するようになっています。そのためこの場合は左へ1bitシフトし、b01000110/Write, b01000111/Readとなり、それぞれ0x46/Write, 0x47/Readとなります。GPIOへのアクセスは8bitでRead/Writeする形になります(AND書込みなので、どこかのポートだけ制御したい場合は、読み込んでからORして書込む必要あり)

f:id:tomo_watanabe:20131026224646p:plain

また回路図を見るとP0-P7がGPIOになっており、P7がSW2に接続されていることがわかります。IOEXPAN4, IOEXPAN5はArduinoのDigitalピンとして出ています。

f:id:tomo_watanabe:20131026225125p:plain

なお、基板上では「XP_4」「XP_5」となっていますが、シルクが間違っていますので注意して下さい。

f:id:tomo_watanabe:20131026225302p:plain

サンプルプログラムの動作

サンプルのプログラムではI2Cの初期化を行い、GPIOエキスパンダのIOEXPANを操作します。動作確認はテスタなどで当たってもらえばわかります。その後、P7のスイッチを監視するようにしていて、スイッチが押されるとLEDを点灯する。というプログラムです。

I2Cのピンアサインの設定

コードの最初のところでI2Cへのピンアサインを行っています。この辺は以前にも出てきたピンマルチの設定です。

  • SDA : PIO0_10
  • SLA : PIO0_11
  /*
   * Initialize I2C pin connect
   */
  /*connect the I2C SCL and SDA sigals to port pins(P0.10-P0.11)*/
  regVal = LPC_SWM->PINASSIGN7 & ~(0xFFUL<<24);
  LPC_SWM->PINASSIGN7 = regVal | (10 << 24); /* P0.10 is I2C SDA, ASSIGN0(31:24) */
  regVal = LPC_SWM->PINASSIGN8 & ~(0xFF<<0);
  LPC_SWM->PINASSIGN8 = regVal | (11 << 0);    /* P0.11 is I2C SCL. ASSIGN0(7:0) */
  regVal = LPC_IOCON->PIO0_10 & ~(0x3<<8);
  LPC_IOCON->PIO0_10 = regVal | (0x2<<8);  /* Enable Fast Mode Plus */
  regVal = LPC_IOCON->PIO0_11 & ~(0x3<<8);
  LPC_IOCON->PIO0_11 = regVal | (0x2<<8);  /* Enable Fast Mode Plus */

  /* Enable I2C clock */
  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<5);
  /* Toggle peripheral reset control to I2C, a "1" bring it out of reset. */
  LPC_SYSCON->PRESETCTRL &= ~(0x1<<6);
  LPC_SYSCON->PRESETCTRL |= (0x1<<6);

後半の部分はI2Cブロックへのクロックの供給とリスタートです。

I2Cの初期化

/* For master mode plus, if desired I2C clock is 1MHz (SCL high time + SCL low time). 
  If CCLK is 36MHz, MasterSclLow and MasterSclHigh are 0s, 
  SCL high time = (ClkDiv+1) * (MstSclHigh + 2 )
  SCL low time = (ClkDiv+1) * (MstSclLow + 2 )
  Pre-divider should be 36000000/(1000000*4)-1 = 9-1 = 8. 
  If fast mode, e.g. communicating with a temp sensor, Max I2C clock is set to 400KHz.
  Pre-divider should be 36000000/(400000*4)-1 = 22.5-1 = 22.
  If standard mode, e.g. communicating with a temp sensor, Max I2C clock is set to 100KHz.
  Pre-divider should be 36000000/(100000*4)-1 = 90-1 = 89. */

I2C_MstInit(LPC_I2C, I2C_FMODE_PRE_DIV, CFG_MSTENA, 0x00);

今回はLPC812はI2Cマスターとして動作するので、その初期化を行います。I2Cバスプロトコルには3種類あります。今回アサインされているPIO0_10, PIO0_11は全てのモードをサポートしています。この設定ではFast-modeでの動作設定を行っています。またマスターモードをイネーブルにセットしています。

  • Standard-mode :
  • Fast-mode :
  • Fast-mode Plus :

その後にI2Cがアイドルかどうかチェックしている箇所があったり、バッファを用意しています。

GPIOの初期化(LEDの初期化)

最初の頃にやったGPIOの設定です。ここでは赤色LEDを使うために初期化しています。

/* GPIO初期化 */
GPIOInit();
GPIOSetDir( 0, 7, 1 ); // RED
GPIOSetBitValue( 0, 7, 1 ); // RED OFF

GPIOエキスパンダの操作

GPIOエキスパンダに一度0を書き込んでから、B0を書き込んで確認しています。0x46/Write, 0x47/Readです。

  /* Clear GPIO = 0 / GPIO = 0xB0 */
  uint8_t data;
  I2CMasterTXBuffer[0] = 0x00;
  I2C_MstSend( LPC_I2C, 0x46, (uint8_t *)I2CMasterTXBuffer, 1 );
  I2C_MstReceive( LPC_I2C, 0x47, &data, 1 );
  printf("GPIO: %x\n", data);
  I2CMasterTXBuffer[0] = 0xB0;
  I2C_MstSend( LPC_I2C, 0x46, (uint8_t *)I2CMasterTXBuffer, 1 );
  I2C_MstReceive( LPC_I2C, 0x47, &data, 1 );
  printf("GPIO: %x\n", data);

0xB0を書き込んでいる理由は、b10110000で、P7 = HIGH, P5 = HIGH, P4 = HIGHに設定するためです。これでArduino端子のDigital 5, 6がHIGHになります。 またP7をHIGHにすることで、SW2の押下でP7がGND(=0)になるのを検知できるようになります。

スイッチの取得

永久ループ内でレジスタを読みだしています。0x80と比較して、つまりb1000000と比較して1(=HIGH)なら、押されていないのでLEDを消灯、0(=GND,LOW)ならスイッチが押されているのでLEDを点灯するようにしています。

  while (1) {
      I2C_MstReceive( LPC_I2C, 0x47, &data, 1 );
      if ( data &= 0x80 ) {
          GPIOSetBitValue( 0, 7, 1 ); // RED OFF
      } else {
          GPIOSetBitValue( 0, 7, 0 ); // RED ON
      }
  }

まとめ

とりあえずLPC800-MAXを使ったLPCXpressoでの組み込みマイコンはこれくらいになります。Arduinoでもマイコンでもだいたい

  • GPIO
  • UART
  • I2C

がだいたいできれば、あとはなんとかなるんじゃないかと思います。LPC812単体の価格ですが@200以下で買えます。ArduinoはUnoで@3000くらいしますので、一桁価格が違うことになります。もちろん、LPC812単体ではダメで、周辺回路やデバッグ回路を作成することになりますので、LPC812の場合は100台以上の量産向けという選択になります。

  • Arduinoで必要な周辺機器の動作検証をだいたい行っておく。プロトタイプ作成
  • LPC812やMSP430(TIのマイコン)での量産を考える。

こんな感じになるでしょうか。ある程度の量産を視野にいれる場合、このようなマイコンへの習熟やコスト感が後々重要になってきます。


# 僕自身は回路屋さんじゃないので実際の量産基板設計とかはできないのですが、回路図を読むことはできるので、ソフトウェア開発を行うためのハードの要件を回路屋さんと詰めることができます。ユーザーズマニュアルを読めることと、回路図を読めることで、ソフトウェア開発者としての幅が広がります。まぁ需要があるかどうかは置いておきますが(^_^;)