STM32F10x Standard Peripherals Firmware Library: Overview

出典: Wikimura

「STM32F10x標準周辺回路ファームウェアライブラリ概要」の読めるとこだけ適当に読んだ訳。丁寧には書かない。

STM32F10x Standard Peripherals Firmware libraryは以下の3つの抽象化レベルを持つ。

C言語で宣言されたビット・ビットフィールド・レジスタ群からなる完全なレジスタマップ
これにより面倒な仕事が不要になる。更に重要なことに、バグのないレジスタマップにより、開発の初期段階を高速化できる。
全てのペリフェラル機能を網羅したルーチン及びデータ構造群
リファレンスフレームワークとして使える。マクロによる、コアの内部機能への対応(多分コアの機能...C言語でコンパイルできない機械語のマクロ関数化とか)、一般的な定数やデータ定義がある。
利用可能な全てのペリフェラルに関するサンプルと、主なツールチェインに対応したプロジェクトテンプレート
適当なハードウェア評価ボードさえあれば、新しいプロセッサをすぐ使い始めることができる。


各ドライバは、ペリフェラルの全ての機能をカバーする関数からなる。 ドライバーは、構造体、関数、パラメータ名を標準化する共通のAPIになるよう開発された(命名規則や考え方が統一されていると言うこと)。 ドライバのソースコードは「Strict ANSI-C」で開発された(サンプルプロジェクトは緩い?ANSI-C)。 これ(Strict ANSI-C?)は完全に文書化されており、MISRA-C 2004準拠している。 「Strict ANSI-C」で書かれているため、ツールチェインに依存せず使用することができる。 ただし、スタートアップファイルのみツールチェインに依存している。

Standard Peripherals Firmware libraryrun-time failure detectionを実装している。 (ライブラリ関数への引数をチェックすることで、実行時に変なことが起こるのを検出する機能) この動的なチェックにより、ソフトウェアのロバスト製を高めることができる。 実行時検出は、ユーザアプリケーションの開発・デバッグに適している。 この機能はオーバーヘッドを生ずるが、最終的なコードから除外することが可能。結果として、コードサイズと実行速度を最小化することが可能。詳しくはRuntime-checkingの節を参照されたい。

Standard Peripherals Firmware libraryは一般化されており、全ての機能を網羅しているものの、 アプリケーションのサイズと実行速度は最適化されない。 多くのアプリケーションではそのまま使うことができるだろう。 しかし、コードサイズや実行速度にきつい制約があるアプリケーションの場合、ライブラリのドライバは、ペリフェラル設定のリファレンスとして使うに留め、用途に合わせて調整するべきである。

注釈:サイズと実行速度で見たアプリケーションコードの性能は、Cコンパイラの最適化設定に依存する。 アプリケーションをより小さく、より速く、またはこれらを両立するには、ニーズに合わせて最適化設定を行う必要がある。詳しくはCコンパイラのドキュメントを参照されたい。

目次

対応するSTM32F10xデバイス

「StdPeriph_Lib」は全ての「STM32F10xxxファミリ」製品に対応している。High/Medium/Low-density全てのデバイスに対応している。 「StdPeriph_Lib」はプロセッサ定義により全てのファミリの製品にコンフィギュレーション可能となっている。(High/Medium/Low毎にレジスタ構成が違うので、対応するためにマクロ定義しろと言う話) マクロ定義は以下の製品について利用可能。(STM32F10x_LDというマクロを定義すれば、Low-density用になる)

  • STM32F10x_LD: STM32 low-density devices
  • STM32F10x_MD: STM32 medium-density devices
  • STM32F10x_HD: STM32 high-density devices


