Windows IoT CoreでIOエクスパンダーを使ってLチカしてみた

Raspberry PiやArduinoでLEDドットマトリクスなどを使うと、あっという間にGPIOピンを使い尽くしてしまい、複数の機器をどうすれば同時に動作させられるのかと思うのが電子工作の初心者あるある。今回はRaspberry Pi 2とWindows 10 IoT CoreによるGPIOを増やす方法を紹介します。

用意するのはIOエクステンダーとよばれる200円ほどのIC。これとWindowsのAPIによってプログラミングすることで、4ピン(うち2ピンは電源とGND)だけでより多くのGPIOを用意することができます。エクステンダーは通信方法で大きく分けるとSPIとI2Cがありますが、今回はI2Cを採用している「MCP23017」を使いました。

このチップでは切りかけのある左側上下の16ピンでデータの入出力ができます。上側(GPA)と下側(GPB)ではプログラミングの方法が若干異なってくるので、それを交えた解説をしていきます。

まずは配線図。


写真だとこのような感じになります。

続いてプログラミング。Hackster.ioにあった投稿をベースにしていますが、手を加えているので、独自にカスタマイズした点だけを簡単に記述します。
  1. GPBへの出力を有効にするにはあらかじめ[0x01, 0x00]の2バイトを書き込みます。
  2. GPBバスの番号は0x13です。
  3. GPAとGPBのポート番号は上下で逆(GPAは左から7~0、GPBは左から0~7)になっているので、誤って同じ値を両方に適用しないようにしましょう。

以上を踏まえたソースコードがこちらになります。バックグラウンドで動作し、4つのLEDがランダムで点灯します。
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;

namespace I2CApiTest
{
    public sealed class StartupTask : IBackgroundTask
    {
        private BackgroundTaskDeferral deferral;

        private const byte PORT_EXPANDER_I2C_ADDRESS = 0x20;

        private const byte IODIR_A_REGISTER = 0x00;
        private const byte MCP23017_BUS_A = 0x12;

        private const byte IODIR_B_REGISTER = 0x01;
        private const byte MCP23017_BUS_B = 0x13;

        private I2cDevice mcp23017;

        private async void Start()
        {
            var settings = new I2cConnectionSettings(PORT_EXPANDER_I2C_ADDRESS);
            var ds = I2cDevice.GetDeviceSelector("I2C1");
            var dis = await DeviceInformation.FindAllAsync(ds);
            mcp23017 = await I2cDevice.FromIdAsync(dis[0].Id, settings);

            try {
                mcp23017.Write(new byte[] { IODIR_A_REGISTER, 0x00 });
                mcp23017.Write(new byte[] { IODIR_B_REGISTER, 0x00 });

                var r = new Random();
                var wbuf = new byte[2];
                var m = new int[2,2]{{7, 6}, {0, 1}};

                while (true) {
                    for (int i = 0; i < 2; i++) {
                        wbuf[0] = (i == 0) ? MCP23017_BUS_A : MCP23017_BUS_B;
                        wbuf[1] = 0x0;
                        for (int j = 0; j < 2; j++) {
                            if(r.Next(0, 2) == 1) wbuf[1] |= (byte)(1 << m[i,j]);
                        }
                        mcp23017.Write(wbuf);
                    }

                    await Task.Delay(500);
                }
            } catch (Exception e) {
                System.Diagnostics.Debug.WriteLine("Exception: " + e.Message);
            }
        }

        public void Run(IBackgroundTaskInstance taskInstance)
        {
            deferral = taskInstance.GetDeferral();
            Start();
        }
    }
}
, | 2016年10月5日