2013年9月30日

  • EV3技術情報
  • ネイティブLinuxプログラミング方法 (Ver0.3)
  • 外部連携

ネイティブLinuxプログラミング方法 (Ver0.3)

1.はじめに

 このドキュメントは、教育版 レゴ マインドストーム EV3(以下EV3)をC言語によるLinuxネイティブなプログラムによって制御を行う為の手順について説明しています。
 このドキュメントでは、ホストOS環境として、Ubuntu Desktop LTS 12.04 (32bit)環境を使用しています。64bit環境でも動作に問題はありません。64bit環境のOSを使用する場合は、32bitの互換ライブラリをインストールする必要があります。32bit互換ライブラリのインストール方法については、「3.ホスト環境の設定」で説明しています。
 Ubuntuの他のバージョン、および、他のディストリビューション環境での動作確認は行っていません。Ubuntuの追加パッケージやEV3のソースファイルなどをダウンロードしますので、インターネットへの接続が必要です。
 ホスト環境であるUbuntu Linuxの操作やエディタの使い方などについては、このドキュメントでは説明していません。このドキュメントでは、テキストエディタはviを使用しています。geditなどGUIベースのテキストエディタを使用する場合は、表記のvi を適宜置き換えて下さい。

 このドキュメントでは、ほぼ全ての作業をLinuxのコマンドベースの操作で行っています。開発ホストのコマンドプロンプトとEV3のコマンドプロンプトを区別する為に、次の様に表記しています。

Host$:    ← Ubuntu開発ホストのコマンドプロンプト
root@EV3:   ← EV3上のLinuxのコマンドプロンプト

このドキュメントでの内容を実行する為に必要な環境

  • インテリジェントブロックEV3
  • モーター/ケーブル
  • USB-Ether アダプタ
  • 4Gbyte以上のmicro SDカードメディア
  • USB micro SD R/W アダプタ
  • コンソールアダプター(EV3/NXTシリアル―USB変換ボード)
  • Ethernet ケーブル/Hub
  • Ubuntu LinuxホストPC環境

このドキュメントでの内容を実行する為に必要な機材

このドキュメント で記載されているEV3の制御対象は、次の通りです。
     LED制御
     KEY制御
     簡単なモーターの動作

 公開されているEV3のデバイスドライバは修正せずに、アプリケーションからEV3のデバイスドライバを操作して、これらのデバイスを制御しています。EV3のデバイスドライバは、EV3を制御しているlms2012という仮想マシンのアプリケーションから操作することを前提につくられているため、汎用的な構造にはなっていません。そのため、各デバイスの細かい制御はできないようです。  モーターの詳細制御、各種センサー制御、公開されているソースコードの解析などにつきましては、順次情報をアップデートしていく予定です。

2.シリアルコンソールでの接続

コンソールアダプターを使用して、EV3のコンソール出力を確認します。 使用するコンソールアダプターについては、『EV3のLINUXアクセス方法』を参照してください。

 EV3のInputポート1からコンソールアダプターに接続します。次に、コンソールアダプターからUSBケーブルでPCに接続します。
 PC側ではシリアルターミナルソフトなどを起動します。シリアルターミナルが動作するホストはWindows、MacOSなどでも問題ありません。
    ボーレート:115200
    データー長:8ビット
    パリティ :無し
    ソフトフロー;無し
    ハードフロー:無し
 UbuntuなどのLinuxホスト環境では、minicomというターミナルエミュレータソフトが使用できます。Minicomの設定と使い方については、「付録 A:minicomの設定」を参照して下さい。

EV3の電源を投入するとシリアルターミナルに次の様なコンソール出力が表示されます。

EV3 initialization passed!
Booting EV3 EEprom Boot Loader

   EEprom Version: 0.50
   EV3 Flashtype: N25Q128A13B

EV3 Booting system

Jumping to entry point at: 0xC1080000

U-Boot 2009.11 (Oct 26 2012 - 10:30:38)


I2C: ready
DRAM: 64 MB
MMC: davinci: 0
In: serial
Out: serial
Err: serial
ARM Clock : 300000000 Hz
DDR Clock : 132000000 Hz
Invalid MAC address read.
Hit 'l' to stop autoboot: 0
Card did not respond to voltage select!
16384 KiB M25P128 at 0:0 is now current device
##Booting kernel from Legacy Image at c0007fc0 ...
 Image Name: Linux-2.6.33-rc4
 Image Type: ARM Linux Kernel Image (uncompressed)
 Data Size: 1965008 Bytes = 1.9 MB
 Load Address: c0008000
 Entry Point: c0008000
 Loading Kernel Image ... OK
OK

Starting kernel ...

Uncompressing Linux... done, booting the kernel.
Linux version 2.6.33-rc4 (root@ubuntu) (gcc version 4.3.3 (Sourcery G++ Lite 2009q1-203) ) #1 PREEMPT T2
CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr=00053177
CPU: VIVT data cache, VIVT instruction cache
Machine: MindStorms EV3
Memory policy: ECC disabled, Data cache writeback
DaVinci da850/omap-l138 variant 0x1
Built 1 zonelists in Zone order, mobility grouping on. Total pages: 16256
Kernel command line: mem=64M initrd=0xC1180000,10M root=/dev/ram0 rw rootfstype=cramfs console=ttyS1,110
PID hash table entries: 256 (order: -2, 1024 bytes)

