BeagleBone BlackのAngstrom Linuxに入ってるNode.jsについて

ちょっとだけ調べてみました。

Nodeのバージョン

root@beaglebone:~# node -v
v0.8.22

Node Package Managerも入っているようです

root@beaglebone:~# npm

Usage: npm <command>

where <command> is one of:
    add-user, adduser, apihelp, author, bin, bugs, c, cache,
    completion, config, ddp, dedupe, deprecate, docs, edit,
    explore, faq, find, find-dupes, get, help, help-search,
    home, i, info, init, install, isntall, issues, la, link,
    list, ll, ln, login, ls, outdated, owner, pack, prefix,
    prune, publish, r, rb, rebuild, remove, restart, rm, root,
    run-script, s, se, search, set, show, shrinkwrap, star,
    stars, start, stop, submodule, tag, test, tst, un,
    uninstall, unlink, unpublish, unstar, up, update, version,
    view, whoami

npm <cmd> -h     quick help on <cmd>
npm -l           display full usage info
npm faq          commonly asked questions
npm help <term>  search for help on <term>
npm help npm     involved overview

Specify configs in the ini-formatted file:
    /home/root/.npmrc
or on the command line via: npm <command> --key value
Config info can be viewed via: npm help config

npm@1.2.14 /usr/lib/node_modules/npm

Nodeのパッケージを調べてみる

root@beaglebone:~# npm list -g
/usr/lib
├─┬ bonescript@0.2.2
│ ├─┬ express@3.1.0
│ │ ├── buffer-crc32@0.1.1
│ │ ├── commander@0.6.1
│ │ ├─┬ connect@2.7.2
│ │ │ ├── bytes@0.1.0
│ │ │ ├── formidable@1.0.11
│ │ │ ├── pause@0.0.1
│ │ │ └── qs@0.5.1
│ │ ├── cookie@0.0.5
│ │ ├── cookie-signature@0.0.1
│ │ ├── debug@0.7.2
│ │ ├── fresh@0.1.0
│ │ ├── methods@0.0.1
│ │ ├── mkdirp@0.3.3
│ │ ├── range-parser@0.0.4
│ │ └─┬ send@0.1.0
│ │   └── mime@1.2.6
│ ├── UNMET DEPENDENCY serialport 0.7.5
│ ├─┬ socket.io@0.8.7
│ │ ├── policyfile@0.0.4
│ │ ├── redis@0.6.7
│ │ └─┬ socket.io-client@0.8.7
│ │   ├── uglify-js@1.0.6
│ │   ├── websocket-client@1.0.0
│ │   └── xmlhttprequest@1.2.2
│ ├── systemd@0.2.0
│ └─┬ winston@0.6.2
│   ├── async@0.1.22
│   ├── colors@0.6.0-1
│   ├── cycle@1.0.2
│   ├── eyes@0.1.8
│   ├── pkginfo@0.2.3
│   ├── request@2.9.203
│   └── stack-trace@0.0.6
└─┬ npm@1.2.14
  ├── abbrev@1.0.4
  ├── ansi@0.1.2
  ├── archy@0.0.2
  ├── block-stream@0.0.6
  ├── chmodr@0.1.0
  ├── chownr@0.0.1
  ├── fstream@0.1.22
  ├─┬ fstream-npm@0.1.3
  │ └── fstream-ignore@0.0.6
  ├── glob@3.1.21
  ├── graceful-fs@1.2.0
  ├── inherits@1.0.0
  ├── ini@1.1.0
  ├─┬ init-package-json@0.0.6
  │ └── promzard@0.2.0
  ├── lockfile@0.3.0
  ├── lru-cache@2.2.2
  ├─┬ minimatch@0.2.11
  │ └── sigmund@1.0.0
  ├── mkdirp@0.3.5
  ├── node-gyp@0.8.5
  ├── nopt@2.1.1
  ├─┬ npm-registry-client@0.2.18
  │ └── couch-login@0.1.15
  ├─┬ npmconf@0.0.23
  │ └─┬ config-chain@1.1.5
  │   └── proto-list@1.2.2
  ├── npmlog@0.0.2
  ├── once@1.1.1
  ├── opener@1.3.0
  ├── osenv@0.0.3
  ├─┬ read@1.0.4
  │ └── mute-stream@0.0.3
  ├── read-installed@0.1.1
  ├── read-package-json@0.2.2
  ├── request@2.9.203
  ├── retry@0.6.0
  ├── rimraf@2.1.4
  ├── semver@1.1.4
  ├── slide@1.1.3
  ├── tar@0.1.16
  ├── uid-number@0.0.3
  └── which@1.0.5

