ハードウェア機能をカスタマイズする

本章では、Armadillo-400 シリーズのハードウェア機能をカスタマイズする方法について説明します。

2.1. シリアルインターフェース

2.1.1. コンソールとして別のシリアルインターフェースを使用する

Armadillo-400シリーズは、標準状態でシリアルインターフェース1(CON3)をコンソールとして使用します。コンソールには、起動ログやカーネルメッセージなどが出力されるため、標準の設定ではシリアルインターフェース1に外部機器を接続して使用するといったことはできません。ここでは、コンソールとして別のシリアルインターフェースを使用する方法について説明します。例として、コンソールをシリアルインターフェース2(CON9_3、CON9_5)に変更します。

第1部「起動の仕組み」でも説明したように、コンソールに文字を表示するプログラムには、ブートローダー、Linuxカーネル、ユーザーランドアプリケーションプログラムの3種類があります。

まず、ブートローダーの起動ログとカーネルメッセージを出力する先を変更します。カーネルメッセージの出力先は、カーネルパラメータのconsoleオプションで指定できます。カーネルパラメータは、ブートローダーのsetenvコマンドで設定します。ブートローダーは、consoleオプションが指定されている場合、それと同じシリアルインターフェースに起動ログを出力します。

カーネルパラメータを設定するには、Armadilloを保守モードで起動して、図2.1「コンソールをシリアルインターフェース2に変更する」のように、使用するシリアルデバイスのデバイスファイル名を入力します。シリアルインターフェースとデバイスファイルの対応は、「Armadillo-400シリーズ ソフトウェアマニュアル」の「UART」の章を参照してください。

hermit> setenv console=ttymxc2

図2.1 コンソールをシリアルインターフェース2に変更する


現在のカーネルパラメータは、setenvコマンドを引数なしで実行することで確認できます。また、カーネルパラメータの指定を解除し、標準状態にもどすには、clearenvコマンドを使用します。

hermit> setenv
1: console=ttymxc2

図2.2 カーネルパラメータの確認


console=ttymxc2を指定した状態で起動すると、起動ログやカーネルメッセージなどはシリアルインターフェース2に出力されるようになります。但し、ログインプロンプトはまだシリアルインターフェース1に出力されます。

atmark-dist v1.34.1 (AtmarkTechno/Armadillo-440)
Linux 2.6.26-at19 [armv5tejl arch]

armadillo440-0 login: 

図2.3 ログインプロンプトの表示


ログインプロンプトをシリアルインターフェース2に表示するには、/etc/inittab/etc/securettyを修正する必要があります。標準の、Atmark Distで作成したユーザーランドの場合、/etc/inittab図2.4「標準のinittab」のようになっています。3行目のttymxc1をttymxc2に変更すると、ログインプロンプトをシリアルインターフェース2に表示するようになります。また、/etc/securetty図2.5「標準のsecuretty」のようになっています。ttymxc2を追加すると、シリアルインターフェース2に表示されたログインプロンプトから、rootユーザーでログインできるようになります。

::sysinit:/etc/init.d/rc

::respawn:/sbin/getty -L 115200 ttymxc1 vt102
#::respawn:/sbin/getty 38400 tty1 linux

::shutdown:/etc/init.d/reboot
::ctrlaltdel:/sbin/reboot

図2.4 標準のinittab


ttymxc1
tty1

図2.5 標準のsecuretty


inittabsecurettyを変更するには、ユーザーランドを再構築する必要があります。第1部「Atmark Distを使ったルートファイルシステムの作成」などを参照し、使用するプロダクト用に基本的な設定をして、一度ビルドしたAtmark Distを用意してください。そして、atmark-dist/vendors/AtmarkTechno/プロダク ト名/etc/inittabatmark-dist/vendors/AtmarkTechno/プロダク ト名/etc/securetty図2.6「ログインプロンプトをシリアルインターフェース2にしたinittab」図2.7「シリアルインターフェース2からのrootログインを許可したsecuretty」に示すように修正してユーザーランドをビルドし、作成されたルートファイルシステムイメージ(romfs.img.gz)をArmadilloのフラッシュメモリのユーザーランド領域に書き込んでください。Armadilloを再起動すると、ログインプロンプトもシリアルインターフェース2に表示されるようになります。

::sysinit:/etc/init.d/rc

::respawn:/sbin/getty -L 115200 ttymxc2 vt102
#::respawn:/sbin/getty 38400 tty1 linux

::shutdown:/etc/init.d/reboot
::ctrlaltdel:/sbin/reboot

図2.6 ログインプロンプトをシリアルインターフェース2にしたinittab


ttymxc1
ttymxc2
tty1

図2.7 シリアルインターフェース2からのrootログインを許可したsecuretty


ユーザーランドをDebian GNU/Linuxで構築している場合は、/etc/inittabのログインプロンプトに関連する部分は図2.8「Armadillo-400シリーズ用Debian GNU/Linuxのinittab(抜粋)」のようになっています。ログインプロンプトをシリアルインターフェース2に出力するには、ttymxc1をttymxc2に変更します。また、securettyは、図2.9「Armadillo-400シリーズ用Debian GNU/Linuxのsecuretty(抜粋)」のようになっているので、ttymxc2を追加します。

# Example how to put a getty on a serial line (for a terminal)
#
#T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100
#T1:23:respawn:/sbin/getty -L ttyS1 9600 vt100
T0:23:respawn:/sbin/getty -L ttymxc1 115200 vt100

図2.8 Armadillo-400シリーズ用Debian GNU/Linuxのinittab(抜粋)


# MXC serial ports
ttymxc0
ttymxc1

図2.9 Armadillo-400シリーズ用Debian GNU/Linuxのsecuretty(抜粋)


2.1.2. コンソールへの出力を止める

実際の製品においては、コンソールが使える事自体が問題となる場合もあるでしょう。コンソールへの出力を止めるのも、「コンソールとして別のシリアルインターフェースを使用する」と同様の手順で行うことができます。

コンソールへの出力を止めるには、カーネルパラメータのconsolenoneを指定します。 

hermit> setenv console=none

図2.10 コンソールへの出力を止める


また、/etc/inittabgettyに関する行は、削除するかコメントアウトします。/etc/securettyに関する設定は、ログインプロンプトを表示しなければ関係ないので、そのままで構いません[5]

::sysinit:/etc/init.d/rc

#::respawn:/sbin/getty -L 115200 ttymxc1 vt102
#::respawn:/sbin/getty 38400 tty1 linux

::shutdown:/etc/init.d/reboot
::ctrlaltdel:/sbin/reboot

図2.11 ログインプロンプトを表示しない(標準のinittab)


# Example how to put a getty on a serial line (for a terminal)
#
#T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100
#T1:23:respawn:/sbin/getty -L ttyS1 9600 vt100
#T0:23:respawn:/sbin/getty -L ttymxc1 115200 vt100

図2.12 ログインプロンプトを表示しない(Armadillo-400シリーズ用Debian GNU/Linuxのinittab)


2.2. I2C接続A/Dコンバーター

Armadillo-400 シリーズでは、CON11とCON14にI2Cバスが出ており、外部のデバイスと接続することができます。Armadillo-400 シリーズLCD拡張ボードやArmadillo-400 シリーズRTCオプションモジュール、Armadillo-400シリーズWLANモジュールでは、I2Cバスにリアルタイムクロックを接続しています。ここでは、I2CバスにA/Dコンバーターを接続する方法を紹介します。

使用するソフトウェア、デバイスは以下のとおりです。

  1. Linuxカーネル: linux-2.6.26-at19
  2. A/Dコンバーター: PCF8591(NXP社製)

2.2.1. I2C概要

I2C(Inter Integrated Circuit)は、IC間のデータ転送に使われる2線式の通信方式です。正式にはI2Cと記述し、I-squared-C(アイ・スクエアド・シー)と読みます。[6]

I2Cではシリアルデータライン(SDA)とシリアルクロック(SCL)の2本の信号線のみを使用して通信をおこないます。この2本の信号線に複数のデバイスを接続し、バスを構成することができます。I2Cバスに接続するデバイスの出力段はオープンドレイン(またはオープンコレクタ)とし、信号線はプルアップします。そのため、全てのデバイスがHighを出力しているときだけ信号線はHighとなり、どれかひとつのデバイスがLowを出力すると信号線はLowとなります[7]

I2Cバスに接続されたデバイスは、その役割によってマスタとスレーブに分かれます。マスタとスレーブは、一つのI2Cバスにそれぞれ複数接続することができます。通信は必ずマスタが開始し、バスに接続されたスレーブとデータのやりとりを行います。スレーブはそれぞれ固有のアドレスを持っており、マスタはアドレスを指定することで通信をおこなうスレーブを特定します。Armadillo-400シリーズは、I2Cマスタとなることができます。

I2Cでは、1クロックにつき1bitのデータの転送を行います。そのため、データ転送速度はクロックの速度によって決まります。I2Cにはいくつかのモードがあり、モードごとに転送速度の上限が決まっています。標準モードでは0から100kbit/sec、ファーストモードでは400kbit/secまで、ハイスピードモードでは3.4Mbit/secまでとなっています。Armadillo-400シリーズは、ファーストモードまで対応しています。

データの転送はクロックに同期して行われます。クロックは転送を開始するマスタが生成します。SCLがHighの時にSDAをHighからLowに変化させることで転送が開始されます。これをスタートコンディションと呼びます。また、SCLがHighの時にSDAをLowからHighに変化させることでデータの転送を終了します。これをストップコンディションといいます。スタートコンディションとストップコンディションの発行は、必ずマスターによって行われます。

I2Cでは、1回の転送で複数のバイトを送受信することができます。各バイトの長さは必ず8bitになります。データは最上位ビット(MSB)から順に送信されます。SCLがHighの時のSDAのレベルによって、論理が0(SDA=Low)か1(SDA=High)かが決定します。SCLがHighの間、SDAのレベルは一定でなければなりません。SDAのレベルを変更できるのは、SCLがLowの時だけです。

各バイト(8bit)の転送ごとに、アクノリッジ(ACK)が必要になります。受信側は、正常に通信がおこなえている場合、アクノリッジ信号として、SDAをLowにします。アクノリッジ信号としてSDAをHighにすることで、送信側にデータの終了を知らせることができます。

I2Cプロトコル

図2.13 I2Cプロトコル


2.2.2. サンプル回路

今回使用するA/DコンバーターPCF8591は、以下の特長を持ちます。

  1. 単一電源(2.5Vから6V)動作
  2. I2C接続
  3. 3つのハードウェアピンでアドレス指定可能
  4. オンチップ サンプルアンドホールド回路
  5. 8bit分解能逐次比較型A/D入力×4
  6. 8bit分解能D/A×1

Armadillo-400シリーズと、A/Dコンバーターとの接続を図2.14「I2C接続A/Dコンバーター回路図」に示します。Armadillo-400シリーズのCON14から出ているI2C2にPCF8591を接続します。アドレスを指定するA0、A1、A2ピンは全てプルダウンしておきます。AIN0からAIN3ピンがアナログ入力ピンです。アナログ入力にかかる電圧を、20kΩの可変抵抗で変えられるようにしています。リファレンス電圧VREFに電源電圧と同じ3.3Vを入力しているため、0Vから3.3Vの範囲のアナログ入力を8bit(256段階)のデジタル値に変換します。

I2C接続A/Dコンバーター回路図

図2.14 I2C接続A/Dコンバーター回路図


2.2.3. PCF8591通信プロトコル

PCF8591は内部にレジスタを持っており、レジスタの値を変更することで、デバイスの機能を変更することができます。レジスタへの書き込みは、図2.15「PCF8591通信フォーマット(コントロールバイト書き込み)」に示すフォーマットにしたがっておこないます。

PCF8591通信フォーマット(コントロールバイト書き込み)

図2.15 PCF8591通信フォーマット(コントロールバイト書き込み)


まず、マスタがスタートコンディションを発行し、アドレスバイトを送信します。スレーブからのACKが返ってきたら、続いてコントロールバイトを送信します。再びスレーブからのACKが返ってきたら、ストップコンディションを発行して、レジスタへの書き込みを完了します。

アドレスバイトのフォーマットを図2.16「PCF8591通信フォーマット(アドレスバイト)」に示します。上位7bitでスレーブのアドレスを指定します。A2、A1、A0は、それぞれPCF8591のA2、A1、A0ピンのレベルに対応した値とします。今回の例では全てプルダウンしたので、A2、A1、A0は全て0を指定します。書き込み転送の場合は、R/W*を0とします。

PCF8591通信フォーマット(アドレスバイト)

図2.16 PCF8591通信フォーマット(アドレスバイト)


コントロールバイトのフォーマットを図2.17「PCF8591通信フォーマット(コントロールバイト)」に示します。AOEはアナログ出力有効の場合、1を指定します。AISEL1とAISEL0で、どのようにアナログ入力ピンを使用するか指定します。シングルエンド(方線接地)入力×4とする場合は0b00、ディファレンシャル(差動)入力×3とする場合は0b01、シングルエンド入力×2+ディファレンシャル入力×1とする場合は0b10、ディファレンシャル入力×2とする場合は0b11となります。AINCはオートインクリメント有効の時、1を指定します。CH1とCH0にはアナログ入力に使用するチャンネルを指定します。今回の例では、AOE、AISEL1、AISEL0、AINCを全て0とします。

PCF8591通信フォーマット(コントロールバイト)

図2.17 PCF8591通信フォーマット(コントロールバイト)


PCF8591からデータを読み出すと、CHで指定したチャンネルの値を得ることができます。データの読み出しは、図2.18「PCF8591通信フォーマット(データバイト読み出し)」に示すフォーマットでおこないます。

PCF8591通信フォーマット(データバイト読み出し)

図2.18 PCF8591通信フォーマット(データバイト読み出し)


まず、マスタがスタートコンディションを発行し、アドレスバイトを送信します。スレーブはアドレスバイトへのACKを返し、続いてデータバイトを送信します。マスタはデータバイトを受信したあと、続けてデータが欲しい場合はACKを返します。データバイトの受信を終了したい場合はNACKを返し、ストップコンディションを発行します。

アドレスバイトのフォーマットは、R/W*が1になる以外、コントロールバイト書き込みの場合と同じです。

データバイトのフォーマットを図2.19「PCF8591通信フォーマット(データバイト)」に示します。PCF8591では、アクノリッジ信号のトレイリングエッジで、指定されたチャンネルの入力電圧がサンプルされ、データバイトの送信中にA/D変換がおこなわれます。そのため、データバイトで転送される値は、一つ前のデータバイト送信中に変換された値となります。なお、パワーオンリセット後の最初のデータバイトで転送される値は、0x80となります。

PCF8591通信フォーマット(データバイト)

図2.19 PCF8591通信フォーマット(データバイト)


2.2.4. i2cdevドライバー

一般的に、I2C接続のデバイスをLinuxシステムで使用する場合、I2Cスレーブデバイス用のデバイスドライバーを作成して、デバイスの制御をおこないます。今回は、PCF8591専用のデバイスドライバーを作成するのではなく、汎用のi2cdevドライバーを使用することにします。i2cdevドライバーを使用すると、デバイスファイルインターフェースを経由して、ユーザーランドで動作するアプリケーションプログラムからデバイスの制御をおこなうことができます。Armadillo-400シリーズでは、標準のカーネルでi2cdevドライバーが有効になっているため、特に何も設定しなくとも使用可能です。

i2cdevドライバーでは、/dev/i2c-N(Nは0から始まる数値文字)デバイスファイルに対して、open/close/read/write/ioctlシステムコールを発行することで、I2Cデバイスの制御をおこないます。Armadillo-400シリーズで使用できるデバイスファイルを表2.1「I2Cバスとデバイスファイルの対応」に示します。

表2.1 I2Cバスとデバイスファイルの対応

I2Cバス コネクタ デバイスファイル

I2C1

なし[a]

/dev/i2c-0

I2C2

CON14

/dev/i2c-1

I2C3

CON11

/dev/i2c-2