<一部省略>

eth0: link up, 100Mbps, full-duplex, lpa 0xC5E1
Sending discover...
Sending select for 192.168.10.103...
Lease of 192.168.10.103 obtained, lease time 864000
adding dns 192.168.10.1
done.
Setting up IP spoofing protection: rp_filter.
INIT: Entering runlevel: 5
Starting system message bus: dbus.
Setting up VirtualDrive...
Initialize Bluetooth...
Found a Texas Instruments' chip!
Firmware file : /lib/firmware/TIInit_6.2.31.bts
Loaded BTS script version 1
texas: changing baud rate to 2000000, flow control to 1
Device setup complete
Serial Port service registered
Starting lms2012

3.ホスト環境の設定

このドキュメントで使用したホスト環境は次の通りです。
     CPU i5 2core
     HDD 20G
     Memory 4G

 上記ホスト環境に、Ubuntu Desktop LTS 12.04環境を開発ホストにインストールします。このドキュメントでは、ubuntu-ja-12.04.2-desktop-i386.isoをクリーンインストールした環境をもとに記載しています。  EV3の開発に必要なツールチェーンやソースコードを展開、コンパイルする為には5G程度の空きディスク容量があれば十分です。

 EV3の公開されているソースコードをビルドするために必要となる追加パッケージをインストールします。  まず、ホストOSを最新の環境にアップデートしておきます。

Host$: sudo apt-get update
Host$: sudo apt-get upgrade

 EV3のソースコードの入手の為に必要なパッケージをインストールします。

Host$: sudo apt-get install git

EV3のファイルシステム構築に必要なパッケージをインストールします。

Host$: sudo gpt-get install squashfs-tools
Host$: sudo apt-get install u-boot-tools

EV3のファームウェアに関するドキュメントを生成する為に必要なパッケージをインストールします。

Host$: sudo apt-get install doxygen
Host$: sudo apt-get install imagemagick libmagickcore-dev

EV3のVM(仮想マシン)上で動作するアプリケーションをビルドする為に、Javaのランタイム環境が必要になります。ここでは次の様にインストールします。

Host$: sudo apt-get install openjdk-7-jre-headless

ここではOpen JDK 7を使用していますが、Open JDK 6やOracleのJava環境でも問題ありません。

アプリケーションの開発用にNFSサーバーを使用します。NFSサーバーを利用する為に必要なパッケージをインストールします。

Host$: sudo apt-get install nfs-kernel-server

使用するCodeSourceryのツールチェーンが32bit 版になりますので、Ubuntu 64bit環境を使っている場合は、32bit互換パッケージをインストールする必要があります。

Host$: sudo apt-get install ia-libs

4.ツールチェーンのインストール

EV3で使用するコンパイラーはCode Sourcery(現メンターグラフィックス社)の次のバージョンを使用します。      Code Sourcery Lite for ARM
     Version 2009q1-203
 メンターグラフィックス社のサイトから、無償版のCode Sourcery Liteがダウンロードできますが、Web上からのアカウント登録が必要です。

 次のURLアドレスから直接ダウンロードする事も出来ます。

https://sourcery.mentor.com/GNUToolchain/package4571/public/arm-none-linux-gnueabi/arm-2009q1-203-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2

ここでは、URLを直接指定してダウンロードする方法を説明します。URLアドレスが非常に長いですが、下記の様にwgetコマンドを使用して、CodeSourceryのツールチェーンをダウンロードします。

    Host$:    wget    https://sourcery.mentor.com/GNUToolchain/package4571/public/ arm-none-linux-gnueabi/arm-2009q1-203-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2    (1行で入力して下さい。)

ダウンロードしたファイルを次の様な手順でインストールします。

Host$: mkdir ~/CodeSourcery
Host$: cd <ダウンロードディレクトリ>

ダウンロードしたファイルを展開します。

Host$: tar -jxvf arm-2009q1-203-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2 -C ~/CodeSourcery     (1行で入力してください)

展開されたファイルの名前を変更します。EV3のMakefileでは、PATH名がホームディレクトリからの相対パスで決められていますので、必ずこの名前に変更してください。

Host$: mv ~/CodeSourcery/arm-2009q1 ~/CodeSourcery/Sourcery_G++_Lite

lsコマンドでファイルが展開されているか確認します。

Host$: ls ~/CodeSourcery/Sourcery_G++_Lite
arm-none-linux-gnueabi bin lib libexec share

.bashrcにツールチェーンのPATHを追加します。

Host$: vi ~/.bashrc
(ファイルの最後に次の行を追加します)
export PATH=~/CodeSourcery/Sourcery_G++_Lite/bin:$PATH

.bashrcで設定した内容を次の様に再度読み込みます。

Host$: source ~/.bashrc

次回ログインした時や、別のコンソールを開いた場合は、.bashrcが読み込まれていますので、sourceコマンドにより読み込む必要はありません。

インストールされて、パスが通っているかを次の様に、コンパイラーを実行して確認します。

Host$: arm-none-linux-gnueabi-gcc -v
Target: arm-none-linux-gnueabi
Configured
<途中省略>
Thread model: posix
gcc version 4.3.3 (Sourcery G++ Lite 2009q1-203)

以上で、ツールチェーンのインストールと環境設定は終了です。

5.EV3ソースコードからのビルド方法

