汎用32ビットマイコンとして使える、PIC32MXを使ってみる( Coretex-M0 ,Coretex-M3 その他と比較 )

このPICは、32ビットRISCである、MIPSの4Kと呼ばれるCPUが内臓され、PIC16,PIC18,PIC24など、いわゆる従来のPICマイコンとは、ガラッと変わった、ハイパーフォーマンスのマイコンです。汎用32ビットマイコンと同じような使い方ができ、小型マイコンによく使われている、ARMの、Coretex-M0 よりかなり優れた性能があります。(感覚的には、同じクロックでほぼ2倍のパーフォーマンス)

では、どれほどの実行速度の違いがあるのか、PIC32MXと、トランジスタ技術(2014年3月号)の付録基板、トラ技ARMライタ で、いくつかテストプログラムを動作させ、比較してみます。トラ技ARMライタには、LPC11U35 が使われていて、Coretex-M0 がCPU部分です。水晶は12MHzが付いていますが、分周して1MHzで動作させます。また、Coretex-M3 として、mbed LPC1768 と比較してみます。こちらは、クロックは100MHzになるので、1/100 として比較します。

CPU プログラム開発環境とソース Dhry Stone v2.1 1回の実行時間 測定方法
PIC32MX250F128B MPLABX XC32 670μ秒 1MHz 1MHzでカウントするタイマー
トラ技ARMライタ Coretex-M0 LPC11U35 GCC 1320μ秒 1MHz 1MHzでカウントするタイマー
mbed LPC1768 評価キット Coretex-M3 LPC1768 mbed 971μ秒 1MHz換算 mbed のタイマーライブラリを使ってで計測
 他のマイコンについても、随時追加していきたいと思います。

トラ技ARMライタです。Coretex-M0 LPC11U35

mbedLPC1768 評価キットです。Coretex-M3 LPC1768

製作したPIC32MXユニバーサル基板です。余分なアナログ回路が付いていますが、今は使いません。
左がPIC32MXで、水晶は、16MHzを付けていますが、分周して1MHzで動作させます。


製作したPICの回路は以下です。
図の中央の、PICの21番ピンに、テストピンを引き出して、オシロスコープで実行時間を測定できるようにしています。

PIC32MXに モニタプログラムと、ドライストーンを移植

 PIC32MXを使うには、MPLABXのIDEと、xc32 というコンパイラツールチェーンをインストールします。PIC32MXのCPUは、MIPSなので、中身はGCCみたいですが、フリーでの使用は、最適化が、O1 までになっています。

 UARTでPCと接続し、ターミナルソフトから、ドライストーンを実行させます。PIC32MXは、汎用32ビットCPUなので、32ビット用に作ったモニタと、ドライストーンもすんなり移植できます。使用しているPIC32MXには、32kバイトのRAMがあるため、ドライストーンで宣言される、int Arr_2_Glob [50] [50]; もそのまま使えますが、他のCPUでは、8kバイトのRAMしかないので、同じように、配列へのポインタとして登録します。ポインタには、使用前に絶対アドレスを設定します。

 UARTを使うため、PIC32MXのポートを設定します。PPSInput と、PPSOutput は、マクロで、インストールした、xc32 ツールチェーンのフォルダ以下、\xc32\v1.31\pic32mx\include\peripheral\pps.h に書かれているようです。UARTなどの、周辺機器の設定は、マニュアルを読んでもわかりにくく、また、ポートをどこにでも配置できるわけではないので、この点では、使いにくいマイコンです。PPSInput を使って、ポート指定しても、エラーになれば、ポート割り付けができないということになります。PPSInput を使う方法も、WEB検索で見つけたものです。

 MPLABXのプロジェクトソースはこちらpic32mxdhry.lzh
 上記を解凍して、MPLABXで開き、 pickit3 で、rx_pic_sw.production.hex を書き、ターミナルに、
PIC32MX monitor 1M 2016.5.8-1
PIC32-Bug>
 と表示されるので、D1 ( enter ) と、ドライストーンを1回実行すると、結果を報告します。

 ドライストーンの結果報告が60行ほどになるので、最後の部分がこのようになり、1回の実行で、670クロック( 670μ秒 )かかったことがわかります。

 ここで、最後に、dhrydbgdata = 8, としていますが、dhry_2.c で、処理途中の変数がどうなっていたか、記録して、終わってからモニタで報告したものです。




