Quantcast
Channel: Todotaniのはやり物Log
Viewing all 48 articles
Browse latest View live

LPCXpresso LPC1769でFatFsを動かす

$
0
0

前回に引き続き、LPCXpresso + MARY-XBを使って、micro SDカードとFatFsを使えるようにしました。Low level I/Oの部分は、Interfacing ARM controllers with Memory-Cardsの実装をベースにしました。


ソースの入手と変更点

ソースコードは冒頭のリンクから、ChaN's FAT-code with interface for NXP LPC17xx (LPC1766, LPC1768 and others)をたどってダウンロードします。

このソースはOlimex LPC1766-STKがターゲットになっておりSSP1を使っていますが、今回micro SDカードをSSP0に接続しているため、Low level I/OのSSP定義部分だけを変更すれば簡単に動作しそうです。Low level I/OにCMSIS Peripheral Libraryを使っておりダウンロードしたzipにも入っているのですが、バージョンがv1.0とずいぶん古いです。人様の成果をそのまま使うのも何なので、この部分だけ最新のv3.0を使うことにしました(ついでに、Low level I/O codeの学習も兼ねて)。

v3.0ではオリジナルコードで使っている旧APIが削除されたりしており、部分的に書き換えが発生しました。以下変更箇所の概要です。

  • comm.c(コンソール出力用のUARTドライバ):
    割り込みハンドラー内でライブラリが提供しているUART0_StdIntHandler()を呼び出して、割り込み要因毎の処理(送信やら受信)はUART_SetupCbs()関数を使ってCallback関数を登録する方法になっていますが、最新のCMSISライブラリではUART_SetupCbs()関数が削除されています。そのため、割り込みハンドラー内で割り込み要因別の処理を書く形にしました
  • spi_sd_lpc17xx.c(SPIの制御とLow Level I/Oを受け持つ部分):
    使用するSSPポートの変更(SSP1→ SSP0)に加えて、SSPの初期化関連でLibraryの差し替えに伴う変更がありました。SDカードの初期化とその後のファイルI/OでSPIクロックを変えるために、オリジナルはSSP_SetClock()関数を呼んでいますが、v3.0ではpublicな関数として定義されていいません(名前も変わっている)。そのため、SSP_Init()を呼ぶ形で対応。SSP_Init()を呼ぶとSSPのEnable bitがクリアされてしまうため、SSP_Cmd(LPC_SSP0, ENABLE)を再度呼ぶ必要があります。

変更を行った箇所を含むファイル一式は以下です。


プロジェクトのビルド

LPC1769_FatFS.zipをダウンロードして、workspaceにインポートを行います。CMSISライブラリは含んでいないため個別に用意して下さい。最新のCMSISは、このリンクからLPC17xx CMSIS-Compliant Standard Peripheral Firmware Driver Libraryをダウンロードします。

最新版はCore Libraryがv2.01になっているため、LPCXpresso IDEに別のCMSIS Libraryを作っていたのですが、新規プロジェクトを作るとデフォルトの参照先がIDE組み込みのv1.30になってしまい毎回変更が面倒です。そのため、workspace\CMSISv1p30_LPC17xxにPeripheral Library v3.0をコピーします。

  • system_LPC17xx.c → IDE添付版の方が版数が進んでいるため、IDE添付版を使用
  • LPC1700CMSIS\Drivers\includeフォルダ配下のファイル一式 → workspace\CMSISv1p30_LPC17xx\inc
  • LPC1700CMSIS\Drivers\sourceフォルダ配下のファイル一式 → workspace\CMSISv1p30_LPC17xx\src


動作確認

対話式のコマンドシェルから操作を行って、ディスクのマウント→ ファイルの書き込み → 読み出しの試験を行いました。SPIのクロックは30MHzにしています → Peripheral clock (pclk)の1/2

動作確認時のログは以下の通りです。

Hello from the ChaN FatFs Demo on LPC1700
Version 0.0.2, Martin Thomas 7/2010
xprintf is working
CPU Clock:        120MHz
Peripheral Clock: 60MHz
SPI Clock:        30MHz

FatFs module test monitor for LPC17xx/SSP
LFN Enabled, Code page: 1252

>di 0
rc=0
>fi 0
rc=0 FR_OK
>fs
FAT type = FAT32
Bytes/Cluster = 4096
Number of FATs = 2
Root DIR entries = 0
Sectors/FAT = 3744
Number of clusters = 479214
FAT start (lba) = 841
DIR start (lba,clustor) = 2
Data start (lba) = 8329

...0 files, 0 bytes.
0 folders.
1916856 KB total disk space.
1916852 KB available.
>
>fo 10 test1.txt
rc=0 FR_OK
>
>fw 10000000 30
10000000 bytes written with 866 kB/sec.
>fw 10000000 31
10000000 bytes written with 471 kB/sec.
>fw 10000000 32
10000000 bytes written with 535 kB/sec.
>fw 10000000 33
10000000 bytes written with 478 kB/sec.
>fw 10000000 34
10000000 bytes written with 606 kB/sec.
>
>fc
rc=0 FR_OK
>fl
----A 2010/07/15 11:59  50000000  test1.txt     
   1 File(s),  50000000 bytes total
   0 Dir(s), 1912852480 bytes free
>
>fo 1 test1.txt
rc=0 FR_OK
>
>fr 10000000
10000000 bytes read with 2369 kB/sec.
>fr 10000000
10000000 bytes read with 1581 kB/sec.
>fr 10000000
10000000 bytes read with 1655 kB/sec.
>fr 10000000
10000000 bytes read with 1657 kB/sec.
>fr 1    
>fc
rc=0 FR_OK
>fo 1 test1.txt
rc=0 FR_OK
>
>fr 30000000
30000000 bytes read with 2371 kB/sec.
  • 34行目で10MBの文字(文字コード30)を書き込み → 転送レート866KB/sec
  • 36行目以降で、10MB毎に追加書き込みを行うと転送レートが増減します
  • 55行目で、10MBの読み出しを実施 → 転送レート2369KB/sec
  • 57行目以降で、10MB毎に追加読み出しを行うと、書き込み同様に転送レートが低下
  • 64行目でファイルを一旦クローズして新規に読み出しを行うと元通りの転送レートになります


おわりに

転送はFIFOモードでDMAは使っていません。DMAのコードも作りこまれているのですが、"does not work yet"のコメントが入っていたりしてまだ動かないようです。コンパイラオプションでDMAを有効にできますが、割り込みハンドラーが旧CMSIS Library v1.0をベースにしているためそのままでは動きません。DMAの有効化は今後の課題です。

追記:DMAハンドラーを最新CMSISにあわせて修正したらDMA版も動作してくれました。DMA版は未完成という訳ではなく、メモリ上のデータをSSP_TxポートからSDカードに転送する際に、現状はSSP_Rxポートで受信したデータをダミーのバッファーに格納しているのですがこれを読み飛ばす処理がまだできていないようです。(メモリーリードに加えてライトが発生するため性能に影響があると思われます)

DMAを使わないFIFO転送ですが、転送レートはそこそこ出ていると思います。
追記:DMA版はダミーデータの読み書きを行うせいか、FIFO版より若干転送性能が落ちました。

今回の移植は低レベル処理が主体だったので、SWD(JTAG)デバッグに助けられました。SPIクロック変更のためにSSP_Init()を呼ぶとSSPのEnable bitがクリアされる問題は、当初SPIが動かないように見えていたのですが原因が分からず、SSP0CR1レジスタの値を見ながらトレースして原因判明するなどデバッガが活躍してくれました。時々エミュレーターが起動できなくなってWindows再起動で復旧させることがあったのですが、このあたりがもうちょっと安定すると文句なしです。


参考資料


LPCXpresso LPC1769でFatFsを動かす - DMA編

$
0
0

前回の延長で、FatFsをDMAモードで動かしました。オリジナルコードをベースにDMA完了割り込み処理を修正する程度で動いたのですが、そこからDMAの最適化を試みるも敢え無く敗退でした。


動作概要

DMA処理の概要は以下の通りです(オリジナルコードの処理です):

<SD Cardからの読み出し>

  • SDカードとのインターフェースにはSPIを使用しますが、SPIは以下の図に示すとおり、Master (LPC1769)から①ダミーデータ(今回は0xFFを使用)を送信することによって、②Slave (SD Card)のデータ送信を促します

SPI_DataTransfer2

  • そのため、ダミーデータの送信用に0xFFを512個(一回のDMA転送で読み出すデータ量)設定した配列を宣言。データは固定値のためROMに配置
  • DMA_CH0を上記①のダミーデータ送信用として使用
  • DMA_CH1を②の読み出しデータ受信用に使用
  • DMAを起動し、完了割り込みを待つ

<SD Cardへの書き込み>

  • 読み出しの逆で、①送信バッファには書き込みデータを設定。データを書き込むと②ダミーデータを受信します
  • そのため、②ダミーデータ受信用のバッファを512バイト宣言
  • DMA_CH0を書き込みデータの送信用として使用
  • DMA_CH1をダミーデータの受信用に使用
  • DMAを起動し、完了割り込みを待つ

動作概要を見ていただくと分かるのですが、DMAの半分はダミーデータの送受信に使っておりなんとなく無駄が多いです。そこで、以下の最適化にチャレンジしました。


DMA処理の最適化

最適化1:片方向のDMA

SD Card読み出しではCH1しか有効データの処理に使っていないのだから、CH0のDMA送信を止めてしまえという無謀なアイデア。しかし、SPIの仕様としてマスターからダミーデータを送らない限りSD Cardからのデータが出てこないので、敢え無くボツ


最適化2:ダミーデータ用バッファをなくす

送信用のダミーデータは広大なROMに配置すればよいので容量的には気にならないのですが、ダミーデータ受信用のRAMに512バイト無駄使いするのはちともったいないです(まあ、64KBあるのでけちなことを言うなですが・・)。あと、余計なデータ書き込みがあるとバス帯域を消費して転送性能にも影響が出そうです。オリジナルコードの作者さんは、この点を改善しようとして挫折した痕跡がコードに残っていました。

LPC17xx GPDMAの機能として、転送のDestinationバッファのポインタを自動インクリメントする・しないを指定するフラグDI(Destination increment)がDMACCxControlレジスタ内に存在します。CMSISライブラリのGPDMA_Setup() 関数を使ってPeripheral to MemoryのDMAをセットアップするとライブラリ内でDIを有効に設定するのですが、受信データがダミーになる場合はGPDMA_Setup()を呼んだ後でDIを無効にすることによって、ダミーデータを配列に格納せず読み捨てることができないかというものです。受信したダミーデータを汎用レジスタに書き込むだけでRAMを使わないようにすれば、メモリー帯域の節約にもなりそうです。

しなしながら、オリジナルコードのコメントにもありますが、DIを後付で無効にすると、DMA完了割り込みが発生してくれません。原因は分からないのですが、残念ながらボツです。


最適化3:バーストサイズの拡張

GPDMA_Setup() 使ってDMAの初期設定を行うと、Memory - SPI間のDMAではバーストサイズが4byteに設定されます。すなわち、4byte転送する毎にDMACがバスマスタを一旦放棄してMCUが動ける隙間を作ります。リアルタイムOSを使用する場合は、DMAがMCUを占有しないという点でこれくらいのバーストサイズが適切なのかと思いますが、頻繁にバスマスターの遷移を行うせいか、前回行ったソフトベースの転送に比べて若干性能が劣ります(転送性能の詳細は次項に記載)。そのため、バーストサイズの拡大を試みました。

バーストサイズの設定は、GPDMA_Setup() 内でDMACCxControlレジスタのSBSize, DBSizeを設定することで行っているため、ライブラリのコードを変更して強制的に8byteのバーストサイズを設定してみました。結果はP2M(SD Cardからの読み出し)時に、DMA_CH0(ダミーデータの送信)はDMA完了割り込みが発生するのですが、DMA_CH1(SD Cardからの受信データー)の完了割り込みが発生してくれず、NGでした。

SPIクロックを下げる、バッファのアライメントをバーストサイズ境界にする(8byteバーストの場合、DWORD境界に配置)など試したのですが解決せず、残念ながらボツです。


最適化4:ダミーデータ用RAMの配置移動

オリジナルコードではDMA処理関数内のローカル変数として512byteのダミーデータ用配列を宣言しています。すなわち、スタックにダミーデータを取るため、0x1000 0000~0x1000 7FFFのメインSRAMを消費することになります。この領域を使うのはもったいないので、0x2007 C000~0x2008 3FFFのAHB SRAMにバッファを配置してみました。せめてもの最適化ということで・・ ダミーデータバッファの宣言に __attribute__ ((section (".bss.$RAM2*"))) を付けることで配置を制御しています。

結局最適化は動かず、最適化4以外はオリジナルコードのまま(CMSISライブラリv3対応の書き換えのみ実施)となりました。ソースは以下のリンクから:


動作確認結果

以下の通り性能測定を行いました。条件は、前回のソフト転送モードと同等です。

Hello from the ChaN FatFs Demo on LPC1700
Version 0.0.2, Martin Thomas 7/2010
xprintf is working
CPU Clock:        120MHz
Peripheral Clock: 60MHz
SPI Clock:        30MHz

FatFs module test monitor for LPC17xx/SSP
LFN Enabled, Code page: 1252

>di 0
rc=0
>fi 0
rc=0 FR_OK
>
>fo 10 test. 1.txt
rc=0 FR_OK
>
>fw 10000000 30
10000000 bytes written with 833 kB/sec.
>fw 10000000 31
10000000 bytes written with 465 kB/sec.
>fw 10000000 32
10000000 bytes written with 526 kB/sec.
>fw 10000000 33
10000000 bytes written with 468 kB/sec.
>fw 10000000 34
10000000 bytes written with 587 kB/sec.
>
>fc
rc=0 FR_OK
>fl
----A 2010/07/15 11:57  50000000  test1.txt     
   1 File(s),  50000000 bytes total
   0 Dir(s), 1912852480 bytes free
>
>fo 1 test1.txt
rc=0 FR_OK
>
>fr 10000000
10000000 bytes read with 2149 kB/sec.
>fr 10000000
10000000 bytes read with 1483 kB/sec.
>fr 10000000
10000000 bytes read with 1541 kB/sec.
>fr 10000000
10000000 bytes read with 1543 kB/sec.
>fc
rc=0 FR_OK
>fo 1 test1.txt
rc=0 FR_OK
>
>fr 10000000
10000000 bytes read with 2149 kB/sec.
>

結果は、書き込みではソフト転送とほぼ同じ性能が出ていますが、読み出しでは7~10%程度劣る結果となりました。たぶんバーストサイズが小さいことが響いているのではと思うのですが。


おわりに

人様のコードベースではありますが、DMAまでなんとか動作。最適化については敢え無く敗退でした。最適化2のDI無効化は強引感がありますが、最適化3のバーストサイズ拡大は動いてもよさそうなんですが。DMACの設定だけでなく、SSP(SPI)側の設定とか何か抜けているところがあるのか。CMSISライブラリのお仕着せパターンに従った設定ベースでの使用ならなんとかなるのですが、それを超える個別パターンになるといきなりハードルが高くなる感じです。

LPCXpresso LPC1769でFatFsを動かすUSB Host編

$
0
0

LPCXpresso LPC1769でmicro SDカードの読み書きとDMA転送の実験を行いましたが、借用したコードにNXP提供のUSB Hostドライバ(USB Host Lite)が入っていることが分かりました。こいつを使えば、LPCXpressoでUSBメモリーの読み書きもできるということなので、遅まきながら試してみました。


使用したコード

これまで使用した、ChaN's FAT-code with interface for NXP LPC17xx (LPC1766, LPC1768 and others)を使っています。USBドライバの部分はそのまま使用できましたが、以下の2箇所は変更が必要でした。

1. USB Host Liteの有効化とパラメータの変更

USB Host Liteはデフォルトでは無効になっているため、プロジェクトのプロパティー → Build Settings → Symbolsに”WITH_USB_MS=1”を追加します。加えて、ffconf.hの以下の部分を変更します。

/*---------------------------------------------------------------------------/
/ Physical Drive Configurations
/----------------------------------------------------------------------------*/

#if WITH_USB_MS
#define _DRIVES		2   // 2に変更
/* Number of volumes (logical drives) to be used. */
#else
#define _DRIVES		1
#endif

(2011/7/20修正)
2. ff_test_term.cの変更

fiコマンドで論理ドライブを初期化した際に、FatFSのf_mount()関数を呼んでいますが、FatFSのドライブ変更を行っていないため、FatFSは依然としてデフォルトのDrive = 0をポイントしたままになっています。そのため、f_chdrive()を呼んでカレントドライブの変更を行う処理を追加しました。変更した箇所は以下の部分です:

int ff_test_term(void)
{
  
  // ---- snip -----

	for (;;) {
		xputc('>');
		get_line(Line, sizeof(Line));

		ptr = Line;
		switch (*ptr++) {

  // ---- snip -----

		case 'f' :
			switch (*ptr++) {

			case 'i' :	/* fi <log drv#> - Initialize logical drive */
				if (!xatoi(&ptr, &p1)) break;
				put_rc(f_mount((BYTE)p1, &Fatfs[p1]));
				res = f_chdrive((BYTE)p1);							// Added by todotani
				if (res) {
					xprintf("Failed to change current drive.\n");
				}
				break;

		}

	}
}

当初はFatFSのf_mount()関数を修正することで対処したのですが、アプリ部分のが正しくFatFSのAPIを使っていなかったことになります。

ff.cのf_mount()を一箇所だけ変更しています。このサンプルではff_test_term.cという対話型のshellを使いますが、SDカードを使用する場合はDrive 0, USBメモリはDrive 1を指定します。Drive 1を指定した場合でも、内部的に初期値のDrive 0を指定したままになっておりUSBメモリのFAT構造体を参照できないため、f_mount()関数に1行追加を行いました。神のごときChaN様のコードをいじるなど恐れ多きことですが、この変更で動いてくれました。追加を行った部分を以下に示します。32行目が追加したコードです。

コード全体はこちらからダウンロードできます: LPC1769_FatFS_USBv2.zipをダウンロード


LPCXpressoとUSBコネクタの接続

以前自作したLPCXpresso用拡張基盤の空きスペースにUSB Aコネクタを追加しました。コネクタを追加した写真を以下に示します。

FatFS_USB_20110718

USBコネクタとの接続はmbed用Starboard Orangeの回路を参考にして、以下のように行いました:

  • MCU (LPC1769)のUSB D+/D-ピンを直接USBコネクタに接続しています。LPC17xxのUser manual 13.7.2章では、D+/D-に33Ωの抵抗を直列に挿入すること、D+/D-を15KΩの抵抗でプルダウンすることが示されており抵抗を入れたほうがよいかもしれませんが、USBケーブルの引き回しがない写真の状態ではとりあえず動いています。
  • +5Vの給電を行っているため、5Vラインに保護用のポリスイッチを入れています
  • +5V – GND間に47uF, 0.1uFのコンデンサを挿入


動作試験

これまでと同様にff_test_term.cを使って転送速度を計ってみます。結果は以下のとおりでした。

Hello from the ChaN FatFs Demo on LPC1700
Version 0.0.2, Martin Thomas 7/2010
xprintf is working
CPU Clock:        120MHz
Peripheral Clock: 60MHz
SPI Clock:        30MHz

FatFs module test monitor for LPC17xx/SSP
LFN Enabled, Code page: 1252

>di 1
rc=0
>
>fi 1
rc=0 FR_OK
>
>fs
FAT type = FAT16
Bytes/Cluster = 32768
Number of FATs = 2
Root DIR entries = 512
Sectors/FAT = 239
Number of clusters = 60986
FAT start (lba) = 8066
DIR start (lba,clustor) = 8544
Data start (lba) = 8576

...1 files, 30000000 bytes.
0 folders.
1951552 KB total disk space.
1922240 KB available.
>
>
>fl
----A 2010/07/15 11:58  30000000  test1.txt     
   1 File(s),  30000000 bytes total
   0 Dir(s), 1968373760 bytes free
>
>fo 10 test2.txt
rc=0 FR_OK
>
>fw 10000000 62
10000000 bytes written with 576 kB/sec.
>fw 10000000 63
10000000 bytes written with 393 kB/sec.
>fw 10000000 64
10000000 bytes written with 398 kB/sec.
>
>
>fc
rc=0 FR_OK
>
>fl
----A 2010/07/15 11:58  30000000  test1.txt     
----A 2010/07/15 11:58  30000000  test2.txt     
   2 File(s),  60000000 bytes total
   0 Dir(s), 1938358272 bytes free
>
>
>fo 1 test2.txt
rc=0 FR_OK
>
>fr 10000000
10000000 bytes read with 682 kB/sec.
>fr 10000000
10000000 bytes read with 442 kB/sec.
>fc
rc=0 FR_OK
>
>fo 1 test1.txt
rc=0 FR_OK
>fr 10000000
10000000 bytes read with 682 kB/sec.
>fr 10000000
10000000 bytes read with 442 kB/sec.
>fr 1000000
1000000 bytes read with 442 kB/sec.
>
>fc
rc=0 FR_OK
>

結果はリードで682KB/sec (5.4Mbps)とあまりふるわず。SDメモリでは2149KB/sec出たため、1/3以下の性能です。LPC17xxのUSB Hostは12Mbps動作(Full speed)なので、USBプロトコルのオーバヘッド(詳細分かっていませんが)を加味すると妥当な線なのかも知れません。


おわりに

USB Hostの制御をどのようにやっているのかソースを追ってみたのですが、サッパリ分かりませんでした。

usbhost_lpc17xx.cのHost_ProcessTD()関数の中でTransfer descriptorを組み立て、転送をキックしているように見えるのですが、パラメータの意味が分かりません。LPC17xxのマニュアルを見ると、詳細はOHCI (Open Host Controller Interface)のドキュメント参照とさらっと書いてあるだけなので、OHCIの仕様書を読まないとだめそうです。ドキュメントはダウンロードしましたが、160ページの英語文書なのでハードル高そうです。

Parallels DesktopでWindows on Mac

$
0
0

6月に自宅PCをiMacにリプレースし、当初はWinodwsを動かすためにBoot Campを使っていたのですが、Parallelsを使った仮想環境に変更しました。Parallelsの使用は当初考えたのですが、Windowsの起動回数はきっと減ると思っていたため結構お高いParallelsはペイしないと考えて回避していました。ここに至る変遷と試行錯誤を記載します。


我が家におけるWindowsの必要シーン

Mac OSのきびきびした動作ときれいな画面に慣れると、できるだけ多くの作業をMacに移したくなるのですが、我が家の場合、以下のソフト・作業はWindowsベースとなります:

  1. LPCExpressoのIDEとVisual Studio (主にC#)
  2. かみさんが使う家計簿ソフト(家計簿マム): Macの家計簿ソフトへの移行も考えましたが、データ移行が難しく(どうしても欠落してしまうデータが発生)断念
  3. はがき印刷 :年に1回なのでソフトを買い換えるまでもない
  4. このブログ作成(Windows Live Writerを使用): そのうちMacのソフト(Mars Editあたりかな)に変わるかも
  5. ゲームはやらない: グラフィック性能は重視しない

あと外せない条件は、かみさんとアカウントを分けてMacを共有するため、Windowsもそれぞれのアカウントから起動できる必要があります。そのため仮想環境を使った場合、仮想ディスクイメージを共有フォルダに置くなどして、複数アカウントで共有できる必要があります。

冒頭に書いたとおり、Windows環境にあまりお金をかけたくなかったため、先ずはフリーのVirtualBoxを使った仮想化を検討しました。グーグルさまで、マルチアカウントにてVMを共有した事例があるかを調べたのですがヒットせず。インストール回数を浪費しないよう、試しにUbuntuを使って共有を実験したのですがうまくいかない感じ(VirtualBoxでのUSBの認識に問題があり、早々に放棄したため突っ込んで調べてはいません)。

頻繁に起動することはないのでBoot Campを使ったデュアルブートに決定。


Boot Camp

アップル純正のデュアルブート環境なので当然ちゃんと動きますが、使ってみると以下の点が不満。

  • やっぱり起動が遅い: EFIによるハードの認識や初期化時間がばかにならい
  • Mac OSに戻すもの再起動になるため時間がかかる。やっぱり、Mac OSとWindowsの間を行ったり来たりするのでこのオーバーヘッドが無視できない
  • Mac用のMouse, Trackpadを使うとなにやら動きがギクシャク。普通のUSBマウスを使った方がスクロールなどが滑らかに動きます。アップルさん、Windows用ドライバ開発でわざと手を抜いてないか?

結局Parallelsに

Boot Campが今一つなので、Parallelsの情報をあさっていると、複数ユーザで仮想環境を共有できることが分かりました。あと、Boot Campパーティションから仮想マシンを起動することもできるため、これまでに構築したWindows環境も有効に使えそうです。試用版があるのでお試しに使ってみることにしました。

1. Boot Campパーティションからの起動

Boot Camp領域を使う仮想マシンを作ると数MB程度の仮想ディスクイメージ(実体はboot campパーティションにあるので、管理情報だけでしょうか)が作られます。仮想マシンを作成する際に「共有」を選択すると、自動的にUsers/Shared配下にファイルを作成し適切なアクセス権を設定してくれるのですが、サブアカからの起動がどうもうまくいかず。ファイルサイズは小さいので各ユーザフォルダー配下に仮想マシンをつくれば複数アカウントでWindowsを起動できました。

これで運用ができるのですが、制約はサスペンドです。Parallelsのサスペンド・復帰は非常に高速で、自分の環境では10秒以下。Windowsの休止だとブートとたいして時間が変わらない印象があるのですが、これなら十分使えます。Boot Campパーティションから起動した場合、サスペンドはできるのですが、別アカウントから復帰を試みると起動に失敗します。この際、ディスクに不整合が発生する可能性があるようなので危険です。

あと、Boot Campを使った仮想マシンからアプリを立ち上げると、アクティベーションを必要とするアプリの場合、再アクティベーションが発生します。どうも、最初にインストールした生のBoot Campと異なる環境で起動されたと思うようです。自分は気がつかなかったのですが、Web情報ではWindowsも再アクティベーションが発生するようで、その場合マイクロソフトは仮想環境は別ライセンスとみなしアクティベーションを許可しないとのこと。

2. Boot Campを仮想ディスクにインポート

サスペンドとライセンスの問題を考えると、結局仮想ディスクにWindows全体をインストールするのが最善です。一からインストールし直すのもきついので、仮想マシンへのインポート機能を使って、Boot Camp領域のファイルを仮想マシン(仮想ディスク)に取り込んでみました。

インポートはうまくいったのですが、ファイルがホームディレクトリ配下にできてしまいました。これではアカウント間で共有ができないため、共有フォルダに移動してアクセス権を変更。Everyoneやstaffに対して読み書きを許可するも、非オーナのアカウントからはアクセス権違反でVMを起動できず。仮想ディスクファイルにはACLを使ったカスタムアクセス権が設定されており、このあたりをあわせこまないとだめなのか。

アクセス権をうまく設定できないため、インポート作戦も結局放棄。

3. 仮想ディスクに再インストール

仕方なく、Boot Campパーティションを削除して1からやり直しです・・ やっと目的の動作を実現。

仮想マシンを共有環境で動かすと、以下の通りなかなか快適で、やっぱりこれがベストです:

  • マウス・トラックパッドの動きが滑らか。LionのゲストOSにすれば、3本指ドラッグやナチュラルスクロール(Windowsとタッチの方向が逆になるやつ)など、OS Xネイティブの操作も使えます
  • サスペンドと復帰が高速。サスペンドと復帰を別のアカウントで行っても問題なし
  • OS XとWindowsを同時に使えるので、両OS間を行ったり来たりする際のオーバーヘッドが殆どない
  • Boot CampパーティションからVMを起動する場合に比べて、仮想ディスクだとI/O速度が落ちることを懸念しましたが殆ど気にならないレベル

ということで、Parallels正規ライセンスの購入決定。


4. 使用マシンのスペック

項目スペック
マシンiMac Mid 2011(27インチ)
CPUCore i5 2.7GHz
メモリ12GB
グラフィックスRadeon HD 6770M
仮想化ソフトParallels Desktop 6  (6.0.12094)
ホストOSOS X 10.7 (Lion)
ゲストOSWindows 7 SP1

 

5. Windowsエクスペリエンス値

当初、メモリ4Gで仮想環境を動かしたのですが、さすがにメモリ不足。メモリを8G追加して12Gにしたところパフォーマンスが改善されました。仮想環境は8G以上が必須ですね。Windows 7のエクスペリエンス値を以下に示します。

メモリ4Gで仮想マシンに1G割り当て(CPUコアは2個割り当て)

Win7Experience4G-1G


メモリ 12Gで仮想マシンに2G割り当て(CPUコアは2個割り当て)

Win7Experience12G-2G

メモリ増設でメモリ性能が改善しました。仮想環境を使わずに直接Windowsを起動した場合の値を忘れてしまったのですが、殆ど遜色がないレベルかもです(少なくともMac以前に使っていたCore2 Quad 3.0GHzより上です)。


おわりに

短期間にWindowsを2回もクリーンインストールしたので、しばらくはやりたくないです。SP1より前のインストールメディアだとアップデートが超面倒。後、久々にインストール回数が超過して電話でアクティベーションを依頼するはめになりました(MSさまには夜中の3時過ぎに電話してしまいました)。

今考えると、Boot Campからインポートした仮想ディスクファイルに実行許可ビットを立てればよかったのかも。GUIの設定では実行許可は出てこないので見落としました。後々の手間を考えるともう少し粘るべきだったか・・

色々変遷があり手間もかかりました。Parallelsに関しては、ちとお高いですが価格相応の価値はあり満足です。

LPCXpresso LPC1769でFree RTOSを使う

$
0
0

既に多くの方が紹介していますが、LPCXpresso LPC1769でFree RTOSを動かしてみました。

ネタとしては、LPCXpresso supportにFree RTOSカーネルのLPC17XX版とデモが掲載されており、ダウンロードしたファイルからSimpleDemo, FreeRTOSプロジェクトをインポートするだけでLチカのデモが動きます。FreeRTOSプロジェクトをインポートすると、以下のようにFreeROTSカーネルがライブラリとして登録されます。このFreeRTOS_Libraryを使って、新規のFreeRTOSプロジェクトを作ってみます。

FreeRTOS_Library

 

LPCXPresso IDEの新規プロジェクト生成

執筆時点で最新のIDE Version 4.0.5にはNew Projectウイザードに「FreeRTOS Projcet」なるものがあります(3.xにありましたっけ。3.xは消してしまったので忘れました・・)。このウイザードはFreeRTOS 7.0.1のソースツリーをc:\FreeRTOSV7.0.1\FreeRTOS\Sourceに展開していることを前提にしているようです。そのため、このウイザードは使用しません。今回は、普通にC Projcetを選択します。

新規C Projectを起こしたら、SimpleDemoと同様に、Include path, Library, Library Pathを設定します。加えてsrcフォルダにFreeRTOSConfig.hをコピー。後は、main.cにFreeRTOSを使ったコードを書き込んでコンパイル!


何かが足りない

当初はライブラリの設定だけで動くと思ったのですが、これだけでは動きません。デバッガでトレースしてみるとタスクの登録は正しく出来ていますが、タスクが起動しないように見えます。

問題はスタートアップコードでした。新規プロジェクトを生成した際に自動的にsrcディレクトリに書き込まれるcr_startup_lpc176x.cではなく、SimpleDemoに添付されている、cr_startup_lpc17.cを使う必要があります。

cr_startup_lpc17.cでは、リストの最終行に示すとおり、SysTick handlerをFreeRTOSカーネル内のルーチンに変更しています。これがないと、FreeRTOSのスケジューラーが動かずタスクが起動しないというオチでした。

//*****************************************************************************
//
// The vector table.
// This relies on the linker script to place at correct location in memory.
//
//*****************************************************************************
extern void (* const g_pfnVectors[])(void);
__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) = {
	// Core Level - CM3
	&_vStackTop, // The initial stack pointer
	ResetISR,								// The reset handler
	NMI_Handler,							// The NMI handler
	HardFault_Handler,						// The hard fault handler
	MemManage_Handler,						// The MPU fault handler
	BusFault_Handler,						// The bus fault handler
	UsageFault_Handler,						// The usage fault handler
	0,										// Reserved
	0,										// Reserved
	0,										// Reserved
	0,										// Reserved
	vPortSVCHandler,                        // SVCall handler
	DebugMon_Handler,						// Debug monitor handler
	0,										// Reserved
	xPortPendSVHandler,                     // The PendSV handler of FreeRTOS kernel
	xPortSysTickHandler,                    // The SysTick handler of FreeRTOS kernel

Free RTOSを使ったサンプル

タスクを2つ起動して、周期的にコンソールにメッセージを出力するサンプルを作ってみました。シリアルポートへの書き込みが2つのタスクで同時に発生しないように、セマフォを使って排他処理を行っています。

/*
======================================================
  Free RTOS Demo
  Use semaphore
======================================================
*/

#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

#include <cr_section_macros.h>
#include <NXP/crp.h>
#include "uart0.h"

/* FreeRTOS Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

static void vTask1(void *pvParameters);
static void vTask2(void *pvParameter);

#define TASK_PRIORITY	( tskIDLE_PRIORITY + 1 )

xSemaphoreHandle semaphore = NULL;

// Variable to store CRP value in. Will be placed automatically
// by the linker when "Enable Code Read Protect" selected.
// See crp.h header for more information
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

#include <stdio.h>

// TODO: insert other definitions and declarations here

int main(void) {
	UART0_Init(115200);
	printf("Main Start\n");

	semaphore = xSemaphoreCreateMutex();
	if (semaphore != NULL) {
		int res;
		res = xTaskCreate(vTask1, (signed char *)"Task1", 100, NULL, 1, NULL);
		res = xTaskCreate(vTask2, (signed char *)"Task2", 100, NULL, 1, NULL);

		vTaskStartScheduler();
	} else {
		printf("Semaphore creation failed\n");
	}
	
	// Enter an infinite loop
	while(1) { }
	return 0 ;
}


static void vTask1(void *pvParameters)
{
	static int count = 1;

	while(1) {
		if (xSemaphoreTake(semaphore, portMAX_DELAY) == pdTRUE) {
			printf("Task1:%d\n", count++);
			xSemaphoreGive(semaphore);
			vTaskDelay(2000);
		}
	}
}

void vTask2(void *pvParameter)
{
	static int count = 1;

	while(1) {
		if (xSemaphoreTake(semaphore, portMAX_DELAY) == pdTRUE) {
			printf("  Task2:%d\n", count++);
			xSemaphoreGive(semaphore);
			vTaskDelay(4000);
		}
	}
}

XBee APIモードライブラリのLPCXpressoへの移植

$
0
0

XBeeをAPIモードで使うで紹介したmbed用ライブラリ(オリジナルはArduino用で、Suga koubouさんがmbed用に移植)をLPCXpressoに載せてみました。

オリジナルコードはC++ですが、LPCXpresso IDEでC++のプロジェクトを作成してビルドすることができました。表示部分はMARY OLED基板を使っています。5月にポストした「LPCXpresso LPC1769にMARY拡張基板を乗せる」では、XBeeを載せただけのハリボテ写真を掲載していたのですが、やっと動かすことができました。 

   
LPCXpressoでのC++プロジェクトのビルド

LPCXpresso IDEの新規プロジェクト作成ウイザード(New Project)ではC++プロジェクトが作成できませんが、以下のブログを参考にして、ファイルのコピーやプロジェクトプロパティの設定を行うことによってC++コードのビルドができました。 

マイコン風雲録:LPCXpressoで「C++プロジェクト」を作る方法

私のビルド環境では、LPCXpresso v4.1.0のデフォルトに従って、CMSIS v2をライブラリプロジェクトとして参照していますが、CMSISプロジェクトの設定はREDLIBのままで問題ありませんでした(C++プロジェクト側だけをNEWLIBに変える)。  

   

XBee APIモードライブラリの移植 

主要な変更点は、以下の2つです:  

  • mbed版では、SerialクラスとTimerクラスを使っているため、この部分を自作のクラス(実装は最低限の手抜き)で置き換えています。mbed版はXbeeクラスのコンストラクターにシリアルポートのピン番号を指定しますが、今回の実装はUARTの番号(0~2)を指定します。      
  • オリジナルは、XBee.hで送信ステータスとして”SUCCESS”を#define定義しているのですが、CMSISのlpc_types.hに同じ名前のenum定義があるせいか、lpc_types.hでコンパイルエラーが発生してどうしても解決できなかったため、XBee.h側を”OK”に変更しています。   
ソースコードは以下です(スタートアップコード、や表示用に使ったMARYのコードは再配布が微妙なので含みません)。 

XBee_LPCxpresso.zip

       
サンプルコード 

リモート側(End Device)のXBeeにコマンドを送って、ADC3の読み取りと、DIO1(Digital Outに設定)のトグルによるLチカを行うコードを作ってみました。

LPCXpresso-_XBee

   
#include "XBee.h"
extern "C" {
	#include "oled.h"
	#include "systick.h"
}

/*-- AT command and parameters --*/
uint8_t atISCmd[] = {'I', 'S'};      // Forces a read of all enabled digital and analog input lines
uint8_t atD1Cmd[] = {'D', '1'};		 // DIO1 control
uint8_t atDBCmd[] = {'D', 'B'};      // Received Signal Strength
uint8_t cmdVal0[] = {0};             // Clear RSSI register
uint8_t cmdVal4[] = {4};			 // Digital output, low
uint8_t cmdVal5[] = {5};			 // Digital output, High

/*-- Create instance of Xbee object --*/
XBee xbee(2);
XBeeAddress64 remoteAddress(0x0013A200, 0x406B7111);    // Specify your XBee address

/*-- Create instance of Command and Response object --*/
// Remote ATIS command to read ADC value (ADC3 is enabled by X-CTU tool)
RemoteAtCommandRequest remoteSampleRequest(remoteAddress, atISCmd);
// Remote ATD1 command to turn DIO1 low/high
RemoteAtCommandRequest remoteDIO1_Low(remoteAddress, atD1Cmd, cmdVal4, sizeof(cmdVal4));
RemoteAtCommandRequest remoteDIO1_High(remoteAddress, atD1Cmd, cmdVal5, sizeof(cmdVal5));

// Local ATDB command to read signal strength (RSSI)
AtCommandRequest atDB(atDBCmd);
// Local ATDB0 command to clear RSSI
AtCommandRequest atDB0(atDBCmd, cmdVal0, sizeof(cmdVal0));
// Create instanse to handle command response
AtCommandResponse response = AtCommandResponse();
RemoteAtCommandResponse remoteResp = RemoteAtCommandResponse();


/* Receive command response packet
 * If OK response recieved, return pointer to the Response Data Frame
 */
uint8_t* GetResponse() {
    // Read response
    if (xbee.readPacket(1000)) {
        // Got a response! Check if response is AT command respose
        if (xbee.getResponse().getApiId() == AT_COMMAND_RESPONSE) {
            xbee.getResponse().getAtCommandResponse(response);
            if ( response.getStatus() == AT_OK )
                return response.getValue();
        } else  if (xbee.getResponse().getApiId() == REMOTE_AT_COMMAND_RESPONSE) {
            xbee.getResponse().getRemoteAtCommandResponse(remoteResp);
            if ( remoteResp.getStatus() == AT_OK ) {
                return remoteResp.getValue();
            }
        }
    }

    return 0;
}


/* Get ADC data
 * Data frame structure of ATIS
 * Offset
 *   0   : Number of Samples (Always 1)
 *   1-2 : Digital Channel Mask
 *   3   : Analog Channel Mask
 *   4-5 : Digital Samples (Omit if no DIO enabled)
 *   6-7 : First ADC Data
 */
uint16_t getAnalog(uint8_t *FrameData, int ADC) {
    // ADC data feild starts 4 bytes offest, if no DIO enabled
    uint8_t start = 4;

    // Contains Digital channel?
    if (FrameData[1] > 0 || FrameData[2] > 0) {
        // make room for digital i/o
        start+=2;
    }

    // start depends on how many ADCs before this ADC are enabled
    for (int i = 0; i < ADC; i++) {
        // Is Analog channel Enabled ?
        if ( (FrameData[3] >> i) & 1 ) {
            start+=2;
        }
    }

    return (uint16_t)((FrameData[start] << 8) + FrameData[start + 1]);
}


int main() {
    unsigned int loop = 0;
    Timer t;

    xbee.begin(9600);

    Init_SysTick(10);   // Set systick interval to 10ms
    Init_OLED();

    OLED_printf_Font(OLED_FONT_SMALL);
    OLED_Clear_Screen(OLED_BLK);

    OLED_printf_Position(0, 0);
    OLED_printf_Color(OLED_GRN, OLED_BLK);
    OLED_printf("RSSI:");
    OLED_printf_Position(0, 1);
    OLED_printf_Color(OLED_WHT, OLED_BLK);
    OLED_printf("ADC :");

    while (true) {
        uint8_t *responseVal;
        uint8_t rssiVal = 0;
        uint16_t adcVal = 0;

        // Send ATDB command (Read RSSI register from local Xbee)
        xbee.send(atDB);
        responseVal = GetResponse();
        if ( responseVal != 0 )
            rssiVal = responseVal[0];
        OLED_printf_Position(5, 0);
        OLED_printf_Color(OLED_GRN, OLED_BLK);
        if (rssiVal == 0)
            OLED_printf("No Signal");
        else
            OLED_printf("-%ddBm   ", rssiVal);

        // Clear RSSI register, because Xbee hold RSSI value of last received packet even after radio disconneded
        xbee.send(atDB0);
        GetResponse();

        // Read ADC3 value by sending ATIS command
        xbee.send(remoteSampleRequest);
        responseVal = GetResponse();
        if ( responseVal != 0 ) {
            adcVal = getAnalog(responseVal, 3); // Assume ADC3 is enabled
        }
        OLED_printf_Position(5, 1);
        OLED_printf_Color(OLED_WHT, OLED_BLK);
        if (adcVal == 0)
            OLED_printf("-     ");
        else
            OLED_printf("%x   ", adcVal);

       // Turn DIO1 high/low
       if (loop % 2 == 0) {
    	   xbee.send(remoteDIO1_Low);
       } else {
    	   xbee.send(remoteDIO1_High);
       }
       GetResponse();

       t.start();
       while (t.read_ms() < 1000);
       t.stop();

       loop++;
    }
}

MARY基板用OLEDモジュール(MARY-OB)のライブラリのヘッダファイルを3~4行目でインクルードしていますが、MARY-OBのライブラリはCのコードであるため、extern “C”宣言でくくってやる必要があります。

extern “C”って、意味がよく分かっていなかったのですが、C++のコードからCの関数を呼び出すために、以下の関数はCのだよとC++コンパイラに教えてあげるための記述なんですね。extern “C”なしでは、リンクでundefined referenceエラーが出て、その対処を調べる過程で始めて意味が分かりました。ヤッパリ習うより実践かな。

 

追記

10月8日(土)に@shintamainjpさん主催の「LPCXpresso横浜お楽しみ部会」に参加しました。ネタ切れのおり、MARY拡張基盤を持ち込んでXBeeライブラリの動作を目指したのですが、時間内では動作せず。問題は移植コードでなくXBeeの設定誤りというトホホな内容・・ファームを更新した際に設定内容が初期化されてScan Channelの設定が両端でずれていたことあたりが原因みたいです。とは言え、プロフェッショナルな皆さんとお話ができ、終了後の飲み会も含めて楽く有意義なひと時が過ごせました。

LPCXpresso IDEでのC++コードサイズ肥大化の対策

$
0
0

前回ポストしたLPCXpresso用のXBee APIモードライブラリですが、コードサイズが100KB程度にまで肥大化することが分かりました。組み込み用としてはでかすぎます。mbedで前回のサンプルと同様のコードをコンパイルするとバイナリサイズは31KBに収まります。

コードサイズが大きくなるトリガーは何となくわかりました。どうも、親にvirtual関数を持つクラスのインスタンスを生成すると、本来必要ないと思われるライブラリの関数がごっそりとリンクされるのが原因と思われます。以下の通り、実験をしてみました。

2011/10/16追記:
audinさんコメントをいただき、viurual関数を持つクラスを使った際のコードサイズ肥大の問題が解決し、サイズを21KBにまで圧縮できるようになりました。


最小構成でコンパイル

まず、以下のように14行目でXBeeクラスのインスタンスだけを生成するサンプルをコンパイルします。

#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

#include "Xbee.h"

uint8_t atISCmd[] = {'I', 'S'};      // Forces a read of all enabled digital and analog input lines
uint8_t atD1Cmd[] = {'D', '1'};		 // DIO1 control
uint8_t atDBCmd[] = {'D', 'B'};      // Received Signal Strength
uint8_t cmdVal0[] = {0};             // Clear RSSI register
uint8_t cmdVal4[] = {4};			 // Digital output, low
uint8_t cmdVal5[] = {5};			 // Digital output, High

XBee xbee(2);

XBeeAddress64 remoteAddress(0x0013A200, 0x406B7111);    // Specify your XBee address

/*-- Create instance of Command and Response object --*/
// Remote ATIS command to read ADC value (ADC3 is enabled by X-CTU tool)
//RemoteAtCommandRequest remoteSampleRequest(remoteAddress, atISCmd);


int main(void) {

	while(1) {
	}

	return 0 ;
}

Debugビルドですが、この段階でコードサイズが80KB程度になってしまいます。ここで、リンカスクリプトをいじってやるとコードサイズを削減できることを発見。LPCXpresso IDEのリンカスクリプトは通常自動生成ですが、プロパティ設定から;
MCU C++ Linker → Target → Manage Linker Scriptのチェックを外してやると、独自のスクリプトを書くことができます。自動生成される3本のスクリプトをマージし、以下のライブラリ定義部分を書き換えます。

オリジナル:
GROUP(libgcc.a libc.a libstdc++.a libm.a libcr_newlib_nohost.a crti.o crtn.o crtbegin.o crtend.o)

書き換え:
GROUP(libgcc.a libc.a libm.a libcr_newlib_nohost.a crti.o crtn.o crtbegin.o crtend.o)

要は、libstdc++.aを削除します。

再度ビルドすると、コードサイズが12,552byteにまで縮小します。mapファイルを見ると、以下の関数がリンクされていることが分かります。

.debug_frame    0x00000000     0x2884
 .debug_frame   0x00000000       0xc8 ./src/Serial.o
 .debug_frame   0x000000c8       0xac ./src/Timer.o
 .debug_frame   0x00000174     0x1714 ./src/XBee.o
 .debug_frame   0x00001888       0x6c ./src/cr_cpp_config.o
 .debug_frame   0x000018f4      0x158 ./src/cr_startup_lpc176x.o
 .debug_frame   0x00001a4c       0x64 ./src/main.o
 .debug_frame   0x00001ab0       0x4c C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_pinsel.o)
 .debug_frame   0x00001afc      0x16c C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_timer.o)
 .debug_frame   0x00001c68      0x2f8 C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_uart.o)
 .debug_frame   0x00001f60       0x38 C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(system_LPC17xx.o)
 .debug_frame   0x00001f98       0xa0 C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_clkpwr.o)
 .debug_frame   0x00002038       0x20 C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_libcfg_default.o)
 .debug_frame   0x00002058       0x50 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(bpabi.o)
 .debug_frame   0x000020a8      0x288 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(unwind-arm.o)
 .debug_frame   0x00002330       0xf4 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(pr-support.o)
 .debug_frame   0x00002424       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(_divdi3.o)
 .debug_frame   0x00002460       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(_udivdi3.o)
 .debug_frame   0x0000249c       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(_udivsi3.o)
 .debug_frame   0x000024bc       0x28 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-abort.o)
 .debug_frame   0x000024e4       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-init.o)
 .debug_frame   0x00002510       0x30 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-malloc.o)
 .debug_frame   0x00002540       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-mallocr.o)
 .debug_frame   0x0000257c       0x30 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-memcpy.o)
 .debug_frame   0x000025ac       0x30 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-mlock.o)
 .debug_frame   0x000025dc       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-sbrkr.o)
 .debug_frame   0x00002608       0xc0 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-signal.o)
 .debug_frame   0x000026c8       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-signalr.o)
 .debug_frame   0x00002704       0x50 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-freer.o)
 .debug_frame   0x00002754       0x64 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-reent.o)
 .debug_frame   0x000027b8       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(_cr_sbrk.o)
 .debug_frame   0x000027e4       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(_exit.o)
 .debug_frame   0x00002804       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(getpid.o)
 .debug_frame   0x00002824       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(kill.o)
 .debug_frame   0x00002844       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(_cr_check_heap.o)
 .debug_frame   0x00002864       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-errno.o)