ここでは、公開されているEV3のソースコードをダウンロードし、ビルドし直す手順について説明します。

5-1 EV3ソースコードの入手と環境設定

EV3のソースコードは、2013/7/31にイニシャルリリース版のコードが公開されましたが、Makefileの不備や、PATH設定のエラーなどがあったため、2013/8/22にMakefileなど幾つかのコードがアップデートされました。EV3のソースコードは、今後もアップデートされていく事が予想されますが、2013/9/20時点での公開ソースコードをもとに説明します。

EV3のソースコードは、下記のgithub.comにて公開されています。

https://github.com/mindboards/ev3sources

簡単なインストラクションなども記載されていますので参考にしてください。実際にgitコマンドを使用してソースコードをダウンロードします。

Host$: cd
Host$: git clone https://github.com/mindboards/ev3sources.git

ホームディレクトリに ev3sources というディレクトリが作られ、公開されているソースコードがダウンロードされます。

Home$ ls ev3sources
extra lms2012 README.md

 次に、必要なディレクトリを作成し、所定の場所にファイルをコピーします。EV3のソースに含まれているコンパイルのためのスクリプトなどが、ホームディレクトリからの相対パスで指定してありますので、ホームディレクトリの直下に作業用のprojectsディレクトリを作成する必要があります。

Home$: mkdir -p ,p~/projects
Home$: cd ~/projects

gitをダウンロードしたファイルをそのまま使用しても良いのですが、間違って修正してしまった時の為に、作業ディレクトリ(~/projects)にコピーして使用します。