[a] ボード内蔵バスとして使用。


アプリケーションプログラムでI2Cデバイスの制御をおこなうには、まず、デバイスファイルをオープンします。

int fd;

fd = open("/dev/i2c-0", O_RDWR);

図2.20 I2Cデバイスファイルのオープン


デバイスをオープンした後、通信を行うスレーブデバイスを特定するため、スレーブデバイスのアドレスを設定します。これには、ioctlシステムコールを使用します。I2C_SLAVEなどのi2cdevを使用する際に必要となる定義は、<linux/i2c-dev.h>で定義されています。

#include <linux/i2c-dev.h>

int addr = 0x40; /* The I2C address */

ioctl(fd, I2C_SLAVE, addr);

図2.21 I2Cスレーブデバイスのアドレス指定


I2Cスレーブデバイスとのデータ転送をおこなうには、単純にread/writeシステムコールを実行するだけです。スタート/ストップコンディションの発行、アドレスバイトの生成と送信、アクノリッジの処理などは、全てドライバーがおこなってくれるので、アプリケーションプログラム側ではそれらを意識する必要はありません。

i2cdevドライバーに関する詳しい情報は、linux-2.6.26-at19/Documentation/i2c/dev-interfaceを参照してください。

2.2.5. サンプルプログラム

PCF8591と通信をおこない、A/D変換結果を表示するサンプルプログラムを紹介します。プログラムは図2.22「PCF8591を使用したA/D変換プログラム」に示すように、オプションとしてデバイスファイル名とA/D変換をおこなうチャンネルを指定することにします。

adc_pcf8591 <-d|--device FILENAME> [-c|--channel CHANNEL]

図2.22 PCF8591を使用したA/D変換プログラム


main関数を図2.23「adc_pcf8591.c」に示します。pcf8591_で始まる名前の関数で、実際の制御をおこないます。main関数では、以下の処理をおこなっています。

  1. pcf8591_open()で、デバイスファイル名とアドレスを指定してデバイスをオープンする
  2. pcf8591_read()で、チャンネルを指定してデジタル値を読み出す
  3. デジタル値を電圧に変換して表示
  4. pcf8591_close()で、デバイスをクローズする
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>

#include "pcf8591.h"

#define MAIN_C
#include "exitfail.h"

#define BASENAME(p)   ((strrchr((p), '/') ? : ((p) - 1)) + 1)

#define REF_VOLTAGE     3.3     /* 基準電圧[V] */
#define I2C_ADDR        0x48    /* PCF8591のI2Cアドレス */
#define DEFAULT_CH      0       /* オプションで指定されなかった場合に使用するチャンネル */

static void usage(char *prog)
{
        printf("Usage: %s <-d|--device FILENAME> [-c|--channel CHANNEL]\n", BASENAME(prog));
}

static void parse_arg(int argc, char *argv[], char **device, int *ch)
{
        int c;
        char *endptr;

        *device = NULL;

        for(;;) {
                int option_index = 0;
                static struct option long_options[] = {
                        /* name,        has_arg,           flag, val*/
                        {"device",      required_argument, NULL, 'd'},
                        {"channel",     required_argument, NULL, 'c'},
                        {0,             0,                 0,    0},
                };

                c = getopt_long(argc, argv, "d:c:",
                                long_options, &option_index);
                if (c == -1)
                        break;

                switch (c) {
                case 'd':
                        *device = optarg;
                        break;
                case 'c':
                        errno = 0;
                        *ch = strtol(optarg, &endptr, 0);
                        if (errno != 0 || optarg == endptr)
                                goto err;

                        if (*ch < PCF8591_CH_MIN || PCF8591_CH_MAX < *ch)
                                goto err;
                        break;
                default:
                        goto err;
                }
        }

        if (*device == NULL)
                goto err;

        return;

err:
        usage(argv[0]);
        exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
        struct pcf8591 *adc;
        char *device;
        int channel = DEFAULT_CH;
        double voltage;
        uint8_t digital_code;
        int ret;

        exitfail_init();

        parse_arg(argc, argv, &device, &channel);

        adc = pcf8591_open(device, I2C_ADDR);
        if (adc == NULL)
                exitfail_errno("pcf8591_open");

        ret = pcf8591_read(adc, channel, &digital_code);
        if (ret != 0)
                exitfail_errno("pcf8591_read");

        voltage = digital_code * REF_VOLTAGE /
                  ((1 << PCF8591_RESOLUTION_BITS) - 1);

        printf("%1.3fV\n", voltage);

        ret = pcf8591_close(adc);
        if (ret != 0)
                exitfail_errno("pcf8591_close");

        return EXIT_SUCCESS;
}

図2.23 adc_pcf8591.c


実際の処理は図2.25「pcf8591.c」に記述してあります。図2.24「pcf8591.h」には、図2.25「pcf8591.c」で記述されている関数のプロトタイプ宣言が記述されています。

#ifndef PCF8591_H
#define PCF8591_H

#include <stdint.h>

#define PCF8591_RESOLUTION_BITS 8   /* PCF8591の分解能(bit) */

#define PCF8591_CH_MIN 0 /* PCF8591で指定できるアナログ入力チャンネルの最小値 */
#define PCF8591_CH_MAX 3 /* PCF8591で指定できるアナログ入力チャンネルの最大値 */

struct pcf8591;

struct pcf8591 *pcf8591_open(const char *dev_path, int addr);
int pcf8591_read(struct pcf8591 *adc, int ch, uint8_t *digit);
int pcf8591_close(struct pcf8591 *adc);

#endif /* PCF8591_H */

図2.24 pcf8591.h


#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <linux/i2c-dev.h>

#include "pcf8591.h"

#define PCF8591_ADDR_MIN 0x48 /* PCF8591のアドレスの最小値 */
#define PCF8591_ADDR_MAX 0x4F /* PCF8591のアドレスの最大値 */

#define PCF8591_RETRY_MAX 3 /* read()とwrite()のリトライ回数 */

/* PCF8591のCONTROL BYTEの設定 */
#define PCF8591_ANALOG_OUTPUT_ENABLE          (1 << 6)
#define PCF8591_ANALOG_OUTPUT_DISABLE         (0 << 6)

#define PCF8591_SINGLE_ENDED                  (0 << 4)
#define PCF8591_THREE_DIFFERENTIAL            (1 << 4)
#define PCF8591_SINGLE_ENDED_AND_DIFFERENTIAL (2 << 4)
#define PCF8591_TWO_DIFFERENTIAL              (3 << 4)

#define PCF8591_AUTO_INCREMENT_ENABLE         (1 << 2)
#define PCF8591_AUTO_INCREMENT_DISABLE        (0 << 2)

#define PCF8591_CH_SHIFT                      (0)


struct pcf8591 {
        int fd;
};

static ssize_t write_uninterruptible(int fd, const void *buf, size_t count,
                                     int retry)
{
        ssize_t ret;
        int i;

        for (i = 0; i < retry; i++) {
                ret = write(fd, buf, count);
                if (ret < 0 && errno != EINTR)
                        return -1;

                if ((size_t)ret == count)
                        return ret;
        }

        errno = ETIMEDOUT;
        return -1;
}

static ssize_t read_uninterruptible(int fd, void *buf, size_t count, int retry)
{
        size_t read_length = 0;
        ssize_t ret;
        int i;

        for (i = 0; i < retry; i++) {
                ret = read(fd, buf + read_length, count - read_length);
                if (ret < 0) {
                        if (errno == EINTR)
                                continue;
                        else
                                return -1;
                }

                read_length += ret;
                if (read_length == count)
                        return read_length;
        }

        errno = ETIMEDOUT;
        return -1;
}

/**
 * 指定されたデバイスファイルをオープンする
 *
 * @param dev_path  PCF8591が接続されたi2cdevのデバイスファイルへのパス。
 * @param addr      PCF8591のアドレス。
 *
 * @return 成功するとstruct pcf8591へのポインタを返す。
 *         失敗するとNULLを返す。その際、適切なerrnoを設定する。
 */
struct pcf8591 *pcf8591_open(const char *dev_path, const int addr)
{
        struct pcf8591 *adc;
        int error;
        int ret;

        if (dev_path == NULL ||
            addr < PCF8591_ADDR_MIN || PCF8591_ADDR_MAX < addr) {
                errno = EINVAL;
                return NULL;
        }

        adc = calloc(1, sizeof(struct pcf8591));
        if (adc == NULL)
                return NULL;

        adc->fd = open(dev_path, O_RDWR);
        if (adc->fd < 0) {
                error = errno;
                goto err1;
        }

        ret = ioctl(adc->fd, I2C_SLAVE, addr);
        if (ret < 0) {
                error = errno;
                goto err2;
        }

        return adc;

err2:
        close(adc->fd);
err1:
        free(adc);
        errno = error;
        return NULL;
}

/**
 * 指定されたチャンネルの値をA/Dコンバータにセットし、
 * PCF8591からA/D変換結果を読み込む。
 * 読み込んだA/D変換結果をdigitに格納する。
 *
 * @param adc    オープン済みのPCF8591デバイスへのポインタ。
 * @param ch     サンプリングするチャンネル。
 * @param digit  A/D変換された値が格納される。
 *
 * @return 成功すると0を返し、digitにA/D変換された値を格納する。
 *         失敗すると-1を返す。その際、適切なerrnoを設定する。
 */
int pcf8591_read(struct pcf8591 *adc, const int ch, uint8_t *digit)
{
        uint8_t buf[2];
        int ret;

        if (adc == NULL || digit == NULL ||
            ch < PCF8591_CH_MIN || PCF8591_CH_MAX < ch) {
                errno = EINVAL;
                return -1;
        }

        buf[0] = PCF8591_ANALOG_OUTPUT_DISABLE |
                 PCF8591_SINGLE_ENDED |
                 PCF8591_AUTO_INCREMENT_DISABLE |
                 (ch << PCF8591_CH_SHIFT);

        ret = write_uninterruptible(adc->fd, buf, 1, PCF8591_RETRY_MAX);
        if (ret < 0)
                return -1;

        /* 現在のアナログ入力を変換した値を取得するために、2バイト読み込む */
        ret = read_uninterruptible(adc->fd, buf, 2, PCF8591_RETRY_MAX);
        if (ret < 0)
                return -1;

        *digit = buf[1];

        return 0;
}

/**
 * 指定されたPCF8591デバイスをクローズする
 *
 * @param adc オープン済みのPCF8591デバイスへのポインタ。
 *
 * @return 成功すると0を返す。
 *         失敗すると-1を返す。その際、適切なerrnoを設定する。
 */
int pcf8591_close(struct pcf8591 *adc)
{
        int fd;

        if (adc == NULL) {
                errno = EINVAL;
                return -1;
        }

        fd = adc->fd;
        free(adc);

        return close(fd);
}

図2.25 pcf8591.c


サンプルプログラムをビルドするmakefileを図2.26「adc_pcf8591をビルドするmakefile」に示します。

CROSS   := arm-linux-gnueabi

ifneq ($(CROSS),)
CROSS_PREFIX    := $(CROSS)-
endif

CC      = $(CROSS_PREFIX)gcc
CFLAGS  = -Wall -Wextra -O2 -I../common

TARGET  = adc_pcf8591

all: $(TARGET)

adc_pcf8591: adc_pcf8591.o pcf8591.o
        $(CC) $(CFLAGS) -o $@ $^

clean:
        $(RM) *~ *.o $(TARGET)

%.o: %.c
        $(CC) $(CFLAGS) -c -o $@ $<

図2.26 adc_pcf8591をビルドするmakefile


adc_pcf8591.cpcf8591.hpcf8591.cMakefileを同じディレクトリに置き、一つ上のcommonディレクトリに第2部でも使用したexitfail.hを置いておきます。makeコマンドを実行すると、adc_pcf8591がビルドされます。

[ATDE ~/i2c-adc]$ make
arm-linux-gnueabi-gcc -Wall -Wextra -O2 -I../common -c -o adc_pcf8591.o adc_pcf8591.c
arm-linux-gnueabi-gcc -Wall -Wextra -O2 -I../common -c -o pcf8591.o pcf8591.c
arm-linux-gnueabi-gcc -Wall -Wextra -O2 -I../common -o adc_pcf8591 adc_pcf8591.o pcf8591.o

図2.27 adc_pcf8591のビルド


生成されたadc_pcf8591をArmadilloにコピーして、実行権限をつけてください。adc_pcf8591を実行すると、図2.28「adc_pcf8591コマンドの実行」に示すような結果が得られます。

[armadillo ~]# chmod +x adc_pcf8591
[armadillo ~]# ./adc_pcf8591 --device /dev/i2c-1 --channel 0
3.235V

図2.28 adc_pcf8591コマンドの実行


2.3. SPI接続A/Dコンバーター

Armadillo-400 シリーズでは、CON9をSPIバスとして使用することができます。ここでは、SPIバスにA/Dコンバータを接続する方法を紹介します。

使用するソフトウェア、デバイスは以下のとおりです。

  1. Linuxカーネル: linux-2.6.26-at19
  2. A/Dコンバーター: MCP3204(Microchip社製)

2.3.1. SPI概要

SPI(Serial Peripheral Interface)は、IC間のデータ転送に使われる4線式の通信方式です。SPIでは、シリアルクロック(SCLK)、マスタアウトプット/スレーブインプット(MOSI)、マスタインプット/スレーブアウトプット(MISO)、スレーブセレクト(SS)の4本の信号線を使用して通信をおこないます。これらの信号線に、複数のデバイスを接続してバスを構成することができます。

SPIバスに接続されたデバイスは、その役割によってマスタとスレーブに分かれます。SPIでは、1つのバスに1つのマスタと複数のスレーブを接続できます。通信は必ずマスタが開始し、バスに接続されたスレーブとデータのやりとりを行います。マスタは、通信をおこなうスレーブをSS信号によって特定します。そのため、通常、スレーブ1つにつき1本のSS信号を接続します。Armadillo-400シリーズは、SPIマスタとなることができます。

SPIでは、1クロックにつき1bitのデータ転送をおこないます。そのため、データ転送速度はクロックの速度によって決まります。Armadillo-400シリーズは、約16MHzまで対応できます。

データの転送は、マスタがSS信号をアサートすることで開始されます。SCLK信号に同期して、マスターからスレーブへのデータをMOSIに出力し、スレーブからマスタへのデータをMISOから入力します。データの入出力をおこなう線が分かれているため、全二重の通信をおこなうことができます。一度の転送で送受信できるビット数はデバイスごとに異なり、SPIプロトコルとしての制限はありません。

SPIでは、クロックのポラリティとフェーズを指定できます。それぞれの設定をCPOLとCPHAで表します。

  1. CPOL=0: クロックを出力していないときSCLKをLowに保ちます。

    1. CPHA=0: クロックの立ち上がりでデータをラッチします。
    2. CPHA=1: クロックの立ち下がりでデータをラッチします。
  2. CPOL=1: クロックを出力していないときSCLKをHighに保ちます。

    1. CPHA=0: クロックの立ち下がりでデータをラッチします。
    2. CPHA=1: クロックの立ち上がりでデータをラッチします。

CPOLとCPHAの組み合わせを、SPIモードで表現する場合もあります。

表2.2 SPIモード

モード CPOL CPHA

0

0

0

1

0

1

2

1

0

3

1

1


SPIプロトコル

図2.29 SPIプロトコル


2.3.2. サンプル回路

今回使用するA/DコンバーターMCP3204は、以下の特長を持ちます。

  1. 単一電源(2.7Vから5.5V)動作
  2. SPI接続
  3. サンプリング速度 最大100ksps(Vdd=5V時)、50ksps(Vdd=2.7V時)
  4. オンチップ サンプルアンドホールド
  5. 12bit分解能逐次比較型A/D入力×4

