ARM(STM32)

stm32plusを使う - STM32

概要

Andy Brown氏によるSTM32向けC++ライブラリである stm32plus(andysworkshop / stm32plus) を使用する際のメモ.
便利そうなんだが,ザ・C++という感じのコードでC++にあまり馴染みがない人にとっては使いづらいと思うので,コードの解説も加えながらメモを残したい.

セットアップ

stm32plusはスタティックライブラリとしてプロジェクトに組み込むことを想定しているため,事前にビルドが必要である.

下準備

もちろんSTM32向けの開発ツールチェインは必要なので,Windowsならyagartoなど,Linuxなら,Linux上でSTM32のプログラミング・デバッグ環境を整える - STM32を参考にしてセットアップを完了させておく.
確かWindowsだとコマンドプロンプトの仕様上8192文字以上のコマンドを受け付けてくれなくてビルドが失敗するので,CygwinやMingW32などを使用したほうが良いかもしれない.

ほかにもSConsと呼ばれるmake系のツールを使用するので,それを予めインストールしておく. Ubuntuなら,

$ sudo apt-get install scons

でインストールできる.

また,Doxygenでドキュメントを生成できるようになっているため,

$ sudo apt-get install doxygen

しておくと便利.

レポジトリをclone

$ git clone https://github.com/andysworkshop/stm32plus.git
$ cd stm32plus

Linux上でSTM32のプログラミング・デバッグ環境を整える - STM32でビルドしたツールチェインやgcc-4.7.0以降のYAGARTOを使っている場合は,バージョンの都合上SConstructファイルを編集する必要がある.

env.Replace(CXXFLAGS=["-Wextra","-Werror","-pedantic-errors","-fno-rtti","-std=gnu++0x","-fno-threadsafe-statics","-pipe"])
env.Append(CCFLAGS="-DHSE_VALUE="+hse)
- env.Append(LINKFLAGS=["-Xlinker","--gc-sections","-mthumb","-g3","-gdwarf-2"])
+ env.Append(LINKFLAGS=["-Xlinker","--gc-sections","-mthumb","-g3","-gdwarf-2","-nostartfiles"])

# add on the MCU-specific definitions

