カテゴリー「NXP-ARM」の記事

NXP社のARMマイコン

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);
		}
	}
}

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

裏面の写真です。主にUEW線を使って配線しています。配線に加え、裏面にピンヘッダを取り付けてLPCXpressoとの結合を行っています。

IMG_0899.jpg

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を使う

LPCXpresso IDEには、ARMが提供するMCUコアへのアクセスに対応したライブラリ(レジスタ名の定義など)がビルトインされていますが、加えてNXPが提供するPeripheral Firmware Driver Library(以下、Peripheral Library)が存在します。この、Peripheral Libraryを使うとI/Oの設定などのコードの可読性が上がるので私のお気に入りアイテムです。

そこで、LPCXpresso IDEでPeripheral Libraryを使えるようにしてみました。ついでに、IDE添付のライブラリを1.3から最新の2.0にしてみました。IDEは執筆時点の最新版である、v3.6.3(Build 317)を使用。


新規ライブラリプロジェクトの作成

先ずは、このリンクから最新のCMSISファイル一式(LPC17xx CMSIS-Compliant Standard Peripheral Firmware Driver Library)をダウンロード。

CMSISv2.0ベースで、コンパイル済みのライブラリを作成します。手順は以下です。

1. IDEのProject and File WizardsからCreate C Static Library projectを選択

NewLibrary

プロジェクト名はCMSISv2p01_LPC17xxとしました。

2. プロジェクトのフォルダーに以下のファイルをコピー

□inc

  • core_cm3.h, core_cmFunc.h, core_cmInstr.h(LPC1700CMSIS\Core\CM3\CoreSupportフォルダー)
  • system_LPC17xx.h(LPC1700CMSIS\Core\CM3\DeviceSupport\NXP\LPC17xx)
  • LPC1700CMSIS\Drivers\includeフォルダ配下のファイル一式

□src

  • core_cm3.c(LPC1700CMSIS\Core\CM3\CoreSupportフォルダー)
  • system_LPC17xx.c → IDE添付版の方が版数が進んでいるため、IDE添付版を使用
  • LPC1700CMSIS\Drivers\sourceフォルダ配下のファイル一式

3. ビルド → libCMSISv2p01_LPC17xx.aファイルができあがり


サンプルの作成

Peripheral LibraryのSysTick,PINSEL, GPIOを使ってLチカのサンプルを作ってみました。手順は以下です。

1. プロジェクトの作成

  • New Project → NXP LPC1700 C Projectを選択
  • デフォルトのテンプレートに従うため、ライブラリとインクルードファイルの参照先がCMSISv1p30_LPC17xxとなっています。プロパティーからMCU Compiler→Directories , MCU Linker→Librariesのの設定をCMSISv2p01_LPC17xxに修正します
  • LPC1700CMSIS\Examples\SysTick\10ms_baseから、lpc17xx_libcfg.hをコピー。今回はGPIO以外は使わないため、このファイルがそのまま使用できますが、UARTなど他のペリフェラルを使用する場合は、対応する定義をコメントアウト

2. Lチカコードはこんな感じです

/*
===============================================================================
 Name        : main.c
 Author      : Todotani
 Version     : 1.0 2011/4/17
 Description : LED flash for LPCXpresso LPC1769 with CMSIS Libraries
===============================================================================
*/

#include "LPC17xx.h"
#include "lpc17xx_libcfg.h"
#include "lpc17xx_pinsel.h"
#include "lpc17xx_gpio.h"
#include "lpc17xx_systick.h"

FunctionalState LED_State = ENABLE;

// SysTick interrupt handler
void SysTick_Handler(void)
{
    // toggle LED2 port (P0.22)
    if (LED_State == ENABLE)
    {
        // Output Low level
        GPIO_ClearValue(0, (1<<22));
        LED_State = DISABLE;
    }
    else
    {
        GPIO_SetValue(0, (1<<22));
        LED_State = ENABLE;
    }
}


int main(void) {
    //Configure LED port - GPIO P0.22
    PINSEL_CFG_Type PinCfg;
    PinCfg.Portnum   = 0;
    PinCfg.Funcnum   = 0;                        // Set port as GPIO
    PinCfg.OpenDrain = PINSEL_PINMODE_NORMAL;
    PinCfg.Pinmode   = PINSEL_PINMODE_TRISTATE;  // pull-up / pull-down disabled
    PinCfg.Pinnum    = 22;
    PINSEL_ConfigPin(&PinCfg);

    //Use P0.22 is connected to LED2
    GPIO_SetDir(0, (1<<22), 1); //Set P0.22 as output

    //Initialize System Tick with 100ms time interval
    SYSTICK_InternalInit(100);
    //Enable System Tick interrupt
    SYSTICK_IntCmd(ENABLE);
    //Enable System Tick Counter
    SYSTICK_Cmd(ENABLE);

    // Enter an infinite loop, just incrementing a counter
    volatile static int i = 0 ;
    while(1) {
        i++ ;
    }
    return 0 ;
}
  • 38~44行目で、PINSEL/PINMODEレジスタの設定を行っています
  • 47行目で、LEDが繋がっているP0.22をOutputに設定
  • 50行目以下で、systick割り込み周期を設定
  • 19行目以降のsystick割り込みハンドラーで、LEDをトグル