npm ERR! missing: serialport@0.7.5, required by bonescript@0.2.2
npm ERR! not ok code 0

ふむふむ、bonescriptはもちろん、express, socket.io, serialportなどがグローバルインストールされていました。ということは、AngstromのままでもNode.jsでプログラム書いて動かして遊ぶのもいいですね

BeagleBone BlackにNode.jsをインストールする2(セルフビルド)

前回のおさらい

見事にビルドに失敗・・・ググってみたら、傘のお肉はどこにあるの?知り合いの@iwata_nのブログに引っ掛かったw

つまりは

./configure --without-snapshot

が必要ということなので、nodebrewのソースを修正して対応します。

nodebrew本体のコード(~./nodebrew/nodebrew)の「sub _cmd_install」にあるConfigureに--without-snapshotを追加します

system qq[
    cd "$src_dir/$target_name" &&
    ./configure --without-snapshot --prefix="$self->{node_dir}/$version" &&
    make &&
    make install
];

これでもう一度インストールしてみます。一旦さっきのバージョンをuninstallしておいてから再度インストール。

ubuntu@ubuntu-armhf:~$ nodebrew uninstall v0.10.21
v0.10.21 uninstalled
ubuntu@ubuntu-armhf:~$ nodebrew ls
not installed

current: none
ubuntu@ubuntu-armhf:~$ nodebrew install v0.10.21
fetch: http://nodejs.org/dist/v0.10.21/node-v0.10.21.tar.gz
######################################################################## 100.0%
{ 'target_defaults': { 'cflags': [],
                       'default_configuration': 'Release',
                       'defines': [],
                       'include_dirs': [],
                       'libraries': []},
  'variables': { 'arm_fpu': 'vfpv3',
                 'arm_neon': 0,
                 'armv7': 1,
                 'clang': 0,
                 'gcc_version': 46,
                 'host_arch': 'arm',
                 'node_install_npm': 'true',
                 'node_prefix': '/home/ubuntu/.nodebrew/node/v0.10.21',
                 'node_shared_cares': 'false',
                 'node_shared_http_parser': 'false',
                 'node_shared_libuv': 'false',
                 'node_shared_openssl': 'false',
                 'node_shared_v8': 'false',
                 'node_shared_zlib': 'false',
                 'node_tag': '',
                 'node_unsafe_optimizations': 0,
                 'node_use_dtrace': 'false',
                 'node_use_etw': 'false',
                 'node_use_openssl': 'true',
                 'node_use_perfctr': 'false',
                 'node_use_systemtap': 'false',
                 'python': '/usr/bin/python',
                 'target_arch': 'arm',
                 'v8_enable_gdbjit': 0,
                 'v8_no_strict_aliasing': 1,
                 'v8_use_arm_eabi_hardfloat': 'true',
                 'v8_use_snapshot': 'false'}}

「'v8_use_snapshot': 'false'」になっています。失敗した時のログを見てみると

fetch: http://nodejs.org/dist/v0.10.21/node-v0.10.21.tar.gz
######################################################################## 100.0%
{ 'target_defaults': { 'cflags': [],
                       'default_configuration': 'Release',
                       'defines': [],
                       'include_dirs': [],
                       'libraries': []},
  'variables': { 'arm_fpu': 'vfpv3',
                 'arm_neon': 0,
                 'armv7': 1,
                 'clang': 0,
                 'gcc_version': 46,
                 'host_arch': 'arm',
                 'node_install_npm': 'true',
                 'node_prefix': '/home/ubuntu/.nodebrew/node/v0.10.21',
                 'node_shared_cares': 'false',
                 'node_shared_http_parser': 'false',
                 'node_shared_libuv': 'false',
                 'node_shared_openssl': 'false',
                 'node_shared_v8': 'false',
                 'node_shared_zlib': 'false',
                 'node_tag': '',
                 'node_unsafe_optimizations': 0,
                 'node_use_dtrace': 'false',
                 'node_use_etw': 'false',
                 'node_use_openssl': 'true',
                 'node_use_perfctr': 'false',
                 'node_use_systemtap': 'false',
                 'python': '/usr/bin/python',
                 'target_arch': 'arm',
                 'v8_enable_gdbjit': 0,
                 'v8_no_strict_aliasing': 1,
                 'v8_use_arm_eabi_hardfloat': 'true',
                 'v8_use_snapshot': 'true'}}