Armadillo-400シリーズと、A/Dコンバーターとの接続を図2.30「SPI接続A/Dコンバーター回路図」に示します。Armadillo-400シリーズのCON9から出ているCSPI3にMCP3204を接続します。SS信号には、CSPI3のSS0を使用します。AIN0からAIN3ピンがアナログ入力ピンです。アナログ入力にかかる電圧を、20kΩの可変抵抗で変えられるようにしています。リファレンス電圧VREFに電源電圧と同じ3.3Vを入力しているため、0Vから3.3Vの範囲のアナログ入力を12bit(4096段階)のデジタル値に変換します。

SPI接続A/Dコンバーター回路図

図2.30 SPI接続A/Dコンバーター回路図


2.3.3. MCP3204通信プロトコル

MCP3204は、SPIモード0(CPOL=0、CPHA=0)またはSPIモード3(CPOL=1、CPHA=1)で通信をおこないます。MCP3204の通信フォーマットを図2.31「MCP3204通信フォーマット」に示します。

MCP3204通信フォーマット

図2.31 MCP3204通信フォーマット


MOSIから1を出力することで、転送の開始をMCP3204に指示します。SGL/DIFF*、D2、D1、D0の組み合わせにより、A/D変換をおこなうチャンネルを指定します。D0以降、MOSIから出力されるデータは意味を持ちません。

表2.3 MCP3204チャンネル指定

SGL/DIFF* D2[a] D1 D0 入力構成 チャンネル

1

d.c.

0

0

シングルエンド

CH0

1

d.c.

0

1

シングルエンド

CH1

1

d.c.

1

0

シングルエンド

CH2

1

d.c.

1

1

シングルエンド

CH3

0

d.c.

0

0

ディファレンシャル

CH0=IN+、CH1=IN-

0

d.c.

0

1

ディファレンシャル

CH0=IN-、CH1=IN+

0

d.c.

1

0

ディファレンシャル

CH2=IN+、CH3=IN-

0

d.c.

1

1

ディファレンシャル

CH2=IN-、CH3=IN+

[a] MCP3204ではD2は意味を持ちません。


MCP3204は、11番目のクロックの上昇部でアナログ入力のサンプリングを開始し、次のクロックの下降部で完了します。A/D変換結果は、B11からB0に出力されます。

2.3.4. spidevドライバー

一般的に、SPI接続のデバイスをLinuxシステムで使用する場合、SPIスレーブデバイス用のデバイスドライバーを作成して、デバイスの制御をおこないます。今回は、MCP3204専用のデバイスドライバーを作成するのではなく、汎用のspidevドライバーを使用することにします。spidevドライバーを使用すると、デバイスファイルインターフェースを経由して、ユーザーランドで動作するアプリケーションプログラムからデバイスの制御をおこなうことができます。Armadillo-400シリーズでは、標準のカーネルではspidevドライバーが有効になっていないため、spidevを使用するにはカーネルの設定を変更する必要があります。

spidevドライバーでは、/dev/spidevN.M (NM は0から始まる数値文字)デバイスファイルに対して、open/close/read/write/ioctlシステムコールを発行することで、SPIデバイスの制御をおこないます。Armadillo-400シリーズで使用できるデバイスファイルを表2.4「SPIバスとデバイスファイルの対応」に示します。

表2.4 SPIバスとデバイスファイルの対応

SPIバス コネクタ デバイスファイル

CSPI1

CON9

/dev/spidev0.M (Mは0または1)

CSPI3

CON9

/dev/spidev2.M (Mは0、1、2、3のいずれか)


アプリケーションプログラムでSPIデバイスの制御をおこなうには、まず、デバイスファイルをオープンします。

int fd;

fd = open("/dev/spidev0.0", O_RDWR);

図2.32 SPIデバイスファイルのオープン


ioctlシステムコールにより、SPIの設定を変更することができます。

  1. SPI_IOC_RD_MODE, SPI_IOC_WR_MODE: 読み出しまたは書き込み時に使用するSPIモードを設定します。
  2. SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST: 読み出しまたは書き込み時にLSBから転送するか、MSBから転送するか設定します。
  3. SPI_IOC_RD_BITS_PER_WORD, SPI_IOC_WR_BITS_PER_WORD: 1回の読み出しまたは書き込みで転送するビット数を設定します。
  4. SPI_IOC_RD_MAX_SPEED_HZ, SPI_IOC_WR_MAX_SPEED_HZ: 読み出しまたは書き込みの最大転送速度を設定します。

read/writeシステムコールを使用すると、半二重通信をおこなうことができます。全二重通信をおこなうには、ioctlシステムコールのSPI_IOC_MESSAGE(N)メッセージを使用します。SPI_IOC_MESSAGE(N)の使用方法は、サンプルプログラムで解説します。

spidevドライバーに関する詳しい情報は、linux-2.6.26-at19/Documentation/spi/spidev を参照してください。

2.3.5. カーネルコンフィギュレーション

Armadillo-400シリーズの標準のカーネルでは、SPIドライバーは有効になっていません。そのため、SPIマスタドライバーとspidevドライバーが有効になったカーネルを作成する必要があります。

まず、カーネルのソースコードアーカイブを取得します。ここでは、Armadilloサイトからダウンロードしてくることにします。

図2.33 Linuxカーネルの取得と展開


次にArmadillo-400シリーズの標準コンフィギュレーションを適用します。

[ATDE ~]$ cd linux-2.6.26-at19/
[ATDE ~/linux-2.6.26-at19]$ make armadillo400_defconfig (Armadillo-410/420/440の場合)
[ATDE ~/linux-2.6.26-at19]$ make armadillo460_defconfig (Armadillo-460の場合)

図2.34 LinuxカーネルにArmadillo-400シリーズ標準コンフィギュレーションを適用する


続いて、menuconfigを使用して、図2.35「SPIドライバーを有効にする」及び図2.36「SPIに使用するピンを指定する」に示すようにカーネルコンフィギュレーションを変更します。

Linux Kernel Configuration
  Device Drivers  --->
    [*] SPI support  --->                        チェックを入れる
      -*-   Bitbanging SPI master                自動で選択される
      <*>   MXC CSPI controller as SPI Master    チェックを入れる
      <*>   User mode SPI device driver support  チェックを入れる

図2.35 SPIドライバーを有効にする


Linux Kernel Configuration
  System Type  --->
    Freescale MXC Implementations  --->
      MX25 Options  --->
        Armadillo-400 Board options  --->
          [ ] Enable UART5 at CON9           チェックを外す
          [*] Enable SPI3 at CON9            チェックを入れる
          [*]   Enable SPI3_SS0 at CON9_16   標準で選択されているのでそのまま
          [ ]   Enable SPI3_SS1 at CON9_18   チェックを外す
          [ ]   Enable SPI3_SS2 at CON9_15   チェックを外す
          [ ]   Enable SPI3_SS3 at CON9_17   チェックを外す

図2.36 SPIに使用するピンを指定する


また、カーネルのソースコードにも一部修正が必要になります。SPIスレーブデバイスドライバーを使用するには、spi_board_infoを登録する必要があります。spi_board_infoの登録は、linux-2.6.26-at19/arch/arm/mach-mx25/armadillo400.cでおこなっています。armadillo400_spi3_board_infoを、図2.37「CSPI3、SS0をspidevで使用する修正」に示すように修正してください。

static struct spi_board_info armadillo400_spi3_board_info[] __initdata = {
        {
                .modalias = "spidev",
                .max_speed_hz = 1000000,
                .bus_num = 3,
                .chip_select = 0,
        },
};

図2.37 CSPI3、SS0をspidevで使用する修正


コンフィギュレーションの変更と、ソースの修正をおこなったら、カーネルをビルドします。

[ATDE ~/linux-2.6.26-at19]$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- && gzip -c arch/arm/boot/Image > linux.bin.gz

図2.38 Linuxカーネルをビルドする


正常にビルドが完了すると、linux-2.6.26-at19/linux.bin.gzにカーネルイメージが作成されます。linux.bin.gzをArmadilloのフラッシュメモリのカーネル領域に書き込んでください。

2.3.6. サンプルプログラム

MCP3204と通信をおこない、A/D変換結果を表示するサンプルプログラムを紹介します。プログラムは図2.39「MCP3204を使用したA/D変換プログラム」に示すように、オプションとしてデバイスファイル名とA/D変換をおこなうチャンネルを指定することにします。

adc_mcp3204 <-d|--device FILENAME> [-c|--channel CHANNEL]

図2.39 MCP3204を使用したA/D変換プログラム


main関数を図2.40「adc_mcp3204.c」に示します。mcp3204_というプレフィックスがついた関数で、実際の制御をおこないます。main関数では、以下の処理をおこなっています。

  1. mcp3204_open()で、デバイスファイル名を指定してデバイスをオープンする
  2. mcp3204_read()で、チャンネルを指定してデジタル値を読み出す
  3. デジタル値を電圧に変換して表示
  4. mcp3204_close()で、デバイスをクローズする
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>

#include "mcp3204.h"

#define MAIN_C
#include "exitfail.h"

#define BASENAME(p)   ((strrchr((p), '/') ? : ((p) - 1)) + 1)

#define REF_VOLTAGE     3.3     /* 基準電圧[V] */
#define DEFAULT_CH      0       /* オプションで指定されなかった場合に使用するチャンネル */

static void usage(const char *prog)
{
        printf("usage: %s <-d|--device FILENAME> [-c|--channel CHANNEL]\n", BASENAME(prog));
}

static void parse_arg(int argc, char *argv[], const char **device, int *ch)
{
        int c;
        char *endptr;

        *device = NULL;

        for(;;) {
                int option_index = 0;
                static struct option long_options[] = {
                        /* name,        has_arg,           flag, val*/
                        {"device",      required_argument, NULL, 'd'},
                        {"channel",     required_argument, NULL, 'c'},
                        {0,             0,                 0,    0},
                };

                c = getopt_long(argc, argv, "d:c:",
                                long_options, &option_index);
                if (c == -1)
                        break;

                switch (c) {
                case 'd':
                        *device = optarg;
                        break;
                case 'c':
                        errno = 0;
                        *ch = strtol(optarg, &endptr, 0);
                        if (errno != 0 || optarg == endptr)
                                goto err;

                        if (*ch < MCP3204_CH_MIN || MCP3204_CH_MAX < *ch)
                                goto err;
                        break;
                default:
                        goto err;
                }
        }

        if (*device == NULL)
                goto err;

        return;

err:
        usage(argv[0]);
        exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
        struct mcp3204 *adc;
        const char *device;
        int channel = DEFAULT_CH;
        double voltage;
        uint16_t digital_code;
        int ret;

        exitfail_init();

        parse_arg(argc, argv, &device, &channel);

        adc = mcp3204_open(device);
        if (adc == NULL)
                exitfail_errno("mcp3204_open");

        ret = mcp3204_read(adc, channel, &digital_code);
        if (ret != 0)
                exitfail_errno("mcp3204_read");

        voltage = digital_code * REF_VOLTAGE /
                ((1 << MCP3204_RESOLUTION_BITS) - 1);

        printf("%1.3fV\n", voltage);

        ret = mcp3204_close(adc);
        if (ret != 0)
                exitfail_errno("mcp3204_close");

        return EXIT_SUCCESS;
}

図2.40 adc_mcp3204.c


実際の処理は図2.42「mcp3204.c」に記述してあります。図2.41「mcp3204.h」には、図2.42「mcp3204.c」で記述されている関数のプロトタイプ宣言が記述されています。

#ifndef MCP3204_H
#define MCP3204_H

#include <stdint.h>

#define MCP3204_RESOLUTION_BITS 12 /* MCP3204の分解能(bit) */

#define MCP3204_CH_MIN 0 /* MCP3204で指定できるアナログ入力チャンネルの最小値 */
#define MCP3204_CH_MAX 3 /* MCP3204で指定できるアナログ入力チャンネルの最大値 */

struct mcp3204;

struct mcp3204 *mcp3204_open(const char *dev_path);
int mcp3204_read(struct mcp3204 *adc, int ch, uint16_t *digit);
int mcp3204_close(struct mcp3204 *adc);

#endif /* MCP3204_H */

図2.41 mcp3204.h


#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>

#include <linux/types.h>
#include <linux/spi/spidev.h>

#include "mcp3204.h"

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

#define MCP3204_START_BIT    (1 << 2)
#define MCP3204_SINGLE_ENDED (1 << 1)
#define MCP3204_DIFFERENTIAL (0 << 1)
#define MCP3204_CH_SHIFT     (6)

#define MCP3204_SPI_MODE        (SPI_CPOL | SPI_CPHA)
#define MCP3204_SPI_SPEED_HZ    1000000
#define MCP3204_SPI_DELAY_USECS 0
#define MCP3204_SPI_BITS        8

struct mcp3204 {
        int fd;
};

/**
 * 指定されたデバイスファイルをオープンする
 *
 * @param dev_path  MCP3204が接続されたspidevデバイスファイルへのパス。
 *
 * @return 成功するとstruct mcp3204へのポインタを返す。
 *         失敗するとNULLを返す。その際、適切なerrnoを設定する。
 */
struct mcp3204 *mcp3204_open(const char *dev_path)
{
        struct mcp3204 *adc;
        uint8_t mode = MCP3204_SPI_MODE;
        int error;
        int ret;

        if (dev_path == NULL) {
                errno = EINVAL;
                return NULL;
        }

        adc = calloc(1, sizeof(struct mcp3204));
        if (adc == NULL)
                return NULL;

        adc->fd = open(dev_path, O_RDWR);
        if (adc->fd < 0) {
                error = errno;
                goto err1;
        }

        /* SPIモードを設定する */
        ret = ioctl(adc->fd, SPI_IOC_WR_MODE, &mode);
        if (ret < 0) {
                error = errno;
                goto err2;
        }

        return adc;

err2:
        close(adc->fd);
err1:
        free(adc);
        errno = errno;
        return NULL;
}

/**
 * 指定されたチャンネルのA/D変換結果を読み込む。
 * 読み込んだA/D変換結果をdigitに格納する。
 *
 * @param adc     オープン済みのMCP3204デバイスへのポインタ。
 * @param ch      サンプリングするチャンネル。
 * @param digit   A/D変換結果のデジタル値が格納される。
 *
 * @return 成功すると0を返し、voltageに電圧を格納する。
 *         失敗すると-1を返す。その際、適切な errno を設定する。
 */
int mcp3204_read(struct mcp3204 *adc, int ch, uint16_t *digit)
{
        uint8_t tx[3] = {0, };
        uint8_t rx[3] = {0, };
        struct spi_ioc_transfer tr;
        int ret;

        if (adc == NULL || digit == NULL ||
            ch < MCP3204_CH_MIN || MCP3204_CH_MAX < ch) {
                errno = EINVAL;
                return -1;
        }

        /* 送信バッファにスタートビット、SGL/DIFF*、D2、D1、D0をセットする */
        tx[0]            = MCP3204_START_BIT | MCP3204_SINGLE_ENDED;
        tx[1]            = ch << MCP3204_CH_SHIFT;

        /* 転送設定をセットする */
        tr.tx_buf        = (unsigned long)tx;
        tr.rx_buf        = (unsigned long)rx;
        tr.len           = ARRAY_SIZE(tx);
        tr.delay_usecs   = MCP3204_SPI_DELAY_USECS;
        tr.speed_hz      = MCP3204_SPI_SPEED_HZ;
        tr.bits_per_word = MCP3204_SPI_BITS;
        tr.cs_change     = 0;

        /* 全二重通信をおこなう */
        ret = ioctl(adc->fd, SPI_IOC_MESSAGE(1), &tr);
        if (ret < 1)
                return -1;

        /* 受信バッファからA/D変換結果を取り出す */
        *digit = (rx[1] & 0x0f) << 8;
        *digit |= rx[2];

        return 0;
}

/**
 * 指定されたMCP3204デバイスをクローズする
 *
 * @param adc オープン済みのMCP3204デバイスへのポインタ。
 *
 * @return 成功すると0を返す。
 *         失敗すると-1を返す。その際、適切なerrnoを設定する。
 */
int mcp3204_close(struct mcp3204 *adc)
{
        int fd;

        if (adc == NULL) {
                errno = EINVAL;
                return -1;
        }

        fd = adc->fd;
        free(adc);

        return close(fd);
}