LPC11U35に、UARTでいろいろ操作できるモニタプログラム

 トラ技ARMライタは、他のARMプロセッサのフラッシュを書き換えるために作成されていますが、これをライタではなく、1個のARMマイコンとして使うので、いろいろと操作できるように、モニタを移植します。移植する前に、いわゆる ’Lチカ’のサンプルを動作させ、開発環境の構築を完成してから、UARTの操作などを追加していきます。Lチカのサンプルは、NXPのサイトにいくつかありますが、ほとんどが(全部) LPCXpresso のソースなので、GCCのサンプルを探すと、FatFs の作者である、ELM-ChaN のNXPの小ピンARMの試食の、1ページ分ほどのところで、”LPC1114FN28/102でLチカ(gcc)” というサンプルを見つけたので、これを改造します。
 LPCXpresso でもいいのですが、プロジェクトの扱いが、どうも使い勝手が悪く、GCCのツールチェーンをLPCXpresso のものを使う、GCCコマンドラインで開発することにしました。

GCCツールチェーンのコマンドライン用PATHを設定

PATHの設定は、GCCのコンパイラ群と、make などの、GCCコマンドを実行するため、2個追加します。
ここで、NXP の LPCXpresso をインストールしたフォルダを、C:\nxp とすると、

set path=C:\nxp\LPCXpresso_6.1.0_164\lpcxpresso\tools\bin;C:\nxp\LPCXpresso_6.1.0_164\lpcxpresso\msys\bin;%PATH%

となります。ここでは、LPCXpresso は、V6.1.0_164 で、トランジスタ技術(2014年2月)付録の、CDROMに梱包されていたものです。この1行を、path1.bat などと名前をつけたバッチファイルに書き、コマンドラインから、
C:\work\path1 (enter) と、コマンドを与え、環境変数に、コンパイラのツールチェーンのPATHを登録します。work は、ソースなど一式を置いてあるフォルダで、適当な場所でOKです。

 開発環境が設定されたところで、”LPC1114FN28/102でLチカ(gcc)”を解凍し、lpc1114_102 フォルダにコマンドラインを移動し、先ほど作った、path1.bat を実行します。その後、lpc1114_102>make ( enter ) でコンパイルすると、lpc1114_102\obj フォルダが新たに作られ、その中に、FlashMagic で書ける、led.hex ファイルが作成されていれば、コンパイル環境が正常に設定されていることになります。”LPC1114FN28/102でLチカ(gcc)”は、LPC1114 用なので、LPC11U35 で使うには、若干修正が必要ですが、CPU内臓の、UARTとか、タイマーとか、ポート設定などは、ほとんど同じで、そのまま使えますが、アドレスや機能設定などは、マニュアルを確認する必要があります。

 では、ここで、”LPC1114FN28/102でLチカ(gcc)”のソースファイルをLPC11U35 用に改造していきます。

 以下は、オリジナルの、makefile を参考にして作成した、LPC11U35 用に改造した、makefile です。obj フォルダを作成せず、必要な部分だけ抜き出しています。ソースには、モニタのほか、ベンチマークソフトである、ドライストーンを追加しています。


以下は、上記の、makefile で使う、リンカースクリプト( LPC1114_102.ld )です。オリジナルのものから、ROM,RAMの容量を増加させているだけです。


以上の、makefile と、リンカースクリプトを使ったソース一式がこれです。→ ソース一式
このソースの中で使っているのは、改造したドライストーンです。また、モニタが実装され、UART経由で、ドライストーンを起動し、結果をUARTで出力します。
  1. PCと接続するUARTのRXDを、PIO0_18、 UARTのTXDを、PIO0_19 として、9600ボーで初期化する。
  2. PIO0_18 は、CN1の9番ピン、PIO0_19 は、CN1の10番ピンに接続されているので、そこに、USB−−UART変換ケーブルの、TXD,RXD を接続
ターミナルソフトを起動すると以下のように、
LPC11U35 CPU test 2016.5.1-1
LPC11Uxx-Bug> と起動メッセージが出ます。