STM32F10x Standard Peripherals Firmware libraryは全てのサブファミリ(デバイス)に対応し、このライブラリを使用することで、あるSTM32F10x用のアプリケーションを別のデバイスへすぐさま移行することも可能。 ユーザはデバイス選択を、stm32f10x.hのプリプロセッサ宣言(#define)を書き換えることで行える。 これにあわせてライブラリが構成される。 (stm32f10x.hに書かれている通り、個のファイルを直接編集せずに済むよう、プリプロセッサでdefineしておくこと)

stm32f10x.h

...

/* Uncomment the line below according to the target STM32 device used in your
   application 
  */

#if !defined (STM32F10X_LD) && !defined (STM32F10X_MD) && !defined (STM32F10X_HD)
  /* #define STM32F10X_LD */   /*!< STM32 Low density devices */
  /* #define STM32F10X_MD */   /*!< STM32 Medium density devices */
  #define STM32F10X_HD   /*!< STM32 High density devices */
#endif
/*  Tip: To avoid modifying this file each time you need to switch between these
        devices, you can define the device in your toolchain compiler preprocessor.

 - Low-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers
   where the Flash memory density ranges between 16 and 32 Kbytes.
 - Medium-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers
   where the Flash memory density ranges between 64 and 128 Kbytes.
 - High-density devices are STM32F101xx and STM32F103xx microcontrollers where
   the Flash memory density ranges between 256 and 512 Kbytes.
  */

...  


このdefineは、以下のStandard Peripherals Libraryをコントロールする。

  • IRQチャネル定義
  • 周辺回路メモリマップ、レジスタのアドレス定義
  • 周辺回路のポインタ宣言(?)、ヘッダファイルのインクルード
  • その他の細かな調整(発振周波数など)
  • ファミリ間で異なる・互換性のない機能

注:このdefineは周辺回路ドライバには作用しない。これらのドライバはファミリのスーパーセットに対応している。(一番高機能なものに対応と考えてよさそう?部分的に使えないものがあるだけ)


Description of the STM32F10x Standard Peripherals Library

STM32F10x standard peripheral libraryのアーキテクチャは、CMSISレイヤサポートの拡張?

STM32F10x standard peripheral libraryの使い方は、アプリケーションのニーズを配慮した2つのアプローチに基づいている。

  • ペリフェラルドライバを使う: ドライバのAPIを使う場合
  • ペリフェラルドライバを使わない: レジスタ構造とビット定義ファイルを用いる場合


STM32F10x CMSIS ファイルの説明

表にSTM32F10x CMSISファイルについて記述する。

File name Description
stm32f10x.h CMSIS Cortex-M3 STM32F10xxxデバイス用ペリフェラルアクセスレイヤのヘッダファイル。

このファイルはプログラマがCソースコード(大抵はmain.c)において唯一インクルードするファイル。 このファイルは、

  • 全てのペリフェラルの構造体とアドレスマップ
  • 全てのレジスタ宣言とビット定義
  • ペリフェラルのレジスタへアクセスするマクロ
  • 以下を選択するためのコンフィギュレーション
    • ターゲットアプリケーションで使用するデバイス
    • ペリフェラルドライバを使用するか否か(つまりAPIを使うか、そうせずにレジスタを直に叩くか)...この選択は、#define USE_STDPERIPH_DRIVERを書くかどうかで決める。
system.stm32f10x.h CMSIS Cortex-M3 STM32F10xxx device peripheral access layer system header file. system.stm32f10x.c CMSIS Cortex-M3 STM32F10xxx device peripheral access layer system source file.
startup_stm32f10x_hd.s/.c High-densityデバイス用スタートアップファイル
startup_stm32f10x_md.s/.c Medium densityデバイス用スタートアップファイル
startup_stm32f10x_ld.s/.c Low-densityデバイス用スタートアップファイル

詳しくはCMSISドキュメントを参照されたい。

STM32F10x Standard Peripherals Driverファイルの説明

各ペリフェラルは、ソースコードファイルstm32f10x_ppp.cとヘッダファイルstm32f10x_ppp.hを持っている。stm32f10x_ppp.cはペリフェラルPPPに必要な関数を含んでいる。stm32f10x.hは単一のメモリマップファイルで、全てのペリフェラルのレジスタ宣言、ビット定義が書かれている。stm32f10x.hは、ユーザアプリケーションのライブラリとのインターフェースとして、唯一インクルードしなくてはならないファイル。 stm32f10x_conf.hはアプリケーション起動前にドライバのパラメータ群を指定するためのファイル(?)。

以下の表は、Standard Peripherals libraryで使われるファイルについて示す。 The table bellow lists and describes the different files used by the :

File name Description
stm32f10x_conf.h ペリフェラルドライバ設定ファイル。

アプリケーション起動前にドライバが使うパラメータ群を定義するために、ユーザが手を加えなくてはならないファイル。 ユーザはヘッダファイルのインクルードを行うか否かについて、テンプレートから指定できる。 また、特定のアプリケーションでは、水晶の周波数なども指定する。 このファイルでは、ファームウェアライブラリドライバ(色々呼び名が変わる...)をコンパイルする前に、Run-time failure detectionを有効にするか否かを設定することができる。

stm32f10x_ppp.h

ペリフェラルPPPのヘッダファイル。 このファイルはペリフェラルPPPの関数と変数定義を行う。

stm32f10x_ppp.c C言語で書かれたペリフェラルPPPドライバのソースコード。
stm32f10x_it.h 全ての割り込みハンドラのプロトタイプを含むヘッダファイル。
stm32f10x_it.c 割り込みベクタファイルを含むソースファイル。

ファームウェアライブラリファイルのインクルード関係は以下の図のようになる。 (main.cstm32f10x_it.cはそれぞれでstm32f10x.hをインクルードするために、後の実行時チェック設定をstm32f10x_conf.hで行うらしい)

ファイルの関係

ペリフェラルの初期化と設定

ここではペリフェラルの初期化と設定について1つずつ見ていく。 ここでは、ペリフェラル名をPPPとする。

アプリケーション内で構造体PPP_InitTypeDefを宣言する(宣言と定義の使い方が間違っている気がするが...ここは原文まま) 。 ここで、変数PPP_InitStructureは、データメモリ領域に配置される。 これにより、1つ以上のPPPインスタンスを初期化することができる。

PPP_InitTypeDef PPP_InitStructure;

メンバに有効な値を代入することで、変数PPP_InitStructureを埋める。これには2つのやり方がある。

全てのメンバを1つずつ代入するやり方。
PPP_InitStructure.member1 = val1;
PPP_InitStructure.member2 = val2;
PPP_InitStructure.memberN = valN; /* where N is the number of the structure members */

上記と同じことをまとめるやりかた。
PPP_InitTypeDef PPP_InitStructure = { val1, val2,.., valN}

ごく一部のメンバを設定したいだけなら、関数PPP_StructInit(..)で既に値を埋めてある変数PPP_InitStructureに手を加えればよい。これにより、変数PPP_InitStructureの他の値は適当な値(大抵はデフォルト値)で初期化される。

PPP_StructInit(&PPP_InitStructure);
PP_InitStructure.memberX = valX;
PPP_InitStructure.memberY = valY; /*where X and Y are the members the user wants to configure*/

ペリフェラルPPPの初期化は、関数PPP_Init(..)を呼び出す。

PPP_Init(PPP, &PPP_InitStructure);

この段階で、ペリフェラルPPPは初期化される。更に、関数PPP_Cmd(..)により有効化される。

PPP_Cmd(PPP, ENABLE);

これでペリフェラルPPPは関数から機能を使用することができるようになる。 そのための関数はペリフェラル固有である。

注: ペリフェラルを設定する前に、以下に示す関数のいずれかでクロックを有効化しなくてはならない

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_PPPx, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_PPPx, ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PPPx, ENABLE);