Virtual関数を親に持つクラスのインスタンスを生成してみる

サンプル20行目をコメントアウトして再度ビルドすると、以下のようにリンカーでエラーが発生します。

./src/XBee.o:(.rodata._ZTV11XBeeRequest[vtable for XBeeRequest]+0x8): undefined reference to `__cxa_pure_virtual'
./src/XBee.o:(.rodata._ZTV11XBeeRequest[vtable for XBeeRequest]+0xc): undefined reference to `__cxa_pure_virtual'

virutal関連の参照が解決しないと文句を言っているように見えます。ここで、リンカスクリプトのGROUP定義を元に戻して再度コンパイルすとエラーが解消しますが、コードサイズが一気に増大します。

text       data        bss        dec        hex         filename
86876     1424      2368     90668    1622c    LPC1769CppTest.axf

再度mapファイルを見ると、リンクしている関数が山のように増えています。

.debug_frame    0x00000000     0x4288
 .debug_frame   0x00000000       0xc8 ./src/Serial.o
 .debug_frame   0x000000c8       0xac ./src/Timer.o
 .debug_frame   0x00000174     0x1714 ./src/XBee.o
 .debug_frame   0x00001888       0x6c ./src/cr_cpp_config.o
 .debug_frame   0x000018f4      0x158 ./src/cr_startup_lpc176x.o
 .debug_frame   0x00001a4c       0x64 ./src/main.o
 .debug_frame   0x00001ab0       0x4c C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_pinsel.o)
 .debug_frame   0x00001afc      0x16c C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_timer.o)
 .debug_frame   0x00001c68      0x2f8 C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_uart.o)
 .debug_frame   0x00001f60       0x38 C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(system_LPC17xx.o)
 .debug_frame   0x00001f98       0xa0 C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_clkpwr.o)
 .debug_frame   0x00002038       0x20 C:\Users\todotani\Documents\LPCXpresso_4.0\workspace\CMSISv2p00_LPC17xx\Debug\libCMSISv2p00_LPC17xx.a(lpc17xx_libcfg_default.o)
 .debug_frame   0x00002058       0x50 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(bpabi.o)
 .debug_frame   0x000020a8      0x288 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(unwind-arm.o)
 .debug_frame   0x00002330       0xf4 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(pr-support.o)
 .debug_frame   0x00002424       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(_divdi3.o)
 .debug_frame   0x00002460       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(_udivdi3.o)
 .debug_frame   0x0000249c       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/thumb2\libgcc.a(_udivsi3.o)
 .debug_frame   0x000024bc       0x28 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-abort.o)
 .debug_frame   0x000024e4       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-init.o)
 .debug_frame   0x00002510       0x30 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-malloc.o)
 .debug_frame   0x00002540       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-mallocr.o)
 .debug_frame   0x0000257c       0x30 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-memcpy.o)
 .debug_frame   0x000025ac       0x30 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-mlock.o)
 .debug_frame   0x000025dc       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-sbrkr.o)
 .debug_frame   0x00002608       0xc0 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-signal.o)
 .debug_frame   0x000026c8       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-signalr.o)
 .debug_frame   0x00002704       0x50 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-freer.o)
 .debug_frame   0x00002754       0x64 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-reent.o)
 .debug_frame   0x000027b8       0x28 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(pure.o)
 .debug_frame   0x000027e0       0x90 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_terminate.o)
 .debug_frame   0x00002870       0x54 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_call.o)
 .debug_frame   0x000028c4       0xd0 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_exception.o)
 .debug_frame   0x00002994       0x64 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_arm.o)
 .debug_frame   0x000029f8       0x40 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(fundamental_type_info.o)
 .debug_frame   0x00002a38       0x70 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(pointer_type_info.o)
 .debug_frame   0x00002aa8       0x80 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_alloc.o)
 .debug_frame   0x00002b28       0x90 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(tinfo.o)
 .debug_frame   0x00002bb8       0xd0 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(class_type_info.o)
 .debug_frame   0x00002c88       0xb4 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_personality.o)
 .debug_frame   0x00002d3c       0x60 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_throw.o)
 .debug_frame   0x00002d9c       0x30 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_globals.o)
 .debug_frame   0x00002dcc       0x6c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_catch.o)
 .debug_frame   0x00002e38       0x40 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_aux_runtime.o)
 .debug_frame   0x00002e78       0xac c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(si_class_type_info.o)
 .debug_frame   0x00002f24       0x50 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(bad_cast.o)
 .debug_frame   0x00002f74       0x78 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(pbase_type_info.o)
 .debug_frame   0x00002fec       0x50 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(bad_typeid.o)
 .debug_frame   0x0000303c       0x30 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(vterminate.o)
 .debug_frame   0x0000306c       0x28 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(eh_type.o)
 .debug_frame   0x00003094      0x48c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libstdc++.a(cp-demangle.o)
 .debug_frame   0x00003520       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(_cr_sbrk.o)
 .debug_frame   0x0000354c       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(_exit.o)
 .debug_frame   0x0000356c       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(getpid.o)
 .debug_frame   0x0000358c       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(kill.o)
 .debug_frame   0x000035ac       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(_cr_check_heap.o)
 .debug_frame   0x000035cc       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-errno.o)
 .debug_frame   0x000035ec       0x48 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fputc.o)
 .debug_frame   0x00003634       0x40 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fputs.o)
 .debug_frame   0x00003674       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fvwrite.o)
 .debug_frame   0x000036b0       0x58 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fwrite.o)
 .debug_frame   0x00003708       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-memchr.o)
 .debug_frame   0x00003734       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-memcmp.o)
 .debug_frame   0x00003760       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-memmove.o)
 .debug_frame   0x0000378c       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-memset.o)
 .debug_frame   0x000037b8       0x48 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-putc.o)
 .debug_frame   0x00003800       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-realloc.o)
 .debug_frame   0x00003820       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-reallocr.o)
 .debug_frame   0x0000385c       0x64 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-sprintf.o)
 .debug_frame   0x000038c0       0x30 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-strcmp.o)
 .debug_frame   0x000038f0       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-strcpy.o)
 .debug_frame   0x00003910       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-strlen.o)
 .debug_frame   0x00003930       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-strncmp.o)
 .debug_frame   0x0000395c       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-svfprintf.o)
 .debug_frame   0x00003998       0x28 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-syswrite.o)
 .debug_frame   0x000039c0       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-wbuf.o)
 .debug_frame   0x000039fc       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-writer.o)
 .debug_frame   0x00003a28       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-wsetup.o)
 .debug_frame   0x00003a54       0x68 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-dtoa.o)
 .debug_frame   0x00003abc       0x40 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fflush.o)
 .debug_frame   0x00003afc      0x114 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-findfp.o)
 .debug_frame   0x00003c10       0x54 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fwalk.o)
 .debug_frame   0x00003c64       0x98 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-locale.o)
 .debug_frame   0x00003cfc       0x34 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-makebuf.o)
 .debug_frame   0x00003d30      0x244 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-mprec.o)
 .debug_frame   0x00003f74       0x28 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-s_fpclassify.o)
 .debug_frame   0x00003f9c       0x80 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-stdio.o)
 .debug_frame   0x0000401c       0x68 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-svfiprintf.o)
 .debug_frame   0x00004084       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-callocr.o)
 .debug_frame   0x000040b0       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-closer.o)
 .debug_frame   0x000040dc       0x3c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fclose.o)
 .debug_frame   0x00004118       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fstatr.o)
 .debug_frame   0x00004144       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-isattyr.o)
 .debug_frame   0x00004170       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-lseekr.o)
 .debug_frame   0x0000419c       0x2c c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-readr.o)
 .debug_frame   0x000041c8       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(close.o)
 .debug_frame   0x000041e8       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(fstat.o)
 .debug_frame   0x00004208       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(isatty.o)
 .debug_frame   0x00004228       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(lseek.o)
 .debug_frame   0x00004248       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(read.o)
 .debug_frame   0x00004268       0x20 c:/nxp/lpcxpresso_4.1.0_190/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libcr_newlib_nohost.a(write.o)