Home$: cp -a ~/ev3sources/* .

以上で、ソースコードのダウンロード、およびビルドのためのディレクトリの構成が終わりです。

5-2.ソースからのビルド

 取得したソースコードから、u-boot、カーネル、カーネルモジュール、lm2012、バイトコードプログラムの全てをそれぞれビルドしてみます。  まずは、ビルド用のディレクトリに移動します。

Host $: cd ~/projects/lms2012/open_first

(1)ドキュメントの生成

ソースファイルのコメントなどからhtml形式のドキュメントを生成します。

Home$: make doc

ホスト上のブラウザーで~/projects/lms2012/lms2012/doc/html/index.htmlを開くとEV3ファームウェアのドキュメントを見る事が出来ます。

(2)u-bootのビルド

次の様にU-Bootをビルドします。

Home$: make u-boot

ビルドが終了すると、Makeを実行した~/projects/lms2012/open_first(このディレクトリ)にu-bootイメージファイル(uBoot)がコピーされています。コピー元のファイルは次のファイルです。
          ~/projects/extra/uboot-03.20.00.13/u-boot.bin

(3)カーネルのビルド

EV3のカーネルをビルドします。

Host$: make kernel

Makeを実行した~/projects/lms2012/open_firstこのディレクトリにカーネルイメージファイル(uImage)がコピーされています。コピー元のファイルは次のファイルです。
          ~/project/extra/linux-03.20.00.13/arch/arm/boot/uImage

(4)カーネルモジュールのビルド

次の様にEV3向けのカーネルモジュール(デバイスドライバ)のビルドを行います。

Home$: make modules

lms2012で使用する独自のドライバーモジュールだけがコンパイルされます。通常のLinuxカーネルのコンフィグレーションの設定で、モジュール指定 (= m)されたドライバなどのカーネルモジュールがコンパイルされる訳ではありません。 正常にコンパイルが終了すると、lms2012のカーネルモジュールは次のディレクトリにインストールされています。

Host$: Ls ~/projects/lms2012/lms2012/Linux_AM1808/sys/mod
d_analog.ko d_iic.ko d_pwm.ko d_uart.ko d_usbdev.ko
d_bt.ko d_power.ko d_sound.ko d_ui.ko d_usbhost.ko

(5)lms2012(仮想マシン)おより関連ライブラリのビルド

次の様にEV3の仮想マシンであるlms2012および、関連する共有ライブラリのビルド

Host$: make lms2012

ビルドが成功すると、次のディレクトリにlms2012というファイルがせいせいされます。       ~/projects/lms2012/lms2012/Linux_AM1808/sys また、一緒にビルドされた関連するライブラリは次のディレクトリで確認する事が出来ます。

Host$: ls ~/project/lms2012/lms2012/Linux_AM1808/sys/lib
dummy    libc_input.so libc_output.so libc_ui.so
libc_com.so libc_memory.so libc_sound.so

(6)lms2012上のアプリケーションのビルド

lms2012上で動作しているアプリケーションプログラムをビルド(バイトコード化)します。

Host$: make programs

6.EV3とホストの接続

 EV3を操作する為に、コンソールアダプターとUSB-EtherアダプタEV3に接続します。USBケーブルはホストのUSBポートに接続し、Etherケーブルは、ホストPCと同一ネットワーク上のHubなどに接続してください。

 EV3のIPアドレスは、デフォルトではDHCPサーバーから取得します。接続するネットワーク環境でDHCPサーバーが動作している事を予め確認しておいてください。  DHCPサーバーの準備ができ無い場合は、U-BOOTの環境変数で固定IPアドレスを設定する事も可能です。固定IPアドレスの設定方法は、後ほど説明します。

 EV3の電源ボタン(センターボタン)を押し、起動します。u-bootのコンソールのコマンドラインを操作するには、u-bootの起動時にシリアルターミナルから小文字のLを入力します。但し、デフォルトのu-bootの設定では、u-bootの待ち時間が0に設定されていますので、U-Bootの起動メッセージが出力されてから、”l”キーを入力しても間に合いません。そこで、EV3の電源を投入する前に、シリアルターミナルで‘l’(小文字のL)を予め押しておいて、EV3の電源を投入すると、U-Bootのコマンドラインに入る事が出来ます。

U-Bootのコマンドラインに入ると、次の様なプロンプトが表示されます。

U-Boot>

helpと入力するとU-Bootコマンドラインのヘルプ表示がされます。

U-Boot> help

次に、U-Boot起動時のDelay時間を5秒にしておきます。

U-Boot> setenv bootdelay 5

これだけだとU-Bootの環境変数を変えただけなので、再起動するとデフォルトの値に戻ってしまいます。そこで次のコマンドで、u-bootの環境変数をFlashメモリーに書き込んでおきます。

U-Boot> saveenv

こうしておくと、次回の起動時では、u-bootのコマンド入力待ちが5秒間ありますので、その間に、シリアルターミナルから何らかのキー入力を行うと、U-Bootのコンソールを取る事が出来ます。

UbootのコンソールからLinuxカーネルを起動する場合は次のコマンドを入力します。

U-Boot> boot

DHCPサーバーにEV3が接続できないといった環境の場合、U-Bootの環境変数の設定で固定のIPアドレスを指定する事が出来ます。次の様なコマンドで設定します。この例では192.168.10.200に設定しています。

U-Boot> setenv ipaddr 192.168.10.200
U-Boot> saveenv

 続いて、Linux起動後のtelnetによる接続について説明します。USB-Etherアダプタが接続されている場合、Linuxカーネルは自動的にEthernetの初期化を行い、telnetサーバーを起動します。

 DHCPサーバーからIPアドレスを取得している場合は、カーネルの起動メッセージをよく見て、EV3に設定されたIPアドレスを確認します。

Configuring network interfaces... eth0: link up, 100Mbps, full-duplex, lpa 0xC5E1
udhcpc (v1.13.2) started
eth0: link up, 100Mbps, full-duplex, lpa 0xC5E1
Sending discover...
Sending select for 192.168.10.103...  ⇐これが設定されたIPアドレスです。
Lease of 192.168.10.103 obtained, lease time 864000
adding dns 192.168.10.1
done.
Setting up IP spoofing protection: rp_filter.
INIT: Entering runlevel: 5
Starting system message bus: dbus.
Setting up VirtualDrive...
Initialize Bluetooth...
Found a Texas Instruments' chip!
Firmware file : /lib/firmware/TIInit_6.2.31.bts
Loaded BTS script version 1
texas: changing baud rate to 2000000, flow control to 1
Device setup complete
Serial Port service registered
Starting telnet daemon.
Starting lms2012

ホスト環境からtelnetでログインする事が出来ます。Login IDはroot、パスワードはありません。

  Host$: telnet 192.168.10.103
  Trying 192.168.10.103...
  Connected to 192.168.10.103.
  Escape character is '^]'.

   ____       _     _ ___
  |  _  |__ __ __| |____| | ___|
  |    _|  |  |  .  |  .  | | _|
  |___|___|___|___|___|_|_|

  Rudolf 2011.01 EV3

  login:

7.SDカードからの起動

「ソースコードからのビルド」によって新たにコンパイルされたファイルをmicroSDカードに書き込み、EV3をSDカードから起動させます。  SDカードのフォーマットを行うためのスクリプトが準備されています。

Host$: cd ~/projects/open_first
Host$: sudo ./format_sdcard.sh

 SDカードがどのデバイスとして認識しているかを予め確認しておいてください。次のコマンドで、どのデバイスとして認識しているかを確認する事が出来ます。

Host$: cat /proc/partitions

フォーマットが終了したら、SDカードを一度外して、再度、挿入してください。

Ubuntuのデフォルトでは、自動的にSDカードを認識しますので、それぞれ次の様にマウントされます。

/media/LMS2012 /media/LMS2012_EXT

自動的にマウントされない場合は、次の様にマニュアルでマウントして下さい。

U-Boot> Host$: sudo mkdir -p /media/LMS2012
Host$: sudo mkdir -p /media/LMS2012_EXT
Host$: sudo Mount /dev/sdb1 /media/LMS2012
Host$: sudo Mount /dev.sdb2 /media/LMS2012_EXT

次に、SDカード上にカーネルとルートファイルシステムをコピーします。これを行うスクリプトも用意されています。

Host$: cd ~/projects/open_first
Host$: sudo ./update_sdcard.sh

 次に、SDカードに新たに作成したファイルシステムで、lms2012というEV3の仮想マシンを起動しない様に設定を一部変更します。  今、作成したSDカード上の次のファイル(シンボリックリンク)を削除します。

Host$: sudo rm /media/LMS2012_EXT/etc/rc5.d/S99lms

これでEV3を起動した際に、自動的にlms2012が起動されなくなります。

最後に、SDカードをumountして、SDカードをホストから取り出します。

Host$: sudo umount /media/LMS2012
Host$: sudo umount /media/LMS2012_EXT

作成したSDカードをEV3に挿入し、電源を投入します。U-Bootの環境変数の設定により、SDカードがある場合は、SDカードのカーネルから起動されます。

ちなみに、lms2012が自動起動されない環境で、後からlms2012を起動するには、EV3のコマンドラインから次の様に起動します。

root@EV3 /etc/init.d/lms start

8.NFS環境の設定

SDカードからの起動では、SDカードのファイルシステムをルートファイルシステムとして使用します。

EV3の電源を落として、SDカードをホストにマウントします。そして、ルートファイルシステム全体をホスト上のディスクにコピーします。

Host$: mkdir -p ~/projects/filesys
Host$: cd ~/projects/filesys
Host$: sudo cp -a /media/LMS2012_EXT/* ~/projects/filesys/

次に、ホストのNFSサーバーの設定を行います。次の様にエディタを起動し、/etc/exportsというファイルを作成します。

Host$: sudo vi /etc/exportfs

/etc/exportfsの中身は次の様にします。既にNFSの設定がされている場合は、次の行を追加します。XXXXXは、それぞれのユーザー名になります。

/home/XXXXX/projects/filesys *(rw,sync,no_subtree_check,no_root_squash)

/etc/exportfsの変更内容を反映させて、NFSサーバーを再起動します。

Host$: sudo exportfs -a
Host$: sudo /etc/init.d/nfs-kernel-server restart

EV3の電源を投入して、SDカードから起動します。U-Bootのコマンドラインに入って次のコマンドで、環境変数を設定します。

U-Boot> setenv mmcargs setenv bootargs mem=${memsize} console=${console}
noinitrd rw ip=192.168.10.200 root=/dev/nfs
nfsroot=192.168.10.35:/home/XXXX/projects/filesys,nolock,rsize=1024

(長いですが1行で入力してください)

 この環境変数設定に含まれる“ip=192.168.10.200”というのは、EV3のEthernetのIPアドレスを指定します。  また、nfsroot=192.168.10.35:/home/XXXXX/projects/filesysの部分は、NFSサーバーを動かしているホストのIPアドレスとマウントできるディレクトリ名になります。XXXXXはユーザーIDですので、それぞれの環境に合わせて下さい。

環境変数を保存し、カーネルの起動を行うと、ルートファイルシステムをNFSマウントして起動します。

U-Boot> saveenv
U-Boot> boot

9.簡単なC言語によるプログラミング

ここからは、実際に簡単なC言語のプログラムによってEV3を制御する方法を紹介します。

9-1 LED点滅 EV3には、ボタンの左右に2個のLEDが付いてます。それぞれのLEDは赤/緑の2色LEDとなっているため、合計4つのLEDのON/OFFを制御する事が出来ます。

LEDテストプログラム様のディレクトリを作成します。

Host$: cd ~/projects/lms2012
Host$: mkdir -p led-test/source Host$: cd led-test/source

viなどのテキストエディタを使用して次のプログラム(led.c)とMakefileを作成します。

led.c

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

/#define BLACK        0+'0'
#define GREEN        1+'0'
/#define RED           2+'0'
#define ORANGE       3+'0'
#define GREEN_FLASH   4+'0'
#define RED_FLASH     5+'0''
#define ORANGE_FLASH  6+'0'
#define GREEN_PULSE  7+'0'
#define RED_PULSE     8+'0'
#define ORANGE_PULSE 9+'0'

int uifp;   /* File descripter for /dev/lms_ui */