こちらはtrueになっていました。

さてまたしても1時間経過・・・ビルド完了

symlinking ../lib/node_modules/npm/bin/npm-cli.js -> /home/ubuntu/.nodebrew/node/v0.10.21/bin/npm
updating shebang of /home/ubuntu/.nodebrew/node/v0.10.21/bin/npm to /home/ubuntu/.nodebrew/node/v0.10.21/bin/node
ubuntu@ubuntu-armhf:~$ nodebrew ls
v0.10.21

current: none
ubuntu@ubuntu-armhf:~$ nodebrew use v0.10.21
use v0.10.21
ubuntu@ubuntu-armhf:~$ node
>

というわけで、無事Node.jsが起動できるようになりました。

BeagleBone BlackにNode.jsをインストールする(セルフビルド)

いつものようにNode.jsをインストールします。今回はnvmではなくnodebrewを使い、セルフビルドにてインストールします。

BeagleBone Black用のNode.js

ビルドするのが面倒な方はこちらにインストールバイナリがありますので、こちらを使ったほうが楽チンです。その場合はnodebrewは必要ありません。このリンク先のNode.jsのバージョンはv0.10.21なので、同じバージョンをnodebrewでセルフビルドでインストールします

ビルド環境のインストール

ubuntu@ubuntu-armhf:~$ sudo apt-get install curl build-essential libssl-dev

nodebrewのインストール

nodebrewに関してはBlock Rockin’ Codesに記事があるのでご参考に。nodebrewのサイトはこちらです。サイトにあるように下記コマンドでインストールできます。なおnodebrewをインストールするとnpm(Node Package Manager)も同時にインストールされます。

ubuntu@ubuntu-armhf:~$ curl -L git.io/nodebrew | perl - setup
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 18651  100 18651    0     0  18947      0 --:--:-- --:--:-- --:--:-- 18947
fetching nodebrew...
install nodebrew in $HOME/.nodebrew

========================================
Add path:

export PATH=$HOME/.nodebrew/current/bin:$PATH
========================================

インストールしたら、上記パスをvimなどで.bashrcに追加して、sourceコマンドで読み込みなおしておきます

ubuntu@ubuntu-armhf:~$ vim .bashrc
ubuntu@ubuntu-armhf:~$ source .bashrc 

nodebrew ls-remoteで使えるNode.jsのバージョンが取得できます

ubuntu@ubuntu-armhf:~$ nodebrew ls-remote
-- 省略--
v0.9.0    v0.9.1    v0.9.2    v0.9.3    v0.9.4    v0.9.5    v0.9.6    v0.9.7
v0.9.8    v0.9.9    v0.9.10   v0.9.11   v0.9.12   

v0.10.0   v0.10.1   v0.10.2   v0.10.3   v0.10.4   v0.10.5   v0.10.6   v0.10.7
v0.10.8   v0.10.9   v0.10.10  v0.10.11  v0.10.12  v0.10.13  v0.10.14  v0.10.15
v0.10.16  v0.10.17  v0.10.18  v0.10.19  v0.10.20  v0.10.21  v0.10.22  v0.10.23


v0.11.0   v0.11.1   v0.11.2   v0.11.3   v0.11.4   v0.11.5   v0.11.6   v0.11.7
v0.11.8   v0.11.9   

nodebrewにはバイナリのインストール機能がありますので、試しにv0.10.21をインストールしようとすると、サポートしてないよ!ってエラーが出てしまいます。

ubuntu@ubuntu-armhf:~$ nodebrew install-binary v0.10.21
Error: Linux armv7l is not supported. at /home/ubuntu/.nodebrew/current/bin/nodebrew line 585.

Node.jsのセルフビルドでのインストール

というわけで、セルフビルドでインストールします (ちなみに以下、失敗します...ちゃんと動くようにビルドするにはコチラへ)