さっき追加したlibstdc++.aの関数に加えて、libc.aの関数も大量に増えています。殆どはstdio関連の関数で、「こんなの使ってないよ」なやつらばかりです・・


解決策(2012/10/16追記)

audinさんより、__cxa_pure_virtualに対するハンドラーをプロジェクト内で作ってやればリンカーエラーを解消できるとのコメントをいただきました。具体的には、以下のコードをcppファイルのどこかに追加します(今回はcr_startup_lpc176x.cppに追加)。

extern "C" void __cxa_pure_virtual() { while (1); }

__cxa_pure_virtual() はpure virutal function(純粋仮想関数)が呼ばれた場合のエラーハンドラーで、従来はOS環境(ランタイムライブラリ)にて提供されるものと理解。そのため、デフォルトの状態ではランタイム全体がリンクされてしまいコードサイズが肥大化するのですが、__cxa_pure_virtual() を単独で定義してしまえば、「最小構成でコンパイル」したときと同じ関数のみがリンクされるにとどまり、コードサイズを劇的に圧縮できました。

__cxa_pure_virtual() の処理は無限ループですが、純粋仮想関数が呼ばれた時点でプログラムが間違っていますので、エラー処理としてはこれでよいかと。

最終的なコードサイズは以下です:

text       data        bss        dec        hex       filename
20688   1304        520      22512    57f0    LPC1769cpp_Xbee.axf

リンカー関連の設定はデフォルトでOKで、Manage Linker Scriptを有効にしても上記以上にコードサイズが増えることはありませんでした。また、- nostartfilesオプションの有無によるサイズの変化はありませんでした。


まとめ

最初はあきらめモードでしたが、コメントをいただくことによって実用的なコードサイズに圧縮できることが分かりました。そのため、LPCXpresso IDEでのC++プロジェクトの開発は、設定に手間がかかりますが十分可能と思います。

移植したXBee APIモードライブラリはもともとArduino用に開発されたもので、32KB以下のメモリで動かなければならないです。前回のサンプルと同じコードはArduinoでビルドできなかった(コンパイルエラーを解消できず)のですが、APIモードを使ったサンプルをビルドするとコードサイズは6KB程度でした。ArduinoもgccのC++を使っているわけですが半分以下に収まる感じです。対策後のコードでも、「この関数いるのか」みたいなのがいくつかリンクされているように見え、それらを最小限の薄い関数に置き換えるなどすれば、Arduinoのコードサイズに近づけることができるのかもしれません。

このサイトの情報(マイコン風雲録)だとAVRよりLPC2388の方がコードサイズが小さいとあるので、まだ改善の余地はありそうですが、今回はここまでで一段落とします。

上記の実験から、無償版のLPCXpresso IDEでは、マニュアル設定によってC++のプロジェクトを作成・ビルドは可能ですが、親に仮想関数を持つクラスを使うと、一気にコードサイズが増加します。なんとなく、C++で使うnewlibが組み込み用に最適化されていないように思うのですが、原因は分かりません。

この差はライブラリや開発環境の最適化度合いの差でしょうか。LPCXpresso LPC1769でもCの環境だと、FatFsを組み込んだコードで6KB、uIPを使ったコードが9KB程度に収まりますので(Releaseビルドにて)、LPC1769のコードサイズがAVRに比べて明らかに大きいということはないように思います(同一条件の比較ではないですが)。

C++を使いたい場合、Sourcery g++あたりを持ってきて自前で環境を作ったほうがよいということか・・


参考情報

FM3マイコンをOpenOCD + Eclipseでデバッグ

$
0
0

OpenOCD + Eclipseを使って、インタフェース6月号付録の富士通製FM3マイコン(MB9BF618T)用のデバック環境を構築してみました。これまでは、LPCXpresso, STM32 Primer2などのデバッグ環境込みのIDEを使っており、OpenOCDを使うのは今回が初めてです。備忘録を兼ねて環境構築の手順を記載します。

2012/5/12: デバッグ時のプログラム書き込み手順を修正。Managed Projectにアセンブラファイルを追加する方法を追記。


OpenOCD用USB JTAGインタフェースの購入

価格がお手ごろなOLIMEX製ARM-USB-TINY-Hを仙石電商さんで購入。2012年5月時点で税込み6825円でした。あとで分かったのですがStrawberry Linuxさんの方が4620円で2000円も安かった・・メーカー希望価格€39.95らしく、Strawberry Linuxさんの方がユーロ安反映価格ですね。

FM3_board

 

Sourcery CodeBench Lite Editionのインストール

CodeSourcery G++(Mentor Graphicsに買収されてタイトルが変わった)のインストールとmake環境をインストールします。

  • Sourcery CodeBench Lite Editionのwebページから、Download the EABI Releaseをクリック。所定の項目を入力してファイルをダウンロード&インストール
  • このリンクからDownload mingw-get-instをダウンロードして、MinGW + MSYSをインストール。MinGW\binとMinGW\msys\1.0\binにパスを通しておく。今回は使いませんが、x86用のgccもインストールしておく


Eclipseのインストール

2012年5月時点で最新版の3.7.2(Indigo)インストールしました。

  • 先ずは、EclipseのダウンロードサイトからEclipse Classic 3.7.2をDL(Eclipse IDE for C/C++ Developersを使うという手もあるのですが、Classic + CDTを個別にインストールした方がよいとZylinのサイトに記載があったのでそれに従う)
  • CDTのインストール: Eclipseを起動して、Help → Install New Software → Work with:にhttp://download.eclipse.org/tools/cdt/releases/indigoを入力。CDT Main Features全部とCDT Optional Featuresを選択(選択したオプションはここを参考にしました。IndigoのProgramming LanguagesメニューからでもCDT本体はインストールできるのですが、オプション機能をインストールできないため(必要か否かは不明ですが・・)前者のurlから追加インストールします
  • Zylin Embedded CDTのインストール: Help → Install New Software → Work with:にhttp://opensource.zylin.com/zylincdtを入力
  • GNU ARM C/C++ Development Supportのインストール: Help → Install New Software → Work with:にhttp://gnuarmeclipse.sourceforge.net/updatesを入力。このプラグインを入れると新規プロジェクトの作成でSourcery G++ Toolchainが選択できるようになり、includeパスなどが自動設定され便利です。またcpuタイプの選択がプロジェクトプロパティから選択できるようになります(マイコン風雲録さんより)


OLIMEX ARM-USB-TINY-H用ドライバのインストール

  • OLIMEXのサイトから、OLIMEX ARM DEVELOPMENT PACKAGEをダウンロード
  • JTAGインタフェースをPCのUSBポートに接続すると2つ不明なデバイスが見えます
  • ドライバの更新メニューから、DRIVERSフォルダ配下のolimex-libusb-1.2.2.0をインストール
    最新版がこのURLにあるため、こちらを使ってもよいかと)
  • CDM20808フォルダ配下のドライバはARM-USB-OCD-H用のため使用しません
  • 2つのデバイス個々に更新を行うと、以下のように認識されます

ARM-USB-TINY-driver

 

OpenOCDのインストール

  • このサイトから、Windows用のコンパイル済みファイルをダウンロードして適当なフォルダに展開します(OpenOCD 0.5.0を使用しました)
  • targetフォルダにFM3.cfgが既に入っているのですがMB96F506用です。jujurouさんのブログにMB9BF618用の設定ファイルが公開されており、ありがたく使用させていただきました
  • OpenOCD起動用のスクリプトを以下の通り作成(mb9bfxx6.cfg)
telnet_port 4444
gdb_port 3333

set CHIPNAME mb9bfxx6
source [find interface/olimex-arm-usb-tiny-h.cfg]
source [find target/fm3.cfg]


OpenOCDの動作テスト

WiKimurAさんのOpenOCDが動くまでを参考に以下の通り動作を確認。ちゃんと動いているみたい!(実際はここまでの道のりは長かったのですが)

  • openocd –f  mb9bfxx6.cfgでOpenOCDデーモンを起動
  • もう一つコンソールを開いて、telnet localhost 4444でOpenOCDデーモンに接続
  • OpenOCDのコマンドを入力してターゲットを操作
--- 起動時のメッセージ ---

C:\Tools\openocd>openocd -f mb9bfxx6.cfg
Open On-Chip Debugger 0.5.0 (2011-08-09-23:21)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.berlios.de/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
trst_only separate trst_push_pull
Info : ******HWE* FLASH CMD Parameter mb9bfxx6.cpu
Info : ******HWE* fm3 Variant set to: mb9bfxx6
500 kHz
cortex_m3 reset_config sysresetreq
Info : max TCK change to: 30000 kHz
Info : clock speed 500 kHz
Info : JTAG tap: mb9bfxx6.cpu tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0x
ba00, ver: 0x4)
Info : mb9bfxx6.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : accepting 'telnet' connection from 4444

--- 以下Telnetセッションからの操作 ----

C:\>telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> poll
 background polling: on
TAP: mb9bfxx6.cpu (enabled)
target state: running
>
> halt
 target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x21000000 pc: 0x00000218 msp: 0x1ffffff0
>
> reset
 JTAG tap: mb9bfxx6.cpu tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00,
ver: 0x4)
>


Eclipseからのflash書き込みとデバッグ

先ずはLチカあたりのサンプルコードを作る必要があります。インタフェース6月号のサンプルにLチカコードがあるのですが、Keil用となっておりスタートアップコードがARMアセンブラなので、このままではgcc環境で使用できません。ねむいさんのブログ、FM3マイコンはぢめましたにLチカのサンプルコードがありましたのでこちらを使用させていたきました。

Makeファイルを自分の環境用に修正してEclipseのMakefile projectからビルドすれば、当然正しくコンパイル・リンクができます。Managed project (ファイルを追加するとEclipseが自動的にmake fileを生成する) - ARM Windows GCC (Sourcery GLL Lite) - を使用する場合、コンパイル・リンクは通っているのですがうまく動きません。どうも、スタートアップ用のアセンブラファイル(.s)のコンパイル・リンクを行っていないようです(自動生成されるmakefileに.sファイルが入らない)アセンブラソースファイルの拡張子を”.s”でなく”.asm”とします。拡張子が.sのままでは自動生成されるmakefileにアセンブラのソースファイル(今回の例では、startup_mb9bf61xt)が追加されません。

先ずは、ねむいさん版のmakefileプロジェクトでデバッグの動作確認を行います。

手順は以下の通りです:

  • EclipseのRun → External Tools → External Tools Configuration…を開き、以下の設定を追加

ExternalToolsConfig

  • Run → External Tools → OpenOCD (上記で登録した名前)でOpenOCDデーモンを起動
  • ここで、OpenOCD経由でflashのプログラムを行いターゲットをhalt状態にしておきます。プログラムがrun状態ですと、Eclipseを使ったデバッグセッションの起動時にgdbとOpenOCDデーモン間の通信がエラーとなってデバッグができません(私はこれに気がつかず結構悩みました)。書き込みたいelfファイルがあるフォルダーに移動して、telnetセッションから以下のようにhaltコマンドを実行します: 
    2012/9/8追記: mokeshiさんより起動スクリプトでhaltコマンドを自動実行させる方法をコメントいただきました(手順はコメント欄を参照下さい)
C:\Tools\openocd>telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
>
> halt
 target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x21000000 pc: 0x00000726 msp: 0x1fffffe8
>
  • Run → Debug ConfigurationsからMain、DebuggerとCommandsタブを以下のように設定
    C/C++ Applicationにelfファイルを指定

Debug-main

  • Debuggerはarm-none-eabi-gdbを指定

Debug-debugger

  • Commandsは以下を指定
    <Initialize commands>
    target remote localhost:3333
    load
    monitor soft_reset_halt
    break main
    <Run commands>
    continue

Debug-command

  • デバッグを開始すると、以下のようにめでたくEclipseでソースコードデバッグが動きました。
  • 2012/5/12追記: Flashへの書き込みはDebug configurationで指定したelfファイルをloadコマンドで自動的に書き込んでくれます。そのため、telnetセッションからflash write_image erase xxx.elfで書き込みを行う必要はありません。また、flash write_imageコマンドで指定したファイルは、OpenOCDデーモン起動時に指定した.cfgファイルのパスを検索するようで、プロジェクトのビルドターゲットパスにカレントディレクトリを移動してもフルパスでelfファイル名を指定しない限りfile openエラーになります。そのため、flashへの書き込みはDebug Configrationの指定で行うのがよいです。