関数PPP_DeInit(..)は、ペリフェラルPPPのレジスタにデフォルト値をセットする。

PPP_DeInit(PPP);

ペリフェラルの設定後、設定の変更をしたい場合は以下の手順で行うこと。

PPP_InitStucture.memberX = valX;
PPP_InitStructure.memberY = valY; /* where X and Y are the only members that user wants to modify*/
PPP_Init(PPP, &PPP_InitStructure);


Bit-Banding

(bit-bandとは、ビットをバイトとしてアクセスすることを可能にする機能のことらしい。エイリアス領域とbit-band領域という言葉が出てくる) Cortex-M3メモリマップは、2つのbit-bandメモリ領域を持つ。 この領域は、エイリアス領域の各ワードを、bit-band領域のビットに対応させる。 エイリアス領域への書き込みは、bit-band領域へのread-modify-write動作と同等である。

The Cortex-M3 memory map includes two bit-band memory regions. These regions map each word in an alias region of memory to a bit in a bit-band region of memory. Writing to a word in the alias region has the same effect as a read-modify-write operation on the targeted bit in the bit-band region.

STM32F10xの全てのペリフェラルレジスタは、bit-band領域に配置されている。そのためこの機能は、コードサイズを削減し最適化するために、シングルビットのセット・リセットを行う関数においてよく用いられる。