ライブラリのライセンス条件

ARM社のCMSISライセンスアグリーメントを見ると、ライブラリの使用者は、ライブラリのコピーや製品への組み込みは許諾されますが、改変や再配布については特に記載がないことから許していないと考えられます(ヘッダファイルには、can be freely distributed within development toolsとあり、IDE組み込み用の再配布のみ許可)。そのため、ライブラリコードそのものはブログに掲載しないことにしました。

ARMのライセンスはCoreライブラリの部分だけで、Peripheral LibraryはNXPのライセンスが適用されると思います。こちらは、"for illustrative purposes only which provides customers with programming information regarding the products"とあり、「プログラム情報の例示用」という自分には解釈が難しい表現になっています。個人で説明用途に使う分にはOKだが再配布や製品への組み込みはダメよの意味と思っておけばよいのかな・・

トラ技別冊MARY基板のデバッグ

CPU内部にアクセスし、レジスタやメモリ(変数)の内容をチェックするためのデバッグ用ハードウェアインタフェースとしてJTAGがよく使われます。トラ技増刊MARY基板(超小型ARM基板)基板で採用しているLPC1114は、ARM社が提唱するSWD (Serial Wire Debug)と呼ばれるJTAGと同様のデバッグインタフェースを提供しています。

JTAGやSWDを使ってデバッグを行うためには、ターゲット基板と開発環境(PCなど)の間に入って両者のインタフェースを取るJTAGデバッガが必要となるのですが、1万円前後の代物となるためこれまでは手が手が出ませんでした。そこでLPCXpressoの登場です。こいつのLPC Link部分を使えば、JTAG/SWD用のデバッガが3千円弱で購入できるということなので、これはもう試してみるしかありません。ということで今回は、MARY基板ををLPCXpressoを使ってデバッグする方法のお試し結果を記載します。


MARY基板とLPCXpressoの接続

基板が入っている増刊号のP27に示すとおり、LPCXpressoのLPC Link部分とMARY基板を接続します。LPC Link部分とターゲット基板の間に半田ジャンパが乗っているので、半田を取り除いてピンヘッダを取り付け、写真のようにMARY基板に接続します。

MARS_Debug_004

このやり方は、CuBeatSystemsさんの「LPCXpressoを切り離さないで賢く使う方法」を拝借させていただきました。LPCXpressoのターゲット基板 (LPC1769など)を使いたくなった場合はピンヘッダにジャンパピンを挿入すればよいという優れもののアイデアです。


デバッグの開始

IDEのQuickstart viewにあるDebugボタンを押すとプログラムをターゲットにダウンロードしデバッグが始まります。ブレークポイントをあらかじめ指定しない場合、main()関数の最初の行でブレーク(一時停止)します。あらかじめブレークポイントを設定しておけば、以下の画面キャプチャーに示すように、main関数の前にシステム初期化のために実行するsystem_LPC11xx.c内でもプログラムをブレークすることができます。

MARY_Debug2

画面のコードはシステムクロックを設定する箇所で、まだI/Oが動いてない段階のため、printfデバッグでは手が出ない部分です。このようなコードが正しく動作するかを確認するためには、JTAG/SWDを使ったデバッグ環境が必須となります。

418行や423行目のような、ループをまわしている行をステップオーバー(行単位に実行)すると固まってしまうことがあるのですが、チェックを行いたい箇所ブレークポイントを仕掛けて必要な箇所でプログラムを一時停止するようにすれば途中で固まるようなことはありませんでした。

初期化コードを抜けると以下のキャプチャー画面のようにmain関数の実行が始まりますが、ここでも任意の箇所でブレークが可能です。

MARY_Debug3


おわりに

このように安価な環境で、ソースレベルのブレーク・トレースや変数・レジスタ値のワッチなど強力なデバッグ機能が使えます。LPCXpresso 1769はmbedとI/Oピン収容の互換性があるため、DMAなどmbedライブラリがサポートしていないI/Oをたたくプログラムを開発する際は、LPCXpresso + SWDデバッグで動くコードを作ってからmbedに持っていくと効率がよさそうです。

2017年2月
      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        
無料ブログはココログ