Hatena::ブログ(Diary)

滴了庵日録 このページをアンテナに追加 RSSフィード

2017/09/22(Fri)

STM32マイコンの開発環境について(雑談)

STM32マイコンの開発環境はいろいろあるいけど、ぶっちゃけ言って次の3つに絞られると思う。

(※個人の感想です。)

  • 金があるなら IAR(EWARM)かKeil(uVision/MDK-ARM)
  • 金がないなら SW4STM32
  • お手軽志向なら mbed

IARやKeil(カイルと読む)は、ン十万円するのでお仕事用。個人的にはIARのほうが好みだし、サポートも良い気がするけど、客がKeil使えと言うならしたがう。要は世のなか金しだいですよ。ちなみにどちらもバイナリサイズ32kバイトまでなら無償評価版が使える。


SW4STM32(System Workbench for STM32)は、Eclipseベースの開発環境。Eclipseベースの開発環境としては他にもCooCox CoIDEが有名だけど、SW4STM32はSTマイクロが公式にサポートしている。ちなみに、STマイクロが公式にサポートしている開発環境は、Keil、IAR、TrueSTUDIO、SW4STM32の4つ。TrueSTUDIOについてはよく知らない。


mbedはインストール要らずのオンラインの開発環境。STM32は各種Nucleoボードがmbedに対応している。お手軽で良いと思うけど、

  1. オンライン環境である。(いざという時にかぎってサーバが重かったり落ちてたりする)
  2. デバッガが使えない。(printfデバッグしかできないのでハマったら難儀)

というのがネック。抽象化された分かりやすく互換性の高いクラスライブラリは魅力なので、mbedで作ったプロジェクトをローカル環境にエクスポートして使うのが好み。Nucleoの場合、IAR、Keil、SW4STM32、CooCoxのいずれにもエクスポート可能である。

2017/09/15(Fri)

Cortex-M4FのFPUを使う

ARM Cortex-M4は、M3にDSP(SIMD,MAC)命令を追加したものだが、Cortex-M4Fと呼ばれるものは、さらに単精度のFPU(浮動小数点ユニット)を持っている。どれくらい効果があるものか、三角関数の計算で速さを比較してみた。

方法

Cortex-M4Fをコアに持つLPC4370を用い、FPUを使った場合と使わない場合で、三角関数の計算1万回にかかる時間を比較する。

  • マイコンボード: LPC-Link2 (LPC4370搭載 / 動作クロック204MHz)
  • 開発環境: LPCXpresso v8.1.4_606
  • DSPライブラリ: CMSIS_DSPLIB_CM4 v3.20

三角関数は下記の3種類を使用する。

関数ヘッダファイル説明
double sin(double)math.hANSI標準ライブラリの倍精度実数のsin関数
float sinf(float)math.hANSI規格外の単精度実数のsin関数
float arm_sin_f32(float)arm_math.hCMSIS DSPライブラリの単精度実数のsin関数

下記のようなコード(抜粋)をもちい、実行時間をオシロスコープで測定する。

while(1) {
    // 三角関数を1万回計算する
    for( float x=0.0f; x<10.0f; x+=0.001f){
        //volatile float y = sin(x);        // ANSI標準 倍精度版
        //volatile float y = sinf(x);       // ANSI規格外 単精度版
        volatile float y = arm_sin_f32(x);  // CMSIS DSPライブラリ版
    }
    // テストピンをパタパタさせてオシロで計測
    if(toggle){
        Chip_GPIO_SetPinOutHigh(LPC_GPIO_PORT, TEST_PORT, TEST_PIN);
    }else{
        Chip_GPIO_SetPinOutLow(LPC_GPIO_PORT, TEST_PORT, TEST_PIN);
    }
    toggle = !toggle;
}

FPUを使用する設定 (LPCXpressoの場合)

プロジェクト作成時のウィザードの「Floating point unit」で「Enabled_SoftABI」を選択する。「Disabled」を選択するとFPUは無効になる。


もしくは、プロジェクト作成後にプロジェクトのPropertiesで次のように設定する。

  • C/C++ Build > Settings > MCU C Compiler > Architecture で
    • Floating point を「FPv4-SP(Soft ABI)」に設定する。
  • C/C++ Build > Settings > MCU Linker > Architecture で
    • Floating point を「FPv4-SP(Soft ABI)」に設定する。

※ Soft ABIでなくHard ABIを選ぶと、関数の引数も浮動小数点レジスタで渡されるのでより高速になるが、ライブラリも含めてリンクされる全てのコードをHard ABIでビルドする必要がある。