ubuntu@ubuntu-armhf:~$ nodebrew install v0.10.21

セルフビルドは約1時間・・・(class2のSDカードだったのでclass10だともっと速いと思われ)

installing /home/ubuntu/.nodebrew/node/v0.10.21/lib/node_modules/npm/lib/utils/completion/installed-shallow.js
installing /home/ubuntu/.nodebrew/node/v0.10.21/lib/node_modules/npm/lib/utils/completion/file-completion.js
symlinking ../lib/node_modules/npm/bin/npm-cli.js -> /home/ubuntu/.nodebrew/node/v0.10.21/bin/npm
updating shebang of /home/ubuntu/.nodebrew/node/v0.10.21/bin/npm to /home/ubuntu/.nodebrew/node/v0.10.21/bin/node

ちゃんとインストールされたか確認

ubuntu@ubuntu-armhf:~$ nodebrew ls
v0.10.21

current: none

v0.10.21を使用するように指定

ubuntu@ubuntu-armhf:~$ nodebrew use v0.10.21
use v0.10.21
ubuntu@ubuntu-armhf:~$ 

さて、ではnodeを起動してみます

ubuntu@ubuntu-armhf:~$ node 
Segmentation fault

_人人人人人人人人人人人_
> Segmentation fault <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

...orz

続く

BeagleBone BlackにUbuntu12.04をインストールする

作業の備忘録として記録しています。Raspberry Piを触っていれば、ほぼ同じような手順ですが・・・ (本当はIntel Galileoで遊ぶつもりが発売が年明けに延びたので、こっちをやっつけることに...)

追記 この方法では、SDカードからのブート時に「ボタン」を押す必要がありますが、きんねこさんが押さなくても起動するように、解決してくれました。 armhf.comで配布しているUbuntuイメージはボタン押さないと起動できない件 解決方法は最後に追記しました

BeagleBone Blackの購入

たまたま寄った秋月電子に最新版のRev A6が売ってたので購入¥4,980

f:id:tomo_watanabe:20131215153638j:plain

UbuntuをSDカードに書き込む

ホストマシンはMacintoshです。まずはBeagleBone Black用のUbuntuイメージをダウンロードします。今回はLTS版のPrecidse 12.04を使います

ダウンロードしたら解凍します。

$ xz -d ubuntu-precise-12.04.3-armhf-3.8.13-bone30.img.xz

解凍して出来たイメージをSDカードに書き込みます。使用するmicroSDは2GB以上のclass10が良いでしょう。ddコマンドで書きこめばいいのですが、Raspberry Piで使用した「RPi-sd card builder」などを使うと楽になります。

Ubuntuから起動

LANケーブルを接続しておき、SDカードを本体に差し込み起動しますが、そのままでは起動しないので、Bootボタンを押しながら電源を入れます。LEDが点滅し始めたら離しても大丈夫のようです(離すまでの時間が曖昧・・・)無事起動していればsshでログインできるはずです。

f:id:tomo_watanabe:20131215163844p:plain

$ ssh ubuntu@192.168.0.6 <— BeagleBone Blackのアドレス
ubuntu@192.168.0.6's password: 
Welcome to Ubuntu 12.04.3 LTS (GNU/Linux 3.8.13-bone30 armv7l)

 * Documentation:  https://help.ubuntu.com/

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

ubuntu@ubuntu-armhf:~$

パーティションの拡張

今回は4GBのSDカードを使ったのですが、そのままではブートイメージのサイズ上2GBしか使用できないので、パーティションの拡張を行います。電子工作マスターへの歩みを参考にfdiskで拡張します

ubuntu@ubuntu-armhf:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
rootfs          1.8G  244M  1.5G  15% /
/dev/root       1.8G  244M  1.5G  15% /
devtmpfs        248M  4.0K  248M   1% /dev
none             50M  224K   50M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            248M     0  248M   0% /run/shm
/dev/mmcblk0p1 1004K  480K  524K  48% /boot/uboot

今はまだサイズが1.8GBになってますので、fdiskで拡張します

ubuntu@ubuntu-armhf:~$ sudo fdisk /dev/mmcblk0
ommand (m for help): p