Eclipse_DebugWithOpenOCD


おわりに

インタフェースの次回特集に先立ち、Eclipse + OpenOCD環境にてデバッグができるようになりました。LPCXpressoのようにGPIOやコントロールレジスタ類の表示ができないのですが、configファイルを書けばできるのかも(今後の課題)。自動makeプロジェクトでアセンブラファイルを認識しない問題を解決せねばはファイルの拡張子が間違っていた(.sでなく、.asm)という初歩的な問題でした。

参考資料


iPhoneからmbedをBluetooth LE (BTLE)で制御する

$
0
0

久しぶりの更新です。ずっと弾切れでしたが、最近Bluetooth LE (BTLE)を使ったiPhone (iOSデバイス)とマイコンボード間の通信に興味が湧き、mbedとの接続実験を行いました。

以前から、スマホとmbedやArduinoをつないで連携できるとよいなと思っていました。どうせならUSBなどの有線でなく、無線でつなぎたい、あとAndroidよりiOS派の自分としてはiOSとつなぎたい、というのが目標でしたが、皆様ご存知の通り以下の制約で挫折していました:

  • 以前のiOS (4.x以前)ではBluetoothのSPPが使えない
  • xBee WiFiを使って、TCP/IPのソケット通信でつなぐ方法はありそう。でも、APがないとダメ。

で、iOS5とiPhone 4SからBTLEがサポートされ、BTのプロファイル縛りから解放されていることをつい最近知りました。それで、色々と資料をあさり、iPhoneからmbedのLEDをon/offするサンプルを作ってみました。BTLEのGATT/ATTなど、プロトコルの仕組みはまだよく分かっていないのですが、動かしならが確かめて行きたいと思います。


素材

iOS側はiOS5からサポートされたCoreBluetooth Frameworkを使えばOKです。

マイコン側も色んな選択肢がありそうです:

  • ユカイ工学さんのkonasi :  BluetoothモジュールにGPIO/PWM/ADCなどを組み込んで、iOSから制御できる。Bluetoothのサービス・キャラクタリスティック操作など下回りを意識させないiOS用ライブラリも提供されており、すぐに使えそう。ただ少々お高いのと、執筆現在は品切れ中・・勉強がてら、CoreBluetoothやBTLEを意識する低レイヤーなところから始めても悪くない
  • ランニングエレクトロニクスさんのSBDBT : BTLEとシリアルインタフェースのブリッジとして動き、価格がお手頃。mbedやArduinoなどのマイコンボートと通信するためにはシリアルポート経由でコマンドを送受信するようなインタフェースを作る必要がありそう。モジュールが小型なので、ロボットの無線操縦なんかに向いているのかしら
  • RedBearLabさんのArduino BLE shield : Arduino用のライブラリやiOS用のAppもあり、お手軽に使えそう。お値段も手ごろですが、日本の技適がないため法的に使えません。
  • btstack : 組み込み向けのBluetooth stack。もともとはSPPをサポートしていたが、BTLEにも対応。HCI (Host-Controller Interface)から上の層を実装しており、下位層であるLL (Link Layer)/PHYは自分で持ってくる必要がある。下位層はBluetoothドングルを使うことを想定しているので、USB周りのドライバを用意する必要がある。

今回は、mbedを直接iOSから制御できるようにしようということで、btstackを使いました。問題はUSBのドライバをどうするかです。SBDBTもbtstackを使っていますが、PIC用なのでそのままでは使えません。一方、mbedではSPP版のbtstackをNorimasa Okamotoさんがmbedに移植されていました。

実装(移植)の方針として、OkamotoさんのSPP版btstackをベースに、上位プロトコル部分をBTLE (GATT/ATT)に差し替えました。mbedのリソース(現状はLED1だけですが・・)をBTLEのサービスやキャラクタリスティックとして見せる部分の処理は、ランエレさんのSBDBT用コード(ble-server.c)を参考にさせていただきました。


実装

mbed側のコードは、mbed.orgにポストしてあります。SBDBT同様に以下の動作を行います:

  • サービス(UUID: 0xFFF0)配下に3つのcharacteristicsがありcharacteristic 0xFFF2に00 or 01をライトすることでLED1のon/offを制御できます
  • LED3はbtstackが初期化されアドバタイズを始めた時点で点灯、マスターと接続が完了すると点滅します
  • LED2はアトリビュートのリードで点灯、ライトで消灯します。今回のiOS Appからの制御ではライトのみなので、消えっぱなしです

iOS側のコードは、以下の通りです(iOS 6.1 & Xcode 4.6を使用)。チュートリアルなどを参考にして書いてみました:

//
//  ViewController.h
//  mbed Control
//
//  Created by todotani on 2013/02/11.
//  Copyright (c) todotani. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>

@interface ViewController : UIViewController <CBCentralManagerDelegate, CBPeripheralDelegate>

@property (weak, nonatomic) IBOutlet UISwitch *led1Sw;
@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
@property (weak, nonatomic) IBOutlet UISegmentedControl *connectButton;

@end

----

//
//  ViewController.m
//  mbed Control
//
//  Created by todotani on 2013/02/11.
//  Copyright (c) todotani. All rights reserved.
//

#import "ViewController.h"

#define CONECTED     0
#define DISCONNECTED 1

@interface ViewController ()

@property (strong) CBCentralManager *manager;
@property (strong) CBPeripheral *peripheral;
@property (strong) CBCharacteristic *led1Characteristic;

- (void) updateLed1;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}


#pragma mark CoreBuletooth delegate

- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    NSLog(@"State Update:%d",[self.manager state]);
}

- (void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)aPeripheral
      advertisementData:(NSDictionary *)advertisementData
                   RSSI:(NSNumber *)RSSI
{
    self.statusLabel.text = [NSString stringWithFormat:@"Found(RSSI %@)", RSSI];
    [central stopScan];
    self.peripheral = aPeripheral;
    [central connectPeripheral:self.peripheral options:nil];
}

- (void) centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral
                  error:(NSError *)error
{
    NSLog(@"Failed:%@",error);
}

- (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)aPeripheral
{
    NSLog(@"Connected:%@",aPeripheral.UUID);
    self.statusLabel.text = @"Connected";
    [aPeripheral setDelegate:self];
    [aPeripheral discoverServices:@[[CBUUID UUIDWithString:@"FFF0"]]];
}

- (void) centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral
                  error:(NSError *)error
{
    NSLog(@"Disconnected");
    self.statusLabel.text = @"Disconnected";
    self.connectButton.selectedSegmentIndex = DISCONNECTED;
    [self.manager stopScan];
}


// mbed Sericeを検出
- (void) peripheral:(CBPeripheral *)aPeripheral didDiscoverServices:(NSError *)error
{
    for (CBService *aService in aPeripheral.services) {
        NSLog(@"Service UUID:%@", aService.UUID);
        if ([aService.UUID isEqual:[CBUUID UUIDWithString:@"FFF0"]]) {
            [aPeripheral discoverCharacteristics:@[[CBUUID UUIDWithString:@"FFF2"]] forService:aService];
        }
    }
}

// LED1制御用characteristicsを検出
- (void) peripheral:(CBPeripheral *)aPeripheral didDiscoverCharacteristicsForService:(CBService *)service
              error:(NSError *)error
{
    for (CBCharacteristic *aChar in service.characteristics){
        NSLog(@"Characteristic UUID:%@",aChar.UUID);
        if ([aChar.UUID isEqual:[CBUUID UUIDWithString:@"FFF2"]]) {
            self.led1Characteristic = aChar;
            [self updateLed1];
        }
    }
}


#pragma mark ActionMethods

- (IBAction)changeConnection:(id)sender
{
    UISegmentedControl *mySegmented = sender;
    switch (mySegmented.selectedSegmentIndex) {
        case 0:
            [self.manager scanForPeripheralsWithServices: @[[CBUUID UUIDWithString:@"FFF0"]]
                                              options:@{ CBCentralManagerScanOptionAllowDuplicatesKey:@YES }];
            self.statusLabel.text = @"Scanning";
            break;
        case 1:
            if ([self.peripheral isConnected]) {
                [self.manager cancelPeripheralConnection:self.peripheral];
            } else {
                [self.manager stopScan];
                self.statusLabel.text = @"Disconnected";
            }
            break;
        default:
            break;
    }
}


- (IBAction)changeLed1:(id)sender
{
    if ([self.peripheral isConnected]) {
        [self updateLed1];
    }    
}


#pragma mark Private methods

- (void) updateLed1
{
    const unsigned char onData[]  = {0x01};
    const unsigned char offData[] = {0x00};
    
    if ([self.led1Sw isOn]) {
        [self.peripheral writeValue:[NSData dataWithBytes:onData length:1] forCharacteristic:self.led1Characteristic
                               type:CBCharacteristicWriteWithoutResponse];
    } else {
        [self.peripheral writeValue:[NSData dataWithBytes:offData length:1] forCharacteristic:self.led1Characteristic
                               type:CBCharacteristicWriteWithoutResponse];
    }
}

@end

動作の概要は以下です:

  • 105行目:Connectボタンが押されると、CBCentralManagerクラスのscanForPeripheralsWithServicesメソッドを呼び出してBTLEアドバタイズのスキャンを開始。この際、mbedの制御用に定義している、サービス UUID 0xFFF0を検索対象として指定(Arrayに複数の検索対象UUIDをセットできるが、このサンプルでは0xFFF0のみを指定)
  • 39行目:ペリフェラルが見つかると、centralManager:didDiscoverPeripheral: デリゲートがコールバックされます。ここでは何も考えずに、見つかったペリフェラルに接続要求を発行しています。スキャンを行う際に、mbedのサービスを指定していますので、mbed以外と繋がることはないですが、どのmbedかまでは区別していません。接続先をもっと厳密に制御したい場合は、認証などのセキュリティー機能を使うのでしょうか(このあたりはよく分かっていません)
  • 55行目:ペリフェラルとの接続が完了すると、centralManager:didConnectPeripheral:デリゲートがコールバックされます。ここで、見つかったペリフェラルのインスタンスに対して、discoverServicesメッセージを送って、サービスを検索します。引数として、UUID 0xFFF0を指定することで、mbed制御のサービスのみを検出するようにしています(引数をnilにすると、GATT基本サービスの1800や1801が見つかった際にもコールバックされるのでフィルターしておきます)
  • 74行目:サービスが見つかると、peripheral:didDiscoverServices:デリゲートがコールバックされます。今度は、ペリフェラルクラスに対して、discoverCharacteristicsメッセージを送ってキャラクタリスティックの検索を行います。サービス0xFFF0には3つのキャラクタリスティックが定義されていますが、LED1の制御に使うのは0xFFF2のみのため検索条件を指定します。
  • 85行目:キャラクタリスティックが見つかると、peripheral:didDiscoverCharacteristicsForService:デリゲートがコールバックされます。discoverCharacteristicsでフィルター条件をかけているつもりなのですが、3回コールバックされるため、処理対象とする0xFFF2を判定しています。これで、LED1の制御に必要なキャラクタリスティックがViewControllerクラスのプロパティとして設定されました
  • 133行目:スイッチの状態が変化すると、LED1制御用のキャラクタリスティックにon/offに該当する値をライトします

動作中の写真はこんな感じです:

IMG_0123

IMG_0984

 

今後の予定

今回は書き込みのみですので、mbed側のGPIO状態の読み取りを実装するつもり。


参考資料

Bluetooth LE (BTLE)のAdvertisement packet format

$
0
0

btstackのコードを追いかけていると、BTLEペリフェラルがadvertisementを行う際のパケットを規定している配列があります。具体的には、こんなのです:

const uint8_t adv_data[31]="\x02\x01\x05" "\x05\x09mbed" "\x03\x02\xf0\xff";

なんとなく、名前やUUIDを定義していると分かるのですが、この値をいじったときに、iOSのCoreBluetoothでスキャンできなくなるなどの動きになったため、どういう構造になっているのかを知りたいと思っていました。Google先生に聞いてもうまくヒットするコンテンツがなかったのですが、結局本家のBluetooth 4.0 Specificationドキュメントで答えを見つけました。2000ページ以上あるドキュメントなので、目次くらいしかみていませんが、Volume-3 (Core System Package), Part-C (Generic Access Profile), Section-11 “ADVERTISING AND SCAN RESPONSE DATA FORMAT”に必要な情報を見つけました。

小ネタですが、BTLEのadvertisement packet formatということで以下に記載します。


Packet Format

Bluetooth Specificationからの抜粋です。

image

  • Advertisement dataは31byteで構成されます
  • AD Structureと呼ばれる情報要素が31byteのpacketに詰め込まれます
  • 個々のAD Structureは、Length – AD Type – AD Dataで構成されます
  • 未使用部分は0で埋める

AD Type値の意味が分かれば、個々のAD Structureが何をしているのかが分かります


AD Type値の割り当て

AD TypeはAssigned Numberという別のドキュメントで定義しています(デバイス種別の追加などで新規割り当て値が増えていくため、規格文書本体とは分けて管理しているようです)。Assigned Numberを公開しているWebサイトはこちらで、そこから、Generic Access Profileに飛ぶと、目的の情報が出てきます。主な情報を記載すると:

名称
0x01Flag
0x02Incomplete List of 16-bit Service UUIDs
(more 16-bit UUIDs available)
0x03Complete List of 16-bit Service Class UUIDs
0x09Complete Local Name
0x10Device ID


Flagは各bitが意味を持っており、以下の定義になっています:

bit位置意味
0LE Limited Discoverable Mode
1LE General Discoverable Mode
2BR/EDR Not Supported
3Simultaneous LE and BR/EDR capable (Controller)
4Simultaneous LE and BR/EDR capable (Host)
5..7Reserved

btstackでは0x05 = 0101bですので、「LE Limited Discoverable Mode & BR/EDR Not Supported」をadvertiseすることになります。


最終的なAdvertise Data

btstackのケースでは、以下のAD Structureをadvertiseしています(要素と記載した部分は、advertise dataには含まれません):

要素LengthTypeData
AD121 (flag)0x05: LE Limited Discoverable & BR/EDR Not Supported
AD259 (Complete Local Name)mbed
AD332 (Incomplete List of 16-bit Service UUID)0xFFF0 (配列上では、上位下位バイトを逆に並べる)

UUIDのType値を2 (Incomplete)としているのは、advertiseでは0xFFF0のみを広報しますが、GATT profileとして0x1800, 0x1801も別途通知できるようにしているためだと思います。


おわりに

AdvertiseにService UUIDを含めないことも出来るのですが、その場合、iOS CoreBluetoothのCBCentralManagerクラスインスタンスに対してscanForPeripheralsWithServices: メッセージを送った際に、引数としてUUIDを指定するとペリフェラルを検出してくれません(nil指定にする必要あり)。UUIDをadvertiseしていなので当たり前ですが、最初はbtstackが何をadvertiseしているのかを分かっていなかっため悩みました。Twitterで呟いたら、ランニングエレクトロニクスさん(@sibu2)にService UUIDを追加するように教えていただいた次第でした。


参考資料

ブログタイトル変更

$
0
0

ブログのタイトルに使っていたPS3もPS4に世代交代が進んでいること、PS3の話題は長らく書いていないこと(ブログの更新自体、最近停滞していますが・・)から、ブログタイトルを変更することにしました。

電子工作やモバイルの話題が中心になると思いますが、今自分の中ではやっている物について綴りたいと思います。

ブログ更新も近日復活予定!

Intel Galileoのインプレッション

$
0
0

Blog名更新後の初エントリ(久しぶり・・)は、Intel Galileoです。昨年スイッチサイエンスさんに予約して、1月15日に無事届きました。ArduinoとLinuxの共存やIntelの組み込み(IoT)向けプロセッサQuark SoC X1000搭載など、なかなか興味深い製品で、発売即ゲットしてしまいました。

まだ、LチカとLinux環境にちょっと触った程度ですが、気がついたことを書いてみます。

 

パッケージ構成

本体とACアダプタ(各国対応のプラグ付き)というシンプルな構成。

量産品発売前のレビュー記事(マイナビさんとか)だと、マスコット人形・箱を開けたときのオルゴール・USBケーブル・基板スタンド用アダプタが添付とありましたが、量産版では削除されています。USBケーブルや基板スタンドは手持ち品があるので、Arduino(DIY)の精神としては余計なものは付けないほうが私的には好ましいです。でも、オルゴールは聴いてみたかった・・・ 電源は、5V/2AのACアダプターで(USBからの給電は不可。ボードが壊れると、恐ろしいことがマニュアルに書いてあります)、2Aの手持ち品もあるのですが、こちらは添付の純正品を使っています。

IMG_0326

(左側はMFTのIntelブースでもらったポストイット)

 

ハードウェア構成

Arduinoシールド互換とするために、5V GPIO/PWM, ADCが乗っていますが、Quark SoC X1000の機能は使わず、I2C経由の外付けのチップで対応しています。GPIOはQuarkにも搭載されているようにデータシート上見えますが、敢えて外付けチップ経由になっています。恐らく、5V/PWM対応がQuark内臓のGPIOではできないためと思われますが、GPIOが100KHz動作のI2C経由なので、GPIOのトグル周期は230Hz程度とオリジナルのArduinoより遅そうです(比べた訳ではないですが)。そのため、表示用のLCDも従来の4bitパラレルよりI2Cで直接接続した方が効率がよいかも知れません。