図2.42 mcp3204.c


サンプルプログラムをビルドするmakefileを図2.43「adc_mcp3204をビルドするmakefile」に示します。

CROSS   := arm-linux-gnueabi

ifneq ($(CROSS),)
CROSS_PREFIX    := $(CROSS)-
endif

CC      = $(CROSS_PREFIX)gcc
CFLAGS  = -Wall -Wextra -O2  -I../common
LFLAGS  =

TARGET  = adc_mcp3204

all: $(TARGET)

adc_mcp3204: adc_mcp3204.o mcp3204.o
        $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@

clean:
        $(RM) *~ *.o $(TARGET)

%.o: %.c
        $(CC) $(CFLAGS) -c -o $@ $<

図2.43 adc_mcp3204をビルドするmakefile


adc_mcp3204.cmcp3204.hmcp3204.cMakefileを同じディレクトリに置き、一つ上のcommonディレクトリに第2部でも使用したexitfail.hを置いておきます。makeコマンドを実行すると、adc_mcp3204がビルドされます。

[ATDE ~/spi-adc]$ make
arm-linux-gnueabi-gcc -Wall -Wextra -O2 -I../common -c -o adc_mcp3204.o adc_mcp3204.c
arm-linux-gnueabi-gcc -Wall -Wextra -O2 -I../common -c -o mcp3204.o mcp3204.c
arm-linux-gnueabi-gcc -Wall -Wextra -O2 -I../common -o adc_mcp3204 adc_mcp3204.o mcp3204.o

図2.44 adc_mcp3204のビルド


生成されたadc_mcp3204をArmadilloにコピーして、実行権限をつけてください。adc_mcp3204を実行すると、図2.45「adc_mcp3204コマンドの実行」に示すような結果が得られます。

[armadillo ~]# chmod +x adc_mcp3204
[armadillo ~]# ./adc_mcp3204 --device /dev/spidev3.0 --channel 0
3.235V

図2.45 adc_mcp3204コマンドの実行


2.4. 1-Wire接続温度センサ

Armadillo-400シリーズでは、CON9 2ピンとCON9 26ピンを1-Wireバスとして使用することができます。ここでは、1-Wireバスに温度センサICを接続する方法を紹介します。

使用するソフトウェア、デバイスは以下のとおりです。

  1. Linuxカーネル: linux-2.6.26-at19
  2. 温度センサ: DS18B20(MAXIM社製)

2.4.1. 1-Wire概要

1-Wireは、IC間のデータ転送に使われる1線式の通信方式です。最低限、1本の信号線と接地線の2本だけでバスを構成することができます。このとき、電力は信号線から得ます。なお、電源線を別途用意し、3線で構成することもできます。1-Wireバスに接続されたデバイスの出力段はオープンドレインとし、信号線はプルアップします。

1-Wireバスに接続されたデバイスは、その役割によってマスタとスレーブに分かれます。1-Wireでは、1つのバスに1つのマスタと複数のスレーブを接続できます。通信は必ずマスタが開始し、バスに接続されたスレーブとデータのやりとりを行います。スレーブデバイスは、チップごとに固有な64bitのROM IDを持っており、マスタはROM IDを指定することで通信をおこなうスレーブを特定します。Armadillo-400シリーズは、1-Wireマスタとなることができます。

1-Wireでは、クロック信号がないため、タイムスロットに基づいてデータの転送をおこないます。マスタからスレーブに値を書き込む場合、マスタからローパルスを出力します。パルスの立ち下がりエッジでスレーブ内の単安定マルチバイブレーターが開始し、立ち下がりエッジから約30μsecの時点でサンプリングをおこないます。そのため、マスタは1を書き込む場合は1から15μsecの短いローパルスを出力し、0を書き込む場合は60μsecの長いローパルスを出力します。

スレーブからの値を読み出す場合、まず、マスタがローパルス1から15μsecの短いローパルスを出力します。スレーブ側は、1を送信したい場合何もしません。0を送信したい場合、60μsecの間信号線をローに引っ張ります。マスタは、立ち下がりエッジから30μsecの時点でサンプリングをおこない、スレーブからの出力をサンプリングします。

なお、タイムスロットにはスタンダード(1タイムスロット60μsec)とオーバードライブ(1タイムスロット8μsec)の二つがあります。上記の説明はスタンダードの場合のタイミングです。

1-Wireプロトコル(ビット転送)

図2.46 1-Wireプロトコル(ビット転送)


マスターとスレーブ間でのデータ転送は3つのシーケンスでおこないます。3つのシーケンスは、リセットシーケンス、ROMコマンドシーケンス、ファンクションシーケンスの順番に実行されます。

リセットシーケンスでは、まず、マスタがリセットパルスを出力します。1-Wireバスにスレーブが接続されている場合、スレーブはプレゼンスパルスを出力します。

ROMコマンドシーケンスでは、マスタが8bitのROMコマンドを出力した後、64bitのROM IDを出力します。ROM IDの先頭8bitはデバイス種類を示すファミリーコードです。続く48bitがシリアルナンバーになっています。最後の8bitはCRCです。ROMコマンドには次のものがあります。

  1. SERARCH ROM(0xF0): バスに接続されているスレーブデバイスのROM IDを得ることができます。1回のSERARCH ROMコマンドで1つのデバイスのROM IDを特定することができます。
  2. READ ROM(0x33): バスに接続されているスレーブデバイスが一つだけの場合、SEARCH ROMコマンドの代わりにREAD ROMコマンドを使用して、ROM IDを得ることができます。
  3. MATCH ROM(0x55): MATCH ROMコマンドでマスタが出力したROM IDに一致したスレーブデバイスが、続くファンクションコマンドに応答します。それ以外のデバイスは、次のリセットシーケンスを待ちます。
  4. SKIP ROM(0xcc): SKIP ROMコマンドに続いて送信されたファンクションコマンドは、バスに接続されているスレーブデバイス全てに同時に適用されます。

ファンクションシーケンスは、スレーブデバイスごとに異なります。基本的には、マスタが8bitのフォワードコマンド出力した後、データの読み出しまたは書き込みをおこないます。

1-Wireプロトコル

図2.47 1-Wireプロトコル


2.4.2. DS18B20

今回使用する温度センサDS18B20は、以下の特長を持ちます。

  1. 単一電源(3.0Vから5.5V)動作
  2. 電源は信号線から供給可能
  3. 外部部品不要
  4. 温度計測範囲-55℃から+125℃
  5. 分解能9bitから12bit
  6. 変換時間750msec(最大12bit時)

DS18B20のファンクションコマンドには以下のものがあります。

  1. CONVERT T(0x44): このコマンドにより、温度変換がおこなわれます。変換結果は、DS18B20の内蔵2バイトレジスタに格納されます。
  2. WRITE SCRATCHPAD(0x48): DS18B20の内蔵メモリに書き込みをおこないます。書き込むデータは3バイト長で、TH、TL、Configuration Registerの順番に送信します。
  3. READ SCRATCHPAD(0xbe): DS18B20の内蔵メモリを読み出します。読み出すデータのバイト数は最大9バイトです。途中で、マスタからリセットパルスを送信することで、データの読み出しを中断できます。

DS18B20内蔵レジスタは次のようになっています。

表2.5 DS18B20内蔵レジスタ

バイト内容

0

Temperature Register LSB

1

Temperature Register MSB

2

TH or User Byte 1

3

TL or User Byte 2

4

Configuration Register

5

Reserved (0xff)

6

Reserved

7

Reserved (0x10)

8

CRC


DS18B20の温度センサ分解能は、Configuration Registerの5bit目と6ビット目で決まります。それ以外のConfiguration Registerのビットは内部的に使用され、上書きすることはできません。

表2.6 DS18B20温度センサ分解能

BIT 6BIT 5分解能

0

0

9 bit

0

1

10 bit

1

0

11 bit

1

1

12 bit[a]

[a] パワーオンリセット時の設定


Temperature Registerのフォーマットは次のようになっています。温度は摂氏で格納されています。12ビット分解能の場合は、BIT10からBIT0全てのビットが有効です。11ビット分解能の場合、BIT0が不定となります。10ビット、9ビット分解能の場合も同様です。BIT15からBIT11は、温度が正の場合0、負の場合1となります。

DS18B20 Temperature Registerフォーマット

図2.48 DS18B20 Temperature Registerフォーマット


2.4.3. サンプル回路

Armadillo-400シリーズと温度センサとの接続を、図2.49「1-Wire接続温度センサ回路図」に示します。信号線は、CON9 2ピンに接続します。また、今回はVDDに+3.3Vを接続し電源は外部から供給します。

1-Wire接続温度センサ回路図

図2.49 1-Wire接続温度センサ回路図


2.4.4. 温度センサドライバー

一般的に、1-Wire接続のデバイスをLinuxシステムで使用する場合、1-Wireスレーブデバイス用のデバイスドライバーを作成して、デバイスの制御をおこないます。今回は、1-Wireスレーブデバイスドライバーの「Thermal family implementation」を使用します。

「Thermal family implementation」を使用すると、1-Wireバスに接続された温度センサデバイスを自動で検出し、sysfs経由で温度データの読み出しを可能にします。

[armadillo ~]# cd /sys/devices/w1_bus_master1/
[armadillo /sys/devices/w1_bus_master1]# ls
28-0000022e2355/           w1_master_name
driver@                    w1_master_pointer
power/                     w1_master_search
subsystem@                 w1_master_slave_count
uevent                     w1_master_slaves
w1_master_attempts         w1_master_timeout
w1_master_max_slave_count
[armadillo /sys/devices/w1_bus_master1]# cat 28-0000022e2355/w1_slave
c4 01 4b 46 7f ff 0c 10 3b : crc=3b YES
c4 01 4b 46 7f ff 0c 10 3b t=28250
c3 01 4b 46 7f ff 0d 10 2f : crc=2f YES
c3 01 4b 46 7f ff 0d 10 2f t=28187

図2.50 1-Wire接続温度センサドライバーの使用例


/sys/devices/w1_bus_master1/が、1-Wireに関連するsysfsディレクトリです。「Thermal family implementation」が有効になっていて、スレーブデバイスが検出されると、28-0000022e2355のようにデバイスのROM ID に対応したディレクトリが作成されます。その中のw1_slaveを読み出すと、ドライバーはCONVERT Tコマンドを実行したあとREAD SCRATCHPADコマンドを実行し、DS18B20の内蔵レジスタを表示します。「crc=xx YES」でCRCが一致したことを表します。また、「t=28250」は温度(摂氏)を1000倍した値を示します。1度の読み出しで、2回分の変換結果を表示します。

2.4.5. カーネルコンフィギュレーション

Armadillo-400シリーズの標準のカーネルでは、1-Wireドライバーは有効になっていません。そのため、1-Wireマスタドライバーと「Thermal familyimplementation」ドライバーが有効になったカーネルを作成する必要があります。

まず、カーネルのソースコードアーカイブを取得します。ここでは、Armadilloサイトからダウンロードしてくることにします。

図2.51 Linuxカーネルの取得と展開


次にArmadillo-400シリーズの標準コンフィギュレーションを適用します。

[ATDE ~]$ cd linux-2.6.26-at19/
[ATDE ~/linux-2.6.26-at19]$ make armadillo400_defconfig (Armadillo-410/420/440の場合)
[ATDE ~/linux-2.6.26-at19]$ make armadillo460_defconfig (Armadillo-460の場合)

図2.52 LinuxカーネルにArmadillo-400シリーズ標準コンフィギュレーションを適用する


続いて、menuconfigを使用して、図2.53「1-Wireドライバーを有効にする」に示すようにカーネルコンフィギュレーションを変更します。

Linux Kernel Configuration
  System Type  --->
    Freescale MXC Implementations  --->
      MX25 Options  --->
        Armadillo-400 Board options  --->
          [*] Enable one wire at CON9_2  ←チェックを入れる

  Device Drivers  --->
    <*> Dallas's 1-wire support  --->  ←チェックを入れる
      1-wire Bus Masters  --->
        <*> Freescale MXC driver for 1-wire  ←チェックを入れる
      1-wire Slaves  --->
        <*> Thermal family implementation  ←チェックを入れる

図2.53 1-Wireドライバーを有効にする


コンフィギュレーションの変更をおこなったら、カーネルをビルドします。

[ATDE ~/linux-2.6.26-at19]$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- && gzip -c arch/arm/boot/Image > linux.bin.gz

図2.54 Linuxカーネルをビルドする


正常にビルドが完了すると、linux-2.6.26-at19/linux.bin.gzにカーネルイメージが作成されます。linux.bin.gzをArmadilloのフラッシュメモリのカーネル領域に書き込んでください。

図2.49「1-Wire接続温度センサ回路図」に示すようにArmadilloとDS18B20を接続してからArmadilloを起動し、図2.50「1-Wire接続温度センサドライバーの使用例」に示す手順で動作確認をおこなってください。

2.5. CAN

Armadillo-400 シリーズでは、CON14をCANバスとして使用することができます。ここでは、Armadillo同士をCANで接続する方法を紹介します。

使用するソフトウェア、デバイスは以下のとおりです。

  1. Linuxカーネル: linux-2.6.26-at19
  2. ユーザーランド: Atmark Dist v20131122
  3. CAN通信プログラム: can-utils (Atmark Distに含まれるもの)
  4. CANトランシーバー: AMIS-42673(ON Semiconductor社製)

2.5.1. CAN概要

CAN(Controller Area Network)は、機器間のデータ転送に使われる、2線差動電圧式の通信方式です。差動電圧式を採用しているため耐ノイズ性に優れる点や、エラー検出方法と検出後の動作が明確化されているといった特長から、比較的信頼性の求められるネットワークに用いられます。

CANでは、CAN+とCAN-の2本の信号線間の電圧差を変化させることで通信をおこないます。この2本の信号線に複数のノード(機器)を接続し、バスを構成します。CANの物理的な仕様に関連する規格には、通信速度が125kbpsまでの低速CAN(ISO1159-2)、通信速度125kbpsから1Mbpsの高速CAN(ISO11898-2)など様々なものがあります。一般的にCANのノードは、物理層の処理をおこなうCANトランシーバとその後のデータ処理をおこなうCANコントローラから構成されます。今回の例では、CANコントローラはi.MX25内蔵のFlexCANコントローラを使用し、CANトランシーバにはISO 11898-2に対応したAMIS-42673を使用します。

CANプロトコルでは、CAN+とCAN-間の電圧差を、RS-232C通信のようにあらかじめ決められた通信速度(ビットレート)に従って変化させることで、データの転送をおこないます。転送される各ビットは、ドミナントかリセッシブのいずれかの状態を取ります。高速CANでは、CAN+とCAN-の電圧差がある場合ドミナント、無い場合リセッシブとなります。通常、ドミナントを論理0、リセッシブを論理1として扱います。CANはマルチマスタ構成のため、複数のデバイスが同時に通信をおこない、バス上でデータの衝突がおこる場合があります。この場合、どれか一つのノードがドミナントを出力していた場合、バスの状態はドミナントとなります(ドミナントがリセッシブに対して優先される)。CANでは、この特性を利用して調停をおこないます。

データの転送は、フレームという単位でおこないます。フレームには、表2.7「CANプロトコルフレーム」に示す4つの種類があります。

表2.7 CANプロトコルフレーム

名称 機能

データフレーム

データを送信する。

リモートフレーム

データフレームを要求する。

オーバーロードフレーム

前回のフレーム処理が完了していないことを通知する。

エラーフレーム

エラーが発生したことを通知する。


データフレームとリモートフレームを合わせて、メッセージフレームといいます。CANでは、ノードごとのアドレスというものはなく、その代わりにそれぞれのメッセージが固有なID(識別子、Identifier)を持っています。受信ノードは、IDによって、自分が処理すべきメッセージかどうか判断します。メッセージに含まれるIDの長さによって、メッセージフレームには標準フォーマット(11bit長)と拡張フォーマット(29bit長)の2種類の形式があります。