All the STM32F10x peripheral registers are mapped in a bit-band region. This feature is consequently intensively used in functions which perform single bit set/reset in order to reduce and optimize code size.

実行時チェック

ファームウェアライブラリは、全てのライブラリ関数において入力値をチェックすることで、実行時故障検出(run-time failure detection)を実装している。実行時チェックは、マクロassert_paramにより行われている。このマクロは1つ以上のパラメータをとる全てのライブラリ関数で使われている。これにより、ユーザは入力値が定義されたパラメータ値であるかチェックすることが可能となっている。

関数EXTI_Initの例:

stm32f10x_exti.c----------------------------------------------------------------
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
{
  /* Check the parameters */
  assert_param(IS_EXTI_MODE(EXTI_InitStruct->EXTI_Mode));
  assert_param(IS_EXTI_TRIGGER(EXTI_InitStruct->EXTI_Trigger));
  assert_param(IS_EXTI_LINE(EXTI_InitStruct->EXTI_Line));  
  assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct->EXTI_LineCmd));
...
}

stm32f10x_exti.h----------------------------------------------------------------
#define IS_EXTI_MODE(MODE) (((MODE) == EXTI_Mode_Interrupt) || ((MODE) == EXTI_Mode_Event))
#define IS_EXTI_TRIGGER(TRIGGER) (((TRIGGER) == EXTI_Trigger_Rising) || \
                                  ((TRIGGER) == EXTI_Trigger_Falling) || \
                                  ((TRIGGER) == EXTI_Trigger_Rising_Falling))

#define IS_EXTI_LINE(LINE) ((((LINE) & (uint32_t)0xFFF80000) == 0x00) && ((LINE) != (uint16_t)0x00))

マクロassert_paramに渡された式がfalseであった場合、関数assert_failedが呼ばれ、それ以外では何も起きない。

関数assert_failedの実装は2つ提供されている。どちらを使うかはstm32f10x_conf.hUSE_FULL_ASSERTを定義するか否かで決める。

  • "#define USE_FULL_ASSERT 1"がコメント解除されている場合: マクロassert_paramへ渡される値がfalseのとき、assert_failed関数は、ソースファイル名エラーの起きた行を受け取る。
  • "#define USE_FULL_ASSERT 1"がコメントされる場合、マクロassert_paramへ渡される値がfalseでも、assert_failed関数は呼び出されず、ソースファイル名エラーの起きた行は返されない?(なんか原文が読めない)

(原文では2つ目がFULL_ASSERTがコメントされる場合と書いてあったが、多分USE_が抜けていると思い、追加した。stm32f10x_conf.hのある行のことだと思う。)

マクロassert_paramstm32f10x_conf.hで以下のように実装される。

#ifdef  USE_FULL_ASSERT
/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param expr: If expr is false, it calls assert_failed function
  *   which reports the name of the source file and the source
  *   line number of the call that failed. 
  *   If expr is true, it returns no value.
  * @retval : None
  */
  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
   void assert_failed(uint8_t* file, uint32_t line);
#else
   #define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */

関数assert_failedはユーザが作るmain.cやその他のCソースファイルで実装する。これらはエラー発生時にユーザが行動がとれるよう手を加えることが可能である。

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param file: pointer to the source file name
  * @param line: assert_param error line source number
  * @retval : None
  */
void assert_failed(uint8_t* file, uint32_t line)
{ 
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

実行時チェックはアプリケーション開発・デバッグ段階において使用し、 完成版からは取り除くことで、コードサイズと速度を向上することが推奨される(この機能はオーバーヘッドを生ずるため)。しかし、この機能を最終版に残しておくことも可能である。


コメント

  • Bit-bandingに関する記述を追加しました。ビットの操作をマスキングやビットセット・クリア命令なしで行えるので便利そうです。ポインタのインクリメントで1ビットずつ走査することもできるでしょう。ソフトウェアモデムを作る際、1ビットずつ走査する処理が必要になるので、これは役立ちそうです。
個人用ツール