HALを使うべきか使わざるべきか

HALとは Hardware Abstraction Layer  すなわちハードウェアを抽象化して、様々なハードウェアに同じプログラムでアクセスできるようにする仕組みのことです。

PCでおなじみのBIOSもHALの一種に入ります。

STMicroのSTM32CubeMXが生成するライブラリはHALライブラリと呼ばれていて、MPLAB-Xが生成するコードも似たようなもので、HALライブラリを使うとデータシートを見ながら周辺機能レジスタの値を設定する面倒な作業から解放されます。

HALを使わずにPWM出力をおこなう

PWMについて調べていて、HALを使わないでPWM出力をおこなうプログラムについて書いてある上の記事を見つけました。記事の主題は次のようなものです。

「HALを使わないでPWM設定プログラムを書いてみた。 基本的なペリフェラルならレジスタ手打ちで行くのが一流STM32erの流儀らしい(英文サイト参照)。でも、やはりSTM32CubeMXは便利。」

ここで紹介されている About STM32 HAL quality and performance – Stack Overflowを見るとHALに対して「わかりにくい、バグがある、オーバヘッドがある。」というコメントがありました。

STM32CubeMXが発表されて既に3年以上経ちます。当初は「わかりにくいし、バグだらけで使いにくい。」という意見を多く見かけましたが、最近になって安定してきたせいか、前向きの評価が増えてきたようです。

STMicroはSTM32CubeMXを発表する前に Standard Peripheral Library という名前のHALライブラリを提供していました。

Standard Peripheral Library も良く出来たライブラリでUSBやLANを使うためには欠かせず、使い込んだライブラリでしたが、新し物好きの私はSTM32CubeMXが出てすぐにHALライブラリに乗り換えました。

STM32CubeMX+HALライブラリの効果

私が組み込み用として使うのはstm32シリーズに決めていますが、アプリケーションによって最適なCPUが異なるのと、次々と高性能なstm32が発表されることからプロジェクトごとに新しいCPUボードを設計しています。

新規CPUボードのソフトウェア開発で一番時間がかかるのは、周辺機能の動作を一通り確認出来るまでの段階でしたが、STM32CubeMX+HALライブラリのおかげで、この段階は劇的に効率化されました。

確かにHALライブラリには「わかりにくい、バグがある、オーバヘッドが大きい。」というデメリットもありますが、開発の効率化とプログラムの標準化という大きなメリットに比べれば些細な問題に過ぎません。

HALライブラリはわかりにくい?

レジスタに値を設定する単純なプログラムに比べると、HALライブラリはとっつきにくいのは確かです。
ただ、初期化プログラムの開発で発生するトラブルは、データシートからは読み取りにくいレジスタ設定の抜けや設定の順番に起因するもので、USBやLANなどの複雑な処理では、解決までに試行錯誤で数週間を費やすことも珍しくはありませんでした。
HALライブラリを使うと、その無駄な試行錯誤をしなくて済むという効果が大きいのです。
つまり、プログラム表記としては複雑でわかりにくく見えますが、プログラム開発で行き詰まってしまうわかりにくさは大幅に減ったといえます。

HALライブラリのバグ

実際のところHALライブラリのバグで悩まされた経験は殆どありません。
HALライブラリは多くの人が使っていて、不審な動作に出会ったときは検索するとたいてい誰かが情報を発信しているので回避策を素早く見つけることが出来るためです。
コンパイラやライブラリのバグを見つけて鬼の首を取ったように非難している人がたまにいますが、よほど枯れたシステムでない限りバグフリーのコンパイラやライブラリはあり得ないので、バグを上手く回避して使うのがコツです。

気にならないHALライブラリのオーバヘッド

民生分野でなく、開発効率化優先で豊富なハードウェアリソースを使うようにしている私の経験からはHALライブラリのオーバヘッドが気になったことはありません。
最近のCPUはワンチップの中にLinuxを組み込めるのではないかと思うほどFlashもRAMサイズも大きくなっていて、もともとコードサイズの小さい制御プログラムを書く上では、コードサイズの肥大は無視できるレベルです。