データフレームの形式を図2.55「CANプロトコル(データフレーム)」に示します。上の線はリセッシブを、下の線はドミナントを意味します。データフレームは、データを送信するノードがバスをドミナントにすることから始まります。これをスタート・オブ・フレーム(SOF)と呼びます。SOFに続き、アービトレーションフィールド(ARBI)、コントロールフィールド(CONT)、データフィールド(DATA)、CRCフィールド(CRC)が順に送信されます。続いて、受信ノードはACKフィールド(ACK)を送信します。最後に、7ビット分バスをリセッシブに保ちエンド・オブ・フレーム(EOF)とします。

CANプロトコル(データフレーム)

図2.55 CANプロトコル(データフレーム)


アービトレーションフィールドは、標準フォーマットか拡張フォーマットかによって異なります。標準フォーマットの場合、11bitのIDを送信したあと、リモート・トランスミッション・リクエスト・ビット(RTR)にドミナントを送信します。拡張フォーマットの場合、11bitのベースID(BASE ID)を送信したあと、代替リモート・リクエスト・ビット(SRR)、アイデンティファイヤ・エクステンション・ビット(IDE)として、2bit分バスをリセッシブに保ちます。続いて、18bitの拡張ID(Ext ID)を送信したあと、RTRにドミナントを送信します。

[注記]CANプロトコルのバリエーション

CANプロトコルには、バージョン2.0Aと2.0Bの2つがあります。プロトコルバージョン2.0Aでは、標準フォーマットしか扱うことができません。もし拡張フォーマットのフレームを受信した場合、エラーフレームを送信します。プロトコルバージョン2.0Bパッシブでは、拡張フォーマットの送信はできませんが、拡張フォーマットを受信しても、無視します。プロトコルバージョン2.0Bアクティブでは、拡張フォーマットの送受信が可能です。

Armadillo-400シリーズは、プロトコルバージョン2.0Bアクティブに対応しています。

コントロールフィールドは、最初の2ビットが予約ビットとなっており、常にドミナントとします。続く4bitのデータ長コード(DLC)に送信するデータのバイト数を送信します。そのため、データフィールドは0から8バイト(64bit)長となります。

CRCフィールドのCRCシーケンス(CRC sequence)には、SOFからデータフィールドまでのCRC(Cyclic Redundancy Check)を送信します。CRCフィールドの区切りを示すCRCデリミタとして、1bit分リセッシブとします。

受信ノードは、受信したメッセージのCRCが一致した場合、ACKスロット(ACK slot)でドミナントを送信します。ACKスロットでバスがドミナントとなることで、送信ノードは少なくとも一つの受信ノードがデータフィールドを正常に受信できたことを確認できます。ACKスロットに続いて、ACKフィールドの区切りを示すACKデリミタ(ACK delimiter)として、1bit分リセッシブとします。

リモートフレームは、データフレームの要求に使用されます。リモートフレームを受信したノードは、リモートフレームで指定されたIDと同じIDのメッセージを返信します。リモートフレームの形式を、図2.56「CANプロトコル(リモートフレーム)」に示します。RTRをリセッシブにして、データフィールドが無い以外、データフレームと同じです。DLCは、リモートフレームへの返信として帰ってくるデータフレームのデータ長と同一にします。

CANプロトコル(リモートフレーム)

図2.56 CANプロトコル(リモートフレーム)


メッセージフレームのアービトレーションフィールドという名前は、このフィールド送信中にバスの調停をおこなうことに由来します。同時に複数のノードがメッセージの送信を開始した場合、バスの衝突が発生します。送信ノードは、各ビットでバスの状態を確認し、もし自身がリセッシブを送信したにも関わらず、バスがドミナントとなっていた場合、以後の送信を中止します。そのため、より小さなIDが優先して送信されます。また、RTRにより、リモートフレームよりもデータフレームが優先されます。

オーバーロードフレームとエラーフレームは、一般にCANコントローラによって自動で処理されます。そのため、ここでは説明を割愛します。

[注記]同期とビット・スタッフィング・ルール

CANでは、ビットレートに従ってデータの送受信をおこなうため、ノードごとのクロックに誤差がある場合、タイミングが少しずつずれていきます。これを補正するため、バスがリセッシブからドミナントへ変化するとき、タイミングの同期をおこないます。

しかし、リセッシブやドミナントだけが続いた場合、この同期が行われないことになります。そこで、ビット・スタッフィング・ルールが適用されます。これは、同じ状態が5bit連続した場合、反対の状態のビット(スタッフビット)を一つ送信するルールです。このルールにより、一定期間内に必ず同期が行われることを保証しています。

なお、ビット・スタッフィング・ルールの処理はCANコントローラで自動で行われるため、ユーザー側は通常それを意識することはありません。

2.5.2. サンプル回路

Armadillo-400シリーズとCANトランシーバーとを接続する回路図を、図2.57「CAN接続回路図」に示します。ArmadilloのCON14から出ているCAN2を使用します。CANトランシーバーには、AMIS-42673(ON Semiconductor社製)を使用します。LM2731YMF(National Semiconductor社製)は、3.3Vから5Vを生成するスイッチングコンバーターです。CON9 2ピン(GPIO3_17)で出力のON/OFFを切り替えることができます。

CAN接続回路図

図2.57 CAN接続回路図


Armadillo-400シリーズ同士を接続する場合は、次のように接続してください。3つ以上のArmadilloを接続しても構いません。

CANバスを介したArmadillo同士の接続

図2.58 CANバスを介したArmadillo同士の接続


2.5.3. CANドライバー

Armadillo-400シリーズのCAN機能は、SocketCANフレームワーク[8]を使用して実装されています。SocketCANでは、通常のネットワークデバイスと同様に、socketインターフェースを用いてデータの送受信を行います。

CAN通信をおこなうプログラムは、TCP/IPなどを用いたネットワークプログラムと同様に記述できます。図2.59「CANソケットのオープン」に、CAN通信用のソケットをオープンし、can0インターフェースに関連付けるコード例を示します。プロトコルファミリーには、PF_CANを指定します。プロトコルには、ローソケットプロトコル(CAN_RAW)または、ブロードキャストマネージャ(BCM)を指定します。bindシステムコールでCANインターフェースとソケットを関連付けます。

int s;
struct sockaddr_can addr;
struct ifreq ifr;

s = socket(PF_CAN, SOCK_RAW, CAN_RAW);

strcpy(ifr.ifr_name, "can0" );
ioctl(s, SIOCGIFINDEX, &ifr);

addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;

bind(s, (struct sockaddr *)&addr, sizeof(addr));

図2.59 CANソケットのオープン


以降の処理は、通常のネットワークプログラムと同様です。CANメッセージの送受信には、read/writeシステムコールや、send/sendto/sendmsgシステムコール、recv/recvfrom/recvmsgシステムコールを使用できます。

SocketCANに関する詳しい情報は、linux-2.6.26-at19/Documentation/networking/can.txtを参照してださい。

SocketCANフレームワークでは、sysfsインターフェースを用いて、CAN通信に関わる設定をおこないます。CAN2を使用する場合、/sys/devices/platform/FlexCAN.1/以下のファイルを使用します。使用可能なsysfsファイルの一覧は、「Armadillo-400シリーズ ソフトウェアマニュアル」の「CAN」を参照してください。

通常のSocketCANフレームワークには無い、Armadillo-400シリーズ独自の拡張として、リモートフレームのサポートを追加しています。set_resframeファイルに、ID#DATAという形式で値を書き込むと、対応するIDのリモートフレームワークを受信した場合、自動でデータフレームを返信します。

2.5.4. カーネルコンフィギュレーション

Armadillo-400シリーズの標準のカーネルでは、CANドライバーは有効になっていません。そのため、CANドライバーが有効になったカーネルを作成する必要があります。

まず、カーネルのソースコードアーカイブを取得します。ここでは、Armadilloサイトからダウンロードしてくることにします。

図2.60 Linuxカーネルの取得と展開


次にArmadillo-400シリーズの標準コンフィギュレーションを適用します。

[ATDE ~]$ cd linux-2.6.26-at19/
[ATDE ~/linux-2.6.26-at19]$ make armadillo400_defconfig (Armadillo-410/420/440の場合)
[ATDE ~/linux-2.6.26-at19]$ make armadillo460_defconfig (Armadillo-460の場合)

図2.61 LinuxカーネルにArmadillo-400シリーズ標準コンフィギュレーションを適用する


続いて、menuconfigを使用して、図2.62「CANドライバーを有効にする」及び図2.63「CANに使用するピンを指定する」に示すようにカーネルコンフィギュレーションを変更します。

Linux Kernel Configuration
  Networking  --->
    <*>   CAN bus subsystem support  --->  ← チェックを入れる
      <*>   Raw CAN Protocol (raw access with CAN-ID filtering)  ← チェックを入れる
      <*>   Broadcast Manager CAN Protocol (with content filtering)  ← チェックを入れる
      CAN Device Drivers  --->
        <*> Freescale FlexCAN  ← チェックを入れる

図2.62 CANドライバーを有効にする


Linux Kernel Configuration
  System Type  --->
    Freescale MXC Implementations  --->
      MX25 Options  --->
        Armadillo-400 Board options  --->
          [ ] Enable I2C2 at CON14  ← チェックを外す
          [*] Enable CAN2 at CON14  ← チェックを入れる

図2.63 CANに使用するピンを指定する


コンフィギュレーションの変更と、ソースの修正をおこなったら、カーネルをビルドします。

[ATDE ~/linux-2.6.26-at19]$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- && gzip -c arch/arm/boot/Image > linux.bin.gz

図2.64 Linuxカーネルをビルドする


正常にビルドが完了すると、linux-2.6.26-at19/linux.bin.gzにカーネルイメージが作成されます。linux.bin.gzをArmadilloのフラッシュメモリのカーネル領域に書き込んでください。

2.5.5. CAN通信プログラムの準備

Atmark Distには、CAN通信プログラムのサンプルとしてcan-utilsが含まれています。can-utilsには、一つのメッセージを送信するcansend、複数のメッセージを連続して送信するcangen、受信したメッセージを表示するcandumpがあります。今回は、これらを使用してCANの動作確認をおこなうことにします。

第1部の「Atmark Distを使ったルートファイルシステムの作成」などを参照し、使用するプロダクト用に基本的な設定をして一度ビルドしたAtmark Distを用意してください。can-utilsを使用可能にするには、Atmark Distのユーザーランドコンフィギュレーションで以下の項目にチェックを入れます。

Userland Configuration
  Network Applications  --->
    [*] can-utils  ← チェックを入れる
    [*]   cansend  ← チェックを入れる
    [*]   candump  ← チェックを入れる
    [*]   cangen   ← チェックを入れる

図2.65 can-utilsを選択する


これらを選択した状態でユーザーランドをビルドし、作成されたルートファイルシステムイメージ(romfs.img.gz)をArmadilloのフラッシュメモリのユーザーランド領域に書き込んでください。

2.5.6. 使用例

実際に、CANバスを通じてArmadillo同士で通信をおこなう手順を説明します。

まず、通信速度を設定します。通信速度は送受信をおこなうノード全てで一致している必要があるので、それぞれのArmadilloでおこなってください。FlexCANフレームワークを使っている場合、通信速度はsysfsインターフェースで設定します。通信速度は図2.66「CAN通信速度の計算式」に示す式で算出します。通信速度の設定例を表2.8「CAN通信速度の設定例」に示します。

src_clk = 66,500,000 (br_clksrc = bus の場合)
src_clk = 24,000,000 (br_clksrc = osc の場合)
通信速度[bps] = src_clk / br_presdiv / (1 + br_propseg + br_pseg1 + br_pseg2)

図2.66 CAN通信速度の計算式


表2.8 CAN通信速度の設定例

通信速度 br_clksrc br_presdiv br_propseg br_pseg1 br_pseg2

500000

bus

7

5

5

8

1007575

bus

3

7

7

7

950000

bus

5

4

5

5

25000

bus

133

6

6

7

10390

bus

256

8

8

8

945231

osc

2

4

4

5


次に、CANインターフェースを有効にします。これも、それぞれのArmadilloで実行します。

[armadillo ~]# ifconfig can0 up

図2.67 CANインターフェースの有効化


CANメッセージを受信するArmadilloで、candumpを実行しておきます。

[armadillo ~]# candump can0

図2.68 CANメッセージの受信


別のArmadilloでcansendを実行すると、一つのメッセージを送信できます。図2.69「CANメッセージの送信」の例では、ID=0x5a5、データ=0x1234567を送信しています。

[armadillo ~]# cansend can0 5a5#01234567

図2.69 CANメッセージの送信


candumpコマンドを実行している受信側のArmadilloでは、メッセージを受信すると図2.70「CANメッセージの受信」に示しすような表示が得られます。

[armadillo ~]# candump can0
  can0  5a5  [4] 01 23 45 67

図2.70 CANメッセージの受信


また、cangenを実行すると、連続したメッセージを送信できます。オプションにCANインターフェース名だけを指定した場合、cangenはアドレス、データ共にランダムな値を送信します。

[armadillo ~]# cangen can0

図2.71 連続したCANメッセージの送信


candumpを実行している受信側のArmadilloでは、図2.72「連続したCANメッセージの受信」に示すような受信結果が得られます。

[armadillo ~]# candump can0
  can0  567  [6] 69 98 3C 64 73 48
  can0  451  [8] 4A 94 E8 2A EC 58 55 62
  can0  729  [8] BA 58 1B 3D AB D7 7E 50
  can0  1F2  [8] E3 A9 E2 79 46 E1 45 75
  can0   7C  [2] 54 08
  :
  :
  :

図2.72 連続したCANメッセージの受信


2.6. USB無線LANモジュール

IEEE 802.11b/g/n対応のUSB無線LANモジュールをArmadillo-400シリーズに接続して使用方法について紹介します。現在入手可能なUSB無線LANデバイスのほとんどは、RaLink社製モジュールを内蔵しているものとRealtek社製モジュールを内蔵しているものの、いずれかのようです。ここで紹介する方法は、RaLink社製モジュールRT8070/RT3070/RT3370を内蔵しているものを対象とします。

使用するソフトウェア、デバイスは以下のとおりです。

  1. Linuxカーネル: linux-2.6.26-at19
  2. ユーザーランド: Atmark Dist v20131122
  3. USB無線LANドライバー: 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO
  4. USB無線LANモジュール[9]

    1. GW-USMicroN (Planex社製)
    2. GW-USMicroN-G (Planex社製)
    3. GW-USMicro300 (Planex社製)
    4. LAN-W150N/U2D (Logitec社製)

おおまかな手順としては、次のようになります。

  1. USB無線LANドライバーのソースコードアーカイブの取得
  2. USB無線LANドライバーのビルド
  3. USB無線LANの設定

2.6.1. USB無線LANドライバーのソースコードアーカイブの取得

USB無線LANモジュールのドライバーはLinuxカーネルに含まれていないため、メーカーのサイトからソースコードをダウンロードしてきて、ビルドする必要があります。作業用PCのWebブラウザでRaLink社のLinuxドライバーページ[10]にアクセスし、「RT8070/RT3070/RT3370 USB」をクリックしてください。ライセンス(GPL v2)に同意するかを聞かれるページに移動するので、ライセンスに同意する場合は、「Your Name」と「Your Email」を入力して「Accept」をクリックします。すると、USB無線LANドライバーのソースコードアーカイブ2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO.bz2をダウンロードできます。

2.6.2. USB無線LANドライバーのビルド

ダウンロードしたソースコードアーカイブをビルドし、ユーザーランドに組み込みます。事前に、第1部「Atmark Distを使ったルートファイルシステムの作成」などを参照し、使用するプロダクト用に基本的な設定をして、一度ビルドしたAtmark Distを用意してください。

まずは、ソースコードアーカイブを展開します。ソースコードアーカイブは、atmark-distディレクトリと同じディレクトリに置いてから展開してください。