CMSIS DSPライブラリを使用する設定 (LPCXpressoの場合)

プロジェクト作成時のウィザードの「CMSIS DSP Library to link project to」で「CMSIS_DSPLIB_CM4」を選択する。

もしくは、プロジェクト作成後にプロジェクトのPropertiesで次のように設定する。

  • C/C++ Build > Settings > MCU C Compiler > Includes で
    • Include paths に"${workspace_loc:/CMSIS_DSPLIB_CM4/inc}"を追加
  • C/C++ Build > Settings > MCU C Linker > Libraries で
    • Libraries に CMSIS_DSPLIB_CM4 を追加
    • Library search path に"${workspace_loc:/CMSIS_DSPLIB_CM4/lib}"を追加

結果

表:三角関数の計算1万回にかかった時間

三角関数FPUを使ったときFPUを使わないとき
double sin(double)110msec115msec
float sinf(float)9.5msec73msec
float arm_sin_f32(float)4.7msecN/A
  • Cortex-M4FのFPUは単精度なので、double型の計算については差が出ない。
  • float型の計算ではFPUを使うと劇的に高速になる。
  • DSPライブラリの三角関数を使うと、さらに高速になる。(精度は未検証)

まとめ

PCアプリでは実数といえばdoubleを使うことが常で、floatはあまり使われない。しかし、floatといえども仮数部が23ビットあり(doubleは仮数部52ビット)、マイコンで扱うセンサの精度がせいぜい16ビットくらいであることを考えれば、たいていの場合には十分な精度と思われる。特にCortex-M4FではFPUを使うことでfloatの計算が劇的に高速になるので、効果的に使っていきたい。

2017/09/13(Wed)

LPC4370のRAM上実行について

LPC4370は、282kBのRAMを内蔵するかわりFlashを内蔵しておらず、RAM上で実行することを前提としている。204MHzという高速な動作クロックのため、内蔵Flash上の動作では読み出しアクセスがボトルネックになるためである。(回路の特性上、SRAMはFlashより高速動作できる。)

しかし、LPCXpresso + LPC Open の開発環境は、どうもLPC4370のRAM上実行に関してサポートがおざなりな感じである。サンプルコードや新規作成プロジェクトをどう修正すべきか、以下にまとめる。


準備

写真

LPC-Link2を2個用意し、片方をデバッガアダプタ、もう片方をLPC4370のターゲットボードとする。これはジャンパで設定できる。デバッガアダプタのJ7とターゲットボードのJ2をケーブルで接続する。

ジャンパデバッガターゲット
JP1開放 (デバッガとして使用)短絡 (ターゲットとして使用)
JP2短絡 (ターゲットへの給電)開放 (給電しない)

やりたいこと

  • コードは外部SPI Flashに保存する
  • スタートアップルーチン(リセットハンドラ)はSPI Flash上で動作 ※
  • スタートアップルーチン内で、コードをSPI FlashからSRAMにロード
  • main関数以降のコードはSRAM上で動作

※ LPC4370はSPI Flashがメモリマップ上にマッピングされており、SPI Flash上のコードを実行できる。ただし、当然ながら実行速度は桁違いに遅い。


ところが…

LPC4370の仕様を考えれば、とうぜん上記のようにすべきである。しかし、サンプルコードも新規作成プロジェクトもそのようにはなっていない。

  • LPC Openのサンプルコードでは、コードはSPI Flashに書き込まれ、SPI Flash上で実行される。そのため、実行速度が非常に遅い。
  • LPC Xpressoで新規作成したプロジェクトでは、コードはデバッガからSRAMに直接ダウンロードされ、SRAM上で実行される。そのため実行速度は速いが、デバッガなしには実行できない。

解決

Flashドライバやメモリマップの設定 (プロジェクト新規作成の場合)

サンプルコードと同様の設定をする。

  • 「Default flash driver」に LPC18_43_SPIFI_16MB_64kB.cfx を指定する。
  • プロジェクトのProperty > C/C++ Build > MCU settings > Memory details(LPC4370) のメモリマップにFlashの領域を追加する。
TypeNameAliasLocationSize
FlashSPIFIFlash0x140000000x400000

リンカスクリプトの修正

デフォルトではリンカスクリプトは自動生成される。プロジェクトのProperty > C/C++ Build > Settings > MCU Linker > Managed Linker Script で、Manage linker scriptのチェックを外し、Linker scriptでリンカスクリプトを指定する。自動生成のリンカスクリプトをベースに下記のように修正する。