int SetLed(unsigned char pat)
{
    unsigned char Buf[2];
    int ret;
    Buf[0] = pat; /* 上記Defineされているパターンを渡します */
    Buf[1] = 0;
    ret = write(uifp, Buf, 2); /* /dev/lms_uiにwrite()でパラメータを渡している */
    return ret;
}

int main(void)
{
    int cnt;

    uifp = open("/dev/lms_ui",O_RDWR); /* LED/KEY用デバイスドライバをオープン */
     if (uifp < 0) {
        printf("Cannot open /dev/lms_ui\n");
        exit(-1);
     }

    for (cnt=1; cnt<5; cnt++) {
        SetLed(BLACK);
        sleep(2);
        SetLed(GREEN);
        sleep(2);
        SetLed(RED);
        sleep(2);
        SetLed(ORANGE);
        sleep(2);
        SetLed(GREEN_FLASH);
        sleep(2);
        SetLed(RED_FLASH);
        sleep(2);
        SetLed(ORANGE_FLASH);
        sleep(2);
        SetLed(GREEN_PULSE);
        sleep(2);
        SetLed(RED_PULSE);
        sleep(2);
        SetLed(ORANGE_PULSE);
        sleep(2);
    }
    close(uifp);
    return 1;
}

Makefile

SOURCES = led.c
SUBDIRS = led

TARGET = led

CONF = Linux
ARCH = AM1808

include ../../open_first/rules.mk

key.cとMakefileが出来ている事を次の様に確認します。

Host$: ls ~/projects/lms2012/led-test/source
Makefile led.c

作成したプログラムを次の様にコンパイルします。

Host$: make led

ビルドしたledというファイルをEV3のファイルシステムの/home/rootにコピーしてください。EV3のルートファイルシステムをSDカードで使用している場合は、SDカードにコピーしてください。NFS環境の場合は、所定のディレクトリにコピーします。