[ATDE ~]$ ls
atmark-dist 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO.bz2
[ATDE ~]$ tar xjvf 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO.bz2

図2.73 USB無線LANドライバーのソースコードアーカイブを展開する


Armadillo用にドライバーをビルドするために、いくつか修正が必要です。修正はパッチにまとめてあるので、パッチをダウンロードして適用します。

[ATDE ~]$ wget http://download.atmark-techno.com/misc/2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo_20101220.diff
[ATDE ~]$ cd 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO
[ATDE ~/2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO]$ patch -p1 < ../2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo_20101220.diff

図2.74 USB無線LANドライバーへのパッチの適用


パッチの内容は、図2.75「USB無線LANドライバーへのパッチ」に示すものです。Armadillo用にビルドするための設定、生成物(カーネルモジュール、設定ファイル)をatmark-dist/にコピーする処理、いくつかのデバイスサポートを追加しています。

diff -urN 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO/Makefile 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo/Makefile
--- 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO/Makefile    2010-08-31 18:12:20.000000000 +0900
+++ 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo/Makefile  2010-12-10 14:11:18.000000000 +0900
@@ -15,7 +15,8 @@
 RTMP_SRC_DIR = $(RT28xx_DIR)/RT$(CHIPSET)

 #PLATFORM: Target platform
-PLATFORM = PC
+#PLATFORM = PC
+PLATFORM = ARMADILLO
 #PLATFORM = 5VT
 #PLATFORM = IKANOS_V160
 #PLATFORM = IKANOS_V180
@@ -147,6 +148,12 @@
 CROSS_COMPILE =
 endif

+ifeq ($(PLATFORM),ARMADILLO)
+DIST_SRC = `pwd`/../atmark-dist
+LINUX_SRC = $(DIST_SRC)/linux-2.6.x
+CROSS_COMPILE = arm-linux-gnueabi-
+endif
+
 ifeq ($(PLATFORM),IXP)
 LINUX_SRC = /project/stable/Gmtek/snapgear-uclibc/linux-2.6.x
 CROSS_COMPILE = arm-linux-
@@ -347,6 +354,11 @@
        cp -f $(RT28xx_DIR)/os/linux/rtnet$(CHIPSET)apsta.ko /tftpboot
 endif
 else
+ifeq ($(PLATFORM),ARMADILLO)
+       mkdir -p $(DIST_SRC)/romfs/lib/modules $(DIST_SRC)/romfs/etc/Wireless/RT2870STA
+       cp -f $(RT28xx_DIR)/os/linux/rt$(CHIPSET)sta.ko $(DIST_SRC)/romfs/lib/modules
+       cp -f RT2870STA.dat $(DIST_SRC)/romfs/etc/Wireless/RT2870STA
+else
        cp -f $(RT28xx_DIR)/os/linux/rt$(CHIPSET)sta.ko /tftpboot
 ifeq ($(OSABL),YES)
        cp -f $(RT28xx_DIR)/os/linux/rtutil$(CHIPSET)sta.ko /tftpboot
@@ -355,6 +367,7 @@
 endif
 endif
 endif
+endif


 release:
@@ -388,6 +401,9 @@
        $(MAKE) -C os/linux clean
        rm -rf os/linux/Makefile
 endif
+ifneq ($(TARGET),THREADX)
+       $(MAKE) -C tools clean
+endif
 ifeq ($(TARGET), UCOS)
        $(MAKE) -C os/ucos clean MODE=$(RT28xx_MODE)
 endif
diff -urN 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO/RT2870STA.dat 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo/RT2870STA.dat
--- 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO/RT2870STA.dat       2010-08-31 18:12:20.000000000 +0900
+++ 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo/RT2870STA.dat     2010-12-09 23:07:37.000000000 +0900
@@ -1,8 +1,8 @@
 #The word of "Default" must not be removed
 Default
 CountryRegion=5
-CountryRegionABand=7
-CountryCode=
+CountryRegionABand=1
+CountryCode=JP
 ChannelGeography=1
 SSID=11n-AP
 NetworkType=Infra
diff -urN 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO/common/rtusb_dev_id.c 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo/common/rtusb_dev_id.c
--- 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO/common/rtusb_dev_id.c       2010-09-01 10:47:30.000000000 +0900
+++ 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo/common/rtusb_dev_id.c     2010-12-10 15:23:56.000000000 +0900
@@ -66,6 +66,8 @@
        {USB_DEVICE(0x13D3,0x3305)}, /* AzureWave 3070*/
        {USB_DEVICE(0x1044,0x800D)}, /* Gigabyte GN-WB32L 3070 */
        {USB_DEVICE(0x2019,0xAB25)}, /* Planex Communications, Inc. RT3070 */
+       {USB_DEVICE(0x2019,0xAB29)}, /* Planex Communications, Inc. */
+       {USB_DEVICE(0x2019,0xED14)}, /* Planex Communications, Inc. */
        {USB_DEVICE(0x2019,0x5201)}, /* Planex Communications, Inc. RT8070 */
        {USB_DEVICE(0x07B8,0x3070)}, /* AboCom 3070 */
        {USB_DEVICE(0x07B8,0x3071)}, /* AboCom 3071 */
@@ -84,6 +86,7 @@
        {USB_DEVICE(0x1D4D,0x0011)}, /* Pegatron Corporation 3072 */
        {USB_DEVICE(0x5A57,0x5257)}, /* Zinwell 3070 */
        {USB_DEVICE(0x5A57,0x0283)}, /* Zinwell 3072 */
+       {USB_DEVICE(0x04BB,0x0944)}, /* I-O DATA */
        {USB_DEVICE(0x04BB,0x0945)}, /* I-O DATA 3072 */
        {USB_DEVICE(0x04BB,0x0947)}, /* I-O DATA 3070 */
        {USB_DEVICE(0x04BB,0x0948)}, /* I-O DATA 3072 */
@@ -106,9 +109,16 @@
        {USB_DEVICE(0x13D3,0x3307)}, /* Azurewave */
        {USB_DEVICE(0x13D3,0x3321)}, /* Azurewave */
        {USB_DEVICE(0x07FA,0x7712)}, /* Edimax */
-       {USB_DEVICE(0x0789,0x0166)}, /* Edimax */
+       {USB_DEVICE(0x0789,0x0162)}, /* Logitec */
+       {USB_DEVICE(0x0789,0x0163)}, /* Logitec */
+       {USB_DEVICE(0x0789,0x0164)}, /* Logitec */
+       {USB_DEVICE(0x0789,0x0166)}, /* Logitec */
+       {USB_DEVICE(0x0789,0x0168)}, /* Logitec */
        {USB_DEVICE(0x0DB0,0x822B)}, /* MSI 3070*/
        {USB_DEVICE(0x0DB0,0x871B)}, /* MSI 3070*/
+       {USB_DEVICE(0x0411,0x015D)}, /* Buffalo */
+       {USB_DEVICE(0x0411,0x016F)}, /* Buffalo */
+       {USB_DEVICE(0x0411,0x01A2)}, /* Buffalo */
 #endif // RT3070 //
 #ifdef RT3370
        {USB_DEVICE(0x148F,0x3370)}, /* Ralink 3370 */
diff -urN 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO/os/linux/config.mk 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo/os/linux/config.mk
--- 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO/os/linux/config.mk  2010-08-31 18:12:20.000000000 +0900
+++ 2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO-armadillo/os/linux/config.mk        2010-12-10 14:05:40.000000000 +0900
@@ -148,7 +148,7 @@
 # config for STA mode

 ifeq ($(RT28xx_MODE),STA)
-WFLAGS += -DCONFIG_STA_SUPPORT -DDBG
+WFLAGS += -DCONFIG_STA_SUPPORT #-DDBG

 ifeq ($(HAS_XLINK),y)
 WFLAGS += -DXLINK_SUPPORT
@@ -545,6 +545,10 @@
     endif
 endif

+ifeq ($(PLATFORM),ARMADILLO)
+EXTRA_CFLAGS := $(WFLAGS) -I$(RT28xx_DIR)/include -O3
+endif
+
 #If the kernel version of RMI is newer than 2.6.27, please change "CFLAGS" to "EXTRA_FLAGS"
 ifeq ($(PLATFORM),RMI)
 EXTRA_CFLAGS := -D__KERNEL__ -DMODULE=1 -I$(LINUX_SRC)/include -I$(LINUX_SRC)/include/asm-mips/mach-generic  -I$(RT28xx_DIR)/include -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fno-strict-aliasing -fno-common -DCONFIG_IFX_ALG_QOS -DCONFIG_WAN_VLAN_SUPPORT -fomit-frame-pointer -DIFX_PPPOE_FRAME -G 0 -fno-pic -mno-abicalls -mlong-calls -pipe -finline-limit=100000 -mabi=32 -G 0 -mno-abicalls -fno-pic -pipe -msoft-float -march=xlr -ffreestanding  -march=xlr -Wa,--trap, -nostdinc -iwithprefix include $(WFLAGS)

図2.75 USB無線LANドライバーへのパッチ


パッチを適用したドライバーをビルドするには、makeコマンドを実行してください。正常にビルドできると、カーネルモジュール(rt3370sta.ko)がatmark-dist/romfs/lib/modules/に、設定ファイル(RT2870STA.dat)がatmark-dist/romfs/etc/Wireless/RT2870STA/にコピーされます。

[ATDE ~/2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO]$ make

図2.76 USB無線LANドライバーのビルド


atmark-distに移動し、make imageコマンドを実行すると、USB無線LANモジュールのカーネルモジュールを含んだルートファイルシステムイメージ(romfs.img.gz)が作成されます。それを、Armadilloのフラッシュメモリのユーザーランド領域に書き込んでください。

[ATDE ~/2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO]$ cd ../atmark-dist
[ATDE ~/atamrk-dist]$ make image

図2.77 USB無線LANドライバーを含んだルートファイルシステムの作成


2.6.3. USB無線LANの設定

ここからは、Armadillo実機での作業になります。まずは、設定ファイル/etc/Wireless/RT2870STA/RT2870STA.datをアクセスポイントに合わせて設定します。例として、ESSID=my-essid、認証方式=WPA-PSK、暗号化方式=AES、WPA共有キー=my-preshared-keyの場合、図2.78「RT2870STA.dat設定例」のように設定します。設定ファイルの記述方法は、2010_0831_RT3070_Linux_STA_v2.4.0.1_DPO/README_STA_usbを参照してください。

SSID=my-essid
AuthMode=WPAPSK
EncrypType=AES
WPAPSK=my-preshared-key

図2.78 RT2870STA.dat設定例


ArmadilloにUSB無線LANモジュールを接続すると、図2.79「USB無線LANモジュールを接続したときに表示されるカーネルメッセージ」に示すようなカーネルメッセージが表示されます。(カーネルメッセージは、接続するポートによって異なります。)

usb 2-1: new high speed USB device using fsl-ehci and address 2
usb 2-1: configuration #1 chosen from 1 choice

図2.79 USB無線LANモジュールを接続したときに表示されるカーネルメッセージ


insmodコマンドで、カーネルモジュールをロードします。

[armadillo ~]# insmod /lib/modules/rt3370sta.ko
Using /lib/modules/rt3370sta.ko
rtusb init
---> usbcore: registered new interface driver rt2870

図2.80 USB無線LANドライバーのカーネルモジュールのロード


ifconfigコマンドで、ネットワークインターフェースを有効にします。ネットワークインターフェース名はra0です。

[armadillo ~]# ifconfig ra0 up
0x1300 = 00064300

図2.81 ネットワークインターフェースの有効化


後は、通常のネットワークデバイスと同様にIPアドレスの設定などをおこなってください。また、iwconfigiwlistiwprivコマンドを利用して無線LAN接続の状態確認や管理ができます。

2.7. USB接続Webカメラ

USB接続のWebカメラを使用する例として、カメラで撮影した映像をストリーミング配信し、Webブラウザから閲覧する方法を紹介します。現在入手可能なUSB接続のカメラは、ほとんどのものがUVC(USB Video Class)対応のものとなっています。Armadillo-400シリーズでは、UVCクラス対応のカメラであれば、大抵のものが使用可能です[11]

使用するソフトウェア、デバイスは以下のとおりです。

  1. Linuxカーネル: linux-2.6.26-at19
  2. ユーザーランド: Atmark Dist v20131122
  3. ビデオサーバー: MJPG-streamer (Atmark Distに含まれるもの)
  4. Webカメラ: Qcam Orbit AF「Logicool(Logitech社)製」[12]

Armadillo-420ベーシックモデル開発セットの場合、標準状態で、UVC対応Webカメラを接続すると、自動でそれを認識しビデオサーバーを起動するようになっています。つまり、Webカメラを接続するだけで、Webブラウザでリモートからカメラ画像を閲覧可能な状態になります[13]

ここでは、Armadillo-440を対象にArmadillo-420ベーシックモデル開発セットで行っていることと同じ機能を実現します。手順としては、次のようになります。なお、LinuxカーネルのUVC対応機能は標準で有効になっているため、カーネルを変更する必要はありません。標準のものを使用してください。

  1. MJPG-streamerの動作を確認する
  2. Webカメラが接続されたら、MJPG-streamerが自動起動するよう設定する

2.7.1. MJPG-streamerを使う

MJPG-streamerの自動起動設定を行う前に、単体で動作することを確認しましょう。

Armadillo-440にWebカメラ(Qcam Orbit AF)を接続すると、コンソールに以下のような表示が出力されます。Webカメラは多くの帯域を消費しますので、USB High Speedポート(CON5 下段)に接続してください。

usb 2-1: new high speed USB device using fsl-ehci and address 2
usb 2-1: configuration #1 chosen from 1 choice
uvcvideo: Found UVC 1.00 device <unnamed> (046d:0994)
input: UVC Camera (046d:0994) as /devices/platform/fsl-ehci.1/usb2/2-1/2-1:1.0/input/input2

図2.82 Webカメラを接続したときに表示されるカーネルメッセージ


Webカメラが認識されると、ビデオ入力用のデバイスファイル/dev/video0が作成されます。これを引数として、図2.83「mjpg_streamerコマンドの実行」に示すコマンドを実行すると、MJPG streamerが起動します。MJPG streamerが動作している状態でhttp://ArmadilloのIPアドレス/:8080にアクセスすると、図2.84「MJPG-Streamer Demo Pages画面」が表示されます。静止画、動画、およびPan/Tilt/LEDのOn/Off等の制御をすることができます[14]

[armadillo ~]#  mjpg_streamer -i "/usr/lib/mjpg_streamer/input_uvc.so --device /dev/video0 --yuv --resolution QVGA --fps 10" -o "/usr/lib/mjpg_streamer/output_http.so --www /usr/lib/mjpg_streamer/www"
MJPG Streamer Version.: 2.0
 i: Using V4L2 device.: /dev/video0
 i: Desired Resolution: 320 x 240
 i: Frames Per Second.: 10
 i: Format............: YUV
 i: JPEG Quality......: 80
 o: www-folder-path...: /usr/lib/mjpg_streamer/www/
 o: HTTP TCP port.....: 8080
 o: username:password.: disabled
 o: commands..........: enabled

図2.83 mjpg_streamerコマンドの実行


MJPG-Streamer Demo Pages画面

図2.84 MJPG-Streamer Demo Pages画面


mjpg_streamerコマンドの引数のとり方は少々特殊です。-iオプションに続き、入力に関する引数を「"」で囲って指定します。出力に関する引数は、-oオプションの後に指定します。

表2.9 mjpg_streamerコマンドの引数

引数 説明

/usr/lib/mjpg_streamer/input_uvc.so

UVCカメラからの入力を処理する入力プラグイン使用します。

--device

ビデオ入力用のデバイスファイルを指定します。

--yuv

カメラが出力する画像形式をYUYV形式にします。このオプションを指定しない場合、MJPG形式となります。

--resolution

カメラが出力する画像の解像度を指定します。QVGAやVGAなどの文字列で指定する他、640x480のように数値で指定することもできます。

--fps

