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

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コマンドを実行します: 
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
    brake 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)という初歩的な問題でした。

参考資料

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

前回ポストした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++あたりを持ってきて自前で環境を作ったほうがよいということか・・


参考情報

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

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 LPC1769でFree RTOSを使う

既に多くの方が紹介していますが、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);
		}
	}
}

Parallels DesktopでWindows on Mac

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インチ)
CPU Core i5 2.7GHz
メモリ 12GB
グラフィックス Radeon HD 6770M
仮想化ソフト Parallels Desktop 6  (6.0.12094)
ホストOS OS X 10.7 (Lion)
ゲストOS Windows 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でFatFsを動かす–USB Host編

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ページの英語文書なのでハードル高そうです。

LPCXpresso LPC1769でFatFsを動かす - DMA編

前回の延長で、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を動かす

前回に引き続き、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にMARY拡張基板を乗せる

トラ技別冊小型ARMマイコン基板(MARY)用の拡張基板をLPCXpresso LPC1769で使えるようにしました。MARY拡張基板のコンパクトさを生かして、OLEDディスプレイ基板(OB)と無線モジュール基板(XB)をLPCXpressoの上に乗っけてみました。

LPCXpresso_MARY


LPCXpressoとの接続

秋月さんの140x40mm両面スルーホール基板を使って、LPCXpressoの上に拡張基板を搭載しました。搭載モジュールと接続先は以下の通りです。

搭載モジュール LPCXpressoの接続先
OLEDディスプレイ(OB基板) SSP1(P0.6~P0.9)
RESET(GPIO P0.23)
OLED_VCC_ON(GPIO P0.24)
加速度センサー(OB基板) I2C1(P0.0~P0.1)
MEMS_INT(GPIO P0.25)
XBee(XB基板) UART3(P0.10~P0.11)
RESET(GPIO P2.0)
micro SDカード(XB基板) SSP0(P0.15~P0.18)
INSERT(GPIO P2.1)
USB-シリアル変換(FT232RL) UART0(P0.2~P0.3)
Ethernet J6-32~J6-35
ISPジャンパー P2.10 - GND
MCUリセットSW J6-4 - GND
外部電源(5V)コネクタ J6-2, J6-1

IDEからのプログラム転送ができなくなった際(例の" load failure: Et: Flash driver not ready"が出た場合)の復旧用に、ISPジャンパーとリセットSWは必須アイテムです。

LPCXpressoは5Vの出力ピン(mbedの5V USB out相当)がないため、OLEDなどの駆動用に外部の5V電源が必要です。電源コネクタの取り付けが今一でした(変換アダプタを使わずに、基板に穴を開けて直接半田付けした方がよかったな・・)。


LPC17xx用のドライバ

連休を費やしてXBee以外のモジュールは動作を確認できました。使用したドライバ類は以下の通りです。

モジュール ドライバ
OLED MARY(LPC11xx)用のドライバを移植
加速度センサー(MEMS) MARY(LPC11xx)用のドライバを移植
micro SD / FatFs ChaN's FAT-Module with LPC17xx SPI/SSPを移植
Ethernet/TCP/IP nxp\lpcxpresso_3.6\Examples配下にインストールされるRDB1768cmsis_uIPを使用
  • OLED/MEMSはLPC11xxとLPC17xxレジスタ関連の差分を直す程度で動作。ペリフェラルの初期化部分は最新のCMSIS Peripheral Libraryベースに書き換え
  • FatFsは Olimex LPC1766-STKがベースになっているところをLPCXpresso用に修正(SSP1→SSP0に変更など)。本来は、MARYドライバの移植より楽な筈ですが、そのまま拝借も芸がないため、最新のCMSIS Peripheral Libraryを使ったところ結構修正が出ました。オリジナルはCMSIS v1.0を使っている(ファイルも一緒に配布)のですが、最新のv3.0では廃止されたAPIがありその部分の書き換えが発生しました
  • uIPはLCD表示部分をOLEDに変更。uIP本体は無修正で動くため、移植は簡単


おわりに