Disk /dev/mmcblk0: 4023 MB, 4023386112 bytes
4 heads, 16 sectors/track, 122784 cylinders, total 7858176 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x80008000

        Device Boot      Start         End      Blocks   Id  System
/dev/mmcblk0p1   *        2048        4095        1024    1  FAT12
/dev/mmcblk0p2            4096     3751935     1873920   83  Linux

Command (m for help): d
Partition number (1-4): 2

Command (m for help): p

Disk /dev/mmcblk0: 4023 MB, 4023386112 bytes
4 heads, 16 sectors/track, 122784 cylinders, total 7858176 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x80008000

        Device Boot      Start         End      Blocks   Id  System
/dev/mmcblk0p1   *        2048        4095        1024    1  FAT12

Command (m for help): n
Partition type:
   p   primary (1 primary, 0 extended, 3 free)
   e   extended
Select (default p): p
Partition number (1-4, default 2): 
Using default value 2
First sector (4096-7858175, default 4096): 
Using default value 4096
Last sector, +sectors or +size{K,M,G} (4096-7858175, default 7858175): 
Using default value 7858175

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 16: Device or resource busy.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.

ここまで来たら、先のBootボタンを押しながらリブートします

ubuntu@ubuntu-armhf:~$ sudo reboot

起動したら再びsshで接続してログイン後、resizeコマンドを叩きます

ubuntu@ubuntu-armhf:~$ sudo fdisk /dev/mmcblk0

Command (m for help): p

Disk /dev/mmcblk0: 4023 MB, 4023386112 bytes
4 heads, 16 sectors/track, 122784 cylinders, total 7858176 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x80008000

        Device Boot      Start         End      Blocks   Id  System
/dev/mmcblk0p1   *        2048        4095        1024    1  FAT12
/dev/mmcblk0p2            4096     7858175     3927040   83  Linux

Command (m for help): q           

ubuntu@ubuntu-armhf:~$ sudo resize2fs /dev/mmcblk0p2   
resize2fs 1.42 (29-Nov-2011)
Filesystem at /dev/mmcblk0p2 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 1
The filesystem on /dev/mmcblk0p2 is now 981760 blocks long.

ubuntu@ubuntu-armhf:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
rootfs          3.7G  246M  3.3G   7% /
/dev/root       3.7G  246M  3.3G   7% /
devtmpfs        248M  4.0K  248M   1% /dev
none             50M  224K   50M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            248M     0  248M   0% /run/shm
/dev/mmcblk0p1 1004K  480K  524K  48% /boot/uboot
ubuntu@ubuntu-armhf:~$ 

これで領域が3.7GBに拡張されました

ボタンを押さなくてもSDカードブートするように修正

前述のように、きんねこさんがSDカードブートするように解決してくれました。 調査内容はエントリ上部のリンクを参照してください。ここでは修正部分だけ追記します。

nEnv.txtに2行追加します

# cd /boot/uboot
# vim nEnv.txt

mmcpart=2
bootfile=zImage  <---- 追加
optargs=fixrtc
uenvcmd=i2c mw 0x24 1 0x3e; kd=0; if test $mmcdev -eq 1; then mmc dev 0; if mmc
rescan; then kd=1; fi; mmc dev 1; fi; setenv mmcroot /dev/mmcblk${kd}p${mmcpart} ro
loadfdt=load mmc ${mmcdev}:${mmcpart} ${fdtaddr} ${bootdir}/dtbs/${fdtfile}
loadimage=load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${bootfile}
mmcboot=echo Booting from mmc ...; run mmcargs; bootz ${kloadaddr} - ${fdtaddr}  <---- 追加

これで保存して、一度シャットダウンして再度電源を入れればSDから自動起動するようになります\(^o^)/

HTML5 Conference 2013でLTしてきました

きっかけ

第0回HTML5エンタメ部の開催に参加した際に、白石さん@Shumpeiに、こんなネタありますが、と話しをしたら「LTに応募してみてください」ということだったので、軽い気持ちで応募したらなぜか当選/(^o^)\してしまった。ため(どうしてこうなった)、Web開発者とデザイナーのためのイベントと銘打った「HTML5 Conference 2013」という完全アウェイの中、5分間のLTをしてきました。

反応