カメラが出力する画像のFPS(frames per sec)を指定します。

/usr/lib/mjpg_streamer/output_http.so

HTTP出力を行う出力プラグインを使用します。

--www

MJPG streamerが表示するWebページが置かれたディレクトリを指定します。


mjpg_streamerコマンドに指定できる引数は、プラグインごとに異なります。各プラグインが取ることができる引数を確認するには、図2.85「mjpg_streamerコマンドのヘルプを調べる」のようにします。

[armadillo ~]#  mjpg_streamer -i "/usr/lib/mjpg_streamer/input_uvc.so --help"

図2.85 mjpg_streamerコマンドのヘルプを調べる


2.7.2. MJPG-streamerを自動起動する

mjpg_streamerコマンドが単体で動作することが確認できましたので、次はWebカメラが接続されたときにそのコマンドを自動で実行するよう設定します。「デバイスが接続された時に何かの処理をおこなう」という機能は、Linuxシステムのudevの仕組みを利用して実現します。

udevにより、デバイスが新たに接続されるとそれに対応するデバイスファイルが自動で作成されます。Webカメラが接続された時に/dev/video0が作成されたのはudevの働きによるものです。また、udevdデーモンは、デバイスが接続または取り外された時にカーネルから送られてくるメッセージ(uevent)を受け取り、それに対応した処理を実行します。どのようなデバイスが接続または取り外された時にどのような処理を行うか、というルールは、/etc/udev/rules.d/ディレクトリに置かれたファイルに記述します。udevルールについての詳細は、man 7 udevを参照してください。

今回は、図2.86「Webカメラが接続/取り外しされた時にスクリプトを実行するudevルール」に示すルールを使用します。これを、/etc/udev/rules.d/z10_mjpg-streamer.rulesというファイル名で保存してください。USB High Speedポート(CON5 下段)にWebカメラが接続されると、/etc/config/mjpg-streamer.sh start $kernelというコマンドが実行されます。$kernelは、デバイスファイル名に置き換えられます。同様に、Webカメラが取り外されると/etc/config/mjpg-streamer.sh stop $kernelというコマンドが実行されます。

KERNEL=="video*", SUBSYSTEM=="video4linux", DRIVERS=="uvcvideo", DRIVERS=="usb", ATTRS{busnum}=="2", ACTION=="add", RUN+="/etc/config/mjpg-streamer.sh start $kernel"

KERNEL=="video*", ACTION=="remove", RUN+="/etc/config/mjpg-streamer.sh stop $kernel"

図2.86 Webカメラが接続/取り外しされた時にスクリプトを実行するudevルール


/etc/config/mjpg-streamer.shは、図2.87「mjpg_streamerを実行するシェルスクリプト」を使います。/etc/config/mjpg-streamer.shに実行権限をつける(chmod +x /etc/config/mjpg-streamer.sh)のを忘れないようにしてください。Webカメラが接続されたときはstartを伴って実行されるため、start_action()を行います。start_action()内では、ledctrlコマンドを使用して赤色LEDを点滅させ、mjpg_streamerコマンドを実行します。また、PIDを使用して多重起動の防止をしています。Webカメラが取り外されたときは、stop_action()が実行され、mjpg_streamerプロセスの停止とLED点滅の停止を行います。

#!/bin/sh

ACTION=$1
DEVICE=$2

RUNPREFIX=/var/run/mjpg-streamer
RUNFILE=${RUNPREFIX}.${DEVICE}.pid

LIBDIR=/usr/lib/mjpg_streamer

make_run_file() {
    echo $1 > $RUNFILE
}

start_action() {
    if [ ! -z "`ls ${RUNPREFIX}.* 2>/dev/null`" ]; then
        return
    fi
    ledctrl red blink_on 500
    mjpg_streamer -i "${LIBDIR}/input_uvc.so --device /dev/${DEVICE} --yuv --resolution QVGA --fps 10" -o "${LIBDIR}/output_http.so --www ${LIBDIR}/www" >/dev/null 2>&1 &
    make_run_file $!
}

stop_action() {
    if [ ! -e ${RUNFILE} ]; then
        return
    fi
    kill `cat ${RUNFILE} 2>&1` >/dev/null 2>&1
    ledctrl red blink_off
    rm -f $RUNFILE
}

case $ACTION in
    start)
        start_action
        ;;
    stop)
        stop_action
        ;;
    *)
        ;;
esac

図2.87 mjpg_streamerを実行するシェルスクリプト


以上の設定を行うと、Webカメラを接続すると自動でビデオサーバーが起動するようになります。

2.8. USB接続モニタ

Armadillo-400シリーズは基本的にはLCD以外のビデオ出力機能を持っていませんが、PC用に販売されているUSB接続可能なモニタやディスプレイアダプタを使うことで、簡単にビデオ出力機能を追加することができます。これを応用すると、Armadilloをベースにしたデジタルサイネージを作るといったことも可能になります。ここでは、DisplayLink社のチップが搭載されたUSBディスプレイアダプタLDE-SX015Uを使用する方法を紹介します。

使用するソフトウェアおよびハードウェアは、以下の通りです。

  • linuxカーネル: linux-2.6.26-at19
  • ユーザーランド: Atmark Dist v20131122
  • 開発環境: ATDE3 v20100309
  • USBディスプレイアダプタ: LDE-SX015U (Logitec社製)

2.8.1. 基本的な構造

ArmadilloでUSBディスプレイアダプタを使用する場合でも、基本的な仕組みはPCで使う場合と同じです。PCやArmadilloからDisplayLink社製のチップが搭載されたUSBディスプレイアダプタは、USB 2.0で接続します。

USBディスプレイアダプタの接続

図2.88 USBディスプレイアダプタの接続


ディスプレイアダプタからモニターまでは、アダプタによって異なります。HDMIやVGAなど、製品によって異なりますので、お使いの製品のマニュアルを確認してください。USB接続モニタの場合、DisplayLinkとモニタが一体になっている場合があります。その場合、DisplayLinkとモニタを接続する必要はありません。

LinuxシステムでDisplayLink社製チップが搭載された製品を使うには、2通りの方法があります。一つはフレームバッファとして使う方法、もう一つは専用のライブラリを使って表示する方法です。フレームバッファのドライバを組み込んでしまえば、一般的なフレームバッファにアクセスする方法と同じように動きますので、X Window SystemやAndroidも動かすことができます。但し、これにはカーネルでの対応が必要など、手順が複雑です。

他の方法としては、専用のライブラリ(libdlo)を使った方法があります。この方法であれば、カーネルが対応していなくても簡単にアプリケーションだけで描画することが可能です。ここでは、libdloを使った方法を紹介します。

[注記]libdlo

libdloは、freedesktop.orgで開発されているDisplayLinkチップ用のライブラリです[15]。libusb[16]を使って、DisplayLinkチップとの通信をおこないます。

2.8.2. DisplayLink

USBディスプレイアダプタの使用方法について説明する前に、DisplayLinkとそれを使用する利点について解説します。DisplayLink社は、USB接続のグラフィックスチップを製造しているメーカーです。これまでのUSB接続のグラフィックスチップでは、高解像度や高いリフレッシュレートのビデオ出力をおこなおうとすると、USBの帯域がボトルネックになっていました。DisplayLink社製チップを使った場合、画像をそのままUSBを通して転送するのではなく、一度圧縮してから転送し、DisplayLinkチップ内で展開してモニタに出力するというアプローチで、USB帯域の問題を解決しています。既存のUSB接続モニタ用のチップと異なり、DisplayLink社製のチップを使った製品では比較的高解像度の出力が得やすいことから、近年採用製品が増えています。

ArmadilloでDisplayLinkを採用することにはいくつか利点があります。

まず挙げられるのは、高解像度の描画が可能になる点です。Armadilloが表示できる画面サイズは、使用しているLCDコントローラによって決まります。Armadillo-400シリーズの場合、i.MX25内蔵のLCDコントローラーを使用しているため、最大800×600サイズとなります。しかし、DisplayLinkを使えば、1920×1080という大きさの表示をすることも可能です。[17]

また、画面出力に要するメモリを節約可能というメリットもあります。これには少し説明が必要でしょう。

モニタは、基本的に常時データを表示している機器です。PCで通常のビデオカードを使用した場合、この表示されているデータは、ビデオカードからモニタに随時転送されています。モニタが中でバッファしているわけではありません。これは、液晶モニタでもCRTモニタでも変わりありません。

この事情は、Armadilloでも同様です。Armadillo-440液晶モデルの場合、オンボードメモリの一部をLCDに表示するための画像を格納するメモリ領域として確保し、そこから常にデータを読み出し、LCDの信号に変換しています。この処理はi.MX25内蔵のLCDコントローラが担っています。

つまり、メモリ帯域の一部を常に一定量消費していることになります。動画のように、常時画面が変化していくのであればこれも仕方の無いことですが、静止画のように一定時間同じ画像を表示する場合、これはいささかもったいないように思えます。

DisplayLinkは内部にビデオメモリを持っていますので、Armadilloから画像を転送し終わった後は、Armadillo側のメモリを開放してしまっても問題ありません。もちろん、DisplayLinkとモニタとの間では常にデータが流れているわけですが、ArmadilloからDisplayLinkへは必要なときにデータを転送するだけで良いわけです。

Armadillo-400シリーズの動作クロックは400MHzとそう速いわけではないので、残念ながら、高いリフレッシュレートで描画をおこなうといったことは、できません。しかし、DisplayLinkを使用すると高解像度のきれいな画像を大きなモニタで表示するといったことが可能になります。

2.8.3. libdloのビルド

それでは実際に、DisplayLinkのデバイスをArmadilloに繋いで、画像を描画してみましょう。libdloにはテスト用のコードも入っていますので、今回はこれを使用することにします。

libdloをビルドする前に、ATDE3にlibdloをビルドするために必要なパッケージをインストールします。

[ATDE ~]$ sudo apt-get update && sudo apt-get upgrade
[ATDE ~]$ sudo apt-get install autoconf automake libtool
[ATDE ~]$ apt-cross --arch armel --suite lenny --install libusb-0.1-4 libusb-dev

図2.89 libdloをビルドするために必要なパッケージのインストール


libdloのソースコードは、freedesktop.orgのgitリポジトリからクローンしてきます。

[ATDE ~]$ git clone git://anongit.freedesktop.org/libdlo

図2.90 libdloリポジトリのクローン


以下のようにして、libdloをビルドしてください。

[ATDE ~]$ cd libdlo
[ATDE ~/libdlo]$ mkdir config
[ATDE ~/libdlo]$ ./autogen.sh
[ATDE ~/libdlo]$ ./configure --host=arm-linux-gnueabi
[ATDE ~/libdlo]$ make

図2.91 libdloのビルド


libdlo/src/.libs/libdlo.so.0.1.0にlibdloの共有ライブラリが生成されます。

2.8.4. サンプルプログラムの実行

以下のファイルをArmadilloにlftp等でコピーしてください。

表2.10 libdloサンプルプログラムの動作に必要なファイル

ATDE上のパス Armadillo上でのパス

/usr/arm-linux-gnueabi/lib/libusb-0.1.so.4.4.4

/home/ftp/pub/libusb-0.1.so.4.4.4

libdlo/src/.libs/libdlo.so.0.1.0

/home/ftp/pub/libdlo.so.0.1.0

libdlo/test/.libs/test1

/home/ftp/pub/test1

libdlo/test/images/test08.bmp

/home/ftp/pub/images/test08.bmp

libdlo/test/images/test16.bmp

/home/ftp/pub/images/test08.bmp

libdlo/test/images/test24.bmp

/home/ftp/pub/images/test08.bmp

libdlo/test/images/test32.bmp

/home/ftp/pub/images/test08.bmp


サンプルプログラムからライブラリが使用可能なように、ライブラリディレクトリに共有ライブラリのシンボリックリンクを作成しておきます。

[armadillo ~]# cd /home/ftp/pub
[armadillo /home/ftp/pub]# ln -s `pwd`/libdlo.so.0.1.0 /usr/lib/libdlo.so.0
[armadillo /home/ftp/pub]# ln -s `pwd`/libusb-0.1.so.4.4.4 /usr/lib/libusb-0.1.so.4

図2.92 libdloライブラリのシンボリックリンクを作成


ArmadilloとUSBディスプレイアダプタ、USBディスプレイアダプタをモニタをそれぞれ接続してください。USBディスプレイアダプタは、ArmadilloのUSB 2.0High Speedポート(CON5 下段)に接続してください。

サンプルプログラムtest1を実行するとモニタにテスト画面が表示されます。

[armadillo /home/ftp/pub]# chmod +x test1
[armadillo /home/ftp/pub]# ./test1

図2.93 テストプログラムの実行


2.9. LCDのカスタマイズ

Armadillo-440に、Armadillo-400 シリーズLCD拡張ボードで採用しているLCD以外のLCDを接続する方法について紹介します[18]

タッチパネルディスプレイ接続例

図2.94 タッチパネルディスプレイ接続例


Armadillo-440液晶モデル開発セット(以降、LCDモデル)では、以下のデバイスがLCD拡張ボード経由でArmadillo-440に接続されています。

  1. LCD:4.3インチ WQVGA
  2. タッチスクリーン:4線式抵抗膜方式

この章では以下の物を接続する場合を例に説明します。

  1. LCD:10.4インチ VGAFG
  2. タッチスクリーン:4線式抵抗膜方式

また、ソフトウェアは以下を使用します。

  1. Linuxカーネル: linux-2.6.26-at19

大きく、ハードウェアのカスタマイズとソフトウェア対応の2つに分けて説明していきます。ハードウェアのカスタマイズは、LCDパネルの接続、バックライトへの電源供給、タッチスクリーンの接続、そしてボタンの接続について順番に説明していきます。ソフトウェア対応は、LCDパネル、バックライト、タッチスクリーン、ボタンそれぞれのドライバーについて説明していきます。

2.9.1. ハードウェアのカスタマイズ

Armadillo-440にLCDディスプレイを接続する場合は、LCDモデルのように外部基板でコネクタ形状を変換します。また、LCDバックライト用の電源も別途用意する必要があります。

イメージとして、以下のようなカスタムインターフェース基板を作ることになります。

カスタムインターフェース基板イメージ

図2.95 カスタムインターフェース基板イメージ


今回の例に利用するデバイスの詳細は以下の通りです。

表2.11 タッチパネルディスプレイ

製品型番

FG100410DNCWBGT1(DATA IMAGE社製)

解像度

640 x 480 dot

LCDパネルの種類

TFT

タッチスクリーンの種類

4線式抵抗膜方式

バックライトの種類

CCFL蛍光管

インターフェースの種類

3.3V C-MOS 18bitパラレルRGBインターフェース

電源電圧範囲

3.0V~3.6V

消費電流(3.3V)

Typ. 420mA

コネクタ

DF9C-31P-1V(32) (HIROSE)


[ティップ]LCDの選択

Armadillo-440のLCDインターフェースは標準でパラレルインターフェースに対応していますので、パラレルインターフェースタイプのLCDパネルを選択するようにしてください。Armadillo-440で出力可能な最大解像度は800x600ピクセルなので、それ以下の解像度のLCDパネルを選択しましょう。

表2.12 DC-ACインバータ(バックライト電源)

製品型番

CXA-P10A-P

出力開放電圧

1.5kV

出力電力

9W

入力電圧

5V


[警告]注意: 拡張基板の消費電流

LCDパネルの電源をArmadillo-440の3.3V電源から供給する場合は、外部回路の消費電流が最大合計500mA以下になるようにしてください。詳細は、Armadillo-400シリーズハードウェアマニュアル「5.18.電源回路の構成」をご確認ください。

2.9.1.1. LCDパネルの接続

今回利用する10.4インチLCD(FG100410DNCWBGT1)には1mmピッチ面実装タイプのコネクタが付いています。このコネクタにArmadillo-440のCON11(LCDインターフェース)を繋ぐためにコネクタの形状を変換します。以下の回路図のようにLCDパネルのコネクタからArmadillo-440のCON11と接続するFPC 0.5mmピッチコネクタに配線します。FPC 0.5mmピッチコネクタとArmadillo-440のCON11はLCDモデルに付属しているFFCケーブルを使用して接続します。