SPI FlashからSRAMにコピーするコードのアドレスとサイズをSPI Flashに保持する指定。

    /* MAIN TEXT SECTION */
    .text : ALIGN(4)
    {
        FILL(0xff)
        __vectors_start__ = ABSOLUTE(.) ;
        KEEP(*(.isr_vector))
        /* Global Section Table */
        . = ALIGN(4) ; 
        __section_table_start = .;
        __data_section_table = .;
        LONG(LOADADDR(.text_RAM)); /* ←追加 */
        LONG(    ADDR(.text_RAM)); /* ←追加 */
        LONG(  SIZEOF(.text_RAM)); /* ←追加 */

実行アドレス(VMA)と格納アドレス(LMA)が異なるセクションの指定

    .text_RAM : ALIGN(4)
    {
        *(.text*)
        *(.rodata .rodata.* .constdata .constdata.*)
        . = ALIGN(4);
    } > RamLoc128 AT>SPIFI  /* ← VMAはSRAM、LMAはSPI Flash */

スタートアップルーチンのセクション指定

cr_startup_lpc43xx.c の ResetISR()関数がスタートアップルーチンである。この関数定義の前にセクション指定の記述をする。

__attribute__ ((section(".after_vectors")))
void ResetISR(void) {

スタートアップルーチンの修正

スタートアップルーチン内の下記の処理で、FlashからSRAMにコードがコピーされる。

    // Copy the data sections from flash to SRAM.
    while (SectionTableAddr < &__data_section_table_end) {
        LoadAddr = *SectionTableAddr++;
        ExeAddr = *SectionTableAddr++;
        SectionLen = *SectionTableAddr++;
        data_init(LoadAddr, ExeAddr, SectionLen);
    }

したがって、これより前の段階では、Flash上のコードしか実行できない。SystemInit関数はSRAM上に配置されているので、処理の順番を変えて、main関数呼び出しの直前に持ってくる。

    SystemInit();
    __main();

ブレークポイントについて

デバッガで実行する場合、ブレークポイントが最初だけうまく動作しない。ResetISR関数でmain関数を呼び出す行にブレークポイントを貼っておくとうまく動作する。原因は不明。

    __main();

ちなみに

コードのサイズが大きくて全てをSRAMにロードできない場合、高速実行したい関数のみをSRAMに配置することもできる。その方法は、下記を参照。


参考文献

2017/09/09(Sat)

Nucleo種類多すぎ

Nucleoボード種類多すぎて、どれがどれか分からないのでまとめ。

図


STM32のラインナップ

まず、STM32マイコンのラインナップについて

アルファベットの次が7ならM7、4ならM4、0ならM0、これは分かりやすい。

でも3ならM3かと思いきやM4。1か2ならM3。おおむね数字の大きいほうが強い。

ペリフェラルに特色のあるものもあるので、必ずしも強いやつが上位互換ではない。


ハイパフォーマンス シリーズ
シリーズコア動作クロック
STM32H7Cortex-M7400MHz
STM32F7Cortex-M7216MHz
STM32F4Cortex-M484-180MHz
STM32F2Cortex-M3120MHz
メインストリーム シリーズ
シリーズコア動作クロック
STM32F3Cortex-M472MHz
STM32F1Cortex-M324-72MHz
STM32F0Cortex-M048MHz
ウルトラローパワー シリーズ
シリーズコア動作クロック
STM32L4Cortex-M480MHz
STM32L1Cortex-M332MHz
STM32L0Cortex-M0+32MHz

Nucleoのラインナップ

Nucleoの型番はSTM32の型番と対応する。

例えば、NUCLEO-F401REは、STM32F401REを搭載している。


Nucleo-32:Arduino Nano型(小型) / 32ピンSTMマイコン搭載

メインストリーム シリーズ

ボードコアクロックFlashRAM
NUCLEO-F303K8Cortex-M472MHz64kB16kB
NUCLEO-F042K6Cortex-M048MHz32kB6kB
NUCLEO-F031K6Cortex-M048MHz32kB4kB

ウルトラローパワー シリーズ

ボードコアクロックFlashRAM
NUCLEO-L432KCCortex-M4 80MHz256kB64kB
NUCLEO-L031K6Cortex-M0+32MHz32kB8kB
NUCLEO-L011K4Cortex-M0+32MHz16kB2kB

Nucleo-64:Arduino Uno+拡張型(中型) / 64ピンSTMマイコン搭載

ハイパフォーマンス シリーズ

ボードコアクロックFlashRAM
NUCLEO-F446RECortex-M4180MHz512kB128kB
NUCLEO-F411RECortex-M4100MHz512kB128kB
NUCLEO-F410RBCortex-M4100MHz128kB32kB
NUCLEO-F401RECortex-M484MHz512kB96kB

メインストリーム シリーズ

ボードコアクロックFlashRAM
NUCLEO-F334R8Cortex-M472MHz64kB16kB
NUCLEO-F303RECortex-M472MHz512kB80kB
NUCLEO-F302R8Cortex-M472MHz64kB16kB
NUCLEO-F103RBCortex-M372MHz128kB20kB
NUCLEO-F091RCCortex-M048MHz256kB32kB
NUCLEO-F072RBCortex-M048MHz128kB16kB
NUCLEO-F070RBCortex-M048MHz128kB16kB
NUCLEO-F030R8Cortex-M048MHz64kB8kB

ウルトラローパワー シリーズ

ボードコアクロックFlashRAM
NUCLEO-L476RGCortex-M480MHz1024kB128kB
NUCLEO-L152RECortex-M332MHz512kB80kB
NUCLEO-L073RZCortex-M0+32MHz192kB20kB
NUCLEO-L053R8Cortex-M0+32MHz64kB8kB

Nucleo-144:Arduino Uno+拡張型(大型) / 144ピンSTMマイコン搭載

ハイパフォーマンス シリーズ

ボードコアクロックFlashRAM
NUCLEO-F767ZICortex-M7216MHz2048kB512kB
NUCLEO-F746ZGCortex-M7216MHz1024kB320kB
NUCLEO-F446ZECortex-M4180MHz512kB128kB
NUCLEO-F429ZICortex-M4180MHz2048kB260kB
NUCLEO-F412ZGCortex-M4100MHz1024kB256kB
NUCLEO-F207ZGCortex-M3120MHz1024kB128kB

メインストリーム シリーズ

ボードコアクロックFlashRAM
NUCLEO-F303ZECortex-M472MHz512kB80kB

けっきょくどれがいいのか?

価格が大差ないので、電子工作やプロトタイピングに使うなら、とりあえずハイスペックなものを選んでおけばよい気がする。消費電力を気にするとか、特定品種のマイコンの評価に使うとかでなければ、Cortex-M0系を選ぶ理由はあまり無い。

  • Nucleo-64なら、F446、F401、F103、L476 あたり。(秋月ではこの順で人気)
  • Nucleo-32なら、L432、F303 あたり。
  • Nucleo-144は、外部メモリI/FやLANがほしいときに。F767、F746、F429あたり。

参考URL

2017/09/06(Wed)

LPCマイコンのコードのRAM実行

LPC-Link2をLPC4370ボードとして用い、コードを実行させてみると、なぜかLPCXpresso LPC1769と比べて10倍以上遅かった。LPC-Link2は Cortex-M4 @ 204MHz、LPCXpresso LPC1769は Cortex-M3 @ 120MHzである。ぜったいに何かおかしい。


調べてみると、どうやらLPC-Link2のLPC4370は、外部SPI-Flashのコードを実行しているようだった。RAMに展開して実行するには、LPCXpresso環境の場合、下記のようなマクロ関数定義に用いる。

#include <cr_section_macros.h>

__RAMFUNC(RAM) void hoge(void)
{
    ...
}


すると、380usecかかっていた処理が5.4usecに短縮した。2桁違う! LPCXpresso LPC1769と比べても倍以上速い。M4とM3では周波数以上に差があるということだろうか?


各種LPCマイコンでの処理時間を比較してみた。

ボードマイコンCPUコアクロック処理時間
LPCXpresso LPC1115LPC1115Cotex-M050MHz58usec
LPCXpresso LPC1769LPC1769Cotex-M3120MHz15usec
LPC-Link2LPC4370Cotex-M4204MHz5.4usec (RAM実行時)
LPC-Link2LPC4370Cotex-M4204MHz380usec (SPI Flash実行時)


写真


ちなみに、LPC-Link2をマイコンボードとして用いる場合は、LPC-Link2を2個用意し、片方をデバッガ、もう片方をターゲットとする。これはジャンパで設定できる。

ジャンパデバッガターゲット
JP1開放 (デバッガとして使用)短絡 (ターゲットとして使用)
JP2短絡 (ターゲットへの給電)開放 (給電しない)