必要以上にCPUの性能が高くなっているのに加えてDMAなど周辺ハードウェアだけでこなせる処理が増えているため、HALライブラリのオーバヘッドによる処理速度の低下に悩まされることも無くなりました。

初期化時に実行するだけのHALライブラリがほとんどで、繰り返し実行する部分は単純なレジスタ処理だけで済むこともあります。

HALライブラリを使わないプログラムは勉強になる

プログラムのベテランなら積極的にHALライブラリを使うべきだと思いますが、HALライブラリの中で行われていることを全く理解していないと、問題にぶち当たったときに全く前に進めない場合があります。

HALライブラリのレジスタに値を設定している部分を参考にして、レジスタに直接値を設定してみて、HALライブラリの処理を理解するのは解りやすく制御プログラミングのトレーニングとしても大いに役にたちます。

開発環境の進化はプログラミングを簡単にする

高校生モノづくりコンテスト電子回路部門は、課題プログラムに使うCPUボード、プログラミング言語と開発環境を自由に選ぶことが出来ます。

組み込み用CPUの性能と開発環境は、十年二昔の勢いで進歩していて、どのCPUや開発環境を選ぶかがコンテストの成績に大きく影響します。
野球では金属と木のバットで飛距離が違い、高校野球で使える金属バットがプロ野球で使えなかったりしますが、ものづくりコンテストのプログラムで使う開発環境の影響はそれより桁違いに大きいものです。

ですから、モノづくりコンテストにチャレンジするにあたって、開発環境として何を選ぶかが重要になります。

開発環境の進化がもたらすもの

開発環境の進化がもたらすものはプログラミングの効率化です。
開発環境が一世代進化するごとに、少ないプログラムサイズで楽に高度な処理が書けるようになってきました。

例えば、高校生モノづくりコンテスト全国大会の課題は、トップクラスのプログラミング能力を持つ生徒が、平均的な開発環境を使って、制限時間内に解くのが難しいレベルに設定される傾向があります。
課題を解くにはプログラムを書く能力だけではなく、課題を解くアルゴリズムを考える能力も必要ですが、平均的な開発環境の半分の時間でプログラムが書ける開発環境を使うのと、平均の倍の時間がかかる旧世代の開発環境を使うのでは大きな差があります。

CPU初期化プログラムの効率化

最近の組み込み用CPUは高度な周辺機能を内蔵して、高性能になった反面、周辺機能を初期化するために複雑なレジスタ設定が必要になっています。
そして、わかりにくいデータシートを見ながら、数多いCPUと接続するハードウェアに合わせて、ひとつの間違いも無くレジスタに設定する値を調整するまではCPUは正常に動作しません。
これが、組み込みプログラムが難しいと思われる理由のひとつになっていて、Arduinoはこの部分を徹底的に標準化することで、誰でも簡単に組み込みプログラムが始められるようにすることに成功しました。

CPUの性能を最大限に発揮する必要がある本格的な組み込みプログラムの分野では、標準化による初期化プログラムの簡略化という手法を取ることは出来ませんでしたが、最近になっていくつかの組み込みCPUメーカーから「GUI画面で周辺機能パラメータを設定し、自動的に初期化プログラムを生成する。」という手法を使った開発環境が提供されて、実用的レベルになってきました。

MPLAB-Xに内蔵されたMCCがそのひとつで、MCCを使えばデータシートを見なくても簡単に初期化プログラムが完成します。
簡単とはいっても周辺機能に関する知識は必要ですが、周辺機能とレジスタの対応を考えなくても初期化が出来るようになったのは非常に大きな進歩と言えます。

sw4stm32でOpenOCDを使う(たぶんOK?)

自作のJTAG書き込みツール oocdJTAG を使ってsw4stm32とOpenOCDでデバッグ、書き込みが出来るようになりました。
タイトルの たぶんOK? というのはeclipseの設定がややこしいので再現性に自信がないからです 8-)