表示インターフェースの接続図

図2.96 表示インターフェースの接続図


2.9.1.2. バックライトへの電源供給

FG100410DNCWBGT1に電源を供給するように、以下の回路図のようにDC-ACインバータを配線します。

DC-ACインバータ接続図

図2.97 DC-ACインバータ接続図


[警告]注意: バックライト駆動用のインバータ

CFLバックライト駆動用のインバータは高圧電圧が発生しますので、ショートやけがなどに十分ご注意ください。

[ティップ]バックライト電源の選択

バックライト電源はLCDパネルのバックライトの種類に合わせて用意する必要があります。 LCDモデルでは、LEDバックライトタイプのLCDパネルを使用していますが、今回使用するLCDパネルは蛍光灯(CFL)バックライト方式のLCDパネルになります。 バックライト電源の選択では、Armadillo-440の電源電圧と同じ5V入力の物を選択すると、電源が統一できます。またCFLバックライト方式では、蛍光灯を駆動するために高圧電圧に変換する装置「インバータ」が必要で、インバータの出力電流は蛍光管の定格より余裕のあるものを選択しましょう。ただし今回使用した電源は都合により定格より少し下回っていますので定格通りの明るさで使用したい場合は、別の物を選択すると良いでしょう。

2.9.1.3. タッチスクリーンの接続

タッチパネルの信号とArmadillo-440は以下の回路図のように配線します。

タッチスクリーン接続図

図2.98 タッチスクリーン接続図


[ティップ]タッチスクリーンの選択

4線式抵抗膜方式のタッチパネルはArmadillo-440に直接接続することが可能です。また、Armadillo内部のIC保護のためタッチパネルの信号には過電圧および過電流保護素子を入れることを推奨します。

2.9.1.4. ボタンの接続

入力用のボタンはLCDモデルと同じピンに接続しています。

ボタン接続図

図2.99 ボタン接続図


2.9.2. ソフトウェア対応

ハードウェアのカスタマイズに合わせて、ソフトウェアも変更します。該当するファイルで定義されている設定項目を変更するだけで、簡単に新しいハードウェアに対応させることができます。

2.9.2.1. LCDパネル

LCDパネルに描画するには、フレームバッファを使用します。フレームバッファドライバーのソースコードはlinux-2.6.26-at19/drivers/video/mxc/mx2fb.cです。Armadillo-400シリーズのフレームバッファドライバーでは、色々なLCDに対応できるように、LCDパネルごとに異なるタイミング設定をlinux-2.6.26-at19/drivers/video/mxc/mxc_modedb.cに分離してあります。標準以外のLCDをArmadillo-400シリーズに接続したい場合は、mxc_modedb.cに設定を追加し、使用する設定をカーネルコンフィギュレーションまたはカーネルパラメータで指定します。

LCDパネルごとに設定する必要があるタイミング情報には、以下のものがあります。

  • 垂直同期周波数(リフレッシュレート)
  • ドットクロック(ピクセルクロック)
  • 水平解像度(Xレゾリューション)
  • 垂直解像度(Yレゾリューション)
  • 左マージン(水平バックポーチ)
  • 右マージン(水平フロントポーチ)
  • 水平同期期間(水平ブランク)
  • 上マージン(垂直バックポーチ)
  • 下マージン(垂直フロントポーチ)
  • 垂直同期期間(垂直ブランク)

これらの項目に関しては、linux-2.6.26-at19/Documentation/fb/framebuffer.txtに説明が載っているので参照してください。タイミング情報は、LCDパネルの製品マニュアルやデータシートを参照して計算します。

ドットクロック = 水平同期周波数 × (水平解像度 + 左マージン + 右マージン + 水平同期期間)
水平同期周波数 = 垂直同期周波数 × (垂直解像度 + 上マージン + 下マージン + 垂直同期期間)

図2.100 LCDパネルタイミング情報の計算式


これらの値を計算し、linux-2.6.26-at19/drivers/video/mxc/mxc_modedb.cの、mxcfb_modedb(struct fb_videomodeの配列)に追加します。

struct fb_videomode {
        const char *name;       /* optional */
        u32 refresh;            /* optional */
        u32 xres;
        u32 yres;
        u32 pixclock;
        u32 left_margin;
        u32 right_margin;
        u32 upper_margin;
        u32 lower_margin;
        u32 hsync_len;
        u32 vsync_len;
        u32 sync;
        u32 vmode;
        u32 flag;
};

図2.101 struct fb_videomodeの定義(linux-2.6.26-at19/include/linux/fb.h)


FG100410DNCWBGT1の場合、次のようになります。

表2.13 FG100410DNCWBGT1のLCDタイミング

項目 データシート記載の値 struct fb_videomodeのメンバ名

ドットクロック(fCLK=1/tCLK)

21〜29[MHz]

pixclock

39683[pico_secs/clock]

水平同期期間(tHP)

800[tCLK]

なし

水平解像度(tHV)

640[tCLK]

xres

640[clock/line]

水平同期期間(tHBK)

160[tCLK]

hsync_len

160[clock/line]

左マージン

記載なし

left_margin

0[clock/line]

右マージン

記載なし

right_margin

0[clock/line]

垂直同期周波数(fV)

60[Hz]

refresh

60[frame/sec]

垂直解像度(tW)

480[tHP]

yres

480[line/frame]

垂直同期期間(tBK)

45[tHP]

vsync_len

45[line/frame]

上マージン

記載なし

upper_margin

0[line/frame]

下マージン

記載なし

lower_margin

0[line/frame]

シンクタイミング

記載なし

sync

0

ビデオモード

記載なし

vmode

FB_VMODE_NONINTERLACED

その他

記載なし

flag

0


同時に、LCDパネルとの接続線のポラリティなどをmxcfb_mode_disp_db(struct mxcfb_mode_dispの配列)に追加します。FG100410DNCWBGT1の場合、Data Enable信号がアクティブハイなので、disp_ifaceメンバにMXCFB_DISP_OE_ACT_HIGHを指定します。nameメンバには、struct fb_videomodeのnameと同じものを指定してください。

struct mxcfb_mode_disp {
        char    *name;
        int     disp_iface;
};

図2.102 struct mxcfb_mode_dispの定義(linux-2.6.26-at19/include/asm-arm/arch-mxc/mxcfb.h)


実は、FG100410DNCWBGT1用の値は既にカーネルに定義済みです。Armadilloを保守モードで起動し、カーネルパラメータのvideoを指定すると、FG100410DNCWBGT1用の設定を使用することができます。

hermit> setenv console=ttymxc0 video=mxcfb:bpp=16,FG100410DNCWBGT1

図2.103 FG100410DNCWBGT1用の設定を使用する


2.9.2.2. バックライト

バックライトの制御は、PWM機能を使用したPWMバックライトドライバーがおこなっています。バックライトドライバーの仕様に関しては、「Armadillo-400 シリーズソフトウェアマニュアル」の「LEDバックライト」の章を参照してください。

PWMバックライトドライバー自身のソースコードはlinux-2.6.26-at19/drivers/video/backlight/pwm_bl.cです。PWM機能のドライバーのソースコードは、linux-2.6.26-at19/drivers/mxc/pwm/mxc_pwm.cです。これらのドライバーは、Platform Driver[19]というドライバーモデルに従って実装されています。Platform Driverでは、ボードごとに異なる設定をstruct platform_deviceに記述することで、様々なボードに同じドライバーで対応できるようになっています。

Armadillo-400シリーズの場合、ボードごとの設定はlinux-2.6.26-at19/arch/arm/mach-mx25/armadillo400.cに記述しています。バックライトに関連する設定には、図2.104「バックライトに関連する設定」に挙げるものがあります。

static struct platform_pwm_data armadillo400_pwm1_data __maybe_unused = {
        .name = "pwm_bl",
        .invert = 1,
        .export = 0,
};

static struct platform_pwm_backlight_data armadillo440_pwm_backlight_data = {
        .pwm_id = 0,
        .max_brightness = 255,
        .dft_brightness = 255,
        .default_on = 1,
        .pwm_period_ns = 10*1000*1000,
};

図2.104 バックライトに関連する設定


armadillo400_pwm1_dataがPWM機能に関連する設定です。バックライトには、PWM1を使用しています。invertメンバでPWMのポラリティを指定することができます。また、exportメンバに1を指定すると、sysfsからPWM出力の設定ができるようになります。PWM1は、PWMバックライトドライバーによって出力の設定をおこなうため、0が指定されています。

armadillo440_pwm_backlight_dataが、PWMバックライトドライバーに関する設定です。pwm_idメンバに、使用するPWMデバイスのIDを指定します。0はPWM1に対応します。max_brightnessメンバにバックライトの最大輝度を、dft_brightnessにバックライト輝度の初期値を設定します。また、default_onメンバに1を指定することで、起動直後からバックライトをオンにできます。pwm_period_nsメンバで、PWMの周期をナノ秒単位で指定します。PWMバックライトドライバーでは、輝度(brightness)をPWMのディーティー比で調整します。

2.9.2.3. タッチスクリーン

タッチスクリーンの入力は、ADC機能を使用したADCタッチスクリーンドライバーで処理しています。タッチスクリーンドライバーはLinuxのインプットレイヤーに対応しているので、アプリケーションは/dev/input/event*のデバイスファイルを使ってデータを取り出すことができます。タッチスクリーンドライバーの仕様に関しては、「Armadillo-400 シリーズソフトウェアマニュアル」の「タッチスクリーン」の章を参照してください。

ADCタッチスクリーンドライバー自身のソースコードは、drivers/input/touchscreen/imx_adc_ts.cです。ADCドライバーのソースコードは、linux-2.6.26-at19/drivers/mxc/adc/imx_adc.cです。PWMバックライトと同様、ADCドライバーもPlatform Driverとして実装されています。linux-2.6.26-at19/arch/arm/mach-mx25/armadillo400.c内の、ADCに関する設定は次の通りです。

static struct platform_imx_adc_data armadillo440_adc_data = {
        .is_wake_src = CONFIG_ARMADILLO400_TOUCHSCREEN_IS_WAKE_SRC,
};

図2.105 ADCに関連する設定


is_wake_srcメンバに1を指定すると、スリープ状態からタッチスクリーン入力で起床することができるようになります。CONFIG_ARMADILLO400_TOUCHSCREEN_IS_WAKE_SRCは、Linuxカーネルコンフィギュレーションの、「System Type ---> Freescale MXC Implementations ---> MX25 Options ---> Armadillo-400 Board options ---> [*] Select touchscreen for wakeup source」をチェックすると1に、チェックを外すと0に設定されます。

2.9.2.4. ボタン

ボタンからの入力は、GPIOを使用したGPIOキーボードドライバーで処理しています。GPIOキーボードドライバーはタッチスクリーンドライバーと同様にインプットレイヤーに対応しています。ボタンのドライバーの仕様に関しては、「Armadillo-400 シリーズソフトウェアマニュアル」の「ボタン」の章を参照してください。

GPIOキーボードドライバーのソースコードは、linux-2.6.26-at19/drivers/input/keyboard/gpio_keys.cです。GPIOキーボードドライバーも、Platform Driverとして実装されています。PIOキーボードとして使用するGPIOのリストは、linux-2.6.26-at19/arch/arm/mach-mx25/armadillo400.c内に記述されています。

static struct gpio_keys_button armadillo400_key_buttons[] = {
        {KEY_ENTER, GPIO(3, 30), 1, "SW1",     EV_KEY, CONFIG_ARMADILLO400_GPIO_KEYS_IS_WAKE_SRC},
#if defined(CONFIG_MACH_ARMADILLO440)
        {KEY_BACK,  GPIO(2, 20), 1, "LCD_SW1", EV_KEY, CONFIG_ARMADILLO400_GPIO_KEYS_IS_WAKE_SRC},
#if defined(CONFIG_ARMADILLO400_CON11_40_GPIO_2_29)
        {KEY_MENU,  GPIO(2, 29), 1, "LCD_SW2", EV_KEY, CONFIG_ARMADILLO400_GPIO_KEYS_IS_WAKE_SRC},
#endif
#if defined(CONFIG_ARMADILLO400_CON11_41_GPIO_2_30)
        {KEY_HOME,  GPIO(2, 30), 1, "LCD_SW3", EV_KEY, CONFIG_ARMADILLO400_GPIO_KEYS_IS_WAKE_SRC},
#endif
#endif /* CONFIG_MACH_ARMADILLO440 */
};

図2.106 GPIOキーボードに関連する設定


struct gpio_keys_buttonの定義は、次のようになっています。

struct gpio_keys_button {
        /* Configuration parameters */
        int code;               /* input event code (KEY_*, SW_*) */
        int gpio;
        int active_low;
        char *desc;
        int type;               /* input event type (EV_KEY, EV_SW) */
        int wakeup;             /* configure the button as a wake-up source */
};

図2.107 struct gpio_keys_buttonの定義(linux-2.6.26-at19/include/linux/gpio_keys.h)


struct gpio_keys_buttonのそれぞれのメンバを表2.14「struct gpio_keys_buttonのメンバ」に示します。

表2.14 struct gpio_keys_buttonのメンバ

メンバ名 説明

code

キーコードの指定

gpio

使用するGPIO番号を指定

active_low

どのタイミングでキー入力を判断するかを指定

desc

キーの識別する文字列を指定

type

キー入力のイベントタイプを指定

wakeup

GPIOキーボード入力でスリープ状態から起床するかどうかを指定


codeメンバに、キーコードを指定します。gpioメンバには、使用するGPIO番号を指定します。active_lowメンバを1に指定すると、GPIOのレベルがHighからLowに変化したときに入力があったと判断します。descメンバには、キーの識別する文字列を指定してください。typeには、キー入力のイベントタイプを指定します。最後のwakeupメンバに1を指定すると、スリープ状態からGPIOキーボード入力で起床することができるようになります。CONFIG_ARMADILLO400_GPIO_KEYS_IS_WAKE_SRCは、Linuxカーネルコンフィギュレーションの、「System Type ---> Freescale MXC Implementations ---> MX25 Options ---> Armadillo-400 Board options ---> [*] Select GPIO Keys for wakeup source」をチェックすると1に、チェックを外すと0に設定されます。

[ティップ]より多くの入力キーが必要な場合

Armadillo-400シリーズでは、キー入力としてGPIOキーボードの他に、キーバッドを使うこともできます。キーパッドでは、最大4×6=24個のキー入力を扱うことができます。詳しくは、「Armadillo-400 シリーズソフトウェアマニュアル」の「キーパッド」の章を参照してください。



[5] もちろん、セキュリティ面を考慮すれば、securettyにはログインを許可するインターフェースのみを記述すべきです。

[6] I2Cと書かれることから、アイ・ツー・シーと発音される場合もあります。

[7] このような接続を、ワイヤード・ANDといいます。

[9] ここで使用するデバイス以外のArmadillo-400シリーズで動作確認済みのデバイスについては、Armadilloサイトの動作デバイス(http://armadillo.atmark-techno.com/devices)のページを参照してください。

[11] Armadillo-400 シリーズで動作確認済みのデバイスについては、Armadilloサイトの動作デバイス(http://armadillo.atmark-techno.com/devices)のページを参照してください。

[13] Armadillo-420ベーシックモデル開発セットスタートアップガイド 「UVC対応Web カメラ」を参照してください。

[14] Internet Exploler 6及び7では、MJPEGによるストリーム(動画)を閲覧することができません。しかし、Javascriptを使用したストリーム(動画) は、Internet Explorerでも閲覧することができます。

[16] これも、ユーザランドでUSBを操作するためのライブラリです。

[17] 同じDisplayLinkチップ搭載品であっても、出力できる解像度は各製品により異なります。

[18] Armadillo-460にも適用可能です。

[19] linux-2.6.26-at19/Documentation/driver-model/platform.txt参照。