EV3のコンソールで、次の様なコマンドを実行します。まず、初めにd_ui.koというカーネルモジュールをlnsmodコマンドでロードします。次に、このカーネルモジュールで使用しているデバイスファイル(/dev/lms_ui)のパーミッションを変更します。最後に、上記でビルドしたledというアプリケーションを実行します。

root@EV3:~# insmod /home/root/lms2012/sys/mod/d_ui.ko `cat /home/root/lms2012/sys/settings/HwId` (1行で入力してください)
root@EV3:~# chmod 666 /dev/lms_ui
root@EV3:~# ./led

 2秒毎に10パターンのLED表示が行われます。このプログラム(led.c)は5回のループで抜けて終了しますが、EV3のLED表示は、d_ui.koモジュールが表示を続けていますので、ORANGE_PULSEの状態のままになっています。  次の様に、d_ui.koカーネルモジュールを外すと、LED表示は止まります。

root@EV3:~# rmmod d_ui

9-2 キー入力  次にEV3本体のボタンの入力を確認するテストプログラムを作成します。

EV3には、6個の押しボタンが付いています。このプログラムでは、1秒毎にボタンの押されている状態を表示します。 KEYテストプログラム様のディレクトリを作成します。

Host$: cd ~/projects/lms2012
Host$: mkdir -p key-test/source
Host$: cd led-test/source

viなどのテキストエディタを使用して次のプログラム(key.c)とMakefileを作成します。

key.c:

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h> typedef struct {       unsigned char Pressed[6]; } KEYBUF; int uifp; int SetLed(unsigned char pat) {      unsigned char Buf[2];      int ret;      Buf[0] = pat;      Buf[1] = 0;      ret = write(uifp, Buf, 2);      return ret; } int main(void) {      KEYBUF *KeyPt;      int cnt;      uifp = open("/dev/lms_ui",O_RDWR);      if (uifp < 0) {          printf("Cannot open /dev/lms_ui\n");          exit(-1);      }      KeyPt = (KEYBUF *)mmap(0, sizeof(KEYBUF), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, uifp, 0);      if (KeyPt == MAP_FAILED) {          printf("mmap failed\n");          exit(-1);       }      for (cnt=0; cnt<30; cnt++) {          printf("%d %d %d %d %d %d \r\n",             (*KeyPt).Pressed[0],             (*KeyPt).Pressed[1],             (*KeyPt).Pressed[2],             (*KeyPt).Pressed[3],             (*KeyPt).Pressed[4],             (*KeyPt).Pressed[5]);         sleep(1);      }      munmap(KeyPt, sizeof(KEYBUF));      close(uifp);      return 1; }

Makefile:

SOURCES = key.c
SUBDIRS = key-test

TARGET = key

CONF = Linux
ARCH = AM1808

include ../../open_first/rules.mk

 key.cとMakefileが出来ている事を次の様に確認します。

Host$: ls ~/projects/lms2012/key-test/source Makefile key.c

作成したプログラムを次の様にコンパイルします。

Host$: make key

 ビルドしたkeyというファイルをEV3のファイルシステムの/home/rootにコピーしてください。EV3のルートファイルシステムをSDカードで使用している場合は、SDカードにコピーしてください。NFS環境の場合は、所定のディレクトリにコピーします。

 EV3のコンソールで、次の様なコマンドを実行します。まず、初めにd_ui.koというカーネルモジュールをlnsmodコマンドでロードします。次に、このカーネルモジュールで使用しているデバイスファイル(/dev/lms_ui)のパーミッションを変更します。最後に、上記でビルドしたkeyというアプリケーションを実行します。

root@EV3:~# insmod /home/root/lms2012/sys/mod/d_ui.ko `cat /home/root/lms2012/sys/settings/HwId`(1行で入力してください) root@EV3:~# chmod 666 /dev/lms_ui root@EV3:~# ./key

1秒毎に10パターンのKEYの入力状態が表示されます。EV3の6個のキーを押すと出力が変わる事が確認できます。このプログラムは、30回のループで終了します。 次の様に、d_ui.koカーネルモジュールを外してください。

root@EV3:~# rmmod d_ui

9-3 モーター制御  EV3には、4つのOutputポートにモーターを接続して制御する事が出来ます。ボタンの左右に2個のLEDが付いてます。それぞれのLEDは赤/緑の2色LEDとなっているため、合計4つのLEDのON/OFFを制御する事が出来ます。

LEDテストプログラム様のディレクトリを作成します。

Host$: cd ~/projects/lms2012
Host$: mkdir -p motor-test/source
Host$: cd motor-test/source

viなどのテキストエディタを使用して次のプログラム(motor.c)とMakefileを作成します。

motor.c:


#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

#define CH1   0x01
#define CH2   0x02
#define CH3   0x04
#define CH4   0x08


