C++を使うという事は?
あれこれと考慮した結果、C++
を使います。理由はC言語
とアセンブリ言語
に関しては、既に多数の解説があるからです。
しかし悲しい事に、組み込み開発でC++
を利用しようとすると、猛烈に怒り出す方々が一定数います。これも良く考えると、そもそも本記事が業務用途(費用対効果を考えてメモリ容量の削減等の制約があります)を考慮する必要はないですし、ホビー用途でそんな事を言われる筋合いもありません。
AVR
は小規模なMCU
である事に加え、ハードもソフトも必要ならば全て自前で揃える事が出来るので、学ぶには最適な環境だと思っています。なので本記事では、C++
を使うメリットと、何故C++
を使うと怒られるのかを学ぼうと思います。
そうと決まったので、Atmel Studio 7.0
に含まれるC++
の機能を調査した所、大まかに以下の様な結果になりました。
GCC 5.4.0
C++ 14
まで対応(実験的実装であるC++ 17
を除く)libstdc++
がない注:
GCC
の最新(開発中を除く)は、現時点(2020/03/07)で9.2.0
であり、これはC++ 17
まで対応(実験的実装であるC++ 20
を除く)
avr-libc 2.0.0
C++
用の機能対応が不十分- 全くメンテナンスしてないと思われる機能が存在
- ソース
avr-libc-2.0.0.tar.bz2
にパッチを適用しようとすると、エラーが出る箇所がある
プログラミング言語は常に最新の仕様を使いましょう、がモットーの私としては、このままでは問題点だらけです。
C++ 17
を使いたいC++
の本当に使いたい機能(type_traits等)の殆どはlibstdc++
の上に成り立っているavr-libc
を用いずに、共通ランタイム(crtxxxx.o)等を記述する自信も時間もない
これらを解決する為に、GCC 9.2.0
とそのlibstdc++
を含む最新ツールチェーンを自分で作成して、このツールチェーンをAtmel Studio 7.0
に読み込ませる事で対応しました。
作成プラットフォームはmsys2
で行い、ここで以下のツール・ライブラリ等を作成しました。
winpthreads-1.0
(mingw-w64-v7.0.0
に含まれています)binutils-2.33.1
gcc-9.2.0
(AVR
用libstdc++
対応の改造バージョン)avr-libc-2.1.0
(2.0.0
ソースへのパッチ適用を諦め、最新subversion
リポジトリから取得したソースに対するlibstdc++
対応の大改造バージョン)gdb-8.3.1
(Atmel Studio 7.0
で使うだけなら不要)avrdude-6.3
(Atmel Studio 7.0
で使うだけなら不要)avrtest
(winavr
に含まれています。Atmel Studio 7.0
で使うだけなら不要)
記事のコンセプトを練るのに約1年、加えてここまでの環境を構築するのに半年以上を費やす事となり、進捗が大幅に遅れました。加えて、今後も更に問題が発覚した場合、その都度ツールチェーンを再構築する必要があります。2020/03/07現在、作成したlibstdc++
の全ての機能が全てのAVR
で動作するかは、確認していません(というか出来ません)。また AVR
で利用出来ないlibstdc++
の機能をどうするか? については、libstdc++
のソースを出来るだけ改変しない様に、外部で対応する事にしました。具体例として、以下の様に対応しています。
avr-libc
include/unistd.h
read
関数(extern
として宣言されているが定義されていない)静的インライン関数として定義し、
errno
にENOTSUP
を設定して、-1
を返す様に改造
この結果、iostream
の機能はありますが、利用しても動作しません。他には、所謂インクルードガードを追加したり、extern "C"
を追加したり等です。
補足:上記ツールチェーンの作成手順を公開しても良いのですが、何も読まないで「あれが動かない!直せ!」と怒り出す方々が一定数いて、この対応に苦慮する事が容易に想像出来るので、現時点では公開する予定はありません。とりあえず、まずは読み物だけと思って頂ければ幸いです
作成したツールチェーンは、どこか1つのフォルダーに纏めてインストールしておき、このフォルダーをAtmel Studio 7.0
に追加します。まずTools
メニューからOptions...
を選択し、Options
ダイアログを表示します。左のペインからToolchain
を選択し、右のToolchains:
をAtmel AVR 8-bit (CPP language)
に設定します。下のFlavours:
はNative
しかありませんが、Add Flavour
ボタンを押して、表示されるダイアログに項目を設定してAdd
ボタンを押せば完了です。以下の例では、Package Name
にG++9.2.0
を、Package Base Path
にC:\AvrToolchain\bin
を設定しています。
これで、準備が整いました。テストしてみましょう。
新しいプロジェクトを作成します。GCC C++ Executable Project
を選択して、適当な場所に名前はHelloWorld
として作成しました。デバイスはATmega328P
を使いますが、今回はSimulator
を使います。
作成したら、まず最初にプロジェクトのプロパティを設定します。Project
メニューからHelloWorld Properties...
を選択し、プロパティを表示します。左のペインからAdvanced
を選択し、右のToolchain Flavour:
をNative
からG++9.2.0
に変更します。
同様に左のペインからTool
を選択し、Selected debugger/programmer
をSimulator
に設定します。
最後も同様に左のペインからToolchain
を選択します。上のConfiguration:
をAll Configurations
に変更した後、下のAVR/GNU C++ Compiler
配下のMiscellaneous
を選択し、Other flags:
に-std=gnu++17
と入力します。
これでプロパティの設定は終わりです。
次にmain.cpp
を、以下の様にします。
#include <avr/io.h> #include <string> int main(void) { std::string letters{ "Hello World!" }; for (auto letter : letters) { GPIOR0 = letter; } return 0; }
libstdc++
をリンクする為に、敢えてstd::string
を利用している所がポイントで、これをビルドします。
ビルドが完了したら、まず最初に生成されたマップファイルを調べます。上から順に内容を確認していくと、以下の様に出力されています。
Archive member included to satisfy reference by file (symbol) C:/AvrToolchain/lib/gcc/avr/9.2.0/../../../../avr/lib/avr5\libstdc++.a(del_op.o) main.o (operator delete(void*)) C:/AvrToolchain/lib/gcc/avr/9.2.0/avr5\libgcc.a(_exit.o) C:/AvrToolchain/lib/gcc/avr/9.2.0/../../../../avr/lib/avr5/crtatmega328p.o (exit) C:/AvrToolchain/lib/gcc/avr/9.2.0/avr5\libgcc.a(_copy_data.o) main.o (__do_copy_data) C:/AvrToolchain/lib/gcc/avr/9.2.0/../../../../avr/lib/avr5\libc.a(malloc.o) C:/AvrToolchain/lib/gcc/avr/9.2.0/../../../../avr/lib/avr5\libstdc++.a(del_op.o) (free) C:/AvrToolchain/lib/gcc/avr/9.2.0/avr5\libgcc.a(_clear_bss.o) C:/AvrToolchain/lib/gcc/avr/9.2.0/../../../../avr/lib/avr5\libc.a(malloc.o) (__do_clear_bss)
ATmega328P
はavr5
に属するので、正しい各種ランタイムが選択されている事が判ります。
次は実行してみます。GPIOR0 = letter;
の行にブレークポイントを設定し、GPIOR0
の値の変化を確認した所、正しく文字列Hello World!
の先頭'H'
から末尾'!'
(std::string
ではC文字列の終端文字'\0'
を利用しません)まで順に出力されている事が判ります。
どうやら、各種ツールチェーンの作成結果は良さそうに見えますので、次回からはこの環境を用いて、色々と試してみたいと思います。
2020/03/08
目次はこちら
GNU Guix System 日本語インストール方法
gcc
の最新版やクロスコンパイラ等をコンパイルする環境として、GNU
謹製のディストロであるGuix System
をインストールしてみました。ただ、そのまま.iso
からインストールすると、日本語に設定したつもりでも日本語が化けてしまいます。そこで、マニュアルの別の場所に記載されている様に、コンソールから
$ guix install font-adobe-source-han-sans
と入力して、後から追加インストールを行っても良いのですが、これだとシステム全体にインストールが行われる訳ではないので、例えばログインマネージャーの表示は化けたままですし、ほかのユーザーにスイッチした場合も、上記コマンドを再度入力する必要があります。
これを回避するには、システムのインストール時に必要なパッケージを予め追加しておくのが、一番簡単です。
注意
現在のGNU Guix System
は、Ubuntu
やFedora
等の比較的使いやすいディストロではなく、Linux
に慣れた人向け(というよりも開発者向け)です。GUIで設定出来る項目は非常に少なく、システムのメンテナンスには、CUIのコマンドを駆使する必要があります。よって、本記事ではSlackware
等で環境を構築出来る方が対象となります。パーティションの設定方法等は、他の環境と同じ考え方なので、全て割愛させて頂きます。また、バージョンが更新されている場合がありますので、そこは適時読み替えて下さい。
作業した環境
- Windows 10 Pro 64bit 1903(18362.267)
- メモリ 16GB
- Oracle VM VirtualBox 6.0.10
- Virtual Machineの構成(概要)
- OS : Other Linux(64bit)
- メインメモリ : 4GB
- プロセッサー : 2
- 起動順序 : 光学ドライブ・ハードディスクの順
- システムアクセラレーション : デフォルト
- ビデオメモリ : 64MB
- グラフィクスコントローラ : VBoxSVGA
- ディスプレイアクセラレーション : 3D
- リモートデスクトップサーバーとレコーディング : 無効
- ストレージ : IDEに光学ドライブ、SATAに仮想ハードドライブ(32GB可変)
- オーディオ : Windows DirectSound / ICH AC87
- ネットワーク : NAT
- USB : OHCI, EHCI(=USB2.0)
- 共有フォルダー : とりあえずなし
- Virtual Machineの構成(概要)
- GNU Guix System v1.0.1 x86_64
Guix System のインストール
Graphical install using a terminal based interface
を選択して、以下の画面が表示されるまでインストーラを進めます。
ここでCtrl + Alt + F3
を押下し、別のターミナルを呼び出します。表示されている通りEnter
を押し、キーマップを設定(ここでは日本語106キーボード)します。
エディターを起動して、/mnt/etc/config.scm
を編集します。ここではvi
を使っていますが、マニュアルによるとGNU nano
がお勧めらしいです。
追加したいパッケージは、私の場合は以下の通りです。基本的にマニュアルに注意点が記載されていたパッケージと、システム管理で頻繁に使うコマンドとなります。必要に応じて差し替えて下さい。
このファイル(config.scm
)に、以下の様な記述があります。
(packages (append (list (specification->package "nss-certs")) %base-packages))
この部分を以下の様に書き換えて、保存してエディターを終了します。
Ctrl + Alt + F1
を押下し、元のインストーラの画面に戻ります。表示されている構成情報は、編集結果を反映していませんが、OK
を押してこのまま続けます。
インストーラが正常に終了し、再起動を求められますが、実機と違ってブートメディアが自動でイジェクトされないので、何らかの方法(例:再起動ではなく、一度電源OFFする)で除去します。その後、再起動(もしくは電源ON)をした直後の、Guix System
のログインマネージャーの画面を以下に示します。
ちゃんと日本語が表示されています。このままログインしてみます。
大丈夫そうです。
GNOMEデスクトップも日本語で表示されていますので、これで完了です。
上手くいかない場合は、config.scm
の修正ミスが考えられます。この場合、大抵インストーラの画面にメッセージが表示されるので分かり易いと思います。が、現バージョンだとメッセージが表示された後に、上手くインストールプロセスの復帰が出来ないので、毎回仮想マシンをリセットする羽目になります。加えて、毎回config.scm
を手作業で書き換える必要があります。config.scm
を、仮想マシンではなくホストOS側で管理出来ると、少しは楽になるかもしれません。
インストール後のセキュリティアップデートや、日本語の入力システムの構築については、また別の話となりますので、ここでは割愛します。
以上
まずは試してみましょう
ソフトウェアの準備とハードウェアの準備が終わったら、まずは試してみましょう。但し、何も知らない状態から始める事を想定しているので、とても説明が長いです。その結果、大長編となりました。どうか頑張って、最後までお付き合い下さい。
まずIDE
を起動して、以下の様にメニューから新規プロジェクトを作成を選択します。
すると、以下の様な新規プロジェクト作成ダイアログが表示されます。
左のペインから、Installedの配下にあるAssemblerを選択します。
下のペインのNameにプロジェクト名(とソリューション名)として、BlinkAsmと入力します。Solution name(ソリューション名)は、Nameを入力した名前と同じ名前が、自動的に設定されますので、入力は不要です。
同じペインのLocationを確認します。作成したソリューションは、このLocationフォルダー配下に作成されます。デフォルトで問題はありませんが、今後ソリューションが増えてくると、乱雑になって管理も大変になるので、分かり易いサブフォルダー等を指定しておくのも良いでしょう。以下は、デフォルトから一連の本記事用のフォルダー配下に変更した所です。
これでOKボタンを押します。デバイス選択ダイアログが表示されます。
上のペインのDevice Familyプルダウンメニューから、ATmegaを選択します。
下のペインの一覧をスクロールして、ATmega328Pを選択します。ATmega328とか、ATmega328PBという似た様な名前もありますが、間違えないで下さい。
これでOKボタンを押します。ソリューションとプロジェクトが作成されます。遅いPCをお使いの場合は、少し時間がかかるかもしれません。作成が終わると、IDE
が以下の様になります。各ウィンドウのレイアウトや大きさは、各人によって異なります(自由に移動したりリサイズ出来ます)ので、多少違っていても気にしないで下さい。
コードを書き始める前に、先に環境を設定しておきます。以下の様にメニューを選択して、プロジェクトのプロパティを開きます。
プロパティが表示されました。
プロパティウィンドウの左のペインから、Toolを選択します。
プロパティウィンドウの右のペインのSelected debugger/programmerで、Simulatorを選択します。MCU
にいきなり書き込む前に、まずはSimulator
でテストする為です。
プロパティの変更を保存して、プロパティウィンドウを閉じます。
コードエディターウィンドウに戻ります。このままの状態でアセンブルして実行しても、動作はしますが、実行内容はR16レジスタをインクリメントするだけの無限ループです。初めての××系では、最初は大抵Hello World!かLチカが主流みたいなので、どちらにしようか悩んだのですが、ここはLチカで行きたいと思います。
Lチカも、Arduino
だとdelay()
が使えて、n秒間隔の点滅などは簡単に記述出来るのですが、アセンブリ言語だとそのようなライブラリは無いので、自分で1から記述する事になります。加えて割り込みやタイマーも、まだ一連の本記事では学習していないので、原始的で力技ですが待機する命令サイクル数(ステップ数とかクロック数とか言う人も居ます)を自分で計算してコードを記述する羽目になります。まぁ習っていないのだから仕方ありません、我慢です。
結果、出来上がったコードは次の様になります。これをコードエディターで入力します。; で始まる行はコメント行ですので、入力しなくても構いませんが、後で見返した時に困るので、ちゃんと書く事をお勧めします。
; ; BlinkAsm.asm ; ; Created: 2018/11/06 18:04:49 ; Author : neb4nebrin ; ; ArduinoのBlinkスケッチと同じ様に配線しようと考えると、 ; Arduino Uno Rev.3でのLED_BUILTINは、digital 13pinとなる。 ; これをATmega328P-PUのピン配置で読み替えると、PB5となる。 ; しかし、データシートによるとPB5(SCK/PCINT5)と書かれており、 ; このピンは、ISP用に使われている事が判る。 ; よって本プログラムでは、PB5からPD5に出力先を変更する。 ; よってPortDを初期化しなくてはならないので、PD5を出力に設定する。 sbi ddrd,pd5 ; メインループ処理解説: ; PD5をオン->1秒遅延->PD5をオフ->1秒遅延->最初に戻る MainLoop: sbi portd,pd5 rcall DelayLoop cbi portd,pd5 rcall DelayLoop rjmp MainLoop ; サブルーチン解説の前に: ; ; 時間指定の遅延の考え方: ; 16MHzで動作するATmega328P-PUは、1秒間に16,000,000サイクルの命令を実行できる。 ; つまり1秒待機するという事は、16,000,000サイクル分の命令を無駄に実行させれば良い。 ; 実際に16,000,000きっちり無駄にするには、まずループプログラムを書いた後に、 ; その総処理サイクル数を求めて、ループ回数を調整する必要がある。 ; なおこの考え方は、水晶が必ず正確に16MHzを発振する事が前提の考え方なので、 ; 個体差や温度等による影響で、実際にはかなり誤差があると思われる。 ; よって、多少サイクル回数が増減しても、問題はないと思われる。 ; 実行する回数が指定されたループの考え方: ; 汎用レジスタは8bitの大きさなので、[0 - 255]までの最大256回しか実行出来ない。 ; しかし、ループを入れ子とすると、最大256回 * 256回 = 65536回実行出来る。 ; もしこれでも足りないのであれば、さらに桁を足していく事を繰り返す。 ; 遅延ループ処理解説: ; まず基本として、各命令のサイクル数を調べる。 ; ldi/nop/decは1サイクル命令。 ; brneは2サイクル命令 ; retは4サイクル命令。 ; ; InnerLoop3ラベルから、brne InnerLoop3迄の命令サイクルは、計4サイクル。 ; その外側のInnerLoop2ループは、InnerLoop3の分を除くと、計4サイクル。 ; さらにその外側のInnerLoop1ループは、同様に計4サイクル。 ; 残るサブルーチンの最初と最後は、計5サイクル。 ; ; 内部ループ3は、r22 * r21 * r20回実行される。 ; 内部ループ2は、r21 * r20回実行される。 ; 内部ループ1は、r20回実行される。 ; よって内部ループの総サイクル数は、以下の計算式で求められる。 ; 内部ループの総サイクル数 = ; (r22 * r21 * r20 * 4) + (r21 * r20 * 4) + (r20 * 4) ; ; これより総サイクル数は、以下の通りとなる。 ; 総サイクル数 = ; 5 + (r22 * r21 * r20 * 4) + (r21 * r20 * 4) + (r20 * 4) ; ; このコードに設定した値で計算すると、 ; 総サイクル数 = ; 5 + (249 * 250 * 64 * 4) + (250 * 64 * 4) + (64 * 4) = 16,000,261 ; ; もしお使いのクロック周波数が異なるのであれば、これらの考え方で初期値を再定義する事。 DelayLoop: ldi r20,64 InnerLoop1: ldi r21,250 InnerLoop2: ldi r22,249 InnerLoop3: nop dec r22 brne InnerLoop3 dec r21 brne InnerLoop2 dec r20 brne InnerLoop1 ret
入力が終わったら、コードをセーブしてから、アセンブルします。IDE
では、アセンブルだのコンパイルだの区別せずに、単にビルドすると言います。
Build Solutionを選択するとビルドが開始され、結果がOutputウィンドウに表示されていきます。正常に終了すると、次の様に表示されます。
このウィンドウを見つけられない場合は、以下の様にOutputメニューを選択して下さい。
OutputウィンドウにBuild succeeded以外の出力が表示されたなら、環境もしくは入力したソースコードに問題があります。
もし環境に問題があるならば、前の手順に戻って、プロジェクトのプロパティを再確認します。もしソースコードに問題があるならば、以下の様にError Listウィンドウに問題点が表示されます。
このウィンドウを見つけられない場合は、以下の様にError Listメニューを選択して下さい。
IDE
ならではの機能ですが、このError Listウィンドウの行を選択して、ダブルクリックすると、直ちにコードエディターを表示して、問題のある行に移動してくれます。これで直ぐに修正作業に移る事が出来ます。
ビルドが正常に終了するまで、注意深く修正作業を行ってください。上の画面キャプチャーで、コードエディターに色がついているのが判ると思います。コメント行は緑、MCU
への命令は青、命令に対するオペランドやラベル等は黒です。青にならなければならないのに、黒で表示されている等、基本的なミスは色でも判ります。IDE
ならではの便利な機能を利用して、少しでも楽に開発を進めましょう。
ビルドが正常終了したら、次はSimulator
を用いて動作テストをします。以下の様にStart Debugging and Breakメニューを選択します。
すると、以下の様にデバッグ用ウィンドウレイアウトに自動的に切り替わり、コードの先頭位置で実行が中断した状態となります。
コードのデバッグを開始する前に、デバッグに用いるウィンドウを用意します。全てのウィンドウの位置や大きさは、各人の環境で異なると思いますが、IDE
では各ウィンドウをそれぞれ好きな位置に配置したり、ドッキングしたりする事が出来ますので、もし見当たらない場合は、よく探してみて下さい。探しても見つけられない方の為に、今回用いるウィンドウの開き方も載せます。
まず必要となるのは、MCU
の状態を表すProcessor Statusウィンドウです。ここにはプログラムカウンターや各種汎用レジスターが表示される他、Simulator
の動作クロックを設定したり、自分の知りたい範囲のコードを実行した結果のサイクル数が表示されていたりします。
このウィンドウを見つけられない場合は、以下の様にProcessor Statusメニューを選択して下さい。
今回のソースコードは、16MHzで動作する事を前提としていますので、Simulator
のクロック周波数を変更します。Frequencyの値(1.000MHzとなっている箇所)をクリックすると、以下の様に修正可能になります。
これを16.000MHzに修正してEnterキーを押すと、以下の様に赤字で表示されて確定されます。
次に必要となるのは、I/O
の状態を表すI/Oウィンドウです。プロジェクトを作成した時にATmega328P
を選択しているので、最初からATmega328P
が使えるI/O
だけが表示されています。
このウィンドウを見つけられない場合は、以下の様にI/Oメニューを選択して下さい。
今回は、Port Dの5番ピンを用いますので、I/O Port (PORTD) を選択します。
すると、I/Oウィンドウの下ペインにPort Dの詳細が表示されます。データシートにも記載されていますが、リセット直後のPort Dの状態は、全て値(Valueの欄です)が0x00となっています。つまり全て入力ピン(内蔵プルアップ抵抗無し)として設定されています。PIND
とDDRD
とPORTD
の詳細は、後の章で学ぶので省略しますが、ざっくり説明(本当はもう少し面倒くさい)すると、PIND
が入力ピンの扱いで、DDRD
が入出力方向指定レジスタとなり、PORTD
が出力ピンの扱いとなります。Arduino
でもそうした様に、Portの設定はコード中に記述します。
以上が、今回用いるデバッグ用のウィンドウとなります。
次にBreakpointを学習しましょう。今回作成したDelayLoopサブルーチンは、1秒待機するループです。後述するステップ実行を1秒に相当するサイクル数分回すのは、手が疲れるだけでは済まないでしょう。加えてSimulator
を使っているので、どんなに速いPC上で動かしたとしても、実機と同じように16,000,000サイクルを1秒でシミュレートする事は不可能です。そういった訳で、1回のデバッグである程度の量(ブロックとかでも良い)のコードが動いている事が確認出来たら、一度デバッグ実行を終了し、次回のデバッグ実行の時には、次に中断させて状態を確認したいコードの部分をマークしておき、既に確認済み部分は普通に実行させて早送りするのです。この止めたい位置の事をBreakpointと呼びます。
Breakpointの作り方は簡単です。止めたい命令のある行をクリックして選択した後、右クリックメニューからInsert Breakpointを選択します。
設定されたBreakpointは、以下の様に、行番号の前に塗りつぶした赤丸として表示されていて、行が赤で強調表示されています。
Breakpointは、とても頻繁に設定・解除を行うので、毎回メニューから操作するのも面倒です。そこで、簡易手段を使います。コードエディター上で、背景色が異なる行番号の前の部分をクリックするだけで、設定・解除がトグルされます。
上記赤丸部分をクリックすると、
Breakpointが簡易設定されました。解除も同じ方法で、赤丸をクリックします。これでBreakpointを使って、実行を中断させる方法を学びました。次に移る前に、今設定した全てのBreakpointを解除して下さい。
さて、いよいよデバッグを開始します。今、プログラムは先頭の位置で中断されています。この位置は、コードエディターでBreakpointを簡易設定したのと同じ位置に、黄色の矢印として表示されていて、行が黄色で強調表示されています。
この強調表示された行が、次に実行される命令となります。つまり、まだ何も実行されていません。そしてこの、中断されている状態と、実行(デバッグ)を終了している状態を勘違いしないで下さい。実行を終了している状態では、一部のウィンドウが表示出来ません(自動的に閉じてしまいます)し、コードエディター上で黄色の矢印と行の強調表示もされません。
またデバッグ中に、コードエディターでコードを修正しても、今のデバッグ状態を継続したまま新しいコードを入力して試す事も出来ません。修正する時は、一度デバッグを終了します。コードを修正後にビルドし直して、再びデバッグする必要があります。
では、最初の命令を実行してみましょう。ステップ実行には幾つか種類があり、それぞれ次の様な特徴があります。
Step Into
次に実行する命令がサブルーチンコール(C/C++の場合は、関数やメソッドのコールも含む)の場合は、そのサブルーチンの先頭を次の中断位置とする。サブルーチンコールではない場合、次の中断位置は、大抵次の行の命令となる。
Step Over
次に実行する命令がサブルーチンコールの場合は、次の中断位置は、そのサブルーチンから戻ってきた次の命令(大抵今の中断位置の次の行にある)とする。サブルーチンコールではない場合は、Step Intoと同じ。
Step Out
現在サブルーチン内部を実行中の場合、次の中断位置は、そのサブルーチンから戻った(要は呼び出し元の)次の命令とする。もしメインルーチンでこれを実行した場合、どこかにBreakpointがなければ中断する事はない。
まずは基本のStep Intoです。最初の命令sbi ddrd,pd5
は、サブルーチンコールではないので、この状態でStep Intoすると、次の中断位置はラベルMainLoop
の次の行(ラベルは命令ではありません)のsbi portd,pd5
となりそうです。やってみましょう。それぞれのステップ実行は、Debugメニューの中にあります。
Breakpointと同様に、毎回メニューから操作をするのは面倒です。ここはショートカットキーやツールバーの出番です。ショートカットキーは、表示されたメニューに書いてあるので省略します。Debugツールバーは、次の様に表示されています。
このツールバーが見つからない人は、ツールバー領域で右クリックメニューから、Debugを選んでください。
ツールバーの各アイコンの上にマウスカーソルを移動すると、それぞれ意味が表示されますので、各アイコンの説明は割愛します。以下は、Step Intoを実行した結果です。
予想した通りの結果となりました。同様に今度はStep Overすると、次の中断位置はrcall DelayLoop
となりそうです。やってみましょう。
予想した通りの結果となりました。次の命令はrcall DelayLoop
なので、サブルーチンコールとなります。ここでStep Intoすると、次の中断位置はラベルDelayLoop
の次の行のldi r20,64
となりそうです。やってみましょう。
予想した通りの結果となりました。この先は、1秒を待機するループ処理です。折角サブルーチン内部に入ったのだから、Step Outを試してみたい所ですが、実際に自分で試した所、うんざりする程長時間待たされる結果となってしまいました。なので、Step Out機能は封印して、先に習ったBreakpointを使います。更についでに、このループの処理時間を計算させてみましょう。
まずProcessor Statusウィンドウで、Stop Watchの行の上で、右クリックメニューを表示し、Reset Stop Watchを選択します。
Stop Watchが初期化出来たので、次にサブルーチンからの戻り先にBreakpointを設定します。
今度はステップ実行ではなく、Continue(続けて実行)をします。もちろんメニューからではなく、ツールバーの同じアイコンを押しても問題ありません。
この操作は少し待たされると思います。が、Step Outよりもずっと早く終わります。
中断したら、Processor StatusウィンドウのStop Watchの値を確認します。
999,012.25us(マイクロをuと表記)となっています。1秒=1,000,000マイクロ秒ですので、約1秒経過している事が判ります。よって、処理の妥当性が証明されました。
次の命令はcbi portd,pd5
となっていますが、これもStep Intoします。
次の命令がrcall DelayLoop
なので、ここでStep Overすると、次の中断位置はrjmp MainLoop
となりそうです。やってみましょう。
これも少し待たされますが、ちゃんと次の行の命令で中断しています。rjmp
はジャンプ命令なので、次の実行はMainLoop
の先頭に戻る事になります。ここまでに大きな動作確認が出来ましたが、まだ確認出来ていないのは、Port Dの動作です。
ここで、Breakpointをさらに追加します。今中断している状態でrjmp MainLoop
にBreakpointを設定します。すると、この様になる筈です。
現在のPort Dの状態を、I/O
ウィンドウで確認しましょう。
Port Dの5番ピン(つまりNameがPORTDとなっているValue(値)を見て、最下位bitを0bit目とした時の5bit目)が0となっているので、Arduino
で書く所のLOW
となっています。ここでContinueすると、MainLoop
の先頭に戻り、sbi portd,pd5
を実行し、最終的に次のBreakpointであるcbi portd,pd5
で中断すると予想されます。sbi
命令は、指定されたI/O
の指定されたビットをセット(Set BIt)する命令です。つまり、ここでPort Dの5番ピンを1にします。これはArduino
で書く所のHIGH
です。やってみましょう。
中断したので、I/O
ウィンドウを確認します。
Port Dの5番ピンが1になりました。このままContinueすると、cbi portd,pd5
が実行されて、最終的に次のBreakpointであるrjmp MainLoop
で中断すると予想されます。cbi
命令は、指定されたI/O
の指定されたビットをクリアー(Clear BIt)する命令です。つまり、ここでPort Dの5番ピンを0にします。これはArduino
で書く所のLOW
です。やってみましょう。
中断したので、I/O
ウィンドウを確認します。
Port Dの5番ピンが0になりました。これでMainLoop
のコメントに書いた仕様(PD5をオン->1秒遅延->PD5をオフ->1秒遅延->最初に戻る)が、全て正しく動作する事が判りましたので、Simulator
でのテストを終了しましょう。
デバッグを終了するには、Stop Debuggingを選択します。もちろんメニューからではなく、ツールバーの同じアイコンを押しても問題ありません。
もし、ここまでの何処かで想定した動作とならなかった場合は、書いたコードに問題があります。大抵の場合、ミスタイプが原因です。例えばレジスタの名前を間違っている(例:r16
->r17
)・似た様な命令(例:brne
->brie
)に打ち間違えて意図した処理になっていない等です。こうなった場合は、直ちにデバッグを終了し、ソースコードを確認・修正した後、ビルドしなおしてから再デバッグを行います。そして全てが正しく動作するまでこの手順を繰り返します。
と、この様な感じで動作テストは進みます。アセンブリ言語の場合、それぞれの命令が実行する結果は、とても単純です。ですから、ある程度の命令を纏めて1つの機能ブロックと考えて、それぞれの実行結果を確認していくと良いと思います。そういった理由で、ソースコードには、機能ブロック単位でコメントを記述しています。
最後にソースコードの各機能を説明すると、ソースコードの先頭部分(1行しかありませんが)は初期化コードで、Arduino
でいう所のsetup()
に該当します。MainLoop
から始まりrjmp
で終わる部分はメインルーチンで、Arduino
でいう所のloop()
に該当します。DelayLoop
から始まりret
で終わる部分は、Arduino
でいう所のdelay()
関数に相当するユーザー定義のサブルーチンとなります。また、今回はDelayLoop
の詳細動作についての動作テストを割愛しましたが、もし興味がありましたら、汎用レジスタ(r20, r21, r22)に対する初期値(64, 250, 249)を、デバッグ用に(1, 1, 1)と書き換えてテストすると良いと思います。ループ処理の動作が確認出来たら、元の初期値に戻せば良いのです。
さて、次はMCUボードを用いて動作させてみましょう。MCUボードがまだ用意出来ていない方は、記事を最後の方までずっと進めて、MCU
上のメモリサイズの確認に進んで下さい。
まず最初にソースコード中のBreakpointを、全て解除します。
次に、実行する環境をSimulator
からプログラマー(に接続してあるMCUボード)に変更します。この記事の最初でやった通りにして、もう一度プロジェクトのプロパティを開きます。そして、ToolのSelected debugger/programmerを、用意したプログラマー(この場合はSTK500)に変更します。InterfaceはISPに設定します。
変更を終えたら、プロジェクトのプロパティを保存してから閉じます。
もしMCUボードを自作して、新品で1度も使用していないMCU
を取り付けた場合、MCU
が内蔵オシレータを使って動作する設定となっているので、これを外部の水晶振動子を用いる様に、Fuse
ビットを書き換える必要があります。この作業手順は、新品のMCU
を使う最初の1回だけ必要です。製品のMCUボードを利用する方は、大抵設定済みMCU
が付属していると思います。まずは、お手持ちのマニュアル等を参考にして下さい。設定済みのMCUボードを利用する方は、ブレッドボードにLEDと抵抗を接続する手順に進んで下さい。未設定のMCUボードを利用する場合は、この手順を参考にして、ご自身で設定して下さい。
まず最初に知らなくてはいけない事は、データシートによると、出荷時のMCU
はデフォルトクロックソースとして内蔵オシレータ(8MHz)が有効で、CKDIV8
というFuse
がプログラムされている、と記述されています。この結果、出荷時のMCU
は1MHzで動作します。つまり、水晶振動子や外部オシレータをMCU
に接続しただけでは、そのクロック周波数では動作しないのです。以下に、新品のATmega328P-PU
のFuse
ビットとLock
ビットを、ICライターで読み出した結果を示します。
Fuse
ビットは、1の場合が未プログラム状態で、0の場合がプログラム状態となります。画像内で、例えばCKDIV8=0
と書いてあるのは、チェックマークが入っていたらプログラム状態で、それは0である事を意味します。つまりチェックマークが無いなら、1を表します。
読み方を理解したら、データシートに戻ります。CKSEL3
からCKSEL0
までを、CKSEL3
が最上位ビット(MSB)となる様に並べると、4bitの値が出来ます。これをCKSEL[3:0]
の様に記述します。上の読み取り結果のCKSEL[3:0]
は2進数で0b0010と表現出来るので、これをデータシートの13.2 Clock Sources
を参照すると、Calibrated Internal RC Oscillator
が選択されている事が判ります。また、CKDIV8
がプログラムされているので、クロックソースを8分周する事も判ります。13.6 Calibrated Internal RC Oscillator
を調べると、確かに結果として大体1MHzとして動く事が判ります。加えてSUT[1:0]
が0b10となっているので、Power ConditionsがSlowly rising powerで、Start-Up Time from Power-down and Power-Saveが6CKで、Additional Delay from Resetが14CK + 65msに設定されている事も判ります。これらの値は、電源投入直後等(回路が不安定です)から、内蔵オシレータ起動をどれだけ待つか?を表しています。
次に自分のMCUボードです。XTAL1
ピンとXTAL2
ピンに16MHzの水晶振動子と22pFのセラミックコンデンサーからなる発振回路を接続しています。データシートの13.1 Clock Systems and Their Distribution
を参照すると、XTAL1
とXTAL2
ピンに接続したCrystal Oscillator
というパターンになります。このパターンと13.2 Clock Source
と13.3 Low Power Crystal Oscillator
と13.4 Full Swing Crystal Oscillator
を参照した結果、MCUボードをLow Power用に設計していないので、Full Swing Crystal Oscillator
が該当する事になります。つまり、13.4 Full Swing Crystal Oscillator
を参照して値を決める事になります。まずCKSEL[3:1]
(最下位ビット(LSB)の値が1となっている事に注意)の値は0b011となります。次に残っているCKSEL0
とSUT[1:0]
の値も決めます。このMCUボードは特殊用途ではないので、MCU
の起動タイミングをシビアにする必要もありません。つまり、基本的に出荷時動作(汎用設定)と同じ様な動作で問題はありません。よって内蔵オシレータの設定を流用して、Oscillator Source/Power ConditionsがCrystal Oscillator, slowly rising powerで、Start-Up Time from Power-down and Power-saveが16K CKで、Additional Delay from Resetが14CK + 65msである行を参照すると、CKSEL0
が1で、SUT[1:0]
が0b11と記述されています。また16MHzでそのまま動作させるので、このクロックを分周する必要はありません。これらの結果から、最終的に設定する値は、CKSEL[3:0]
が0b0111で、SUT[1:0]
が0b11で、CKDIV8
が1となります。
設定する値が決まったので、MCUボードとプログラマーとPCをそれぞれ接続し、通電します。IDE
に戻って、Available Toolsウィンドウからプログラマーを右クリックし、Device Programmingを選択します。
ハードウェアの準備で行った様にしてMCU
ボードと通信を行い、Fuse
ビットを読み出します。
LOW.CKDIV8のチェックを外します。そしてLOW.SUT_CKSELをExt. Full-swing Crystal; Start-up time PWRDWN/RESET: 16K CK/14 CK + 65 msに変更します。
変更した箇所のアイコンが、緑のチェックマークから、黄色の注意マークに変わります。この2か所以外に黄色マークがあった場合は、Readボタンを押して元の値に戻して下さい。LOWの値が0xF7となっているか確認したら、Programボタンを押して書き込みます。
Fuse
ビット変更に対するWarning(警告)ダイアログが表示されます。Continueボタンを押して継続します。以前に一度でもDon't show this warning againにチェックを入れていた場合は、このダイアログは表示されずに書き込みが行われますので、注意して下さい。
Fuse
ビットの書き込みが完了すると、黄色マークが緑マークになります。Closeボタンを押して、ダイアログを閉じます。もし書き込みに失敗した場合は、書き込む電圧が不安定である位しか思いつきません。自分の使っているプログラマーは、PC上で書き込んだ時のボードの電圧を確認する事が出来ます。以下は、今回Fuse
ビットを設定した時の、MCUボードの電圧です。
MCU
へのFuse
ビット設定が終わったら、MCUボードへの通電を止めます。
次は、ブレッドボード等を使用してLEDと抵抗を直列に接続し、それぞれの両端をMCUボードのPD5とGNDに接続します。この時LEDの極性に注意して下さい。もしLEDや抵抗をお持ちでない場合や、何を使えば良いのか判らない場合は、以下の説明を参考に購入 or 計算して下さい。
今回私が使用したLEDは、OptoSupply
というメーカー製のOSR5JA3Z74A
という3mmの赤色LEDです。OptoSupply
のLEDは、秋月電子通商で簡単に入手出来、データシートも公開(秋月電子通商の商品ページにリンクあり)されているので、良く判らないLEDを使うよりも使いやすいと思います。
LEDに流れる順方向電流を$I_{\rm F}$(A)、LEDの順方向電圧降下を$V_{\rm F}$(V)、電源電圧を$V_{\rm CC}$(V)と表記すると、LEDに接続する抵抗の値$R_{\rm LED}$($\Omega$)は、以下の式で表現出来ます。
$$ R_{\rm LED} = \frac{V_{\rm CC} - V_{\rm F}}{I_{\rm F}} $$
LEDのデータシートを確認すると、順方向電流$I_{\rm F}$が20mAの時、平均(Typ.)順方向電圧降下$V_{\rm F}$が2.1Vである事が判りました。しかし、実際にこれらの値を用いて試してみましたが、眩しすぎる位明るくなったので、$I_{\rm F}$を3mAとしました。これより求める抵抗の値$R_{\rm LED}$は、以下の通りとなります。
$$ R_{\rm LED} = \frac{5 - 2.1}{0.003} = 966.\dot{6} $$
実際に接続する抵抗値は、計算結果より大きくて一番近い値を用います。逆に計算結果より小さくて一番近い値を選択してしまうと、流れる電流が計算よりも多くなる事を意味するので、最悪の場合(データシートの$I_{\rm F}$の最大値を超えた場合)LEDが壊れます。大きくて一番近い値を選択すれば、計算よりは多少暗くなるかもしれませんが、壊れる事もありません。もしくは、複数の抵抗を直列と並列に組み合わせて、計算結果に等しい抵抗値を作り出しても良いのですが、あまり現実的ではありません。よって、今回は一番近い1k$\Omega$(カラーコード:茶黒赤金)を1つだけ用います。
LEDの抵抗値が求まったので、今度はこの抵抗の消費電力を計算します。LEDに接続する抵抗の消費電力$W_{\rm R}$(W)は、以下の式で表現出来ます。
$$ W_{\rm R} = I_{\rm F} \times I_{\rm F} \times R_{\rm LED} $$
つまり今回の消費電力は、以下の通りとなります。
$$ W_{\rm R} = 0.003 \times 0.003 \times 1000 = 0.009 $$
9mWという事は、1/6W($=166.\dot{6}\rm mW$)抵抗で充分という事になります。小さな組み込み機器を作成するなら、部品も小さい方が都合が良いので、1/6W抵抗を使いましょう。これも秋月電子通商で(100本単位ですが)買えます。
カーボン抵抗(炭素皮膜抵抗) 1/6W 1kΩ (100本入)
パーツの選定が終わったので、LEDと抵抗をブレッドボードに配線しましょう。
電子工作のお約束には、赤は電源・黒はGNDというのがあり、これに従ってジャンプワイヤーを使っています。別に守らなくても電気的に問題はないのですが、パッと見て黒はGNDというのが理解出来ると、接続の際に極性を間違える事も少なくなると思います。それ以外の信号線については、赤・黒以外の自分の好きな色を使えば良いと思います。今回は、MCUボードのPD5とGNDに接続するので、上記の写真では、緑のジャンプワイヤーをPD5に、黒のジャンプワイヤーをGNDにそれぞれ接続します。MCUボードへの電力供給は、まだしないで下さい。
この様な感じで接続しました。写真ではプログラマーが既に通電されていますが、ここからMCUボードへの電力供給は行っていませんので、この時点ではMCU
は動作していません。
次にMCUボードへ電力供給を行います。私のMCUボードは、シリアル変換アダプターから電力供給を行うので、ここでシリアル変換アダプターをPCと接続します。プログラマーから電力供給したり、それ以外の外部電源を用いる方も、この時点から電力供給します。また、プログラマーもPCと接続して、ハードウェアが全て動作可能な状態として下さい。
ここからIDE
に戻ります。
これで漸く書き込める状態となりました。今までとは違って、今度はStart Without Debugging(デバッグ無しで実行)を選択します。今まで使ってきたDebugツールバーには、このアイコンはありませんので注意です。
このアイコンはStandardツールバーにあります。
多分最初から表示されていると思いますが、見つからない方は、ツールバー領域を右クリックしてStandardを選択して下さい。
では、書き込んで実行してみましょう。
動画では、大体1秒間隔でLEDが点滅している事が判ります。これでアセンブリ言語で記述する、ATmega328P-PU
のLチカプログラムが完成しました。
最後に、このプログラムが使ったMCU
上のメモリサイズを確認してみましょう。IDE
のSolution Explorerウィンドウで、Output Files配下のBlinkAsm.lssというファイルをダブルクリックして開きます。
このウィンドウが見つからない方は、以下の様にメニューを選択します。
開いたlssファイルの一番最後の行に移動します。
.csegの行はコードセグメント(要はFlash
です)に格納されるコードやデータを表しています。この場合、コードだけ34byte分使っています。.dsegの行はデータセグメント(要はSRAM
です)を表しています。今回はSRAM
にコードやデータ領域を作成していないので、コードもデータも0byteです。.esegの行はEEPROM
セグメントを表しています。これも同様に作成していないので、どちらも0byteです。結局、このプログラムはFlash
をたったの34byteしか使っていません。今回のコードと同様の処理をC/C++で記述すると、これよりもずっと大きなサイズを消費します。組み込み用に用いる小さなMCU
は、搭載しているメモリの量も小さい事が多く、C/C++で記述するとメモリに収まらない事も多いのです。試しにArduino
でBlinkスケッチを何も変更せずにコンパイルしてみましょう。
Flash
を928byte、SRAM
を9byte使っている事が判りました。アセンブリ言語で記述するメリットの一つに省メモリが挙げられるのは、この結果を見ても明らかです。勿論デメリットもありますから、結局はアセンブリ言語とC(出来たらC++も)の両方を使い分けられる様になる事が大事です。今後は、出来るだけアセンブリ言語とCの両方で学習したいと思っています。
補足ですが、今回作成したプロジェクトは、GitHubにアップロードしてあります。どうしても動作しない場合は、こちらを参考にして下さい。
2018/11/16
目次はこちら
ハードウェアの準備
Atmel Studio 7.0で行うAVR開発の一連の記事では、MCU
としてATmega328P-PU
(PDIP-28)を用います。理由は単純で、Arduino Uno
等で広く使われているので、MCU
の機能を理解する事が、他のAVR
よりも早いと思われるからです。加えて価格も入手性も全く問題が無く、地方の方でも通販で簡単に入手する事が出来ます。価格が安いという事は、例え実験で破壊したとしても、あまりダメージを受けない、という事です。学習に失敗は付き物で失敗はする物、という気持ちで気楽に扱いたいと思います。
しかし、Arduino
でもそうでしたが、実験でFlash ROM
やEEPROM
を頻繁に書き換えると、書き換え上限(ATmega328P
では、データシートによるとFlash
が1万回、EEPROM
が10万回)に達してしまった後の動作保証はありません。そこで、通常は出来る限りIDE
のSimulator
を用います。実際にハードウェアを使わないといけない状況になった場合(例:シリアル通信を行う)に初めて、物理的なハードウェアを利用します。従って、最初の章の方ではSimulator
だけで事足りる事が多く、後半に進むに従ってSimulator
と用意したハードウェアの両方を使う事になります。そういった理由で、最初からハードウェア全てを、無理に準備する必要はありません。
MCUボード
MCUボードを用意する手段はいくつかあります。
既存のMCUボード(orキット)を利用する
ATmega328P-PU
を用いたMCUボード(キット)は、探すと結構見つかります。ISP
(ICSP
とも言う。以下ISP
で統一)の端子が付いていれば良いです。沢山あるので一例(私は使った事がありませんが)を挙げると、以下で買えると思います。秋月電子通商 -
Arduino
(or互換機)を流用するATmega328P-PU
が搭載されている入手性の良いArduino
は、Arduino Uno Rev.3
と、その互換機となります。Arduino Uno Rev.3
にはISP
端子があるので問題ありません。互換機では、ISP
端子はあったりなかったりします。これらは例を挙げるまでもなく、普通に通販で買えると思います。Caution: この選択肢を選んだ場合、
Arduino
が用いるブートローダーを破壊してしまう事になります。よって元に戻す(Arduino
に)つもりがあるけれど、自分で戻す方法が判らないのであれば、この選択肢は諦めた方が良いと思います。Note:
Arduino Uno Rev.3
には、PCとのUSB-シリアル通信用にATmega16u2-MU
が搭載されていて、こちらにもISP
端子があります。間違えない様、注意が必要です。自作する
これが一番融通が利く上に、自身の経験値も上がる、良い組み合わせだと思います。私は、自作を選びました。但し、保護回路等が全く追加されていないので、ちょっとした不注意で簡単に壊れてしまいます。が、壊れたら直せば(交換等)良いのです。
下の写真が、ユニバーサル基板に自作した実験用MCUボードです。5V/16MHzで動作します。実験ボードなので、クロック回路・リセット回路(
Arduino
として使う場合の書き込み用)・電源On表示用LED回路・パスコンがあるだけで、後は端子と配線だけです。
Arduino Uno Rev.3
と並べると、これぐらいの大きさです。Note: 今になって考えると、水晶振動子をピンソケットに刺す形にすれば良かったと後悔しています。
-
MCU
に対して、作成したプログラムやデータ等を書き込む装置です。Atmel
純正のAVR ISP mkII
は、秋葉や通販で気軽に買えるとは言えません。Atmel
純正のAtmel-ICE
を利用する場合、32bitのAVR
を扱うならともかく、ATmega328P-PU
を扱うには高価で機能がオーバースペックです。従って、何とか他のプログラマーを探す必要があります。これは、あまり選択肢がありません。AVR ISP mkII
互換機を用いるIDE
とは、最も親和性が高い組み合わせだと思います。Atmel
純正品は入手性に難がありますが、互換機は探せばいくつか見つかります。が、中国製が多く、マニュアルが読めない等の問題もあります。私は使った事がありませんが、以下で買えるみたいです。千石電商 -
IDE
と協調動作出来るプログラマーを用いるAtmel
が発売したスターターキットに、STK500
(廃番)がありました。このSTK500
と同様の書き込み手順を用いるプログラマーは、IDE
と協調動作が出来ます。このタイプは、あまり多くはありませんが、探すと見つかります。私もこのタイプを使っています。以下で買えます。スイッチサイエンス -
Pololu USB AVRプログラマ v2 (廃番ですが、本記事で使っています)
下の写真が、Pololu USB AVR Programmer v2です。意外と小さくて、びっくりしました。
ISP
用の端子に加えて、USB-シリアル変換用の端子も付いています。Pololu
のHPはこちら。
USB-シリアル変換アダプター
以後、シリアル変換アダプターと記述します。
Arduino
でデバッグするのに、シリアル通信モニタを利用した方は多いと思います。IDE
を用いた開発も同様で、MCU
とISP
だけだと、デバッグするのが困難な場合が殆どかと思います。なので、なるべく早い段階でMCU
のUSART
(シリアル通信)機能を学習し、デバッグモニタとして使える様にしたいと思います。これは、その時から利用します。Arduino
への書き込み用を持っている方は、それをそのまま利用して下さい。それ以外用でも、USART
をカスタマイズすれば使えると思います。Arduino
用は、探せばいくつも見つかりますが、動作電圧だけは注意して下さい。使用するMCUボードの電圧と合わせます。私が使っているタイプは、以下で買えます。スイッチサイエンス -
FTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)
下の写真が、私の使っているアダプターです。3.3V/5Vの両対応なので、省電力運用する
Arduino
を扱うのにもピッタリです。
ハードウェアが用意出来たら、次はIDE
側でもハードウェアの準備をします。お使いのプログラマーに記載されている作業手順に従って、進めて下さい。
以下は、IDE
側の準備を完了した後のAvailable Tools
ウィンドウです。私の環境では、STK500(COM4) Program onlyと書かれている箇所が、プログラマーとなります。Simulatorは、最初から表示されています。
このウィンドウが見当たらない場合は、以下の画像の様に、Available Atmel Toolsメニューを選択すると表示されます。
シリアル変換アダプターは、IDE
側では特に何も準備する必要はありません。
これでIDE
とハードウェアの両方の準備が終わりましたので、双方を接続して簡単な接続テストをしてみましょう。
下は、私が自作したMCUボードをPololu USB AVR Programmer v2と接続し、スイッチサイエンス製シリアル変換アダプターから給電した写真です。当たり前ですが、プログラマーとシリアル変換アダプターは、PCとも接続しています。
実はこのプログラマー、付属の設定ユーティリティーを使って、ターゲット(MCU
)ボードへの電源供給を行う事が出来ます。が、自作したMCUボード側で、ISP
端子・シリアル端子もしくはそれ以外のどの端子から給電するか?という選択機能がないので、自分でどこから給電するのか決めなくてはなりません。結論として、シリアル変換アダプターを接続しっぱなし(USART
を使わなくても)にして、常にここから給電する事としました。
ハードウェアを全て接続し、通電を確認したら、IDE
のAvailable Tools
ウィンドウから、プログラマーを右クリックして表示されるメニューから、Device Programmingを選択します。
すると、以下の様なDevice Programmingダイアログが表示されます。
このダイアログのToolを、お使いのプログラマーに設定します。私の環境では、STK500(COM4) となります。DeviceはATmega328P
で、InterfaceはISP
を指定します。
Applyボタンを押すと、プログラマー経由でMCU
の情報を読み出し、ダイアログが以下の様になります。
左のペインの選択を変える事で、このMCU
の各種状態を知る事が出来ます。
MCU
を消去したり、Flash
やEEPROM
に対するHex
ファイルの読み書きをしたり、
Fuse
ビットを読み書きしたり、
Lock
ビットを読み書きしたり、
ELF
ファイルの読み書きも出来ます。
もし上手く動かない場合は、プログラマーかMCUボードに問題があると思います。まずは、プログラマーを接続したCOMポート番号(COMx)がWindows
で認識出来ているか?認識したCOMポート番号とIDE
に設定したプログラマーのCOMポート番号が合っているか?を確認して下さい。Windows
で認識出来ていないならば、プログラマーを再インストールして下さい。COMポート番号が合っていないならば、IDE
のAvailable Tools
ウィンドウ上でプログラマーを右クリックし、表示されたメニューからRemove(削除)して、再設定して下さい。それ以外ならば、それぞれの製品のFAQ等を読んで対処して下さい。
ここまで終われば、ソフトウェアとハードウェアの両方の基本的な準備は完了です。
2018/11/03
目次はこちら
ソフトウェアの準備
Atmel Studio 7.0で行うAVR開発の一連の記事では、IDE
としてAtmel Studio 7.0
(以後IDE
と表記)を使用します。Microchip
(旧Atmel
)のHPから無償でダウンロード出来ます。PIC
の開発環境とは違い、有償版はないので、いつでも全ての機能を利用する事が出来ます。Microchip
のHPはこちら。
HPが更新されてしまい、記事が無用とならない様に、具体的なDLリンクへのページ遷移は省略しますが、HP上の検索ボックスで "Atmel Studio 7" を検索すると見つけられると思います。
Note: MCU
(参照:ハードウェアの準備:別途記述)としてATmega328P-PU
を用いるので、HP上の検索ボックスでATmega328P
の製品ページを検索し、Documentの様なリンクを辿り、ATmega328P
のデータシートもついでに入手しておきます。データシートには、Summaryと呼ばれる簡易版がありますが、こちらではなく完全版を入手します。
Atmel Studio 7.0
のインストーラ(Webインストーラでもオフラインインストーラのどちらでも構いません)をDLし終わったら、早速インストールします。
Note: Atmel Studio 7.0
は、Microsoft Corp.
のVisual Studio IDE
をベースに開発されているので、Windows
でVisual Studio
を使ったことがある方は、一目で大まかな機能が利用出来ると思います。しかしVisual Studio
を一度も使ったことが無い方は、何度もMicrosoft Corp.
のHPで、Visual Studio
の使い方を学ぶ必要があるかもしれません。Visual Studio
のHPはこちら。
インストールが終わったら、早速起動します。そしてソフトウェアのアップデートを行います。アップデートは、以下の画像の様に、Extensions and Updatesメニューを選択します。
すると、以下の様なアップデートダイアログが表示されます。
左のペインからUpdatesを選択します。
ここに表示されている機能拡張は全て更新し、IDE
を常に最新の状態にして使用して下さい。更新は、IDE
を終了しないと始まらないものがあるので、画面に表示されている指示に従って下さい。注意: 上の画像で表示されているMarkdown Mode機能拡張は、標準ではインストールされていないので、表示されません。ご自身のダイアログに表示されている機能拡張で読み替えて下さい。
IDE
や機能拡張の更新が終わったら、追加で必要と思われる機能拡張をインストールします。インストーラのバージョンにより、何が最初からインストールされているのか判らない為、私が現在インストールしている機能拡張リストを以下に示しますので、これを参考に必要であればインストールして下さい。インストールするにはIDE
を再起動して、再びExtensions and Updatesメニューを選択します。
- Annotated Assembly File Debugger
- Atmel Kits
- Atmel Software Framework
- Atmel Start
- AtmelToolchainProvider
- Data Visualizer Extension
- Doxygen Integrator
- FreeRTOS Viewer
- GdbConsole
- Indent Guides
- LiveWatch
- LUFA Library
- Markdown Mode
- MemoryLogger
- Microchip Gallery
- Microsoft Advertising Framework (Windows開発用ですが、アンインストール出来ません。)
- Microsoft Store Services Engagement Framework (Windows開発用ですが、アンインストール出来ません。)
- Terminal for Atmel Studio
- Visual Assist for Atmel Studio
- Visual Studio Extensions for Windows Library for JavaScript (Windows開発用ですが、アンインストール出来ません。)
注意: これら機能拡張の一覧は、私の現時点の環境での一覧であり、私の必要に応じて追加・削除しています。今後の一連の記事で、必ず全て使用する訳ではありません。
機能拡張のインストールが終わったらIDE
を再起動して、Device Pack Managerを起動して、各デバイスの情報(定義情報)を最新に更新します。これを行わないと、定義情報にバグがあった場合、修正されずにそのまま利用する事を意味します。以下の様に
Device Pack Managerメニューを選択します。起動には、Windowsの管理者権限
が必要です。
起動したら、以下の様なウィンドウが表示されます。全画面で表示されますが、大きすぎるのでウィンドウを縮小しました。
Check for Updatesボタンをクリックし、アップデータをチェックします。アップデータがある場合は、Installボタンをクリックして表示されるメニューから、Install all updatesを選択します。基本的に最新のデバイス定義を用いるので、全ての更新が終わったら、それぞれの古いバージョンはUninstallして構いません。全ての作業が終わったら、Device Pack Managerウィンドウを閉じて下さい。
これで、基本的なソフトウェア環境が出来ました。今後IDE
を起動する度に、更新のチェックが自動的に行われ、更新があればIDE
ウィンドウの右上に通知されます。
この通知アイコンが強調表示されると利用可能な更新があるので、この通知アイコンをクリックして表示される通知ウィンドウ(以下の写真:通知なしですが)を読んで、指示に従って更新して下さい。
2018/10/25
目次はこちら
はじめに
Atmel Studio 7.0で行うAVR開発の一連の記事では、Atmel
(現:Microchip
)が提供している無償のIDE
(Integrated Development Environment:統合開発環境)と、STK500
プロトコルを備えたプログラマー(MCU
への書き込み機)を用いて開発・デバッグ・プログラムを行います。avrdude
は使いませんので、予めご了承ください。今後、必要に応じて(例:32bit-AVRをJTAGでデバッグしたい等)、Atmel-ICE
等も視野に入れていきたいと思います。
私がこのカテゴリで記事を書こうと思ったのは、あまりにもAVR
に関する日本語文献が足りなかったからです。Arduino
の入門書は数多くあれど、AVRの入門書は数少なく、また酷いのは10年以上前に刊行されて、それが今でも改版されずに出版されていることでした。
Atmel Studio 7.0で行うAVR開発の一連の記事は、要は簡単に言うと、私の学習ノートです。教科書や副読本を読んで、理解した事を纏め、後から見直せる様に残すものです。この先きっと理解が進むにつれて、古い記事を直す必要に迫られると思います。その時に新しい記事を書いてパッチを当てるのではなく、古い記事を更新する事を選びました。常に新しい記事だけ読めば良い訳ではない事をご理解下さい。
2018/04/11
目次はこちら