LPC11Uxx-Bug>D1 ( enter ) と、ドライストーンを、1回実行する命令を出すと

ドライストーンの結果のメッセージの最後には、以下のように、1回の実行に要したクロック数(1MHz)を報告します。
1320クロック( hex 0x528 ) なので、1回あたり、1.32ms要していることになります。


ベンチマークプログラム、DhryStone2.1 改造要点


 DhryStone 2.1 は古くからある、整数演算のベンチマークプログラムですが、組み込みCPUでは、そのままではコンパイルできないことが多く、慎重に修正します。修正するので、若干正規のものとは異なる結果になりますが、そこは誤差範囲内としておきます。ソースは、 ここのもの を使いますが、Author: Reinhold P. Weicker と記述されている dhry_1.c なるソースがあるはずです。ソースは、他に、dhry_2.c , dhry.h があり、全部で3個になります。
  1. 修正箇所として、必須のものは、タイマーです。組み込みなので、正確な1μ秒の時間がわかるタイマーを、マイコンのマニュアルから探して動作させます。タイマーが簡単に使えない場合は、GPIOを使って、処理のスタートでHigh、終了でLowを出力し、オシロスコープで測定します。
  2. タイマーを改造すると同時に、実行時間からドライストーン値を計算する部分を削除して、実行開始と終了のカウンタの差をリターンします。LPC11U35 への移植では、使っているタイマーがカウントダウンなので、User_Time = Begin_Time - End_Time; となって、Begin_Time の方が値が大きいとしています。
  3. その次に、 int 配列である、Arr_2_Glob[50][50] は、10000バイトのエリアが必要なので、8kBとかのRAMしか無いマイコンでは、そのままの配列宣言では実装できませんので、これを、配列へのポインタ変数に置き換え、ソースも若干修正します。というのは、Arr_2_Glob[50][50] の配列で使われる一番アドレスの大きいのは、Arr_2_Glob[28][8] で、この場所は、(28 x 50 + 8) x 4(long int) で、先頭から、5632バイトの位置になり、RAMが8kバイトても実装できることになります。配列の参照も、配列へのポインタ経由での参照も、たいして変わらないので、ベンチマークテストにほとんど影響はないと思います。
    また、この配列で、アクセスされる場所も限られ、Arr_2_Glob[28][8],Arr_2_Glob[8][7],Arr_2_Glob[8][8],Arr_2_Glob[8][9] の4か所(long) だけです。先頭から、Arr_2_Glob[8][6] まで( 1628バイト )は、まったく使われないので、このエリアに、プログラムの他の変数を割り当てることも可能です。
  4. 配列の扱いを変えたので、配列へのライトとリードの部分を修正し、dhry.h で、配列宣言、struct Arr_2_Glob_s{ int loc[50][50]; }; を追加し、dhry_1.c では、
    struct Arr_2_Glob_s *Arr_2_Glob;
    に変更して、dhry_1.c の起動時に、
    Arr_2_Glob = (struct Arr_2_Glob_s *)0x10000800;
    などと、ポインタを、RAMの最後尾から、5636バイト以上前のアドレスに設定します。このアドレスは、コンパイル後に map を確認して、使っている他の変数領域の最後尾より後ろであることを確認します。
以上で改造は終了しますが、コンパイル時には、ワーニングメッセージがたくさん出てくることがあります。というのは、dhry_1.c と dhry_2.c には、関数の宣言が無く、しかも値を返さない関数には、void も書かれていないからです。