CPUが熱くなるとマイナビさんのレビュー記事にありますが、Linux環境を起動して温度センサーの値を調べてみるとこんな感じ:

□CPU温度の確認
# cat /sys/class/thermal/thermal_zone0/temp

  • Quark SoC X1000 : 69℃~72℃程度
  • RaspberryPi : 44℃程度

確かに、この手の組み込みチップとしては熱いと思います。マイナビさんのレビューでは、電源のチップ周りが熱くなるという記述もありましたが、自分の固体はCPU以外は殆ど熱くならないので量産版では改善されたのかも。

オンボードに8MB Flashが搭載されており、通常はこのFlashからLinuxをブートしてLinux上でArduinoスケッチを動かします。スケッチ用には、Quark SoCオンチップの512KB SRAMの約半分が割り当てられているみたい。512KB SRAM以外にもLinuxを動かす256MB DDR3 DRAMが搭載されています。


ソフトウェア構成

専用IDEを使ってスケッチを作成します。Macにインストールする際の注意点としてアプリケーションフォルダーにDLしたファイルを置くだけですが、appファイル名にスペースを入れてはいけません。私は、当初Arduino用のIDEと区別したかったため、”Arduino Galileo”とスペースありの名前で登録したのですが、一番最初に行うファームウェアのアップデートでエラーが発生し、はまりました(エラーメッセージでググったところ、Intelのサポートフォーラムで解決策が見つかった次第)。

オンボードのFlashから起動した場合、電源を落とすとスケッチも消えてしまい、再アップロードが必要です(スケッチはSRAMに直接書き込まれる模様)。もう一つの起動方法として、MicroSDカードにIntelが提供しているLinuxイメージを書き込んでブートすることができます。この場合、スケッチはMicroSDカードの/sketch/sketch.elfに書き込まれるため、電源を切っても再投入時自動実行されます。

オンボードFlashからブートした場合、Linuxのコマンド操作を行うためにはピンジャックのシリアルケーブルをPCに接続する必要があるようです。このピンジャック3.3V TTLでなく、RS232C (±12V?)で信号が出ているため、直接FT232等のUSB-シリアル変換に接続できません。自作も面倒なので、スイッチサイエンスさんからアクセサリとして接続ケーブルが発売されるのを待ちます。MicroSDカードからLinuxをブートした場合は、宅内ルーターからDHCP経由で割り当てられたIPアドレスを調べることによってssh経由でLinuxにアクセスできるため(ひとりブログさんの記事を参照)、この方法でLinuxに触っています。

MicroSDからブートした場合、Arduinoのスケッチ(sketch.elf)はLinuxの一プロセスとして実行されているように見えます。(下記ps出力の41行目)

root@clanton:/# ps
  PID USER       VSZ STAT COMMAND
    1 root       796 S    init [5]
    2 root         0 SW   [kthreadd]
    3 root         0 SW   [ksoftirqd/0]
    4 root         0 SW   [kworker/0:0]
    5 root         0 SW<  [kworker/0:0H]
    6 root         0 SW   [kworker/u:0]
    7 root         0 SW<  [kworker/u:0H]
    8 root         0 SW<  [cpuset]
    9 root         0 SW<  [khelper]
   10 root         0 SW   [kdevtmpfs]
   11 root         0 SW<  [netns]
   12 root         0 SW   [kworker/u:1]
  119 root         0 SW   [bdi-default]
  121 root         0 SW<  [kblockd]
  200 root         0 SW   [kworker/0:1]
  329 root         0 SW   [kswapd0]
  384 root         0 SW   [fsnotify_mark]
  396 root         0 SW<  [crypto]
  526 root         0 SW<  [deferwq]
  529 root         0 SW   [mmcqd/0]
  777 root         0 SW<  [kworker/0:1H]
  937 root         0 SW<  [loop0]
  938 root         0 SW   [kjournald]
 1002 root         0 SW   [khubd]
 1058 root         0 SW   [irq/61-0-0020]
 1100 root         0 SW   [spi0]
 1103 root         0 SW   [spi1]
 1133 root         0 SW<  [cfg80211]
 1277 root      1264 S    udhcpc -R -n -p /var/run/udhcpc.eth0.pid -i eth0
 1297 messageb  1464 S    /usr/bin/dbus-daemon --system
 1305 root      3256 S    /usr/sbin/sshd
 1320 root      1264 S    /sbin/syslogd -n -O /var/log/messages
 1322 root      1260 S    /sbin/klogd -n
 1326 root      1264 S    /sbin/getty 115200 ttyS1
 1327 root      1264 S    /sbin/getty 38400 tty1
 1328 root       768 S    /opt/cln/galileo/galileo_sketch_reset -v
 1329 root      1856 S    {clloader.sh} /bin/sh /etc/init.d/clloader.sh
 1443 root       876 S    /opt/cln/galileo/clloader --escape --binary --zmodem --di
 1452 root     18292 S    /sketch/sketch.elf /dev/pts/0 /dev/ttyS0
 1455 root      3316 R    {sshd} sshd: root@pts/1
 1457 root      1944 S    -sh
 1463 root      1264 R    ps

MicroSD用に提供されているLinuxの機能はミニマムです。コマンドファイルは以下のようにBusyBoxを使って小型化されており最小限。gccやパッケージアップデートの機能もありません。そのため、RaspberryPiのようにLinux環境での開発は難しく、当面はArduino環境での開発が主体になりそうです。

root@clanton:/# ls -la /sbin
drwxr-sr-x    2 root     root          2048 Sep 30  2013 .
drwxr-xr-x   18 root     root          1024 Jan  1 00:57 ..
lrwxrwxrwx    1 root     root            12 Sep 30  2013 acpid -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 arp -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 blkid -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 blockdev -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 bootchartd -> /bin/busybox
-rwxr-xr-x    1 root     root         12668 Sep 30  2013 bootlogd
lrwxrwxrwx    1 root     root            12 Sep 30  2013 depmod -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 fdisk -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 findfs -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 fsck -> /bin/busybox
-rwxr-xr-x    1 root     root          3944 Sep 30  2013 fstab-decode
lrwxrwxrwx    1 root     root            12 Sep 30  2013 getty -> /bin/busybox
lrwxrwxrwx    1 root     root            19 Sep 30  2013 halt -> /sbin/halt.sysvinit
-rwxr-xr-x    1 root     root         12168 Sep 30  2013 halt.sysvinit
lrwxrwxrwx    1 root     root            12 Sep 30  2013 hwclock -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 ifconfig -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 ifdown -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 ifup -> /bin/busybox
lrwxrwxrwx    1 root     root            19 Sep 30  2013 init -> /sbin/init.sysvinit
-rwxr-xr-x    1 root     root         31268 Sep 30  2013 init.sysvinit
lrwxrwxrwx    1 root     root            12 Sep 30  2013 insmod -> /bin/busybox
-rwxr-xr-x    1 root     root         78556 Sep 30  2013 iwconfig
lrwxrwxrwx    1 root     root             8 Sep 30  2013 iwgetid -> iwconfig
lrwxrwxrwx    1 root     root             8 Sep 30  2013 iwlist -> iwconfig
lrwxrwxrwx    1 root     root             8 Sep 30  2013 iwpriv -> iwconfig
lrwxrwxrwx    1 root     root             8 Sep 30  2013 iwspy -> iwconfig
-rwxr-xr-x    1 root     root         14972 Sep 30  2013 killall5
lrwxrwxrwx    1 root     root            12 Sep 30  2013 klogd -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 loadkmap -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 logread -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 losetup -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 lsmod -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 mdev -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 modprobe -> /bin/busybox
-rwxr-xr-x    1 root     root          3456 Sep 30  2013 nologin
lrwxrwxrwx    1 root     root            12 Sep 30  2013 pivot_root -> /bin/busybox
lrwxrwxrwx    1 root     root            23 Sep 30  2013 poweroff -> /sbin/poweroff.sysvinit
lrwxrwxrwx    1 root     root            13 Sep 30  2013 poweroff.sysvinit -> halt.sysvinit
lrwxrwxrwx    1 root     root            21 Sep 30  2013 reboot -> /sbin/reboot.sysvinit
lrwxrwxrwx    1 root     root            13 Sep 30  2013 reboot.sysvinit -> halt.sysvinit
lrwxrwxrwx    1 root     root            12 Sep 30  2013 rmmod -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 route -> /bin/busybox
lrwxrwxrwx    1 root     root            23 Sep 30  2013 runlevel -> /sbin/runlevel.sysvinit
-rwxr-xr-x    1 root     root          3472 Sep 30  2013 runlevel.sysvinit
lrwxrwxrwx    1 root     root            12 Sep 30  2013 setconsole -> /bin/busybox
lrwxrwxrwx    1 root     root            23 Sep 30  2013 shutdown -> /sbin/shutdown.sysvinit
-rwxr-xr-x    1 root     root         18260 Sep 30  2013 shutdown.sysvinit
lrwxrwxrwx    1 root     root            12 Sep 30  2013 start-stop-daemon -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 sulogin -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 switch_root -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 sysctl -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 syslogd -> /bin/busybox
lrwxrwxrwx    1 root     root             4 Sep 30  2013 telinit -> init
lrwxrwxrwx    1 root     root            12 Sep 30  2013 udhcpc -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Sep 30  2013 vconfig -> /bin/busybox
lrwxrwxrwx    1 root     root            17 Sep 30  2013 vigr -> /sbin/vigr.shadow
lrwxrwxrwx    1 root     root            11 Sep 30  2013 vigr.shadow -> vipw.shadow
lrwxrwxrwx    1 root     root            17 Sep 30  2013 vipw -> /sbin/vipw.shadow
-rwxr-xr-x    1 root     root         39808 Sep 30  2013 vipw.shadow
lrwxrwxrwx    1 root     root            12 Sep 30  2013 zcip -> /bin/busybox


CPU発熱の考察

Arduinoのスケッチはloopの部分で無限ループを廻すので、そのためCPU使用率が高いのではないかと思い、調べてみました。Lチカスケッチを動かした状態でtopコマンドを動かすとCPU使用率は以下の通り。全体のCPU使用率が3%で、sketch.elfのCPU使用率は0%です。ですので、スケッチの無限ループによるCPU使用率の高騰はないです。

Mem: 42132K used, 191680K free, 0K shrd, 1868K buff, 27688K cached
CPU:   0% usr   1% sys   0% nic  97% idle   0% io   0% irq   0% sirq
Load average: 0.00 0.02 0.05 2/45 1478
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
 1478  1464 root     R     1268   1%   1% top
 1455  1305 root     S     3316   1%   1% {sshd} sshd: root@pts/1
  200     2 root     SW       0   0%   0% [kworker/0:1]
 1452  1443 root     S    18292   8%   0% /sketch/sketch.elf /dev/pts/0 /dev/ttyS0
 1305     1 root     S     3256   1%   0% /usr/sbin/sshd
 1464  1457 root     S     1944   1%   0% bash
 1457  1455 root     S     1944   1%   0% -sh
 1329     1 root     S     1856   1%   0% {clloader.sh} /bin/sh /etc/init.d/clloader.
 1297     1 messageb S     1464   1%   0% /usr/bin/dbus-daemon --system
 1320     1 root     S     1264   1%   0% /sbin/syslogd -n -O /var/log/messages
 1326     1 root     S     1264   1%   0% /sbin/getty 115200 ttyS1
 1327     1 root     S     1264   1%   0% /sbin/getty 38400 tty1
 1277     1 root     S     1264   1%   0% udhcpc -R -n -p /var/run/udhcpc.eth0.pid -i
 1322     1 root     S     1260   1%   0% /sbin/klogd -n
 1443  1329 root     S      876   0%   0% /opt/cln/galileo/clloader --escape --binary
    1     0 root     S      796   0%   0% init [5]
 1328     1 root     S      768   0%   0% /opt/cln/galileo/galileo_sketch_reset -v
  937     2 root     SW<      0   0%   0% [loop0]
    6     2 root     SW       0   0%   0% [kworker/u:0]
    3     2 root     SW       0   0%   0% [ksoftirqd/0]
  529     2 root     SW       0   0%   0% [mmcqd/0]
  777     2 root     SW<      0   0%   0% [kworker/0:1H]
   10     2 root     SW       0   0%   0% [kdevtmpfs]
   12     2 root     SW       0   0%   0% [kworker/u:1]
  938     2 root     SW       0   0%   0% [kjournald]
    2     0 root     SW       0   0%   0% [kthreadd]
    5     2 root     SW<      0   0%   0% [kworker/0:0H]
  119     2 root     SW       0   0%   0% [bdi-default]
  121     2 root     SW<      0   0%   0% [kblockd]
    4     2 root     SW       0   0%   0% [kworker/0:0]
  396     2 root     SW<      0   0%   0% [crypto]

じゃあなぜ熱いのかと言うと、パワー・マネジメント機能がちゃんと動いていない、もともと消費電力が高めということかと思います。

Quark SoC X1000のデータシートを見ると、Power Management Stateとして以下をサポートしています:

  • S0: Full On
  • S3: Suspend to RAM (Standby)
  • S4: S4 - Suspend to Disk (Hibernate)

S3以降はCPUが完全に停止してしまうためスケッチ実行中に遷移することはないと思います。ですので、通常S0ステートで動き続ける訳ですが、AtomのようにS0を細分化してS0内に省電力モードを設けるような機能はQuarkにはありません。

Sステート以外に、ACPIアイドルステートして以下があります:

  • C0: Active mode, processor executing code
  • C1: AutoHALT state
  • C2: Stop Grant state

PCのACPIでは、アイドル時OSがHALT命令を発行してCPUをC1ステートに落とすようなことやってませんでしたっけ?だとすると、GalileoのLinuxはACPIが動いておらず、アイドル時もCPUクロック周りがフル回転していることになります。

70℃程度でCPUが壊れることはないと思いますが、消費電力や発熱的には組み込みやIoT向けには今一な感じがします。

 

参考文献

Intel GalileoでCoreMark

$
0
0

Intel Galileoで、以前LPCXpressoで動かしたCoreMark(CPUコアのベンチマークテスト)を試してみました。比較として、Raspberry Piでも。さて、結果はいかに。

IMG_0349


GalileoへのCoreMarkの移植

GalileoのArduino形式スケッチにCoreMarkを移植する際に工夫した点は以下です:

  • 表示に使うprintfは、下地で動いているるLinuxのprintfがリンクできることを発見(773mbarさんのIntel Galileoでprintfを使うを参照)
  • CoreMark本体(Cのコード)はArduinoのLibraryとしてリンク

スケッチのイメージは以下の通りです:

#include <stdio.h>
#include <coremark.h>

void setup() {
  stdout = freopen("/dev/ttyGS0", "w", stdout);
}

void loop() {
  printf("Run CoreMark\n");
  mainCoreMark();
  printf("CoreMark End \n\n");    
}

5行目のコードで、printfの出力をUSBで接続したPCのIDEのシリアルモニターに出力できます。CoreMarkの本体をloopの中で呼んでいるのは、CPU使用率を100%張り付きにして温度変化を調べるためです。CoreMarkの実行結果は以下の通り:

2K performance run parameters for coremark.
CoreMark Size    : 666
Total ticks      : 112070000
Total time (secs): 112.070000
Iterations/Sec   : 446.149728
Iterations       : 50000
Compiler version : 4.7.2
Compiler flags   : #pragma O3, Otime
Memory location  : STACK
seedcrc          : 0xe9f5
[0]crclist       : 0xe714
[0]crcmatrix     : 0x1fd7
[0]crcstate      : 0x8e3a
[0]crcfinal      : 0xa14c
Correct operation validated. See readme.txt for run and reporting rules.
CoreMark 1.0 : 446.149728 / 4.7.2 #pragma O3, Otime / STACK

スコアは、446で以外と伸びていません。LPC1769 120MHzで191出ていましたので、Pentiumクラス400MHz動作のQuark X1000ならもっと数字が伸びてもよいと思うのですが。CoreMarkの設定方法が正しくないのか・・

CoreMarkを連続して動作させると、以下の通りCPU使用率は100%に張り付き、スケッチのCPU使用率が98%になります。

CPU:  98% usr   1% sys   0% nic   0% idle   0% io   0% irq   0% sirq
Load average: 1.00 1.00 0.87 2/44 1416
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
 1333  1330 root     R    18300   8%  98% /sketch/sketch.elf /dev/pts/0 /dev/ttyS0
 1416  1385 root     R     1268   1%   1% top
 1383  1304 root     S     3316   1%   1% {sshd} sshd: root@pts/1
  200     2 root     SW       0   0%   0% [kworker/0:1]
 1304     1 root     S     3256   1%   0% /usr/sbin/sshd
 1385  1383 root     S     1968   1%   0% -sh
 1328     1 root     S     1852   1%   0% {clloader.sh} /bin/sh /etc/init.d/clloader.
 1296     1 messageb S     1464   1%   0% /usr/bin/dbus-daemon --system
 1319     1 root     S     1264   1%   0% /sbin/syslogd -n -O /var/log/messages
 1325     1 root     S     1264   1%   0% /sbin/getty 115200 ttyS1
 1326     1 root     S     1264   1%   0% /sbin/getty 38400 tty1
 1276     1 root     S     1264   1%   0% udhcpc -R -n -p /var/run/udhcpc.eth0.pid -i
 1322     1 root     S     1260   1%   0% /sbin/klogd -n
 1330  1328 root     S      836   0%   0% /opt/cln/galileo/clloader --escape --binary
    1     0 root     S      796   0%   0% init [5]
 1327     1 root     S      768   0%   0% /opt/cln/galileo/galileo_sketch_reset -v
    6     2 root     SW       0   0%   0% [kworker/u:0]
  936     2 root     SW<      0   0%   0% [loop0]
    3     2 root     SW       0   0%   0% [ksoftirqd/0]
  529     2 root     SW       0   0%   0% [mmcqd/0]
  752     2 root     SW<      0   0%   0% [kworker/0:1H]
   10     2 root     SW       0   0%   0% [kdevtmpfs]
   12     2 root     SW       0   0%   0% [kworker/u:1]
    5     2 root     SW<      0   0%   0% [kworker/0:0H]
   11     2 root     SW<      0   0%   0% [netns]
  119     2 root     SW       0   0%   0% [bdi-default]
  121     2 root     SW<      0   0%   0% [kblockd]
    2     0 root     SW       0   0%   0% [kthreadd]
    4     2 root     SW       0   0%   0% [kworker/0:0]
  384     2 root     SW       0   0%   0% [fsnotify_mark]
  396     2 root     SW<      0   0%   0% [crypto]

