« 2010年12月 | トップページ | 2011年5月 »

2011年4月の記事

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に持っていくと効率がよさそうです。

LPCXpresso LPC1769でCoreMarkを動かす

LPCXpresso LPC1769でMCUベンチマークのCoreMarkを動かしてみました。CoreMarkはmbedへの移植版があるので、mbed IDE (RVDSコンパイラ)とLPCXpresso IDE (gcc)のコンパイラの差分が結果に現れるかを確認しました。


CoreMarkの移植

先ずユーザー登録を行ってソースをダウンロード。オリジナルのソースの他に各種プラットホーム向けの移植サンプルが入手できます。移植のために修正が必要な箇所は時間計測のためのタイマー処理をターゲットにあわせて書くのと、結果のコンソール出力まわりです。

LPC1700用の移植サンプルもあるのですが、serial関連のマクロがKeilに依存しており、そのままLPCXpresso IDEに持ってきてもコンパイルできませんでした。mbed版をベースにLPCXpressoのprintfをリンクした方が簡単そうだったので、mbed版をベースにKeil版のタイマー処理を貼り付けた形で動かしました。

移植の際の修正点は以下です。CoreMarkのライセンス条件を見ると、ソースの改変は自由ですが配布はcoremark.orgサイトからのみと書いてあるため、blogに貼るのは控えます。

  • Timer0を使い1MHz周期でTCをカウントする → CLOCKS_PER_SEC = 1000000を設定
  • barebones_clock()関数でLPC_TIM0->TCを返す
  • core_portme.hの各種設定はmbed版を流用 → #define HAS_PRINTF 1でprintfがリンクされる


結果

結果を以下に示します。

<LPCXpresso LPC1769-100MHz>
2K performance run parameters for coremark.
CoreMark Size    : 666
Total ticks      : 31310974
Total time (secs): 31.310974
Iterations/Sec   : 159.688421
Iterations       : 5000
Compiler version : GCC4.3.3
Compiler flags   : -o3
Memory location  : STACK
seedcrc          : 0xe9f5
[0]crclist       : 0xe714
[0]crcmatrix     : 0x1fd7
[0]crcstate      : 0x8e3a
[0]crcfinal      : 0xbd59
Correct operation validated. See readme.txt for run and reporting rules.
CoreMark 1.0 : 159.688421 / GCC4.3.3 -o3 / STACK

<mbed LPC1768-96MHz>  - default
Run CoreMark
2K performance run parameters for coremark.
CoreMark Size    : 666
Total ticks      : 3238
Total time (secs): 32.380000
Iterations/Sec   : 154.416306
Iterations       : 5000
Compiler version : 4.2 (EDG g++ mode)
Compiler flags   :
Memory location  : STACK
seedcrc          : 0xe9f5
[0]crclist       : 0xe714
[0]crcmatrix     : 0x1fd7
[0]crcstate      : 0x8e3a
[0]crcfinal      : 0xbd59
Correct operation validated. See readme.txt for run and reporting rules.
CoreMark 1.0 : 154.416306 / 4.2 (EDG g++ mode)  / STACK

<mbed LPC1768-96MHz> - 最適化

2K performance run parameters for coremark.
CoreMark Size    : 666
Total ticks      : 2654
Total time (secs): 26.540000
Iterations/Sec   : 188.394876
Iterations       : 5000
Compiler version : 4.2 (EDG g++ mode)
Compiler flags   : #pragma O3, Otime
Memory location  : STACK
seedcrc          : 0xe9f5
[0]crclist       : 0xe714
[0]crcmatrix     : 0x1fd7
[0]crcstate      : 0x8e3a
[0]crcfinal      : 0xbd59
Correct operation validated. See readme.txt for run and reporting rules.
CoreMark 1.0 : 188.394876 / 4.2 (EDG g++ mode) #pragma O3, Otime / STACK


実験前はコンパイラの性能差でmbed (RVDS)がLPCXpresso (gcc)を上回ると思ったのですが、LPCXpressoの100MHz動作の場合、mbed (96MHz)に比べてクロック周波数分だけLPCXpressoの方が高スコアという結果になりました。CoreMarkはコンパイラ性能の差が出にくいアルゴリズムのためなんでしょうか。それとも、gccが意外と善戦しているのか。

2011/5/2 追記:
プログラム中に#pragmaでコンパイルオプションを指定することで、mbedでも最適化レベルを最大にすることが可能なことが分かりました(@toyowataさんのツイートより)。coremark.hの先頭に以下の2行を追加します。