/* EV3のバイトコードの定義からの抜粋です(from bytecode.h) */
typedef enum
{
 opPROGRAM_STOP       = 0x02, //     0010
 opPROGRAM_START      = 0x03, //     0011

 opOUTPUT_GET_TYPE     = 0xA0, //     00000
 opOUTPUT_SET_TYPE     = 0xA1, //     00001
 opOUTPUT_RESET        = 0xA2, //     00010
 opOUTPUT_STOP         = 0xA3, //     00011
 opOUTPUT_POWER        = 0xA4, //     00100
 opOUTPUT_SPEED        = 0xA5, //     00101
 opOUTPUT_START         = 0xA6, //     00110
 opOUTPUT_POLARITY      = 0xA7, //     00111
 opOUTPUT_READ          = 0xA8, //     01000
 opOUTPUT_TEST           = 0xA9, //      01001
 opOUTPUT_READY         = 0xAA, //     01010
 opOUTPUT_POSITION       = 0xAB, //     01011
 opOUTPUT_STEP_POWER   = 0xAC, //     01100
 opOUTPUT_TIME_POWER    = 0xAD, //     01101
 opOUTPUT_STEP_SPEED    = 0xAE, //     01110
 opOUTPUT_TIME_SPEED     = 0xAF, //     01111

 opOUTPUT_STEP_SYNC     = 0xB0, //     10000
 opOUTPUT_TIME_SYNC      = 0xB1, //     10001
 opOUTPUT_CLR_COUNT     = 0xB2, //     10010
 opOUTPUT_GET_COUNT     = 0xB3, //     10011

 opOUTPUT_PRG_STOP      = 0xB4, //     10100
} OP;


int pwmfp;

int Stop(unsigned char ch)
{
    unsigned char Buf[4];
    int ret;

    Buf[0] = opOUTPUT_STOP;
    Buf[1] = ch;
    ret = write(pwmfp,Buf,2);
    return ret;
}

int PrgStart(void)
{
    unsigned char Buf[4];
    int ret;

    Buf[0] = opPROGRAM_START;
    ret = write(pwmfp,Buf,1);
    return ret;
}

int PrgStop(void)
{
    unsigned char Buf[4];
    int ret;
  
  Buf[0] = opPROGRAM_STOP;
    ret = write(pwmfp,Buf,1);
    return ret;
}


int Start(void)
{
    unsigned char Buf[4];
    int ret;

    Buf[0] = opOUTPUT_START;
    Buf[1] = CH1 | CH2 | CH3 | CH4;
    ret = write(pwmfp,Buf,2);
    return ret;
}

int Power(unsigned char ch, unsigned char power)
{
    unsigned char Buf[4];
    int ret;

    Buf[0] = opOUTPUT_POWER;
    Buf[1] = ch; /* 複数のCHを指定する時は CH1 | CH2 といった形式で */
    Buf[2] = power;
    ret = write(pwmfp,Buf,3);
    return ret;
}

int SetSpeed(unsigned char ch, unsigned char speed)
{
    
unsigned char Buf[4];
    int ret;

    Buf[0] = opOUTPUT_SPEED;
    Buf[1] = ch;
    Buf[1] = speed;
    ret = write(pwmfp,Buf,3);
    return ret
}


int Reset(unsigned char ch)
{
    unsigned char Buf[4];
    int ret;

    Buf[0] = opOUTPUT_RESET;
    Buf[1] = ch;
    ret = write(pwmfp,Buf,2);
    return ret;
}

int main(void)
{

    pwmfp = open("/dev/lms_pwm",O_RDWR);
    if (pwmfp < 0) {
        printf("Cannot open dev/lms_pwm\n");
        exit(-1);
     }

    PrgStop();      /* PWMの制御を停止します。おまじない(?) */
    PrgStart();     /* PWMのプログラムでの制御開始します。 */
    Reset(CH1|CH2|CH3|CH4);

    Start(); /* PWMによるモーター制御をEnableにします */
    printf("Start\n");
    sleep(2);

    Power(CH4, 30); /* Output CH4 のモーターを30% のスピードで動かします。 */
    sleep(2);

    Power(CH4, 60); /* Output CH4 のモーターを60% のスピードで動かします。 */
    sleep(2);

    Power(CH4, 100); /* Output CH4 のモーターを100% のスピードで動かします。 */
    sleep(2);

    Power(CH4, 0); /* Output CH4 のモーターを0% のスピードにし停止します。 */
    sleep(2);

    Power(CH4, (unsigned char)-30); /* CH4のモーターを逆向きに30%のスピードで動かします */
    sleep(2);

    Power(CH4, (unsigned char)-60); /* CH4のモーターを逆向きに60%のスピードで動かします */
    sleep(2);

    Power(CH4, (unsigned char)-100); /* CH4のモーターを逆向き100%のスピードで動かします */
    sleep(2);

    Power(CH4, 0); /* Output CH4 のモーターを0% のスピードにし停止します。 */
    sleep(2);

    Stop(CH4); /* PWMモーターの制御を停止します。この時点ではテンションが */
    printf("STOP\n"); /* かかった状態で停止しています。                */
    sleep(2);

     PrgStop(); /* PWMの処理を停止します。モーターに掛かっていたテンション */
    printf("ProgStop\n"); /* が解放されます。 */

    return 1;
}

Makefile:

SOURCES = motor.c
SUBDIRS = motor-test

TARGET = motor

CONF = Linux
ARCH = AM1808

include ../../open_first/rules.mk

key.cとMakefileが出来ている事を次の様に確認します。

Host$: ls ~/projects/lms2012/motor-test/source
Makefile motor.c

作成したプログラムを次の様にコンパイルします。

Host$: make motor