このときのCPU温度は72℃で、Lチカ(CPU使用率2%)の時と変わりません。ということは、GalileoのCPUはアイドル状態でも全く電力制御が動いていないということになります。今一な動作ですが、下地のLinuxカーネルのチューニングの問題でしょうか。Raspberry Piだと、変化は少ないですが、アイドル状態からフル負荷に上げると、CPU温度が44℃程度から50℃台に上昇するため、こちらは電力制御が若干行われていると思われます。


Raspberry PiのCoreMark

おまけとして、Raspberry PiのCoreMark結果を以下に示します:

2K performance run parameters for coremark.
CoreMark Size    : 666
Total ticks      : 36340000
Total time (secs): 36.340000
Iterations/Sec   : 1375.894331
Iterations       : 50000
Compiler version : 4.6.3
Compiler flags   : #pragma O3, Otime
Memory location  : STACK
seedcrc          : 0xe9f5
[0]crclist       : 0xe714
[0]crcmatrix     : 0x1fd7
[0]crcstate      : 0x8e3a
[0]crcfinal      : 0xa14c
Correct operation validated. See readme.txt for run and reporting rules.
CoreMark 1.0 : 1375.894331 / 4.6.3 #pragma O3, Otime / STACK

Raspberry PiはCoreMark値 1375とGalileoのトリプルスコアで圧勝。クロック周波数差(700MHz vs 400MHz)を考慮しても、Raspberry PiのARMコアの方がスコアが高いです。使ったコードは同じで、両方ともLinux配下で動かしていますから条件は同じだと思います。


まとめ

こうして比較してみると、(GalileoでのCoreMarkの動かし方の是非がありますが)GalileoのQuark X1000 CPUの性能は今一な感じです。

 CPU clockCoreMark値
Intel Galileo400 MHz446.14
Raspberry Pi700 MHz1375.89


参考文献

SBDBT/SBBLEとiPhoneでBLE通信を行う

$
0
0

以前、「iPhoneからmbedをBluetooth LE (BTLE)で制御する」という記事を書いたのですが、今回はその続編です。前回は、RunningElectronicsさんのSBDBTで使われているファームウェア(btstack)をmbedに移植して、iPhoneから、mbedにつないだLEDをBLEで制御できるようにしました。mbedのGPIOへの出力はうまく行ったのですが、GPIOの読み取りをNotificationベースでリアルタイムに飛ばす処理はどうしても動かせませんでした。

最新ファームではNotificationも使えると教えていただき、この際なので、SBDBT本体を購入して試してみました。

 

出荷時ファームウェアの動作確認

出荷時はBluetooth SPPプロファイルで動くファームウェアが搭載されています。ですので、シリアルデータをBluetoothでPCなどに飛ばすことができます。試験では、RaspBerry Piのシリアル出力をMacに飛ばす実験を行いました。無線による遅延もなく、軽快にコンソール操作ができました。

 

IMG_0379

 

SBDBT-SPP_Mac

 

SBBLEファームの使用

PICkitを使って、ファームをBLE用のSBBLEに書き換えます。版数は、”sbble_131224.hex”を使用しています。

この状態で、SBDBTにLEDとSWをつないで、Micono UtilitiesさんのiOSアプリを入れれば、LEDの制御・SWの読み取り(Notificationベース)が簡単にできてしまいます。ここまで環境が整備されていると、もう、自分でやることもないくらいです。

 

IMG_0015

 

せっかく、iOS Developperの更新もしたことだし、気を取り直して、自作の簡単なiOSアプリを作ってみました。SBBLEはATT/GATTの割り当てがKonashiと同一にしてあるため、KonashiのSDKが使えます。CoreBluetoothのAPIをゴリゴリたたくより簡単で、Arduinoやmbedライクなスタイルでプログラミングが出来そうなので、Konashi SDKを使いました。iOSは7.1で、XCodeは5.1を使っています。作成したアプリのデモ画像を以下に示します。

 

ソースは以下の通りです。

//
//  ViewController.h
//  SBBLE
//
//  Created by Todotani on 2014/03/25.
//  Copyright (c) 2014年 Todotani. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIButton *finedButton;
@property (weak, nonatomic) IBOutlet UIButton *disconnectButton;
@property (weak, nonatomic) IBOutlet UISwitch *ledSwitch;
@property (weak, nonatomic) IBOutlet UILabel *switchLable;

@end

 

//
//  ViewController.m//  SBBLE
//
//  Created by Todotani on 2014/03/25.
//  Copyright (c) 2014年 Todotani. All rights reserved.
//

#import "ViewController.h"
#import "Konashi.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [Konashi initialize];
    [Konashi addObserver:self selector:@selector(ready) name:KONASHI_EVENT_READY];
    [Konashi addObserver:self selector:@selector(input) name:KONASHI_EVENT_UPDATE_PIO_INPUT];
    self.finedButton.backgroundColor = [UIColor grayColor];
    self.switchLable.backgroundColor = [UIColor grayColor];
}


- (IBAction)find:(id)sender {
    [Konashi find];
}


- (IBAction)disconnect:(id)sender {
    [Konashi disconnect];
    self.finedButton.backgroundColor = [UIColor grayColor];
}


- (IBAction)switchChanged:(id)sender {
    [self ledControl];
}


- (void)ready
{
    [Konashi pinMode:PIO1 mode:INPUT];
    [Konashi pinMode:PIO2 mode:OUTPUT];
    [Konashi pinPullup:PIO1 mode:PULLUP];
    [self ledControl];
    self.finedButton.backgroundColor = [UIColor greenColor];
}


- (void)input
{
    if ([Konashi digitalRead:PIO1] == LOW) {
        self.switchLable.backgroundColor = [UIColor redColor];
    } else {
        self.switchLable.backgroundColor = [UIColor grayColor];
    }
}


- (void)ledControl
{
    if (self.ledSwitch.on == YES) {
        [Konashi digitalWrite:PIO2 value:HIGH];
    } else {
        [Konashi digitalWrite:PIO2 value:LOW];
    }
}


- (BOOL)shouldAutorotate
{
    return NO; // YES:自動回転する NO:自動回転しない
}

@end

 

終わりに

Konashi SDKを使ったおかげで、非常に簡潔なプログラムとなりました。Konashiと比べてSBDBTは価格が安くできることはほぼ同じなので大変お得感があります。(PICkitが必要ですが、こちらは他の用途にも使いまわせますし・・)。最新ファームを使うことで、懸案だったNotification送信も簡単にできてしまい(というか、SBDBT/SBBLEのファームは何も触っていない)、非常に充実した環境だと思いました。

REBL600FRで自宅iBeacon

$
0
0

RunningElectronicsさんからFRISKケースサイズのBL600基板、REBL600FRを購入して自宅iBeaconを作ってみました。

 

デフォルト動作

購入品には、シリアル接続用のL字ピンヘッダや電池(CR2032)が付属しています。FRISKケースに入れてみるとこんな感じ(ピンヘッダをつけると、ケースの突起を切り取る工作が必要)。

IMG_0395

 

デフォルトでiBeaconアプリが書き込まれており、IDもAppleのものなので、モジュールの電源をONにして(mode SWはRUN mode)、iPhoneにLocate for iBeacon Appをインストールすると以下のようにiBeaconの信号を検出します。

IMG_0396

 

Beacon IDの書き換え

自宅iBeacon用にBeacon IDを書き換えてみます。まず、REBL600FRとPCをシリアル接続。スイッチサイエンスさんのFTDI USBシリアル変換アダプターを使用するとピン配置があわせてあるため簡単に接続ができます。接続の際は、ボードの電源SWをOFFにしてモード切替SWをDEV modeにしてUSBアダプターから給電します。

IMG_0411

次に、BL600製造元のLaird社Webから、UwTerminalをDLします(執筆時点はVer.6.80)。適当なディレクトリに展開してUwTerminal.exeを起動。COMポートを選択し、Baudrateを9600に変更します。HandshakingはデフォルトのCTS/RTSでもよいのですが(ただし、この場合は、CTS/DTR信号もクロス接続が必要)、Noneを選んでいます。

UwTerminalConf

 

OKボタンを押すと、Terminal画面に切り替わります。AT+EnterでOOが帰ってくれば接続成功です。AT I 3でファームウェアのバージョン、AT+DIRで書き込まれているソフトファイルが出力されます(デフォルトでは、$autorun$がiBeaconアプリとして書き込まれています)。

UwTerminalTerm

 

次に、Lairdの”Download the smartBASIC iBeacon Application file”をダウンロード。$autorun$.iBeacon.Firmware_v1_3_57_19_or_Older.sbファイルのID定義部分を書き換えます。自宅用のID生成はOSXのuuidgenコマンドを使っています。

47行目に新しく生成したUUIDを設定、49, 51行目のMajor/Minor valueは0を設定しています。

'//******************************************************************************
'// Laird Technologies (c) 2013
'//
'// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'// +++++                                                                      ++
'// +++++  When UwTerminal downloads the app it will store it as a filename   ++
'// +++++  which consists of all characters up to the first . and excluding it ++
'// +++++                                                                      ++
'// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'//
'// $autorun$.iBeacon.sb
'//
'//
        '//version of this app in Device Info Service
#define APP_VERSION             "2.1.0"'//
'//**************************************************************************************
'//**** Uncomment the first #define line for firmware older than 1.3.57.19 and older ****
'//**************************************************************************************
#define HANDLERCHARVAL HndlrCharVal(ByVal charHndl)  //for <= v1.3.57.19  
'//#define HANDLERCHARVAL HndlrCharVal(ByVal charHndl, ByVal offset, ByVal len)
'//
'//   ###################################################################################
'//   #     Version history in reverse chronological order (Customer change history)    #
'//   ###################################################################################
'//   #                                                                                 #
'//   #  -----------------------------------------------------------------------------  #
'//   #  ::Changelog:: 05/12/13 -> 2.1.0                                                #
'//   #  -----------------------------------------------------------------------------  # 
'//   #                                                                                 #
'//   ###################################################################################
'//
'//
'//   ###################################################################################
'//   #                                                                                 #
'//   # See file $autorun$.iBeacon.sblib for usage instructions                         #
'//   #                                                                                 #
'//   ###################################################################################
'//    
'//
'//******************************************************************************

'//******************************************************************************
'// Definitions that customers use to customise the source
'//******************************************************************************
        '// New Uuid for beacon  03BA6107-9F0A-4461-B1D8-504DA65632DD
#define BEACON_UUID                          "\03\ba\61\07\9f\0a\44\61\b1\d8\50\4d\a6\56\32\dd"'// Default Major value for beacon (16 bit number)
#define BEACON_MAJOR                         0x0000
        '// Default Minor value for beacon (16 bit number)
#define BEACON_MINOR                         0x0000
        '// Default TxPower, one of (4,0,-4,-8,-12,-16,-20,-40,-50) 
#define BEACON_TXPOWER_DBM                   4
        '//RSSI at 1m from BL600 when tx power = 4 
        '//Set this by advertising at 4 dBm and measuring the RSSI 1m from the BL600
#define CALIBRTD_VALUE_DBM                   -60
        '// Default value for iBeacon format
        '//   --> 0       : Full AD
        '//   --> 1       : UUID + Major + Minor
        '//   --> 2       : UUID
        '//   --> Default : 0
#define BEACON_FORMAT                        0

ソースを別の名前で保存(先頭は$autorun$.とすること、私は、$autorun$.iBeacon.Firmware_v1_3_57_19_or_Older.mod.sbという名前にしています)。

UwTerminal画面を右クリックするとメニューが表示されますので、XCompile + Loadを選択。ファイル選択ダイアログが出ますので、先ほど保存したファイルを指定します。

UwTerminalMenu

 

SmartBASICのソースがバイトコードにコンパイルされ、モジュールに転送されます。これでIDの変更完了。

 

BL600_NewID

 

iBeacon検知アプリの作成

Locate iBでもiBeacon圏内に入るとiOSの「通知センター」に表示をプッシュすることができますが、”Enter Region”とかで味気がありません。せっかくの自宅iBeaconなので、もう少し萌えるメッセージを出したいということで、以下のような簡単なアプリを作ってみました。コードは、Takahiro Octopress Blogさんから拝借しました。iOSは7.1.1, Xcode 5.1.1を使用しています。

//
//  ViewController.h
//  Bea子
//
//  Created by Todontai on 2014/05/11.
//

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface ViewController : UIViewController<CLLocationManagerDelegate>

@end
//
//  ViewController.m
//  Bea子
//
//  Created by Todotani on 2014/05/11.
//

#import "ViewController.h"

@interface ViewController ()

@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSUUID *proximityUUID;
@property (strong, nonatomic) CLBeaconRegion *beaconRegion;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    if ([CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;
        
        self.proximityUUID = [[NSUUID alloc] initWithUUIDString:@"03BA6107-9F0A-4461-B1D8-504DA65632DD"];
        self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID: self.proximityUUID
                                                               identifier:@"com.todotani.ibeacon"];
        [self.locationManager startMonitoringForRegion: self.beaconRegion];
    } else {
        //iBeaconが利用できないOS, Deviceの場合
        NSLog(@"お使いの端末ではiBeaconを利用できません。");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"確認"
                                                        message:@"お使いの端末ではiBeaconを利用できません。"
                                                       delegate:self
                                              cancelButtonTitle:nil
                                              otherButtonTitles:@"OK", nil];
        [alert show];
    }
}


// 指定した領域に入った場合
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
    [self sendLocalNotificationForMessage:@"おかえりなさい、ご主人様"];
}

// 指定した領域から出た場合
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
    [self sendLocalNotificationForMessage:@"いってらっしゃい、ご主人様"];
}

#pragma mark - Private methods

- (void)sendLocalNotificationForMessage:(NSString *)message
{
    UILocalNotification *localNotification = [UILocalNotification new];
    localNotification.alertBody = message;
    localNotification.fireDate = [NSDate date];
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}

@end

実行すると、iBeacon圏内に入ったとき(家に帰ったとき)と圏外に出たとき(家から出たとき)にやさしいメッセージを投げてくれます・・・

IMG_0416

Twitterで、 localNotification.soundName をDefaultでなく、お気に入りのものに変更するとさらに萌えるとコメントをいただきましたが、そこまではできていません。あと、通知センターへのメッセージでなく、一時的にロック画面を「行ってらっしゃい用・お帰りなさい用」に書き換えることができるとよいなぁと思ったのですが、私のテクではAPIが分からず断念しています。


各種mbedのベンチマークテスト

$
0
0

mbedも2.0でオープン化され、対応ハードがずいぶん増えました。Myコレクションも少々増えたので、各mbedのベンチマークテストを行いました。

ベンチマークに使ったソフトは、CoreMarkです。CoreMarkのサイトからソースをダウンロードできるのですが、mbedにポーティングされたものが、ClockControlとして公開されています。このポーティングからLPC1768 CPUクロック変更用のコードを取り去ってCoreMarkだけにして使っています。

LPC1768の場合、インポートしたままのmbedライブラリ版数(かなり古いです)で動くのですが、LPC1768以外のmbedはライブラリを最新化する必要があります。しかし、ライブラリを最新化するとコンパイル時に謎のエラーが大量に発生し、当初は、LPC1768以外でCoreMarkを動かすのをあきらめていました。

あるときCoreMark/core_porteme.cにmbed.hがincludeされていることに気がつき、mbed.hで定義されている各種クラスとCoreMarkのCコードがコンフリクトしている可能性に思い至りました。このパートで、mbed.h全体をincludeする必要はなく、時間を測定するためにclock()関数を使えるよう、time.hをincludeするだけでOKなはずです。この部分のコードを変えるとビンゴで、最新のmbedライブラリでもエラーが出なくなりました。

また、main.cppでは、以下のように、#include “CoreMark/CoreProteme.h”をextern “C”でくくっています(これもコンパイルエラー対策)。

#include "mbed.h"
extern "C" {
#include "CoreMark/core_portme.h"
}

int main() 
{
    printf("Run CoreMark\n");
    printf("CPU clock: %d MHz\n", SystemCoreClock/1000000);
    mainCoreMark();
    printf("CoreMark End \n\n");
}

修正1:SystemCoreClockを表示してCPU周波数を確認するコードを追加