#pragma O3
#pragma Otime

上記のオプション指定は、#pragmaを宣言したファイル単位で有効になるようです。そのため、全てのファイルで参照しているヘッダーファイルに指定すると効率的です。

最適化を行うと、mbed(RVDS)の性能がLPCXpresso(gcc) 100MHzを上回ります。

本家coremark.orgにアップしてあるスコアを見ると、LPC1768-100MHz + armcc 4.0 (-O3 -Otime)で175というスコアが出ているので、ARM純正コンパイラで最大限最適化を行うと、gccのO3オプションより効率がよいコードが生成できると思われます。つまり、mbed IDEのコンパイラはARM純正のRVDSを使っていますが、最適化オプションはデフォルトレベルになっていると思われます。

結果のサマリーを以下に示します。

  最適化 スコア(絶対値) 相対値
mbed (LPC1768-96MHz) 指定なし 154.41 96.7
mbed (LPC1768-96MHz) O3, Otime 188.39 118
LPCXpresso (LPC1769-100MHz) O3 159.68 100
LPCXpresso (LPC1769-120MHz) O3 191.62 120


LPCXpressoでprintfのシリアル出力を行う方法

LPCXpressoでprintfを使用するとIDEのconsoleに出力されます。今回の用途であればこれでも十分なのですが、printfをシリアルポートに出力する方法を試してみたので概要を記載します。

  • printfを使う場合は、新規プロジェクトで"Semihosting C Project"を選択します(IDEのconsoleに出力する場合も同様)
  • プロジェクトプロパティーのCompiler→TargetのライブラリをRedlibに設定、Linker→TargetのライブラリをRedlib (semihost)に設定
  • RDB1768 CMSIS-based Example ProjectsからREB1768cmsis.zipをダウンロード
  • RDB1768cmsis_UART_printfフォルダのretarget.c, uart0.h, uart0.hをプロジェクトフォルダーにコピー
  • mainの先頭でUART0_Init(baud_rate)を呼ぶ


おわりに

LPCXpresso IDEですが、CodeSourcery G++とEclipseを使ってスクラッチでprintfが使える環境を構築した時に比べると格段に楽ちんです。LPCXpresso IDEもEclipseベースですがデバッガーが統合されていたりリンカースクリプトを自動生成してくれたりとか、IDEらしい機能があってグッド。ダウンロードできるコードサイズに128KBの上限がありますが、自分の使用範囲としては十分です。


参考資料

LPCXpresso LPC1769の120MHzクロック設定

久しぶりの更新になってしまいました。簡単なネタですが、LPCXpresso LPC1769のクロック設定ついて記載します。

LPCXpresso LPC1769はその名のとおり、LPC1769を搭載しており120MHzのクロックをサポートしています。ただ、LPCXpresso IDE標準添付のCMSISライブラリを使用すると100MHzで動作します(初期のLPC1768時代の名残でしょうか)。せっかくのLPC1769(高クロック版)なので、持てる能力を引き出すために120MHzクロック動作への変更を行ってみました。

結論としては、CMSISライブラリフォルダーにあるsystem_LPC17xx.cのPLL0CFG_Valを変更することによってクロック周波数を変更できるのですが、その原理についても簡単に記載します。


LPC17xxのクロック設定

LPC17xxには、クロック設定に関連するレジスタとして以下があります:

  • PLL0CFG bit14-0:  MSEL0(PLL0 Multiplier value) → この値+1をMとする
  • PLL0CFG bit23-16:NSEL0 (PLL0 Pre-Divider value) → この値+1をNとする
  • CCLKCFG bit7-0: CCLKSEL(CPUクロックの分周比)→ 3(4分周)を設定

最初の2パラメータから以下の通り、PLLの周波数が決まります
PLL0の周波数  FCCO = (2 x M x Fin) / N  → Finは外部クロックの12MHz

CPUクロックはFCCO / (CCLKSEL+1)となるため、120MHz動作とするためには、FCCO = 480Mとなるように設定する必要があります。一方IDE添付のCMSISでは、system_LPC17xx.cのクロック関連設定が以下のように定義されています。