とりあえず文章だけで手順をメモしておきます。
sw4stm32は英語バージョンのままです、日本語化して使っている人はメニューの部分を読み替えてください。

OpenOCDのバージョン

sw4stm32のバージョンはinstall_sw4stm32_win_64bits-v2.4.exeです。
OpenOCDはsw4stm32に含まれているものでOKでした。

oocdJTAG

oocdJTAGはftdiのft2232Dを使ってJTAG書き込みとシリアル通信が出来るようにしたJTAG書き込みツールです。
書き込みだけならSTLinkを使えば簡単に出来ますがデバッグ時にシリアルポートが使えると何かと便利なので自製書き込みツールを使っています。

oocdJTAGを使えるようにするための手順

oocdJTAG用定義ファイルをコピー

下記の定義ファイル2つを次のディレクトリにコピーします
Ac6\SystemWorkbench\plugins\fr.ac6.mcu.debug_2.1.4.201801121207\resources\openocd\st_scripts\interface
このでぃくとりはシステム環境変数を使うと ${openstm32_openocd_script_root_path}\interface となります

oocdjtag-ftdi.cfg

#
# oocdJTAG
#

interface ftdi
ftdi_device_desc "oocdJTAG"
ftdi_vid_pid 0x0403 0x6010
transport select jtag

ftdi_layout_init 0x0c08 0x0f1b
ftdi_layout_signal nTRST -data 0x0100 -noe 0x0400
ftdi_layout_signal nSRST -data 0x0200 -noe 0x0800

oocdjtag-swd-ftdi.cfg

#
# oocdJTAG
#

interface ftdi
#interface ft2232
ftdi_device_desc "oocdJTAG"
ftdi_vid_pid 0x0403 0x6010
transport select swd

ftdi_layout_init 0x0c08 0x0f1b
ftdi_layout_signal nTRST -data 0x0100 -noe 0x0400
ftdi_layout_signal nSRST -data 0x0200 -noe 0x0800

source [find interface/ftdi/swd-resistor-hack.cfg]
#source [find interface/ftdi/swd-resistor-hack.cfg]
#
# Connect TDI to SWDIO via a suitable series resistor (220-470 Ohm or
# so depending on the drive capability of the target and adapter);
# connect TDO directly to SWDIO.
#
# You also need to have reliable GND connection between the target and
# adapter. Vref of the adapter should be supplied with a voltage equal
# to the target's (preferrably connect it to Vcc). You can also
# optionally connect nSRST. Leave everything else unconnected.
#
# FTDI                          Target
# ----                          ------
# 1  - Vref   ----------------- Vcc
# 3  - nTRST  -
# 4  - GND    ----------------- GND
# 5  - TDI    ---/\470 Ohm/\--- SWDIO
# 7  - TMS    -
# 9  - TCK    ----------------- SWCLK
# 11 - RTCK   -
# 13 - TDO    ----------------- SWDIO
# 15 - nSRST  - - - - - - - - - nRESET
#

ftdi_layout_signal SWD_EN -data 0

ボード定義ファイルの作成

ProjectExplorerでプロジェクトを右クリックしてDebug Asを実行すると [プロジェクト名 Debug]という名前のファイルがプロジェクトディレクトリに作成されるのでこれをベースにボード定義ファイルを作成します。

ボード定義ファイルの修正

source [find interface/stlink.cfg] 
の部分を
source [find interface/oocdjtag-swd-ftdi.cfg]
に置き換えます

# This is an Lantern board with a single STM32F051C8Tx chip
#
# Generated by System Workbench for STM32
# Take care that such file, as generated, may be overridden without any early notice. Please have a look to debug launch configuration setup(s)

#source [find interface/stlink.cfg]
source [find interface/oocdjtag-swd-ftdi.cfg]

set WORKAREASIZE 0x2000
...

ボード定義ファイルを 指定する

Debug Configurations の DebuggerタブでConfiguration scriptの設定を User Definedに変更して修正したボード定義ファイルを指定します。

以上でoocdJTAGボードでデバッグとプログラム書き込みが使えるはずです。