時間測定は機種非依存ののclock()関数を使っているため、コードは全機種で共通です。CoreMarkのLicence条件では、CoreMarkサイト以外でcodeを配布してはいけないようなことが書いてあるので、私の全codeをpublishするのは控えます…

 

実行結果

実行結果(CoreMark値)は以下の通りで、上位から記載すると(mbedライブラリは執筆時点で最新の88版を使用。O3/Otimeの最適化。);

 機種名コアCPUクロック(MHz)CoreMark値
1FRDM-K64FCortex-M4120286.204923
2LPC4088Cortex-M4120263.435195
3NucreoF401RECortex-M484220.167327
4LPC1768Cortex-M396217.485863
5LPCXpresso11U68Cortex-M0+504874.316290
6LPC1114FN28Cortex-M0504868.119891
7HRM1017Cortex-M01630.175015

修正2:LPC1114FN28のCPUクロックを48MHzに修正
修正3:LPCXpresso11U68のCPUクロックを48MHzに修正

 

考察

  • 120MHz Cortex-M4同士の対決は、FRDM-K64Fの勝ち
  • 第二グループの100MHzクラスは、NucreoF401REが84MHzながら、96MHzのLPC1768を僅かに押さえて3位に。NucreoF401REはCortex-M4コアのため、M3コアのLPC1768を上回ったか
  • 第三グループの50MHzクラスは、Cortex-M0+を積むLPCXpresso11U68がLPC1114FN28を僅かに上回り5位。クロック周波数差見合いということか。

以前書いた、「LPCXpresso LPC1769でCoreMarkを動かす」では、mbed LPC1768のCoreMark値は、最適化をかけて188.394876でしたので、15%程度スコアが伸びています。これは、コンパイラのバージョンが上がり、最適化が進んだということでしょうか。<追記>@toyowataさんより、「mbedオンラインコンパイラのarmcc 4.1→5ではCortex-M系の最適化が向上しています。」とのコメントをいただきました。

mbedユーザーは常に最新版のコンパイラが使えるなんてステキです。

自宅iBeaconの改良

$
0
0

今回は小ネタですが、以前「REBL600FRで自宅iBeacon」で書いた、自宅iBeaconの改良記事です。当初の自宅iBeaconは外出(exit region)・帰宅(enter region)で「いってらっしゃい」「おかえりなさい」のメッセージを投げるだけでした。今回、帰宅時にWiFiがOFFの場合は、ONにするよう通知する機能を追加しました。

2014/10/3追記:iOS 8への対応を追記しました。


機能追加の目的

私はau iPhone5を使っているのですが、通勤路線の駅毎にau WiFiを掴んでくれます。一時期掴まなくなっていたのですが、最近なぜか復活しました。多くの方が感じていると思いますが、これ、ハッキリ言って『迷惑』です。たいがい品質の悪いWiFiを掴み停車中はLTEに比べてアクセス速度がスローダウン、電車が動き出してもしばらくWiFiを離してくれない(モバイル/LTEに切り替えてくれない)ためアクセスが止まってしまいます。そのため、電車に乗っている間は通常WiFiをOFFにしています。

家に帰ったときWiFiをONにし忘れていると、そういうときに限って、子供がモバイル/LTEの状態でYou Tubeを使い倒してパケットカウントを上げてくれることがあります・・・まあ、7Gの上限に到達することはないのですが、家にいるときはWiFiを使った方が速度も速いし、キャリアにも優しいので、家ではWiFiをONにするのを忘れないようにしたいものです。

そこで、iBeaconと連動して、家に帰ったときにWiFiがOFFの場合は、ONにするよう通知を出すようにしました。通知のイメージはこんな感じです。

IMG_0518

下から2行目の「おかえりなさい」時はWiFiがONなのでそのまま、上から2行目の「おかえりなさい」時はWiFiがOFFのため、「WiFiをOnにしましょう」と追加メッセージを投げています。


実現方式

最初は、iOSでWiFiのOn/Off状態を取得するAPIがないか調べてみたのですがなさそうです。On/Offの設定をするのは当然、脱獄(Jail Break)しないと無理とstackOverflowにも書いてあります。iOSの場合、インタフェースの状態を調べるAPIはなさそうなので、他の方法として、Reachabilityクラスを使うことにしました(使い方は、このサイトを参考にしました)。

Reachabilityクラスは、iOSでネット接続の状態を調べることができるクラスで、WiFi接続状態、モバイル(3G/4G)接続状態、未接続状態などを取得できます。間接的な方法ですが、家に帰ったとき(iBeacon regionに入ったとき)Reachability状態がモバイル接続の場合、WiFiがOFFであると判定します。

 

コード

コードを以下に示します。

//
//  ViewController.m
//  Bea子v2
//
//  Created by Todotani on 2014/05/11.
//

#import "ViewController.h"
#import "Reachability.h"

@interface ViewController ()

@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSUUID *proximityUUID;
@property (strong, nonatomic) CLBeaconRegion *beaconRegion;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    if ([CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;
        
        self.proximityUUID = [[NSUUID alloc] initWithUUIDString:@"03BA6107-9F0A-4461-B1D8-504DA65632DD"];
        self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID: self.proximityUUID
                                                               identifier:@"com.todotani.ibeacon"];
        [self.locationManager startMonitoringForRegion: self.beaconRegion];
    } else {
        //iBeaconが利用できないOS, Deviceの場合
        NSLog(@"お使いの端末ではiBeaconを利用できません。");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"確認"
                                                        message:@"お使いの端末ではiBeaconを利用できません。"
                                                       delegate:self
                                              cancelButtonTitle:nil
                                              otherButtonTitles:@"OK", nil];
        [alert show];
    }
}


// 指定した領域に入った場合
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
    [self sendLocalNotificationForMessage:@"おかえりなさい、ご主人様"];
    [NSThread sleepForTimeInterval:5.0];
    Reachability *reachablity = [Reachability reachabilityForInternetConnection];
    NetworkStatus status = [reachablity currentReachabilityStatus];
    if (status == ReachableViaWWAN) {
        [self sendLocalNotificationForMessage:@"WiFiをOnにしましょう"];
    }
}

// 指定した領域から出た場合
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
    [self sendLocalNotificationForMessage:@"いってらっしゃい、ご主人様"];
}

#pragma mark - Private methods

- (void)sendLocalNotificationForMessage:(NSString *)message
{
    UILocalNotification *localNotification = [UILocalNotification new];
    localNotification.alertBody = message;
    localNotification.fireDate = [NSDate date];
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}

@end
51行目(Reachabilityインスタンスの生成)~55行目までがWiFi Off判定のために追加した部分です。

50行目に5秒のsleepが入れてあるのは、家に帰った際に自宅WiFiに接続される前に53行目の条件判定が動いて、WiFi ONの場合でもOFFと誤判定するのを回避するためです。5秒のsleepを入れることで、WiFiがONの場合は確実に接続されてから条件判定を行います。sleepを入れるのは、汚いコードで気に入らないのですが、仕方なく妥協。


iOS 8への対応

2014/10/3追記:iPhoen 6の発売早々に機種変してしまいました(キャリアも訳あってドコモにチェンジ)。iPhone 6設定後「Be子 App」を再度入れても動いてくれません。iOS 8から位置情報取得設定(iBeaconを使う場合は位置情報取得をOnにする必要があります)の方法が変わっていますが、設定 → プライバシー → 位置情報サービスから「Be子 App」の位置情報取得を「常に許可」にしても勝手に「許可しない」に戻されてしまい、iBeaconが検知できません。Twitterで症状を流したら、@tw_hoehoeさんから、リンクのblogに記載がある問題であることを教えてもらいました。このblogによると、常時位置情報取得を許可するためにはinfo.plistを編集して、”NSLocationAlwaysUsageDescription”エントリを作り、”This app use iBeacon Loacation”などの適当なString Valueを設定する必要があります。

iOS 8になって、設定条件が変わった箇所に当たってしまったということでした。


我が家のiBeaconたち

当初は、ランニングエレクトロニクスさんのREBL600FR 1個で運用していましたが、部屋を移動するとiBeaconが切れ、「いってらっしゃいませ」「おかえりなさい」を繰り返すフラップ状態になることがあったため、現在はスイッチサイエンスさんのmbed HRM1017との2台体制にしています。BL600FRは電池駆動を生かして、電源が取れない居間の壁に貼り付けてあり、HRM1017はパソコン部屋においてACアダプタから電源を取っています。

Friskケースに入れて壁に貼り付けたREBL600FR

IMG_0521

 

机の上に置いた、mbed HRM1017

IMG_0523

ESP-WROOM-02でMQTTを使う

$
0
0

IoT関連のプロトコルであるMQTTに興味があったのですが、ESP-WROOM-02 Arduino開発環境で試してみました。

準備

ESP-WROOM-02からMQTT broker(test.mosquitto.org)にmessageをpublishし、Raspberry Piでmessageをsubscribeします。Raspberry Piでmessageをsubscribeするためのパッケージを以下の通りインストールします。(subscribeするだけなら、mosquitto-clientsのみで良いかもしれません)。

$ sudo apt-get install mosquitto
$ sudo apt-get install mosquitto-clients

Arduino環境用のライブラリーArduino Client for MQTTをダウンロードしてlibraryフォルダーにコピーします。


サンプルコード

Arduino Client for MQTT (pubsubclient) のサンプルコードにmqtt_esp8266があり、このコードを若干修正して使っています。

/*
 Basic ESP8266 MQTT example

 This sketch demonstrates the capabilities of the pubsub library in combination
 with the ESP8266 board/library.

 It connects to an MQTT server then:
  - publishes "hello world" to the topic "outTopic" every two seconds
  - subscribes to the topic "inTopic", printing out any messages
    it receives. NB - it assumes the received payloads are strings not binary
  - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led,
    else switch it off

 It will reconnect to the server if the connection is lost using a blocking
 reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
 achieve the same result without blocking the main loop.

 To install the ESP8266 board, (using Arduino 1.6.4+):
  - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
       http://arduino.esp8266.com/stable/package_esp8266com_index.json
  - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
  - Select your ESP8266 in "Tools -> Board"

*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

#define LED 12

// Update these with values suitable for your network.

const char* ssid = "ssid";
const char* password = "password";
const char* mqtt_server = "test.mosquitto.org";

WiFiClient espClient;
PubSubClient client(espClient);

char mTopic[]="test-topc-ESP-WROOM-02/sensor";
long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {
  pinMode(LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch on the LED if an 1 was received as first character
  if ((char)payload[0] == '1') {
    digitalWrite(LED, HIGH);   // Turn the LED on (Note that LOW is the voltage level
    // but actually the LED is on; this is because
    // it is acive low on the ESP-01)
  } else {
    digitalWrite(LED, LOW);  // Turn the LED off by making the voltage HIGH
  }

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("outTopic", "hello world");
      // ... and resubscribe
      client.subscribe("inTopic");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  long now = millis();
  if (now - lastMsg > 2000) {
    lastMsg = now;
    ++value;
    snprintf (msg, 75, "ESP-WROOM-02 #%ld", value);
    Serial.print("Publish message: ");
    Serial.println(msg);
    client.publish(mTopic, msg);
  }
}

MessageのSubscribe

RaspberryPiに以下のコマンドを入力することでMessageをSubscribeします。

$ mosquitto_sub -d -h test.mosquitto.org -t test-topc-ESP-WROOM-02/sensor

Received CONNACK
Received SUBACK
Subscribed (mid: 1): 0
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #1
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #2
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #3
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #4
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #5
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #6
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #7
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #8

参考資料

Magic keyboardとMagic trackpad 2を購入

$
0
0

AppleストアでMagic keyboardとMagic trackpad 2を買ってしまいました(マウスは使用頻度が低いのと予算の関係で旧型のままです)。

使い勝手を書いてみたいと思います。


IMG_0796.jpg

外観はこんな感じで、写真奥の旧型と比べると、Trackpad 2の幅が広い分横方向の場所をとります。奥行きは短くなっていますが、Magic Trackpad 2は横幅が結構広く、ここまででかい必要があるのかと思います。

使い勝手はざっくりと以下のような感じです。


Magic Keyboard

キーストロークは旧型より浅くなっていると思いますが、キーの支持がしっかりしており、打鍵感覚は旧型よりよくなっています。このキーボードは気に入りました。

キーボードの種別は日本語版です。「信者はUS版だろ」という意見もあると思うのですが、使い慣れた日本語版にしておきました。会社で使っているWindows PCとキー配列が違うと、マシンを変わるたびに混乱しそうな気がするので、キー配列は日本語に統一しています。


Magic Trackpad2

MacBookと同じ感圧タッチになって、フォースフィードバックもついているということで期待したのですが、使ってみると正直こんなもんかという感じでした。旧型のTrackpadではクリックをするためには、パッドの下半分のエリアを押し込む必要があったのですが、Magic Trackpad 2はどこを押してもクリックを認識するので便利ですが、「タップでクリック」を認識するように設定してあるため、押し込むクリック操作をすることが少なく、どこでもクリック(押し込む)ことができる便利さを享受できるシーンが思ったより少ないです。

iPhone 6sの3D touchのようにプレス(強押し)した際のアクションがOS Xでは少ないことも感圧タッチのアドバンテージを減らしているように思います。

Trackpad_config.png

あと、私のiMac固有の問題だと思うのですが、旧 Trackpadではカーソ移動の追随性が悪くなる傾向がありました(カーソルを動かすとカクカクとカーソルが動く)。OSXをアップデートすると改善したりするのですが、10.11.1にアップデートしてからまた調子が悪かったのですが、Magic Trackpad 2に変えてからは、カーソルの追随性は良好です。ひょっとすると、旧Trackpadでは2.4G帯無線の混信の影響があったのかも知れません。


その他

使い始める時、Lightningケーブルを挿入すればペアリングできると思っていたのですが、ケーブルを挿してもkeyboard/trackpadとも認識してくれません。薄い説明書を見ると、電源スイッチがあるではないですか。旧型には電源スイッチはなかったので、この点は意表を突かれました。

Magic KeyboardとMagic Trackpadと合わせて税込み28,728円は正直、信者価格で高いです。ただ、Magic Keyboardの使い勝手は大変よく、旧型からの買い替え価値はあると思います。Magic Trackpad 2は個人的には微妙でしたが、Magic Keyboardとデザインや奥行きを合わせるためにはまあありかなと思います。

mbed OSでLチカ

$
0
0

既に記事がポストされていますが、mbed OS(執筆時点では、mbed OS 15.11 Technology Previewとなっています)を使ってLチカを行ってみました。ターゲットのmbedボードはFRDM-K64Fを使っています。Lチカができるまでの手順を以下に示します。

yottaのインストール

先ずは、mbed OSのビルドツールyottaをインストールします。私はOS Xを使っていますが、このページの手順に従ってインストール。

執筆時点のyottaバージョンは、0.9.4でした。

ビルド手順

①プロジェクト用のサブディレクトリを作成しyotta initを実行

$ mkdir led
$ cd led

$ yotta init
Enter the module name:  
Enter the initial version: <0.0.0> 
Is this an executable (instead of a re-usable library module)?  yes
Short description: LED blink using D12 pin
Author: todotani
What is the license for this project (Apache-2.0, ISC, MIT etc.)?   

Is this an executableと聞かれるので、yesを入力します。参考にしたサイトの情報では、生成されたmodule.jsonファイルのマニュアル修正が必要とありましたが、執筆時点の環境では不要になっていました。yotta initの挙動も微妙に違っているようで、最終的なリリース版ではまた変更があるかもしれません。

②ターゲットの設定と、ドライバーのインストール

$ yotta target frdm-k64f-gcc
$ yotta install mbed-drivers

③Lチカコード

sourceディレクトリにソースファイルを格納します。ファイル名はなんでもよいのですが、今回の例では、led.cppとしました。mbed OSのチュートリアルには、以下に示すような、mbed OSのスケジューラーminarを使ってLチカルーチンを周期起動するコードが載っています。

#include "mbed-drivers/mbed.h"

static void blinky(void) {
    static DigitalOut led(LED1);
    led = !led;
    printf("LED = %d \r\n",led.read());
}

void app_start(int, char**) {
    minar::Scheduler::postCallback(blinky).period(minar::milliseconds(500));
}

このコードがmbed OS流の書き方なのだと思いますが、以下のような、従来のmbed風の書き方でも動作しました。mbed OSではプログラムのエントリポイントはmain()でなく、app_start()になるようです。

#include "mbed-drivers/mbed.h"

DigitalOut myLed(D12);

void app_start(int, char**) {
    while(1) {
        myLed = 1;
        wait(0.5);
        myLed = 0;
        wait(0.5);
    }
}

DigitalOutクラスを使った出力ピンの指定やピン番号(D12など)の指定は、従来のmbedと共通です。まだ使ったことはないですが、ヘッダーファイルの定義を見ると、I2Cクラスなども従来のmbedと同様の使い方ができそうです。基本的な入出力は従来のmbedと同様に出来そうです。

④ビルドとターゲットボードへの書き込み

$ yotta build
$ cp ./build/frdm-k64f-gcc/source/led.bin /Volumes/MBED/

ビルドが完了すると、build/<target名>/soruce/ディレクトリ配下にbinファイルが生成されるので、mbedにコピーします。リセットボタンを押すとプログラムが動き出します。

参考情報

Viewing all 48 articles
Browse latest View live