mbed LPC1768 評価キット に、ドライストーン2.1を移植


 mbed は、インターネットに接続できれば、どのパソコンでも開発でき、開発ツールチェーンをPCにインストールする必要がないという点では、PCのディスクを汚さないというメリットがありますが、基板側に mbed のインターフェイスが必要になり、最小限な組み込み基板ではなく、評価専用基板の位置付けです。

 mbed LPC1768 評価基板は、LPC1768 以外に、いくつかICが搭載されていて、mbed インターフェイスの他、イーサネットPHYもあって、豪華な構成なので、このままイーサネットや、USBを使わず、組み込みで使うには、もったいないく、かつ消費電力も大きくなってしまいます。

 mbed LPC1768 評価キットをUSBで接続すると、MBED という名前のドライブとして認識され、mbed のユーザー登録をしていると、そのフォルダにある、MBED.HTM を開くと、いままでに作成したプログラムのソースがロードされ、新規ソースの作成を促す画面になります。mbed LPC1768 は、面倒なポート設定などは、すでにライブラリとしてすぐに使えるようです。その代わり、そのライブラリをどのように使うかは、次のようにしてインターネットから検索して適当な例をダウンロードして参考にします。まずは、UARTの例を探して mbed で開発する手順を、備忘録としておきます。ここで使うUARTは、mbed インターフェイスにある、USB−UARTの機能を使うので、評価基板に新たにUARTの変換ICなどを追加する必要がなく、評価基板さえあれば、LPC1768 と、PCとを、UARTで通信できます。( PC側に、USB−UARTの専用ドライバのインストールが必要 )

プログラムワークスペース の「プログラム」を右クリックして、プログラムのインポート --> ウイザードからインポートを選択する。

検索欄で、使いたい機能をを書き、( 例 UART ) 検索をクリック

すると、いくつかの例が表示され、どれだけ例があるか、作者はだれか、いつアップロードされたか、などの一覧がでてくるので、目的に合致しそうなのをロードします。

たとえば、一番下の欄、7番目の、uart_printf を選んで、インポート!ボタンをクリックすると、

Import ボタンで、開始されます。

ダウンロードが進みます。

ダウンロードが終わり、uart_printf のプロジェクトを選んで、 main.cpp を表示。

 この例は、UARTを、921600ボーで使うソースですが、このままでは早すぎので、9600に変え、\n を、\r\n に変更します。
さらに、13行目に、

pc.printf("Hello World! @921.600kbps\n",1285);

 の行があるのを、引数 1285 の意味が不明なので、変数、count を追加し、次のように変更します。

pc.printf("Hello World! @9600kbps %d \r\n",count++);

コンパイルし、ダウンロードした uart_printf_LPC1768.bin を MBED フォルダにコピーして、基板の中央のボタンを押すと、以下のように、ターミナルに表示されます。


ドライストーンに必要な、printf と タイマーの動作が確認できたところで、ドライストーンを移植


 uart_printf の、main.cpp を元に、次のように main.cpp を改造します。ドライストーンの全体である、dhrymain を、1000回呼び出して、その実行時間を測りますが、タイマーのスタート、ストップは、実際の処理の始めと終わりに書きます。dhry_1.c では、実行前と、終了後に、printf が多用されているので、printf の処理時間を測定しないようになっています。元々は、タイマーの差から処理時間を計算し、1秒で何回処理できたかを計算するようになっていますが、ここではその計算は省略しています。
ソースはこちらmain.cpp


 次に、ドライストーンの本体、dhry_1.c と、dhry_2.c そして、dhry.h を、mbed のソースに追加してコンパイルするのですが、いきなりたくさんのエラーが出てきます。

 ドライストーンの dhry_1.c と dhry_2.c のC言語の記述は、以下のように関数を記述するものです。この記述は、GCCなどでは受けて付けてくれますが、mbed ( ライブラリは、C++ のみのようである )では、エラーになり、dhry_1.c dhry_2.c での関数は、全部修正します。また、関数の型が int の場合、省略されているので、これも 参照する前の行で int として宣言しておく必要があります。

オリジナルのドライストーンでの関数記述。mbed のコンパイラではエラーになる。


関数記述の修正

以上の修正を加えて、エラーなくコンパイルできたソースと、実行ファイル
dhry.h
dhry_1.c
dhry_2.c
実行形式 exe ファイル dhry_LPC1768.exe

 上記実行ファイルを、 mbed LPC1768 評価キットに書き、ターミナルソフトで表示させた結果。

 このメッセージは、ドライストーン 2.1 を1000回実行させた結果が正常で、実行時間が、9.710ms だったことを報告しています。 mbed LPC1768 基板がデフォルトで100MHzで動作しているので、1MHzでは、1回あたり、971μ秒かかっていることがわかります。






工事中



inserted by FC2 system