司会のあんどうさん@technohippy曰く「僕には全然わかりませんでした」と言わしめるほどの撃沈っぷりを発揮し、見事アウェイの洗礼を受けるという...まぁLTなんてのは貰った時間分は宣伝だと思って、スーパージョッキーの「熱湯風呂」みたいなもんだと思ってるので、また次回ガンバローと思うわけですが(^^;

スライドの中に出てくるpush型ナビは時間の関係で動画が見せられませんでしたが、こちらにも置いておきます。(スライド内のリンク先にはWeb記事として記載されています)


車載Raspberry PiでNode.js + Socket.IO「push型カーナビゲ ...

言いたかったこと

さすがに5分でやるのは厳しかったなーと思いつつ補足します。HTML5によってWebと組込みがより近くなるような時代が来ています。ハードウェアというと凄く難しくて、Web開発者からすると手を付けられる領域では無いと思いがちですが、Arduinoなどに代表される最近のマイコンは組込みっぽくなく、C言語の知識さえあればそれほど慣れるのに時間は掛かりません。ましてや今後はJavaScriptで制御できるハードウェアや、Node.js, WebSocketなどがすでに実装されたデバイスが出てくるようです。そうなると、Web開発者でも¥5,000〜¥10,000くらいでオレオレガジェットが作れます。特に、今後出てくるのはネットワーク接続が前提のものが多く、クラウドなどのサーバと組み合わせて、面白いことがどんどんできるようになるんじゃないかと思います。来年は何か作りたいなぁ

とりあえず、個人的にはマスになりそうな技術の中の、ニッチな部分を探してなんかやる。くらいのことしかできない、端っこエンジニアなので、しばらくこのスタンスは変わらないかも

さいごに

セッションはWebプロトコルやネットワーク技術を聞いていましたが、現在の所属先がISPなので、ネットワークインフラ技術などは勉強になりました。また、インフラ側からするとHTML5に移行することにより弊害がすでに予想されており「今までのようにインフラとアプリが別々に考えるのではなく、もっとお互いの領域を理解しながらHTML5をどのように活かしていくか考えなければならない」という投げかけは根本的なところを突いていたように思います。

資料等纏められていました。

HTML5 Conferenceスタッフのみなさん、お疲れ様でした\(^o^)/

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

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

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

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

"i2c.c"のコード

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

LPC800-MAXのI2C

I2Cデバイス

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

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

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

I2Cアドレス

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

f:id:tomo_watanabe:20131026223929p:plain

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

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

f:id:tomo_watanabe:20131026224646p:plain

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

f:id:tomo_watanabe:20131026225125p:plain

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

f:id:tomo_watanabe:20131026225302p:plain

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

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

I2Cのピンアサインの設定

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

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

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

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

I2Cの初期化

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

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

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

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

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

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

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

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

GPIOエキスパンダの操作

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

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

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

スイッチの取得

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

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

まとめ

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

  • GPIO
  • UART
  • I2C

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

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

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


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

LPCXpressoとLPC800-MAXでマイコンを学ぶ(その8:UARTでPCと接続)

今回もサンプルを使ってUARTの動作を確認していきます。

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

まずはサンプルプログラムを動かしてみます。サンプルは例のごとく、import project(s)からNXP_LPC8xx_SampleCodeBundle.zipを選択して「UART」をインポートします。メインはuarttest.cです。このサンプルプログラムはUARTでPCのターミナルソフトなどと接続して、PCのからの入力をループバックでエコーを返すプログラムです。それにはPCとLPC800-MAXを接続する必要があるので接続図を示します。

f:id:tomo_watanabe:20131016131300p:plain

LPC800-MAXの0pinをシリアルアダプタのTX, 1pinをRX, GNDを接続します。注意としてはLPC800-MAXは3.3v駆動なので、USBシリアルアダプタを使う場合、3.3v対応のものを使うことです。PCのターミナルソフトではボーレートを9600に設定しておきます。あとはお決まりの、データビット8,、ストップビット1、 パリティ無し、フローコントール無しです。この状態でdebugビルドで動かしてみます(redlib(semihost)に変更しないとエラーになるので注意)

f:id:tomo_watanabe:20131016124601j:plain

緑のLEDが点灯します(これはおまけに記述)。一方PCのターミナルには"Hello World!"が表示されます。ターミナルの方に文字を入力するとエコーバックされて文字が表示されます。

f:id:tomo_watanabe:20131016132644p:plain

UARTの動作を見る

UARTのピンアサインの設定

コードは#ifでいくつかのブロックになっていますが、これはRXとTXのUARTブロックとピンアサインの話なので#if 1のとこだけ見ます。

/* connect the UART0 TXD abd RXD sigals to port pins(P0.4-P0.0)*/
regVal = LPC_SWM->PINASSIGN0 & ~( 0xFF << 0 );
LPC_SWM->PINASSIGN0 = regVal | ( 4 << 0 );   /* P0.4 is UART0 TXD, ASSIGN0(7:0) */
regVal = LPC_SWM->PINASSIGN0 & ~( 0xFF << 8 );
LPC_SWM->PINASSIGN0 = regVal | ( 0 << 8 );   /* P0.0 is UART0 RXD. ASSIGN0(15:8) */
    
regVal = LPC_SWM->PINASSIGN0 & ~( 0xFF << 16 );
LPC_SWM->PINASSIGN0 = regVal | ( 12 << 16 );  /* P0.12 is UART0 RTS, ASSIGN0(23:16) */
regVal = LPC_SWM->PINASSIGN0 & ~( 0xFFUL << 24 );
LPC_SWM->PINASSIGN0 = regVal | ( 13 << 24 );  /* P0.13 is UART0 CTS. ASSIGN0(31:24) */

この部分はUARTのRX, TX, RTS, CTSのピンアサインを行っています。

  • TXD : PIO0_4
  • RXD : PIO0_0
  • RTS : PIO0_12
  • CTS : PIO0_13

LPC800-MAXの回路図で見ると、TX, RXはそれぞれDigitalピンとしての1pin, 0pinに割りあたっています。ちょうどこのピン配置はArduinoと同じ配置にしてあります(当たり前だけど)

f:id:tomo_watanabe:20131016133814p:plain

ここではPIOにUARTの端子を割り付けるという、いわゆるピンマルチの設定を行っています。LPC800シリーズでは機能の割にピン数が少ないため、デフォルト状態ではすべてGPIOに設定されていますが、これを内部のピンマルチ機能を使って、使いたい機能ブロックのピンをGPIOに割りつけるようになっています。通常ピンマルチは制約が多いのですが、LPC800シリーズではかなり自由に割り付けられるようになっていて、Switch Matrix機能と呼ばれています。詳しいことはLPC800シリーズ - 少ピンパッケージ、スイッチ・マトリックス内蔵 Cortex-M0+に書いてあります。設定を見てみます。ユーザーズマニュアルp122-p124にSwitch Matrixのレジスタ設定があります

f:id:tomo_watanabe:20131016143504p:plain

Pin assigne register 0を見ると、該当するビット範囲(ワード設定)にPIOの番号を書き込むことでアサインされるようです。TXDをPIO0_4にアサインするには4を[7:0]で書き込みます。その他のピンアサインも同様に行っています。

f:id:tomo_watanabe:20131016143837p:plain

UART通信の初期化

初期化でUARTのピンアサインを行ったので、UARTの初期化を行います。

UARTInit(LPC_USART0, 9600);

UARTInit関数です。UARTのコードもARMが提供しているところなので詳細は省きます。まぁ普通に使う分にはそのままでいいんじゃないかと。(厳密にはボーレートの生成クロックの選択など、シビアな設定がありますが)lpc8xx_uart.cで提供されている関数で、ボーレートと使うUARTのチャンネルを設定します。USART(非同期および同期が使える)はLPC812では2チャンネル、もしくは3チャンネル用意されています(パッケージで違う)あれれ?フローコントロールとか、パリティの設定が無いなーと思ったら・・・

(lpc8xx_uart.c)

void UARTInit(LPC_USART_TypeDef *UARTx, uint32_t baudrate)
{
    uint32_t UARTSysClk;
        
  UARTTxEmpty = 1;
    
    UARTClock_Init( UARTx );
    
    UARTSysClk = SystemCoreClock/LPC_SYSCON->UARTCLKDIV;
    
  UARTx->CFG = DATA_LENG_8|PARITY_NONE|STOP_BIT_1; /* 8 bits, no Parity, 1 Stop bit */
//  UARTx->CFG = DATA_LENG_7|PARITY_NONE|STOP_BIT_1; /* 7 bits, no Parity, 1 Stop bit */
//  UARTx->CFG = DATA_LENG_8|PARITY_NONE|STOP_BIT_2; /* 8 bits, no Parity, 2 Stop bit */
//  UARTx->CFG = DATA_LENG_8|PARITY_EVEN|STOP_BIT_1; /* 8 bits, even Parity, 1 Stop bit */
//  UARTx->CFG = DATA_LENG_8|PARITY_ODD|STOP_BIT_1; /* 8 bits, odd Parity, 1 Stop bit */

コメントアウトされてる...orz UARTも詳細を自分で設定する場合は、このドライバ書き直す必要がありますね...(;´Д`)

受信・送信処理

実際の受信・送信処理は下記の部分です。UARTSend関数で"Hello World!"をUARTで送信して、永久ループで受信待ちになっています。送信はUARTのチャンネルと、送信データ、サイズです。

  UARTSend(LPC_USART0, (uint8_t *)"Hello World!\r\n", 14);
  for ( i = 0; i < 0x10000; i++ );

  while ( 1 )
  {
      if ( UARTRxCount && !RxErrorCount )
      {
          LPC_USART0->INTENCLR = RXRDY;  /* Disable RXRDY */
          UARTSend( LPC_USART0, (uint8_t *)UARTRxBuffer, UARTRxCount );
          UARTRxCount = 0;
          LPC_USART0->INTENSET = RXRDY;  /* Re-enable RXRDY */
      }
  }
}

受信ですが、これも実装が微妙な気がしますが(゜.゜)・・・UARTRxCountは受信データ数なので、受信データがあって、かつエラービットが立ってない場合処理を行います。

  1. 受信割り込みクリア
  2. 受信バッファの内容を、そのまま送信
  3. 受信データサイズクリア
  4. 受信割り込み有効

という流れになっています。これで、受信した(PCからの入力)データがエコーバックされる動作となっています。しかし、この実装はドライバで使う変数をここで処理するのはあまり良くないですね...(;´Д`) なんかサンプルコードがこんなのばっかなんだけどいいのかな。


おまけ。なぜLEDが点灯するのか?

さて、UARTの動作は理解できたかと思います。ひとつあれ?っと思う部分があったので補足します。LPC800-MAXでこのサンプルプログラムを走らせると、LEDが緑に点灯するんですね。GPIOの設定した覚えないのに...というわけで、そこを調べてみます。実はBlinkyの時からあった、この最初のデバッグに使うよ。という部分が答えでした。

/* Config CLKOUT, mostly used for debugging. */
regVal = LPC_SWM->PINASSIGN8;
regVal &= ~( 0xFF << 16 );
regVal |= ( 17 << 16 );
LPC_SWM->PINASSIGN8 = regVal;    /* P0.17 is CLKOUT, ASSIGN(23:16). */
CLKOUT_Setup( CLKOUTCLK_SRC_MAIN_CLK );

何をやってるのか調べてみると、CLCKOUTを特定のピンに外部出力していました。このプログラムの場合、PIO0_17にCLCKOUT、おそらく12MHzを出力しています。LPCXpressoとLPC800-MAXでマイコンを学ぶ(その3:LEDの動作(GPIO)を調べる)でやりましたが、LPC800-MAXのボードはPIO0_17は緑のLEDに接続していました。ここにCLCKOUTが出力されたことでPWMとして機能するため、緑色に点灯するというわけです。具体的にやっていることは

  • Switch MatrixのレジスタでCLCKOUTピンをPIO0_17に設定
  • CLCKOUTをメインクロックの12MHzに設定

f:id:tomo_watanabe:20131012211624p:plain

CLKOUTは右真ん中あたりにあります。CLKOUT_Setup関数では、CLKOUTのソースクロックを設定します。関数内ではソースクロックの選択と、CLKOUTのためにCLKOUTDIVで分周しています。ただし、ちょっと不思議なのはCLKOUTDIVは初期設定が0なのでDisableのはずなのにLEDが点灯しているんですよね。はて

f:id:tomo_watanabe:20131016161116p:plain

その後にCLKOUTDIV = 1を書き込むことで、12MHzが出力されるということのはずなのですが、オシロスコープがあればこの辺確かめられるはずだけど持ってない...orz