STM32F7 Discovery でOpenOCD+gdbによるLチカ
近いうちにOpenOCDを使う用件があるので、練習のためにSTM32F7 Discoveryで遊んでみました。 初心者向けに書きましたが、執筆時間の時間の都合で以下の事項は既習としてます。
静電容量式のタッチディスプレイやSDカードスロット、オーディオI/Fなどがついてて8000円と良心的な値段設定です。
僕の手元にあるものは、ETという展示会(宇宙人はいませんが、昨年はR2-D2がいました?!)でSTマイクロエレクトロニクスのワークショップ的なものに参加してもらったものです。
来月にETやりますし、きっと似たような方法でもらえるかもしれません。
# ワークショップは有償開発環境の1日限定ライセンスのもとで実施されました
デバッグポートはST-Linkというもので、なんとUSBケーブル一本でデバッグできちゃいます。 組み込み特有のジャンパーコードやらきしめんみたいなリボンケーブルが必要ありません。 経済的で、初心者に優しいですね! 同じボードを用意できない場合は、Nucleoというボードが3000円くらいなので、そちらの方がお求めやすいかもしれません。
本エントリーでは、OpenOCDというデバッグ環境を用意した後、OpenOCDでターゲット(デバックするボードのこと)に接続し、 gdbでOpenOCDにアタッチし、手動でLチカするという内容でお送りします。
OpenOCDのビルド
詳細はそこまで知りませんが、OpenOCDはOpen On-Chip-Debuggerの略で、 JTAGデバッガなどを通じて、ホストPCからCPUが入っているマイコンに直接命令できるようなシステムです。 組み込み開発で有償開発環境(IAR Embedded Workbenchとか)を用意できない場合、gdbと合わせて使われるソフトウェアとして名前が上がるような代物です。 はじめに書きますが、100%ちゃんと動くことを期待しちゃダメです。気楽に使いましょう。
OpenOCDのビルドは比較的簡単です。 ダウンロード先はsourceforgeとgit(github)の2通りあります。 基本的にどちらでも良いですが、githubに上がっている方が良いかもしれません。 というのも、OpenOCDではJTAGデバッガやターゲットの設定を書いたスクリプトファイル(cfgファイル)が付属しているのですが、 github版のほうがそのファイルが豊富です。
OpenOCDのビルドは、基本的なビルド環境が揃っていることを前提に、以下のパッケージを必要とします。 ビルド前にaptとかで入れてください。
- pkg-config
- libusb-1.0*
さらに、gccのバージョンが重要で、4系でmakeが通ります。
色々考えるのが面倒なので、僕はVMのREMNuxでmake&&make install
してから、ホストにインストールしました。
以下は、ダウンロード済みのソースをビルドするためのコマンドです。
./bootstrap # git版のみ必要。configureが生成される ./configure --enable-ftdi # ftdiチップ関連の機能を有効にする ### configureにより、ftdiの項目がyesになっていることを確認する make -j4 sudo make install
OpenOCDでターゲットに接続
OpenOCDでは、コマンドラインオプション-f
で、ターゲットやハードウェアの方のデバッガに対応したcfgファイルを与えねばなりません。
バージョン0.9時点では、それらはtclというディレクトリの中に入っています。
注意すべきことは、-f
で与えたパスが深すぎると依存関係を解決出ないという理由でエラーが出ます。
以下のように、-s
オプションでベースディレクトリを教えるとうまく動いてくれます。
(まあカレントディレクトリにcfgをコピるのが一番楽ですがね)
root権限のあるシェルで実行しないと Error: libusb_open() failed with LIBUSB_ERROR_ACCESS
と出て動きません
openocd -s tcl -f board/stm32f7discovery.cfg
うまく接続するとこんな感じになります。
[root@K_atc openocd-git]# openocd -s tcl -f board/stm32f7discovery.cfg Open On-Chip Debugger 0.10.0-dev-00411-g607edef (2016-11-05-14:18) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD adapter speed: 2000 kHz adapter_nsrst_delay: 100 srst_only separate srst_nogate srst_open_drain connect_deassert_srst Info : Unable to match requested speed 2000 kHz, using 1800 kHz Info : Unable to match requested speed 2000 kHz, using 1800 kHz Info : clock speed 1800 kHz Info : STLINK v2 JTAG v28 API v2 SWIM v16 VID 0x0483 PID 0x374B Info : using stlink api v2 Info : Target voltage: 3.201043 Warn : Silicon bug: single stepping will enter pending exception handler! Info : stm32f7x.cpu: hardware has 8 breakpoints, 4 watchpoints
gdb起動
arm版のgdbを忘れずに用意します。
arm-none-eabi-gdb
あたりがパッケージマネージャー使えば入るはずです。
OpenOCDがlocalhostの3333版ポートでgdbからのアタッチを待っています。
target remote :3333
で会いに行きましょう〜
# -x
は今は無視で
[katc@K_atc jtag]$ arm-none-eabi-gdb -q -x stm32f7.gdb /home/katc/.gdbinit:1: Error in sourced command file: future__ import absolute_import:8: Error in sourced command file: Undefined command: "from". Try "help". 0x00000000 in ?? () (gdb) target remote :3333 Remote debugging using :3333 0x00000000 in ?? () (gdb) monitor targets TargetName Type Endian TapName State -- ------------------ ---------- ------ ------------------ ------------ 0* stm32f7x.cpu hla_target little stm32f7x.cpu running (gdb)
手動Lチカ
gdbを使ってLチカさせていきましょう。
[datasheet]のBlock Diagramと[manual]を見ると、LD1というLEDがPI1に、PI1はGPIOI[1]につながっていることが分かります。
# このボードはArduinoのシールドとしても使えるらしいです。LD1はArduinoから操作することを考慮しているように見えますね。
[datasheet]を見ると、GPIOIはメモリアドレス0x4002 2000 ~ 0x4002 2fffにマッピングされており、 [2]によると、0x40022000をベースアドレスとして、オフセット0にGPIOIのモードを変更するレジスタ(MODER)と、オフセット0x14に出力値を入れるレジスタ(ODR)があることが分かります。
GPIOでは、ポートで外部入力を期待するのか外部出力するのか、またはその他(めいんどいので省略)なのかを特定のレジスタに教えることになります。 今回はそのレジスタはこのMODERというレジスタです。 [2]によると、今回はLEDを操作したいのでポートは出力モードで、そのためにはGPIOI[1]のモード設定でMODERの[3:2](2ビットから3ビット)を1にすれば良いことが分かります。
[2]を見る限り、とりまPI1に1を送るにはODR[1]に1をセットすれば良さそうです(←プルアップされているかどうかで話が変わりうる。結果としては1がLEDのONを意味する)
([2]にプルアップやプルダウンの文字がありますが、見なかったことにします。 0か1でLEDが点く話ですからね。)
以上より、LEDに関する初期化とLEDをon/offするコード、果てはLチカするユーザー関数のコードは次のgdbスクリプトに落ち着きます。
(ついでに、アタッチとターゲットのリセットを行います)
target remote :3333 monitor reset ### wait for target shell sleep 2 set $GPIOI=0x40022000 set $GPIOI_MODER=$GPIOI+0x0 set $GPIOI_ODR=$GPIOI+0x14 ### init led port # MODER[2:3] = 2 (General purpose output mode) set *(int *)$GPIOI_MODER=*$GPIOI_MODER|0x4 def -ld1-on # ODR[1] = High set *(int *)$GPIOI_ODR=*$GPIOI_ODR|1<<1 end def -ld1-off # ODR[1] = Low set *(int *)$GPIOI_ODR=*$GPIOI_ODR&~(1<<1) end def -LD1 printf "SIGINT (Ctrl+C) to quit..." while(1) -ld1-on shell sleep 0.5 -ld1-off shell sleep 0.5 end end
Lチカコマンドこと-LD1
を実行すると無限ループの中で0.5秒毎にLD1が明滅します。おわり
[katc@K_atc jtag]$ arm-none-eabi-gdb -q -x stm32f7.gdb /home/katc/.gdbinit:1: Error in sourced command file: future__ import absolute_import:8: Error in sourced command file: Undefined command: "from". Try "help". 0x00000000 in ?? () (gdb) -LD1 SIGINT (Ctrl+C) to quit...^CQuit (gdb)
おまけ:OpenOCDのこんなときは
ターゲットをリセットしたい
ボードのリセットボタンを押すか、gdb(3333番ポート)からmonitor reset
を送るか、telnet/nc(4444番ポート)からreset
を送ります。
ターゲットをhaltしたい。haltをrunningに戻したい
gdbでの方法の説明に絞ります。
halt
にするとき:monitor halt
running
に戻したいとき: monitor reset run
(gdb) monitor halt stm32f7x.cpu: target state: halted target halted due to debug-request, current mode: Thread xPSR: 0x61000000 pc: 0x0803981c psp: 0x20007658 (gdb) monitor targets TargetName Type Endian TapName State -- ------------------ ---------- ------ ------------------ ------------ 0* stm32f7x.cpu hla_target little stm32f7x.cpu halted (gdb) monitor reset run (gdb) monitor targets TargetName Type Endian TapName State -- ------------------ ---------- ------ ------------------ ------------ 0* stm32f7x.cpu hla_target little stm32f7x.cpu running
# monitor reset run
だと一回リセットが入るみたいだし、haltから再開する方法は無いのかな?
参考文献
- [datasheet] STM32F7 Datasheet
- [manual] Discovery kit for STM32F7 Series with STM32F746NG MCU User Manual
- [1] STM32F7 Discovery の開発環境 (2)
- [2] Understanding the STM32F0's GPIO part 1
ところで抵抗とコンデンサとLEDをラベルを見ずに外見だけで見分けるいい方法無いですかね…