/*--------------------- Clock Configuration ----------------------------------
#define CLOCK_SETUP           1
#define SCS_Val               0x00000020	// The main oscillator is enabled
#define CLKSRCSEL_Val         0x00000001
#define PLL0_SETUP            1
#define PLL0CFG_Val           0x00050063	// MSEL = 99,  NSEL = 5 for 100MHz
#define PLL1_SETUP            1
#define PLL1CFG_Val           0x00000023
#define CCLKCFG_Val           0x00000003
#define USBCLKCFG_Val         0x00000000
#define PCLKSEL0_Val          0x00000000
#define PCLKSEL1_Val          0x00000000
#define PCONP_Val             0x042887DE
#define CLKOUTCFG_Val         0x00000000

PLL0CFG_ValがPLL0CFGレジスタに設定される値で、M = 100, N = 6が設定されており、
FCCO = (2 x 100 x 12M) / 6 = 400Mとなり、CPUクロックはその1/4である100MHzになります。

120MHzのクロックで動かすためには、Mを120に上げます。すなわち、PLL0CFG_Valを以下のように変更します。

#define CLOCK_SETUP           1
#define SCS_Val               0x00000020	// The main oscillator is enabled
#define CLKSRCSEL_Val         0x00000001
#define PLL0_SETUP            1
//#define PLL0CFG_Val           0x00050063	// MSEL = 99,  NSEL = 5 for 100MHz
#define PLL0CFG_Val           0x00050077	// MSEL = 119, NSEL = 5 for 120MHz
#define PLL1_SETUP            1
#define PLL1CFG_Val           0x00000023
#define CCLKCFG_Val           0x00000003
#define USBCLKCFG_Val         0x00000000
#define PCLKSEL0_Val          0x00000000
#define PCLKSEL1_Val          0x00000000
#define PCONP_Val             0x042887DE
#define CLKOUTCFG_Val         0x00000000

LPCXpresso IDEの環境では、CMSISはプリコンパイルしたライブラリとしてリンクするため、クロック設定を変えるとライブラリをビルドし直す必要があります。100MHzと120MHzの両方を使い分ける場合は、CMSISライブラリを2セット作る必要があると思います。


動作確認

以下のサンプルプログラムで100MHzと120MHzの処理速度を比較しました。

  • 37行目:SystemCoreClockUpdate()関数呼び出しでPLL0CFG/CCLKCFGレジスタを読み出し、CPUクロック値を計算して、SystemCoreClock(グローバル変数)に設定します
  • 38行目:SystemCoreClockをprintfでIDEのコンソールに出力 → printfをIDEに表示できるのは便利
  • 41行目:1ms周期のSysTick割り込みを設定
  • 48行目:ダミーの計算を実施
  • 52行目:計算時間を表示
/*
===============================================================================
 Name        : main.c
 Author      : todotani
 Version     : 1.0
 Copyright   : Copyright (C) 
 Description : main definition
===============================================================================
*/

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

#include <cr_section_macros.h>
#include <NXP/crp.h>
#include <stdio.h>

// 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 ;

#define LOOP 1000000
volatile uint32_t msTicks; // counter for 1ms SysTicks

//  SysTick_Handler
void SysTick_Handler(void) {
    msTicks++;
}


int main(void) {
    uint32_t dummy;
    uint32_t startTime, endTime;

    SystemCoreClockUpdate();
    printf("CPU Clock:%dMHz\n", SystemCoreClock/1000000);
	
    // Setup SysTick Timer to interrupt at 1 msec intervals
    if (SysTick_Config(SystemCoreClock / 1000)) {
        while (1);  // Capture error
    }

    uint32_t i;
    double a = 0;
    startTime = msTicks;
    for (i = 0; i < LOOP; i++) {
        a += ((double)i * 3.14);
    }
    endTime = msTicks;
    printf("Execution Time:%dms\n", endTime);

    while(1) {
        // 無限ループ
        dummy++;
    }

    return 0 ;
}

100MHzと120MHz動作で同じコードを走らせると(Debugビルドで比較)、以下のようにちょうど1.2倍性能が向上しました。めでたし、めでたし。

CPU Clock:100MHz
Execution Time:3399ms

CPU Clock:120MHz
Execution Time:2832ms

その他

LPCXpressoはトラ技別冊のMARY基板のデバック用にLPCリンクが使えないかと思って購入したのですが、MARY基板より先にこっちで遊んでしまいました。写真のLPCXpressoの真ん中にジャンパーポストがささっているのは、LPC Link基板部分を分離してデバッカーとして使う構想のために、CuBeatSystemsさんの「LPCXpressoを切り離さないで賢く使う方法」を拝借させていただきました。

LPCXpresso_001

« 2010年12月 | トップページ | 2011年5月 »

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