要は"-notstartfiles"を追加すればよい. こうしないと"crt0.oがありません"と怒られる.
(関連事項:yagartoのGCCを4.7以降にする際の注意 - STM32

ビルド

$ scons mode=debug mcu=f4 hse=12000000 -j9

SConsに渡すオプションは以下の通り.

名称説明
mode最適化のかけ方を変えるdebug(デバッグ情報あり,最適化なし)
fast(-O3に相当)
small(-Osに相当)
mcuシリーズの選択f1hd(STM32F103)
f1cle(STM32F107)
f1mdvl(STM32F100)
f4(STM32F4XX)
hse外部クロックの周波数(Hz)数値(12MHzなら12000000)

上記の -j9 はコンパイルに使用するスレッド数. どっかで((論理コアの数)+1)を指定すると良いという噂を聞いたので9にしてみた.

ドキュメントの生成

$ cd lib
$ doxygen Doxyfile

すると,lib/build/doc/htmlの下にhtmlファイルが大量にできる.
index.htmlをブラウザで開けばドキュメントを見られる.

各機能

GPIO

まず最初にstm32plusを使用するにあたって最もベーシックなGPIOを取り上げる.
といってもベーシックでないように見えるのはC++の機能がふんだんに用いられているからである.(それによるメリットも後述する.)


内部の詳しい解説はstm32plus-GPIO - STM32を参照.

LED点灯

はじめにLEDを点灯させるだけのコードを見てみよう. このコードはstm32plusのexamples/blinkディレクトリにあるものに少し手を加えたものである.

#include "config/stm32plus.h"
#include "config/gpio.h"

using namespace stm32plus;

class Led {
  public:
    void on() {
      GpioC<DefaultDigitalOutputFeature<8> > pc;
      pc[8].reset();
    }
};

int main() {
  Led led;
  led.on();

  while(1);
}

PC8のピンをLOWにするという動作を行うコードである.
STM32VLDiscovery上で実行すると,右下にある青色のLEDが点灯する.

main関数の中では,Ledクラスの実体化と,on関数の呼び出しが行われている.

Ledクラスのon関数の内容を見ていく.

GpioC<DefaultDigitalOutputFeature<8> > pc;

この行では,PC8をデジタル出力のピンとして設定し,GPIOCを扱うためのインスタンスpcを生成している.

pc[8].reset();

そして,この行でPC8をLOWに設定している.


内部の詳しい解説はstm32plus-GPIO - STM32を参照.

<バリエーション>

  • GpioCでなくGpioA
    GpioA<DefaultDigitalOutputFeature<8> > pa;
    pa[8].reset();
  • PA8でなくPA9
    GpioA<DefaultDigitalOutputFeature<9> > pa;
    pa[9].reset();
  • PA8も9も10も
    GpioA<DefaultDigitalOutputFeature<8,9,10> > pa;
    pa[8].reset();
    pa[9].reset();
    pa[10].reset();
    もしくは
    GpioA<DefaultDigitalOutputFeature<8,9,10> > pa;
    pa.reset((1<<8)|(1<<9)|(1<<10));

LED消灯

消灯する場合はresetをsetに書き換えれば良い. だがそれだけでは芸がないのでC++風に書いてみる.

#include "config/stm32plus.h"
#include "config/gpio.h"

using namespace stm32plus;

class Led {
  public:
    void on() {
      pc[8].reset();
    }
    void off() {
      pc[8].set();
    }
  private:
    GpioC<DefaultDigitalOutputFeature<8> > pc;
};

int main() {
  Led led;
  led.on();
  for(volatile int i=0;i<60000;i++); // kuzu
  led.off();

  while(1);
}

Ledクラスのメンバ変数としてpcを宣言した. これによってLedクラスがインスタンス化される時にGPIOCが初期化される.

ボタンによる入力

EXTIを用いたボタン入力の割り込み処理

Timing


内部の詳しい解説はstm32plus-Timing - STM32?を参照.

SysTickによるLED点滅

最も単純に時間を得る方法としてSysTickを用いることが多い.
stm32plusではSysTick自体は隠蔽されていて,timingという区分でRTCなどと一緒に実装されている.

LEDを点滅させるいわゆる「Lチカ」を行うために,SysTickを利用したdelay関数を使用してみる.

#include "config/stm32plus.h"
#include "config/gpio.h"
#include "config/timing.h"

using namespace stm32plus;

class Led {
  public:
    void on() {
      pc[8].reset();
    }
    void off() {
      pc[8].set();
    }
    void blinkOnce() {
      on();
      MillisecondTimer::delay(1000);
      off();
      MillisecondTimer::delay(1000);
    }
  private:
    GpioC<DefaultDigitalOutputFeature<8> > pc;
};

int main() {
  MillisecondTimer::initialise();

  Led led;
  while(1){
    led.blinkOnce();
  }

  return 0;
}

まず

MillisecondTimer::initialise();

でミリ秒精度のSysTickタイマを設定し,

MillisecondTimer::delay(1000);

で1000msのディレイを作り出している.


内部の詳しい解説はstm32plus-Timing - STM32?を参照.

<バリエーション>

  • 500msのディレイ
    MillisecondTimer::delay(500);

MicrosecondDelayを用いた高精度ディレイ

通常のタイマをマイクロ秒オーダーの高精度ディレイを実現するために使用する機能が実装されている.
MicrosecondDelayはデフォルトでTIM6を使用する設定になっているが,MicrosecondDelayTemplateを用いると,その他のタイマにこの機能を割り当てることができる.

<バリエーション>

  • TIM7を使う
    MicrosecondDelayTemplate<Timer7<Timer7InternalClockFeature> >::initialise();

RTCを用いた時刻の取得

デバイスないので未検証>

Timer

TimerはTimingとは異なりSTM32のタイマの機能をそのまま使うためのものである.
STM32のタイマの数と機能は非常に豊富かつ多彩で,それをオブジェクト指向的にかつ簡単に扱うことができるのもstm32plusの魅力である.


内部の詳しい解説はstm32plus-Timer - STM32?を参照.

Timerを走らせるだけ

PWM出力的なのを手動で書いてみた.

変なことをやっているが,これはTIMx->CNTを取得するための苦肉の策である.

重要なコードは以下の2つである.

timer.setTimeBaseByFrequency(5000, 6000,TIM_CounterMode_Up);

これは,タイマをカウントアップモードにし,5kHz(1秒間に5000回)でカウントし,6000回カウントしたら0にリセットするという設定をするコードである.
StdPeriphLibでいうTIM_TimeBaseInit関数にあたる.

もうひとつはこのコードである.

timer.enablePeripheral();

これは,タイマを有効化する関数で,StdPeriphLibでいうTIM_InitとTIM_Cmd関数にあたる.


内部の詳しい解説はstm32plus-Timer - STM32?を参照.

<バリエーション>

  • TIM7を使う
    Timer7<Timer7InternalClockFeature> timer;
  • 6000Hzでカウントアップする
    timer.setTimeBaseByFrequency(5000, 6000,TIM_CounterMode_Up);

コンペアマッチ出力

タイマの機能であるコンペアマッチ出力を使用して,カウンタがある値に等しくなったときにピンの出力を反転させるプログラムを書く.
STM32VLDiscoveryの青LED(PC8)は,ピン機能のリマップを使用するとTIM3のCH3に割り当てることができる.(データシート参照)

出力を反転させる処理はハードウェア的に行われるため,設定だけしてタイマを走らせれば勝手にLEDがチカチカする.

timer.initCompare(1000);

このコードによって,コンペアマッチに指定する値(と処理内容:デフォルトでは出力反転)を記述できる.


内部の詳しい解説はstm32plus-Timer - STM32?を参照.

<バリエーション>

  • PWM出力 PWM出力にすることも可能.
    timer.initCompare(1000,TIM_OCMode_PWM1);

PWMの出力

前項のバリエーションで記述した指定方法ではDuty比を変えるのが面倒である.
そこで,このような記法が用意されている.


内部の詳しい解説はstm32plus-Timer - STM32?を参照.

<バリエーション>

  • 初期Duty比を設定したい
  • PWM2モードを使いたい
  • 百分率ではなくカウント数を直接指定したい

Timerによる割り込み

TimerInterruptFeatureをテンプレート引数に追加して,割り込みハンドラをバインド(紐付け)する処理を行うだけで,割り込みの設定も記述することができる.

ロータリエンコーダを読む

STM32のタイマにはエンコーダインタフェース機能が存在するが,stm32plusではこれを使用するものは実装されていない.(要検討・実装)


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-01-17 (金) 02:59:20 (1256d)