久しぶりに基板の配線を行いましたが、配線ミスとの戦いでした。最初は、LPCXpressoのピン配置図(回路図の最後のページ)に搭載モジュールのピン番号を書くだけで作業を始めたのですが、ピン数が多いのと裏面配線のため作業がはかどらず、途中から裏面から見たピン収容図を画くことで対応しました。準備に時間がかかっても実体配線図などを作ってから作業に入ったほうが結果的に作業が早くなりますね。それでも以下のミスを連発してトホホでした。

  • Ethernetマグジャック(Sparkfanのピッチ変換基板を使用)とFT232RLをLPCXpresso J6コネクタ2本の間に乗っけたのですが、最初に直接半田付けした後で配線スペースがないこととに気がつく。おかげで結構窮屈な配線作業を強いられるはめに
  • マグジャックもSparkfanのものを使ったのですが、ピン配置を秋月のジャックで行ってしまう。Ethernetが繋がらないところまで行って、両者のピン配置が違うことに気がつきやり直し。パルストランス中点につけたコンデンサは再度取り付ける場所がなく割愛することに。まあ、線長が短い場合はコンデンサなしでも問題ないのですが・・
  • ISP用ピン(P2.10)の位置を間違う。J6-51に配線するべきところを、お隣のJ6-50に配線。何度か書き込みができなくなった際に、ひたすらリセットを繰り返していた。"Flash driver not ready"が出て不安定になっている状態でちゃんとISPモードに落ちていなくても、何回か繰り返すと書き込みに成功することがあったんですね。そのため配線ミスと分かるまで時間を要した

生のSPIやI2CをLPC17xxで触るのは始めてでしたが(mbedのSPIくらいは使ったことがありますが、あれはハードに直接触っているとは言えない抽象度なので)、こちらはそれほどはまることもなく通過できました。Arduinoで一通りはまる通過儀礼を行った甲斐があったというものでした。

各モジュールの動作確認結果は別途ポストします。

Debug printf用の可変長引数マクロ

Cプログラマな方には既知だと思いますが、printfデバッグに便利なマクロ定義を発見したので備忘録も兼ねてポストします。


Printfデバッグとは

プログラムのデバッグのために、printf関数を使用して変数の内部状態をコンソールに表示したい場合があります。JTAGデバッグが使えないmbedやArduinoでは必須のテクニックです。今更言うまでもないですね。


デバック用マクロ

デバックが終了してプログラムをリリースする段階に進むと、デバック用のprintfは無効にしたいものです(printfは結構ROM/RAMを消費するなどMCU資源の無駄使いするため)。リリース時にいちちprintfをコメントアウトするのは面倒なので、Cのマクロを使って、デバック用のprintfを一括して無効化できると便利です。

Arduinoでは以下のマクロを定義していました。

#define DEBUG

#ifdef DEBUG
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_PRINTLN(x)
#endif
  • プログラム中でDEBUG_PRINTLN(hoge)と書くと、4行目のマクロで Serial.println(hoge)に変換されます。
  • 1行目をコメントアウトすると、DEBUG_PRINTLN(hoge)は空行に変換され、コンソール出力が無効になります。

Arduinoのように、print関数の引数が固定個数の場合は、この方法でもよいのですが、mbedやLPCXpressoのようにフルセットのprintfが使える場合、引数の数が可変長になるためうまくいきません。

で、マクロで可変長の引数を扱うための機能がないかと調べると、すぐに出てくるんですね。便利な時代だ。


可変長引数マクロ

__VA_ARGS__というパラメータを使うと、可変長引数をマクロ内で展開してくれます。mbedでの使い方はこんな感じです。

#include "mbed.h"

#define DEBUG

// Debug Macro
#ifdef DEBUG
#define DBG(fmt, ...) printf(fmt, __VA_ARGS__)
#else
#define DBG(...)
#endif

int i = 0;
int j = 1000;

int main() {
    while(1) {
        i++;
        j--;
        printf("Loop: %d\n", i);
        DBG("Debug Print: i=%d j=%d\n", i, j);
        // DBG("Degub");       // Cause compiler error
        wait(1.0);
    }
    
    return 0;
}

7行目の、...が可変長引数を示し、 __VA_ARGS__で実際の引数に展開します。
そのため、20行目のマクロは;
  printf("Debug Print: i=%d j=%d\n", i, j);
に展開されます。

この方法では、引数を1つ以上指定しないと、マクロの展開後カンマが1つ残ってしまいコンパイルエラーになります。そのため、21行目のような使い方はできません。


LPCXpressoでは

LPCXpressoのIDEでは、Debugビルドを指定した場合、コンパイラの-DオプションにDEBUGが追加されるため、プログラム中で#define DEBUGを定義する必要はありません。

Releaseビルドに切り替えた場合、-DオプションからDEBUGが自動的に外れます。


おわりに

Cのマクロはコードの可読性が下がるので(特にネストしたマクロの場合)嫌いだったのですが、少し見直しました。ただ、引数をつけなかった場合のエラーの発生原因が直感的に分からないのはマクロの欠点ではあります。


参考資料:

«LPCXpressoでCMSIS Peripheral Libraryを使う

2012年5月
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
無料ブログはココログ