ビルドしたmotorというファイルをEV3のファイルシステムの/home/rootにコピーしてください。EV3のルートファイルシステムをSDカードで使用している場合は、SDカードにコピーしてください。NFS環境の場合は、所定のディレクトリにコピーします。
 EV3のOutputポートDにモーターを接続してください。Lモーター、Mモーターのどちらでも構いません。
 EV3のコンソールで、次の様なコマンドを実行します。まず、初めにd_pwm.koというカーネルモジュールをlnsmodコマンドでロードします。次に、このカーネルモジュールで使用しているデバイスファイル(/dev/lms_pwm)のパーミッションを変更します。最後に、上記でビルドしたmotorというアプリケーションを実行します。

root@EV3:~# insmod /home/root/lms2012/sys/mod/d_pwm.ko `cat /home/root/lms2012/sys/settings/HwId(1行で入力してください)
root@EV3:~# chmod 666 /dev/lms_pwm
root@EV3:~# ./motor

 2秒毎に3パターンのスピードでモーターが回転し、次に逆回転で3パターンのスピードでモーターが回転し、停止します。  次の様に、d_ui.koカーネルモジュールを外してください。

root@EV3:~# rmmod d_pwm

付属A.minicomの設定

 minicom は、オープンソースの端末エミュレーションおよびモデムインターフェースプログラムです。Ubuntuでは次のコマンドによりインストールします。

Host$: sudo apt-get install minicom

次に、minicom の設定を行うには、以下の手順を実行してください。

(1)次のコマンドを入力します。Minicomの設定ファイルを更新するため、sudoで実行してください。

Host$: sudo minicom -s

minicom コンフィグレーションメニュー(設定メニュー)が表示されます。

表示が文字化けしてしまう場合は、次の様に環境変数を指定して起動してください。

Host$: LANG=C sudo minicom -s

(2)Serial port setup( シリアルポート) を選択し、ターゲットに適したオプションを選択します。EV3では、次のように設定します。      シリアルデバイス :/dev/ttyUSB0
     速度/パリティ/ビット : 115200 8N1
     ハードウェア制御 : いいえ
     ソフトウエア制御 : いいえ

Enter キーを押してメインコンフィグレーションメニューに戻ります。

(3)Modem and dialing( モデムとダイヤル) を選択し、下記のオプションを設定します。

(4)A-Init String(初期化文字列)、 B-Reset String(リセット文字列)、 K-Hang-up string (ハングアップ文字列) の値は削除してください。

Enter キーを押してメインコンフィグレーションメニューに戻ります。

(5)Save as dfl を選択して、デフォルト設定として保存します。

(6)Exit form Minicom(Minicom の終了) を選択します。   次回から、次のコマンドによりminicomを使用してEV3と通信できます。

$ sudo minicom

(7)minicom を終了するには、以下の手順を実行してください。   次のコマンドを使用します。    control-A X Minicom 終了の確認プロンプトが表示されます。

Enter キーを押して終了します。

付録B.uBoot 環境変数の初期値

間違ってU-Bootの環境変数を書き換えてしまった場合、Linuxカーネルが起動しなくなるケースがあります。参照用にデフォルトのuBootの環境変数の設定を以下に示します。

デフォルトのuBoot 環境変数

U-Boot > printenv
bootcmd=if mmc rescan 0; then if run loadbootscr; then run bootscript; else if run loadimage; then run mmcargs; run mmcboot; else run flashargs; run flashboot; fi; fi; else run flashargs; run flashboot;
fi
baudrate=115200
bootfile="uImage
hostname=EV3
memsize=64M
filesyssize=10M
ipaddr=dhcp
verify=n
console=ttyS1,115200n8
bootscraddr=0xC0600000
loadaddr=0xC0007FC0
filesysaddr=0xC1180000
mmcargs=setenv bootargs mem=${memsize} console=${console} root=/dev/mmcblk0p2 rw rootwait ip=${ipaddr} lpj=747520
mmcboot=bootm ${loadaddr}
flashargs=setenv bootargs mem=${memsize} initrd=${filesysaddr},${filesyssize} root=/dev/ram0 rw rootfstype=cramfs console=${console} ip=${ipaddr} lpj=747520 quiet
flashboot=sf probe 0; sf read ${loadaddr} 0x50000 0x210000; sf read ${filesysaddr} 0x250000 0x9B0000; bootm ${loadaddr}
loadimage=fatload mmc 0 ${loadaddr} uImage
loadbootscr=fatload mmc 0 ${bootscraddr} boot.scr
bootscript=source ${bootscraddr}
bootdelay=0
stdin=serial
stdout=serial
stderr=serial
ver=U-Boot 2009.11 (Oct 26 2012 - 10:30:38)

著作者: フォーティシックスラボ (http://www.46lab.com) 木内志朗 (skichi@46lab.com)

ライセンス情報: クリエイティブ・コモンズ・ライセンス EV3 Native Linux Programming by Forty Six Lab. is licensed under a Creative Commons 表示 - 非営利 - 継承 3.0 非移植 License.

・LEGO、MINDSTORMSはLEGOグループの登録商標です。
・Linux はLinus Torvalds 氏の登録商標であり、登録商標のLinux® は、Linux Mark Institute からのサブライセンス(Linus Torvalds 氏の占有ライセンス) に従って使用されます。 ・他の商標、登録商標、トレードマークはそれぞれの所有者の所有物です。

改版履歴:
2013/9/26 Ver0.3 章立て、誤記修正など
2013/9/25 Ver0.2 章立て、フォント修正など
2013/9/24 Ver0.1 初版

SPIKE