※このページの内容は、2003年8月17日コミックマーケット64で配布した「すっげ〜AVRマイコン」に加筆修正したものです。
199157
昨日:6 今日:5
すっげ〜AVRマイコンの世界へようこそ。
この本を手にしたあなたは、マイコンについていくらかの興味があるのでしょう。世の中を見渡すと、マイコンと名のつくものがいろいろ見つけられますよね。マイコンジャーとかマイコン炊飯器とか。同じ物ですが(ォ そういったマイコンと呼ばれるものは、さまざまな種類があるので、どんなものかと調べてみようとか、試しに使ってみたくても、どれを選べばよいのか迷ってしまいます。しかし、我々ホビーユーザーが簡単に手に入れられるマイコン(マイクロコントローラ)は、数種類に限られていますので、現実的にはその中から選ぶことになります。この本で扱うのは、そのうちの一つであるAVRマイコンです。安価で使い方が覚えやすいのが特徴です。
AVRマイコンを既によく使われている方もいらっしゃるかもしれませんが、この本はAVRマイコン初心者以前の人、マイコンをまだ使ったことのない人を対象としています。プログラミングをしたことが無くても大丈夫。過去に使ってみようとしたけれど、挫折した経験のある人でもOKです(ォ
内容としては、ハードウェアの製作からプログラムの作成、AVRマイコンへプログラムを書きこんで、ハードウェアを実際に動かすまで、AVRマイコンを使うための一通りの手順を、すっげ〜簡単に書いていきます。AVRマイコンを理解しながら、とにかく動かせるようになるのが目標です。そんなわけで、AVRマイコンのことが、この本だけで細部まで詳しく分かるわけではありませんが、読んで実践すれば、とりあえずAVRマイコン使いの初心者にはなれる、そういう本になっています。
ここで製作するものは実用性に欠けまくりで、しょうもないものですが、AVRマイコンを使う上での基本は身に付けられます。基本を覚えて一度動かせるようになってしまえば、そこから応用して実用的なものを作ったりすることは容易でしょう。また、マイコンなんていじったことない人にも分かるように書いていきますが、コンピュータと電気の知識がちょっと必要かなと思います。電子回路の知識もちょっとだけ説明しますが、あんまり詳しくは書きません。がんばってついてきてくださいね ミ☆
それと、AVRの日本語マニュアルや、この本で使用する各種ソフトは、インターネットからダウンロードすることになります。よって、インターネットが使える環境(*1)が必要です。また、各種検索エンジンを使えば、AVRマイコン関係の資料も調べられたりしてとても便利です。もしも、まだインターネットを個人的に使える状態でないのなら、この際ですから使える環境を手に入れましょう。そして、本文に出てくる内容がどうしても分からない場合は、インターネットで調べてください(ォ
*1: このページが見えるのであれば全然大丈夫! |
AVRマイコン(以下AVR)とは、ATMEL社が販売しているワンチップマイコンです。ワンチップと言うことで、プログラム可能なMPU(CPU)、ROM、RAM、周辺のI/O 、A/Dなどが一つのチップに入っています。また、とても安価に買えるのに加えて、開発環境が無料で入手できるので、アマチュアのホビー用にうってつけというわけです。世間的には、同じくワンチップマイコンであるMicrochipTechnology社のPICマイコンの方がポピュラーみたいですが、AVRの方が使いやすそうだったので、私はこっちを使っています。
AVRを制御するためには、基本的にアセンブラというプログラム言語(*2)で記述しなければなりません。その辺りが普通の人にはネックですが、制御の対象を「I/OのON、OFFをどのタイミングでどのように切換えるか」にしぼるなら、AVR自身でやることも限られてきます。また、実際にもそれがAVRを使う主な目的になると思いますので、最初に覚えなければならないことは、そんなに多くないでしょう。(*3) ただし、世に出まわっている日本語の資料がまだ少ない(!) ために、覚えようにも調べるのがなかなか大変だったりすることはあるかもないかも。まぁそういうこともあるということで、この本が参考になっていただければ幸いです。
とりあえず、CQ出版社からでている「マイクロコントローラAVR入門」というムックは、初心者向けの内容なのでおすすめです。ハードの仕様からアセンブラの命令まで、割と詳しく書いてあります。書かれてないこともあります(ォ 今回は、このムックを読まなくても内容が分かるようにしていますが、AVRをもっと分かりたいと思ったときには参考にしてみてください。
それと、ATMELのWebページで公開されている英語のマニュアル関係が、レディオテクニカさんにて日本語に翻訳されています。ありがたいことです。英語が得意な人はATMELのページで原本を読んでください(ォ このマニュアルの内容が理解できるのなら上記入門書は必要ないでしょう。AVRの型式毎に別々のマニュアルが用意されており、それぞれ初期化のやり方とか、使える命令が少しずつ違いますので、脱初心者を望む方は、こちらも後で軽く目を通しておいた方がよいと思います。
*2: 正しくはアセンブリ言語ですがアセンブラと呼んでも普通は通じます。 |
*3: 気休めですが(ォ |
まずは、動かす電子回路を製作しちゃいましょう。我々は、まだ初心者ですらないので、凝った物は作りません。大した目的もないですし(ォ 手段のためなら目的を選ばないぜ! 物理的に必要なものは次の通りです。電子部品を入手するには、秋葉原であれば秋月電子通商や千石電商などで買えるでしょう。通販もやっていますので便利です。部品の金額は参考価格です。
ざっと勘定しても2000円以下で揃います。パソコンと工具は無理ですが(ォ お小遣い程度で気軽に遊べますね〜。これだけ安ければ、失敗してダメだったとしてもダメージは少ないですよ(ォ
次からは、各部品について簡単に説明します。
1.パソコン
後述のプログラマはMS-DOSプロンプト(コマンドプロンプト)で使います。パラレルポート版は、Windows95,98,Meだけにしか対応していないようです。シリアルポート版はWindowsNT,2000,XPのNT系でも使えます。USBを変換する機器は使えないようです。よって、シリアル、パラレルポートのいずれかが必要となりますので、ノートパソコンだと辛いものがあると思います。最近だとレガシーフリーで、デスクトップでもそれらが付いてないものがあるかもしれませんね。後述する最も安価で簡単なプログラマを使うには、パラレルポートがどうしてもいりますので、パソコンに付いてない人は何とかしてください(ォ 私はIBMの2609-53JというB5ノートパソコンをWindows98で使っていますが、シリアルもパラレルもついているので(ていうか付いてるのを狙って買った)重宝しています。
2.AVR StudioATMELのWebページで公開されていますのでダウンロードしましょう。英語のページが表示されるのでビックリしますが、怖くないので逃げないでください(ォ
Productsのページから、Microcontrollers→AVR 8-Bit Risc→Tools & Softwareとリンクをたどっていくとダウンロードできます。付属のインストーラで簡単にインストールできると思いますが、ファイルの数が多いせいか、終わるまで結構時間がかかります。
現在ダウンロードできるAVR Studioは、Ver4.07と3.56がありますが、私はなんとなく3.56の方を使っています。ファイルサイズが小さかったのと、4.07は出たばかりの感があるので、バグが取れてそうな3.56を選びました。この本でも3.56を使用します。また同様に、アセンブラの設定ファイルをここでダウンロードする必要があります。ダウンロードページの左にあるSearchボックスに avr000 と入力して検索してください。そうすると、AVR000: Registerなんたらかんたらといった項目が検索されますのでクリック。その先でAVR000.ZIPをダウンロードします。
3.プログラマChaN氏が公開されているWebページ「えるむ」にて、AVRのプログラマが3種類発表されています。この中で最も安価で簡単なのはパラレルポートを使ったプログラマです。(44頁参照) なんとD-Sub25Pコネクタと抵抗4本、ピンヘッダのみで作れます(笑 書き込みソフトも同じところで公開されていますのでダウンロードしましょう。ただし、パラレルポートの仕様の隙間をねらって動作させるらしく、パソコンの個体差で動かないことがあるかもしれません。私が作ったのは軽快に動いています。
シリアルポートを使ったものも割と安価にできそうです。こちらは汎用ロジックICの74HC126(千石とかで売っています)を使って、信号の電圧レベル変換と、ハイインピーダンスによる回路の切断をするので、より安全確実に動くでしょう。また、WindowsXPなどのNT系OSでも動かせるようになったみたいです。私も試しに一つ作ってみましたが、見事に動きませんでした(ォ ※その後もう一回挑戦して動く物が作れました。
4.AVRとりあえず、最も安価なAT90S1200を使ってみます。秋月なら1個200円で買えます。安すぎ。その代わり、SRAMが無いのでスタックが3レベルしかとれないとか、使える命令が少なくなっているとか、不便なところは後で出てきます。しかし、初心者見習いの我々には十分過ぎるほどの機能を持っていますので、しばらくはこれのお世話になることでしょう。
もしも、AT90S1200では機能的に物足りなくなったなら、SRAMが入っていて、使える命令の多いAT90S2313に交換します。AT90S2313は、AT90S1200とピンの配置が完全に一緒なので、他のハードを変えることなく差し替えられます。割り込みの初期化とスタックの初期化の部分を書き換えれば、プログラムもそのまま動くでしょう。秋月で買うと1個270円です。安すぎる。こないだまで370円だったはずなのに、何かが間違っている(ォ
5.その他 電子部品1) セラロック(とりあえず4MHz)
AVRを動かすクロック周波数を作り出すための発振子です。端子が3本出ているものは、中にコンデンサを内蔵しているので、外部にコンデンサをつける必要がなくなります。迷わず3本脚を使いましょう。
パソコンの場合、CPUの周波数が高ければ高いほど処理速度が速くなってよいのですが、AVRは制御用であるため、あまり速すぎてもプログラミングの段階で不便なところがでてきます。例えば時間待ちループでレジスタを何個も使わなければならなくなるとか。他には思いつきませんが(ォ
AT90S1200-12PCは定格で12MHzまで使えます。実際はオーバークロックでも使えるようで16MHzくらいまでなら普通に使えるようです。マイクロコントローラAVR入門には22MHzまで問題なく使えたと記述されています。AT90S2313-10PCの定格は10MHzです。ちなみに、12PCとか10PCとかは定格の周波数とパッケージの形と動作温度範囲を表しています。マニュアルに書いてありますので確認してみてください。
2) 5V出力の3端子レギュレータ(7805とか)
型式にもよりますが、基本的にAVRはDC5Vの電源で動作します。そこで+5Vの電圧を作らなければならないのですが、3端子レギュレータを使えば簡単に+5Vが作成できます。
3端子レギュレータはその名の通り、端子が3本出ており、IN,GND,OUTの名前がついています。INとGNDに入力電圧を加えると、OUT,GND間に+5Vが出力されます。入力電圧は、出力電圧よりも2.5V以上高い電圧にしなければなりません。入力電圧が出力電圧に近いと、レギュレータ自身が動作する電圧が足りず、出力にノイズが乗るらしいです。また、7805を使った場合、IN側は30Vくらいまで入力できる、ような気がしましたが(ォ、入力と出力の電位差が大きいと発熱が多くなる(*4)ので、入力電圧が高すぎるのはよくありません。それと、定格電流の40%以上を出力するときは放熱器を付ける必要があります。実際に使うときは、発振防止用に下記パスコンを付けましょう。それと、電池の代わりにACアダプタなどを使う場合、リプルなどの電源変動が激しいので、入力側に100〜1000μF程度の電解コンデンサも入れた方がよいでしょう。
ちなみに、+9Vを出力するのは7809、+12Vを出力するのは7812といったような型式になっていて、出力電圧の種類は他にもたくさんあります。また、7805は+5Vを出力しますが、-5Vが欲しいときには7905を使います。±が逆になっているものがどのタイプにもあるようです。それと、7805の仲間に電流容量が少ないタイプの78L05というのがありますが、こいつは何故か、正面から見たIN,GND,OUTの脚の順番が、7805と逆になっています。画像に書いたので確認してください。私は見事に騙されて逆接続したために、レギュレータが燃えました(ォ 定格で100mAしか取れないので、今は使っていません。
*4: 発熱でエネルギーを捨てることによって、一定の電圧を出力しているのだ。多分(ォ |
3) パスコン0.1μF(積層セラミックコンデンサ 頭が青いやつ)
上記の3端子レギュレータの入出力側にそれぞれ入れます。最低でも出力側にこれが無いと、出力が発振するらしいです。どんな資料を見ても必ずついています。なので付けてください。付けないと実際どうなるかは、やったことが無いので知りません(ォ
パスコンとは、バイパスコンデンサの略です。何をバイパスするのかというと、電流の中の周波数(交流)成分のみを迂回させます。今回の電源は直流なので、余計な周波数成分(要するにノイズ)を除くために使うのです。また、あくまでバイパスさせるために使うコンデンサという意味であり、パスコンという商品が売っているわけではありません。お店でパスコン下さいなんて言ったら温かい目で見られてしまいます(ォ また、IC等の電源入力付近に取り付けて、短時間小容量の補助電源として使う場合もパスコンというようです。電気的には同じ現象が起きていると思います。今回は安価でサイズの小さい積層セラミックコンデンサを使います。
※コンデンサの定格の読み方 コンデンサのような小さな部品には、部品自体に多くの表示をすることができないので、定格表示が簡略化されています。今回は0.1μF(マイクロファラド)のコンデンサを使いますので、コンデンサの表面に104と書かれたものを選びます。104とは、10×104 [pF]の意味になります。105 pF = 102 nF = 0.1μFです。 |
4) 006P角型電池9V+電池ホルダ
006Pという型式の電池です。コンビニとかで普通に売っている四角い電池のことです。定格電圧は9Vなので、5V三端子レギュレータの入力にちょうどいいですね。電池にハンダ付けするわけにはいかないので、電池ホルダを使いましょう。
5) ピンヘッダ オスメス両方(お約束アイテム)
安価にコネクタを作るにはこれしかないような気がします。秋葉原でいろんなコネクタを探しましたが、最後は結局これに戻ってきました。
オスとメスの両方があり、1列、2列、ピン数、ストレート型、L型などいろいろ種類がありますが、一つ言えることは、何を使うにしても売っている中でピン数が一番多いものを購入しましょう。それを必要なピン数に後でぶった切って使います。オスピンは元々折りやすいようにくぼみがついています。メスピンにはくぼみがありませんが、切るところのピンを引き抜き、ニッパで切断、ヤスリで仕上げれば好きなピン数にできます。秋月ではピン数が変わっても何故か値段が同じだったりするので、この方が安く買えるのです。
メスピンは結構簡単に引き抜けてしまうので、ケーブルの先に付けるコネクタとしては、少し心許ないです。それが嫌な場合は、少々お金がかかってしまいますが、圧着式のピンを使うコネクタを使いましょう。電線にピンを圧着(*5)してコネクタへ差し込むと、ピンが抜けない構造になっています。専用の圧着ペンチが高いというデメリットもありますが、うまくやればラジオペンチでも圧着できるでしょう。
*5: ハンダ付けは行わず、金属の締め付ける力だけで電線をピンに固定する方法。 |
6) 発光ダイオード(適当な大きさのやつ なんでもいい)
いわゆるLEDってやつです。定番は赤、黄、緑でしょうか。青と白色以外はどれを使ってもほとんど定格が変わらないと思うのでなんでもいいです。そもそも、どのメーカーのどの型式かもわからないことが多いですし(ォ 10個150円くらいのやつとか、手ごろな大きさで安いのを買いましょう。
7) 押ボタン(a接点 押すと導通するやつ)
これもお好きなものを。今回は基板に挿せるタイプの方がいいですね。ON-OFFできればDIPスイッチとかでもかまいません。
ちなみに、押したときに導通する接点をa接点といいます。逆に、通常は導通していて、押したときにだけ開路する接点をb接点といいます。a接点とb接点の片側が共通になっているものをc接点といいます。
8) ユニバーサル基板(穴が開いていて片面に丸ランドがついているもの)
いわゆる基板です。これも適当な大きさのものを用意してください。1枚100円程度のやつで十分です。秋月のCタイプなら70円だったかな。
ランドとは、基板に付いているハンダ付けするための金属のことです。ランドの真ん中に穴が空いていて、穴へ通した電線等とランドをハンダで固定するわけです。ランドが基板の片側だけに付いているものと、両面に付いているものがあります。また、両面のランドが中で電気的につながっている穴をスルーホールと言います。
9) 20PのDIPソケット(丸ピンは高いので角?型)
AT90S1200が20Pなので20ピンのソケットを使います。秋月で10個100円です。1個だと20円か30円くらいで割高なので、後で使うと思って10個買いましょう。
ソケットなしでAVRを直接ハンダ付けしてもいいのですが、熱でAVRを壊してしまうこともありえますので、ソケットを使いましょう。また、ソケットを使えばあとでAVRを交換したいときにも簡単に外せるメリットがあります。DIP-ICの場合、2列のソケットを使いますが、ソケットの中には1列しかないものもあります。見た目はピンヘッダと似ていますが、よく見ると丸ピンのソケットです。ピンヘッダと同じように、好きなピン数で切って使います。それを私は、セラロックを取り付けるために使っています。後でクロック周波数を変えたい時に便利です。
10) 基板用の脚
脚です(ォ 本当はスペーサーと言います。スタックとか絶縁ポストとも言うかも。これは基板を浮かせるために使います。なぜそうするのかというと、普通は部品面の裏側をハンダ付けするので、脚を使って基盤を浮かさないと、部品側を作業台の方へ向けたときに、据わりが悪くてハンダ付けしづらいからです。力入れると逃げるし(ォ よって、使う部品の高さより長いスペーサーを選んで使いましょう。ハンダ付けが終わったら、今度は逆に部品面が上に来るように取り付けます。
11) 電線少々
あんまり太いと使いづらく、細すぎても使いづらい。難しいものですな。AWG24か26くらいのを買いましょう。φ0.5とかφ0.4くらい。AWG28でもいいかも。ちなみにAWG(AmericanWireGauge)表記だと数字が大きくなるにつれて電線が細くなります。
φ0.5というのは、電線の直径が0.5mmという意味です。また、0.5sqとか0.5□(スケアと読む)といった表示方法もありますが、これは電線の断面積が0.5mm2という意味になります。外側の、色がついた被覆のサイズは含まれていません。本来は、流す電流の大きさで電線の種類や太さを決めるのですが、電子回路は微量の電流しか扱わないので、使い勝手で選んでしまってかまいません。
12) 抵抗少々
正確には抵抗器と呼ぶ。秋月ですと100本100円で袋売りしています。即ち1本1円。相変わらずよく分からない価格設定です。1本3銭くらいで仕入れているのでしょうか(ォ 千石だと1本15円、または200本200円で売っています。LEDを光らせるときの電流制限用に使うので、300Ω〜470Ω(オーム) 1/8W(ワット)くらいあればいいです。秋月では1/8Wが売ってないので1/6Wを買いましょう。
単に抵抗と言えば、普通はカーボン抵抗を使います。抵抗値の精度はそれなりですが小型で安価です。もうちょっと精度が欲しい時には金属皮膜抵抗を使います。カーボン抵抗よりは高価です。とりあえず今回は330Ω 1/6Wのカーボン抵抗を使います。
ニッパで電線を切ります。ラジオペンチは抵抗の脚を曲げたりするのに便利。ストリッパは電線の被覆をむきます。無くてもニッパでむけますが、職人技がいります。ハンダごてと、こてを置く台、ハンダも回路製作には必要です。ハンダ吸取り器があるとやり直したいときに便利です。セロハンテープはハンダするときに部品が動かないよう、軽く固定するために使います。テスターは、組上げた回路の配線確認や、回路の電圧をチェックするために必要です。
さて、部品がそろったのでさっそく組み立てたいところですが、まだ回路がありませんでした。とりあえず回路図はこれ。
簡単そうな回路ですが、これだけでも設計するために知っておくことはたくさんあります。一番下にあるのが電源回路です。3端子レギュレータ7805(REG1)を使って、9Vの電池から+5Vを作っています。3端子レギュレータの脚の名前は、部品説明の写真を参照してください。入力、出力両方にパスコンを付けます。ノイズとかを考えると容量が大きめのアルミ電解コンデンサを入れた方がよい場合もありますが、とりあえず動くので今回はこれでいいです。AT90S1200(MCU1)は、外部に4MHzのセラロック(Xtal)を取り付け、Vccに+5Vを印加します。セラロックはピンが3本出ていますが、真ん中のピンをGNDにつなぎます。両側のピンはどちらをつないでもOKです。この状態で+9VとGNDに電池をつなぐと、プログラムをまだ入れていないので何も起きませんが、ワンチップマイコンとしては動いていることになります。
右の方にあるCN1は、プログラマと接続するピンヘッダです。プログラマ側の信号名、端子番号と合うように結線します。私はここを間違えて、プログラムが書き込めずに悩むことが多いです。プログラマも自分で製作することになりますので、ピンの配置は適当に決めちゃってください。MCU1のPB0〜PB7,PD0〜PD6は、デジタル入出力(以下、I/Oポート)として使えます。設定によって、ピン毎に他の機能を持たせたりできますが、基本的には入力と出力のON-OFFができるのだと覚えましょう。入力として使うのか、出力として使うのかはピン毎にプログラムで設定できます。電源投入時には全て入力に設定されています。
今回は、PB0に押ボタン(PB1)・・・すんませんPB1ってダブってますね(ォ 今気づきました。MCU側はPortBの1番という意味で、押ボタンの方はPushButton1の略です。とりあえずこのまま進みます(ォ とにかくPB0に押ボタンをつけて、PB1,PB2にはLEDランプ(LED1,LED2)をぶら下げます。
LEDには極性があるので取り付ける際に気をつけましょう。LEDをよく見ると、2つある脚の長さが違っています。長い方をプラス側につなぎます。もし脚を切ってしまって分からなくなった場合は、あきらめて捨てましょう。嘘です(ォ もう一度LEDをよく見てください。中の金属の大きさが違っていますよね。小さい方がプラスになっています。
ところで回路図をよく見ると、入力は押ボタンを押したときGNDにつながるようです。この場合、I/Oポートから電流が流れ出てきます。そして、出力の電流は、LEDが点灯できる方向に流すのですから、I/Oポートに電流が流れ込むようになっています。入力と出力とで、I/Oポートに流れる電流を逆にしているのはなぜでしょうか。ここからは入出力の設計の考え方を説明します。
まず入力側を説明します。押ボタンPB1を押すとPB0にはGNDが接続されるようになっています。電圧で言うとPB1を押したとき、PB0に0Vが入力されます。AVRの内部では、入力端子の電圧が0Vのとき0、5Vの時に1であると認識します。0と1というのは、デジタル信号ではOFFとONの意味だと思ってください。つまり、PB1を押すとOFF、PB1を放すとONになります。逆じゃん。でも実はこれでいいのです。
デジタル回路では、2つの状態しか取り扱いません。すなわちONかOFFか、有るか無いか、HiかLoか、1か0か。AVRは基本的に正論理で動いているので、電圧レベルが高い方(H、Hi、ハイレベル)を1、低い方(L、Lo、ローレベル)を0と表現します。正論理とは、電圧レベルが高い方を能動とみなす事をいいます。能動ってなんやねん。簡単に言うと、電圧レベルが高い場合に「おお!うごいとる」と感じることです。正論理の逆は負論理です。逆なので、電圧レベルが低い場合に「おお!うごいとる」となります。能動の場合を1、非能動の場合を0と表現します。
AVRはあくまで正論理で動いています。しかし、今の状態が能動かどうかは、AVR側と人の感覚とで常に一致するとは限らないのです。それによって、押ボタンの動作が見かけ上、負論理になることがありえます。上記のボタンの例もそうですし、例えばAVRの出力を0にした時にLEDランプが点灯するような場合、AVRは出力を非能動にしたのに、人は能動であると思うでしょう。
話がややこしくなってきました(ォ 押しボタンの動作が負論理になる原因ですが、今回は入力端子をプルアップするからです。AVRは入力端子を、プログラムによってプルアップに設定することができます。プルアップってなんですか?
プルアップの絵
適当な絵でごめんなさい。プルアップとは電源のプラス側を常に入力しておくことです。つまりAVR内部では常時H(Hi、ハイレベル、1)がI/Oポートへ入力されることになります。逆に電源のマイナス側、つまりGNDを常に入力しておくことをプルダウンといいます。AVRのプログラム設定でプルアップするということは、AVRの内部でプルアップされることになります。この時、AVRの外部からマイナス側(GND)を入力するとL(Lo、ローレベル、0)がI/Oポートへ入力されます。プルアップ抵抗に電流が流れるので短絡はしません。PB0に何もつながない場合、GNDをつないだ場合、それぞれPortにどんな電圧がかかるか、理解できるでしょうか。ちなみにAVRの入力はCMOSなので、理論的には入力電流が流れません。あとはオームの法則とキルヒホッフの法則を知っていれば分かるでしょう。
☆特別解説コーナー 電気回路基礎
オームの法則は義務教育で習うので思い出していただきたい。キルヒホッフの法則はなんのこっちゃという感じですが、言葉が難しいだけの話で、図にすればすぐ分かります。第一法則はこんな感じです。Ia、Ib、Icは電流。 入ってくるのと出ていくのは一緒です、というだけの話です。電線の途中でいきなり電流の大きさが変わるなんて事はないと言っているのです。こんなの当たり前だと思った方は電気の才能があるので自信を持ちましょう。電圧の方も同じことなので図は省略します。 |
ということでプルアップの動作は分かりましたが、なんでこんなことをするのでしょうか。例えば、プルアップもプルダウンもしていない状態で、入力端子に何もつながなかったらどうなるでしょう? 何もつないでないのだから0Vが入力されるだろうって? そうなるとは限りません。0VつまりGNDにつながってない端子は0Vではない自然電位を持っています。つまり、GNDに対していくらかの電圧が加わります。その高さは不定です。計ってみないと分かりませんし、もしかしたら計ろうとしても計れないかもしれません。さて、その高さの分からない電位は、AVRの中で1(Hi)と認識されるでしょうか、0(Lo)と認識されるでしょうか。難しいですね。5Vなら1、0Vなら0なのは分かりますが、もしたまたま2.5Vだったらどっちでしょうね。デジタル回路では2つの状態しかとらないので、必ずどちらかになっているはずですが、これでは分からないので困ります。デジタル回路とは言っても、あくまでアナログ回路をデジタル的に使っているだけなので、このような問題が出てきてしまうのです。
デジタル回路の1と0の境目をスレッショルド・レベル(threshold level)といいます。日本語だと「しきい値」。このしきい値より上か下かで0か1のどちらかが認識されます。よって、入力に何もつながないで、なんだか分からない電位になっていたのでは困ります。しきい値より上にあるのか下にあるのかわからないからです。しかも、もし端子の電位がたまたましきい値の近くにあったなら、ちょっとしたノイズでしきい値をまたいで電位が上下します。0か1のどっちかに留まっているならまだしも、かってにON,OFFされてはたまったものではありません。よって予め電位をどちらかに引っ張っておくことにします。それがプルアップ(プルダウン)です。AVRではプログラムによって入力を個別にプルアップすることができます。よって外側に余計な回路をつける必要はありません。
ちなみに、AVRのRESET端子には、文字の上に ̄が付いています。これは、この端子が負論理であることを意味します。負論理なので、電圧レベルが低い方、つまりGNDへつないだとき、リセットがかかる(能動)ようになっています。実はAVRのRESETの端子にはプルアップの回路が標準で組み込まれています。そして、入力がローレベルになったときリセットがかかります。よって何もつながないでおいても、なんらかの原因で勝手にリセットされるなんてことが起きにくいようになっています。しかし現実的には、強いノイズに対してそれで全くリセットがかからないかというと、そうとも言いきれない(*6)ので、安全性を求めるために外部でプルアップする場合や、リセット回路をつけることがあります。今回はあまり気にせず何もつなぎません。
リセットの動作は押ボタンの動作と似ていますね。つまりプルアップしたときには負論理になります。ただし、AVRの入力はあくまで正論理で動いているので勘違いしないようにしましょう。ボタンを押した時にローレベル、放した時にハイレベルが入力されるよう回路を作成した為に、全体として負論理で動いているように見えているだけなのです。負論理になると、入力が逆になってしまうので、勘違いしないよう気をつけましょう。プルダウンの場合は正論理になるので分かりやすいのですが、残念ながらAVRはプルダウンできません。なぜかは詳しく知りませんが、理由の一つは昔からの慣例です。TTL(*7)では、しきい値が電源電圧の中心より0Vの方に近いので、プルダウンよりもプルアップしたほうがノイズなどで誤動作する可能性がより低くなります。よって、デジタル回路では一般的にプルアップする方が多かったようです。AVRはCMOSなので、プルダウンでも、しきい値の差はないのですけどね。
*6: 煮え切らないですが(ォ |
*7: バイポーラトランジスタで構成されているIC。 |
次に出力の説明です。出力側にはLEDと抵抗がついており、出力をOFFにすると電流が流れ込んでLEDが点灯するようにしています。AVRの出力は、電流を流すことも、電流を流れ込ませることもできます。I/Oポートから電流を流すときはソース電流、逆に電流を流れ込ませるときはシンク電流と言います。資料を見るとシンク電流は、ポートの1ビット当りに20mAまで流せるようです。ポート全体(*8)では一度に80mAくらいまでです。ソース電流については書いてあるところが見つからなかったのでよくわかりませんが、通常、デジタルICのソース電流は、シンク電流の半分も電流が流せません。1/4くらいのこともあります。よってここではシンク電流が流れるような回路としました。ただし、シンク電流の回路は、出力を0にするとLEDが点灯する、すなわちプルアップした押ボタンと同じように、見かけ上の負論理となりますので、プログラミングするときは勘違いしないように気をつけましょう。
R1,R2は電流制限用の抵抗です。LEDはいわゆるダイオードの仲間で、順方向には電流を流しますが、逆方向には電流を流しません。整流用のダイオードは、電流を流したり、流さなかったりと両方の働きで使いますが、LEDは必ず電流を流す方向(順方向)で使用します。絵的に言うとダイオードの三角が尖っている方に電流が流れ、そのときに光を発します。光っているときには、LEDの両端に1.5〜2V程度の順方向電圧がかかります。これは一般的なLEDに共通する特性(*9)です。2Vと考えてしまってよいでしょう。
*8: PortB、PortDに、それぞれ80mA流せるという意味。 |
*9: ただし、青と白のLEDは特殊なので、特性を必ず確認して下さい。 |
はて? 電源は5Vなのに2Vしかかかってないとはどうゆうことでしょうか? 残りの3VはGNDに直結されたと同然ですので、短絡(ショート)しているのと同じような現象が起きます。よって、電流がいくらでも流れようとしますので、どっかが壊れます。LEDが壊れるならまだしも、AVRが壊れたら泣きをみます。そんなん嫌やがな。そこで、順方向に電流を流したときに、残りの3Vの電圧を抵抗にかけることで、電流がダダ漏れしないようにします。今回は330Ωの抵抗をつけたので、流れる電流は10mA程度になります。オームの法則で。シンク電流は20mAまで流せるので、これなら問題なくLEDを点灯できます。
・抵抗値の決め方
オームの法則より(E = I×R)なんだか適当な感じがしますがこんなもんです(ォ LEDにかかる電圧は、厳密には電流値で変わってきますし、色によっても特性が微妙に違います。そもそも抵抗自体に±5%程度の誤差(*10)がありますので、実際に何mA流れるかはテスターで測ってみないとわからないでしょう。電流の大きさによって、LEDの光る明るさが多少変わるだけなので、おおよそ10mA流れるように設計しているのです。実際には9mAや11mAになっていたとしても何ら問題ありません。部品が壊れる程の大きな電流が流れなければよいのです。
*10: 330Ω±5%の抵抗は、実際には313.5〜346.5Ωのいずれかの値になっている。 |
それでは、実際に組み立ててみましょう。私が組み立てたらこんな風になりました。
最も注意する点は、AVRのピンの位置を間違えずに配線することです。AVRを上から見ると、半円のくぼみがついているのが見えると思います。そっち側を上にすると、回路図とピンの位置が一致します。よく見ると回路図にも半円が書いてありますね。ピンの位置がわかるよう、このように表記されるのが一般的です。
基板上の部品の位置は決まっていませんので、当然ながら自分で考えて配置しましょう。最初はどこに置こうか悩むでしょうが、自分で決めるということはそうゆうことです(^^ 配線ルートと見た目のバランスを考えながら決めるのがコツですよ。配線ルートは、なるべく他の配線と交差しないようにしましょう。また、個々の配線ルートが短ければ、実際の配線は抵抗やLEDの足を折り曲げるとか、足を切った残りを使うだけでほとんど済んでしまうはずなので、電線は使わないかもしれません。
組み立て終わったら、必ずテスターで導通チェックをしましょう。回路図通りに配線されているかどうか、一本ずつ確認して回路図にチェックを入れていくのがセオリーです。回路が動かない場合のほとんどは、配線を間違えていることが原因です。また、ハンダ付けは技術を要しますので、きちんとくっついているように見えても、導通しない場合が多々あります。見た目ではなかなか分かりませんので、この意味でも必ずテスターで確認しましょう。
これでハードの説明は終わり。つぎはプログラムを作成します。
ハードの説明だけで疲れてしまいました(ォ しかし、これから先の方が長いので少々覚悟してください。ここからはソフトウェアをいじってみることにします。AVRのソフトは基本的にアセンブラで記述するのでした。ということで、とにかくアセンブラをいじってみることにしたいのですが、どんなことをやるにしても基礎知識というものが必要でありまして、しばらく基礎的なことをつらつらと書き記します。
AVRを制御するには、AVRをどのように動かすか定めたものを、予めAVRの中に入れておく必要があります。どのように動かすか記したものをプログラムと呼びます。運動会や学芸会のプログラムと同じ意味です。運動会のプログラムは上から順番に競技を行っていき、一番下(閉会式?)まで行ったら終了しますよね。しかし、AVRのプログラムは、基本的には上から順に実行されますけれども、時には条件によってどちらかを選択し、あるいは同じ事を何度も繰り返します。また、最後まで行ったら最初に戻って、また始めから実行されたりもします。押ボタンが押されている条件で動作を選択したり、条件によって繰り返し回数を変えたりすることで、AVRの外側から人間の目で見たときに、違う動きをさせることができるのです。
しかし、どんなに複雑な動きであっても、予め入れておいたプログラムは、予め決められた動きしかできないのです。つまり、プログラムを作るということは、AVRにやらせたい全てのことを予め人間の頭で考えて! 全ての動作を予め用意しておくことと同じです。後でアレをやらせたかったなんて嘆いてもどうしようもありません。なにせ予め入れておいたプログラム以外の動きを、AVRが勝手にやってくれるなんてことはありえないのですから。
AVRを制御するプログラムとはどんなものでしょうか。AVRは機械語という命令しか理解することができません。機械語とは人間にとっては数字の羅列です。例えば2進数で表すと010011001000101・・・、8進数だと23105・・・、16進数なら4C8A・・・といった具合で、いったい何を意味しているのか普通の人にはさっぱり分かりません。普通じゃないと分かるらしいですが(ォ あと、今書いた数字は今適当に考えました(ォ このように人間には理解しづらい命令で、AVRを制御することは問題があるため、機械語の意味に対応する英単語(の略)に置き換えて、機械語の変わりに使う方法が考えられました。これがアセンブリ言語です。
ということで、AVRを制御するプログラムを作成するために、この本ではアセンブリ言語で記述することにします。もう一度言いますが、アセンブリ言語とはAVRが理解できる機械語を、人間が理解しやすい単語(ニモニックと呼びます)に1対1で置き換えたものです。単なる数字の羅列を眺めるよりは簡単に理解できるという寸法です。例えば、汎用レジスタ(*11)に数値を入れるニモニックはLoad Immediateを略してLDI。プログラムの順番を飛ばして、別の場所にあるプログラムを実行させるニモニックは、Relative Jumpを略してRJMP、などなど。なるほど、単なる数字を眺めているだけよりは、人にとって遥かに理解しやすそうです。
*11: AVRの中にある一時的に数値を記憶する場所 |
しかし、人が理解しやすくなったのですが、AVRにとっては全く理解できなくなってしまいました。そこで、アセンブリ言語で記述したプログラムを、最終的に機械語へ変換する必要があります。その変換をするためのソフトをアセンブラと呼びます。アセンブリ言語のニモニック(+それに続く値の組、例えばジャンプ先の値とか)と機械語は1対1の関係にありますから、相互に変換することができます。ちなみに、機械語からアセンブリ言語に変換するソフトを逆アセンブラと言います。また、アセンブリ言語をアセンブラで機械語に変換することをアセンブル、人間がニモニックと機械語の対応表を見ながら自力でアセンブルすることをハンドアセンブルといいます。こんじょのある人はやってみましょう(ォ また、巷ではアセンブリ言語のことを、単にアセンブラと呼ぶことが多いので、以降はアセンブラと呼ぶことにします。
AVRを制御するためのプログラムは、BASICやCやPascalなど、アセンブラ以外のプログラム言語でも記述できます。最終的に機械語へ変換できればいいのです。しかし、これらの高級言語は高級と言うだけあり、機械語に変換するソフトを手に入れるためにお金を払わなければならない(*12)ことが多く、また言語を理解して使用するまでに時間がかかります。よって、ここではATMELさんがタダでお配りになられているアセンブラ(が使えるAVR Studio)を使用します。
そもそもAVRは、いったい何ができるのでしょうか。AVRは機能、性能が違うものが何種類か用意されていますが、AVRの外側に対してできる基本的なことは、外部入出力であるデジタルI/Oを使って、電圧のON、OFF状態を検知することや、外部に電圧を出力することです。具体的な例としては、スイッチのON、OFFの状態を知り、LEDを点灯させることができます。その他にも、アナログコンパレータ(*13)とか、シリアル通信機能をハードで持っているとか、いろいろ機能がありますが、今回は使用しません。とにかく、入力にしても出力にしても、ON、OFFの制御ができるのだと覚えておけば間違いありません。しかも人間が認識できないくらいの速さで動作するので、様々な制御を実行させることができます。その気になれば、テレビに絵を表示させるなんてこともできます。私は試しにやってみて失敗しましたが(ォ
逆にAVRの内部でできることは、足し算ができます。・・・・・他には何もできないんかい(ォ
他にも論理演算、一時的な記憶、時間を数えることができます。プログラム的には、連接、選択、繰り返しのプログラムの3要素は全て表現できるので、どんなアルゴリズム(*14)でも表現できると思います。論理演算は、bitのON、OFFの演算をand,or,xor,notで表現します。一時的な記憶は、32個もある汎用レジスタと、内部SRAM(*15)に数値を出し入れすることができます。また、時間を計るためのタイマが内蔵されています。
*12: 高級言語の高級とは、人間の言語により近いという意味であって、金銭的に高価という意味ではない。要するに冗談です(ォ |
*13: 2つの電位の高低を比較してデジタル値に変換する回路。 |
*14: 目的の動作をさせるために、どのような順で命令を書くかということ。 |
*15: いわゆるメモリ。残念ながらAT90S1200にはありません。 |
四則演算、加減乗除、足引掛割は当然できるのですが、例外がありまして、2バイトの減算命令が何故かありません。また、乗除算命令は、AT90S1200にはありません。仕方ないので、2バイトの引き算はマイナスの数字を足したり、掛け算、割り算は、足し算と引き算で表したり、ビットシフトで表します。例えば掛け算は、2×3であれば2を3回足すことで表現できます。割り算はあまり使わないと思いますが、例えば6÷2なら、余りが0になるまで2を引き、引いた回数を数えることで表現できます。なんだかあんまり凄いことができないように思えてきましたが(ォ、一つだけ凄いと言えることは、とにかく実行する速度が速いということです。クロック周波数を10MHzで動かした場合、AVRは一つの命令を0.1μ秒で実行します。1秒の1000万分の1です。こんな速さでON、OFFされた日にゃ、ONしてるんだかOFFしてるんだか、人間には分かりゃしません。
ということで基礎知識はこのへんにして、アセンブラを早速いじってみたいと思います。ところで、アセンブラは何で記述すれば良いのでしょうか? ここで紙と鉛筆を持ち出す人はあんまりいないと思いますが(ォ、一応説明します。
AVR Studioは、拡張子が.asmであるファイルをアセンブラのファイルとして認識しますが、中身は単なるテキストファイルです。ですから、テキストエディタや、AVR Studioが内蔵しているエディタを使って記述します。もちろん、Windowsのメモ帳を使うこともできます。ワープロソフトでもできますが、拡張子が.asmだとそのまま読み込めないとか、保存する時にテキストファイル(.txt)で保存して、あとで.asmにファイル名を変える必要があったりするかも。面倒なので普通はテキストエディタを使います。それと、さっきは止めませんでしたが、紙に書いてOCRで読み取るなんて事も、もしかしたらできるかもしれません(ォ だがしかし、そんなわけの分からないことをするのは止めましょう。いや、お願いだから止めてください(ォ
とりあえず、手軽にメモ帳でも起動しましょう。Windowsなら最初からインストールされているはずです。新しいファイルなのでまっさらです。白いぜ。アセンブラは全て半角の英数字で記述しますので、日本語は使いません。よって、IME(FEP?)は不要です。それでは次ページのコードを書いてみましょう。
・穴埋めコラムその1 余談ですが、コードとは、頭で考えたプログラムを具体的に記述したものです。また、記述する行為をコーディングといいます。更に、コードを書くことを"打ち込む"と表現することもあります。人によって違うかもしれませんが、打ち込むとゆう表現は、既にあるコードを、そのまま書き写すようなときに使う気がします。プログラムとコードは違うものかといえば、ほとんど同じだと思います。使い分けは適当です(ォ また、機械語以外の言語で書かれたコードを、ソースコードと呼びます。ソースコードが書かれたファイルはソースファイル。両方とも単にソースという場合もあります。ソースとは源という意味で、機械語に変換する前の元プログラムのことを、一般にそう呼ぶのです。 |
(1) LEDを点灯するプログラム
.include "1200def.inc" .org $0000 rjmp reset reti reti reti reset: ldi R16,0b11111110 out DDRB,R16 sbi PORTB,0 sbi PORTB,1 loop: rjmp loop
コードの左側が開いているのはTABです。キーボードの一番左にあるTABキーで入力できます。ワープロなどではほとんど使わないでしょうが、コーディングする時はコードを見やすくするためによく使います。もちろんスペースを適当に入れてもかまいません。アセンブラにとっては、どちらもコードの区切りとして認識されます。
どうですか?打ち込み終わりましたか? 誰ですかコピペしたのは(ォ もったいぶった割にはこんだけしかありませんが、これでも立派なアセンブラのコードです。これをアセンブルしてAVRに入れれば、きちんと動いてくれます。
では、内容を説明します。このコードは、PB2につなげたLED2を点灯させるコードです。赤いLEDをつなげたのであれば赤く光ります。そんだけです(ォ 別に馬鹿にしているわけではありません。前にも書きましたが、AVRができることは、入力と出力を高速にON、OFFさせることです。その内の出力の制御をやろうって魂胆です。入力については後でやります。また、基礎知識のところで飛ばした内容があるので、ここでまた知っといてもらいます。例によって基礎知識の方が多いです(ォ
コードの書き方の基礎の基礎を説明します。アセンブラのコードは1行に1つの命令を書いて行きます。命令には、AVRが本来もっている命令と、アセンブラが用意する擬似命令があります。擬似命令とは、AVRが持ち合わせていない命令を、アセンブラが代りに補う命令のことです。これには、複数の命令の組み合わせをマクロ命令として一つにまとめたリ、プログラムを書き込むメモリの位置を決めたり、レジスタに好きな名前を付けたりできる命令などが含まれます。これをアセンブルすると、最終的にAVRの命令(の組み合わせ)や、実際のレジスタ名に置き換えられるわけです。
今まで散々コードコードと言ってきましたが、コードとは厳密には一つの命令のことです。今まで言っていたコードは命令群と言った方が、意味が通じるかもしれません。英語だとcodes。ですが、日本語読みだとやっぱりコードなので、命令群の事を、ここではコードと呼ぶことにします。そして、一つの命令は、擬似命令を含めて単に命令と呼ぶことにします。
それでは、打ち込んだコードを解読していきましょう。命令は基本的には一番上から順番に一行ずつ実行されていきます。各種条件による分岐命令や、ジャンプするための命令が出てきたときにだけ、順番が変わります。ということで、まず一番上にある命令から読んでいきましょう。
.include "1200def.inc"
なんですかねこれは。Includeってのが英語っぽいので辞書で調べてみますと、含めるとか盛り込むとか、そんな感じの意味らしいです。この命令は、いま作成しているアセンブラファイルとは違う、別のファイルの内容を読み込む時に使います。ここでは1200def.incという設定用テキストファイルの内容を読み込んでいるのです。
ちなみにこれは擬似命令です。行の最初がピリオド( . )で始まる命令は全てアセンブラの擬似命令です。話を戻します。1200def.incの中身は何かと言うと、AT90S1200を使う上での設定値や、メモリアドレスの定義などが記されています。つまり、このファイルを.includeで読み込んでおくだけで、基本的な設定が済んでしまいます。また、アセンブラのコーディングをする上で便利な定義も含まれています。よって、この命令はプログラムの最初に記述することにします。それと、今回はAT90S1200を使用していますが、他のAVRを使う場合には、それぞれ専用の設定ファイルが用意されていますので、そちらを.include(インクルード)します。例えば、AT90S2313であれば2313def.incをインクルードします。これらの設定ファイルは、AVR Studioをダウンロードした時、一緒に拾ってきたAVR000.ZIPの中に入っています。
次に行きましょう。
.org $0000
orgって何でしょうね。オルグ?(ォ $0000ってのもよく分かりませんね。こんなの学校で習っていません。ふぅ。説明しよう! この命令は、AVRの中にあるコードセグメントの、どのアドレスからプログラムを書き込むかを指定しているのだ! 意味不明ですみません。
AVRの中というのは・・・これは分かりますか(ォ コードセグメントとは、AVRの中にあるフラッシュメモリの事を指しています。フラッシュメモリとは至極簡単に言うと、電源がなくなっても書き込まれている内容が消えない(*16)種類のメモリの一つです。でき上がったプログラムをこの中に書き込むことで、電源が投入されたときに目的の動作をさせることができます。もしも、電源がなくなると内容が消えてしまうメモリであったなら、電源を入れる度にプログラムを書き込まなければならなくなり、とても面倒ですね。それと、フラッシュメモリは内容を電気的に書き換えできるという特徴も持っています。書き換え可能回数の仕様は1000回となっていますが、普通に使っている分には1000回も書き換えしないでしょうから問題ありません。飽きるまで使えると思います(ォ
*16: 不揮発といいます。 |
とにかく、でき上がったプログラムをコードセグメントに書き込むのですが、そのコードセグメントのどの場所にプログラムを書き込むのかを指定するのが.org命令です。ちなみにセグメントを辞書で訳すと、部分とか切片とかなにかの一部分のような意味になるようです。個人的には領域と訳した方が私はしっくりきますね。そんな事はどうでもいいですね(ォ
気を取り直して、次に続く $0000 は、コードセグメントのアドレスを示しています。0が4つ並んでいるのですが、即ち0の事です。何で4つも書くのよ! とお思いでしょうが、説明しますのであせらないでください。また、0の前に $ が付いていますが、この数字が16進数で表現されている事を示しています。出ました16進数。そんなの義務教育で習わん。しかしご安心を。何進数であっても0は0なのです。よって、単に0と書いても実は同じなのです。
物を投げないでください(ォ AT90S1200のコードセグメントのアドレスは、アドレスバスのビット数により、1ワード、即ち4桁の16進数で表します。4桁より多くはなりません。そこで、4桁に満たない数字の場合、4桁になるように0を埋めて行くのです。こうすることによって、アドレス表現の進数と桁をコード全体で統一させます。とどのつまり、コードを見やすく理解しやすくしたいためにやっている(*17)のです。そのために、コードセグメントの出発点、即ち0番地の表現を、$0000としているわけです。
まとめると、.org $0000は、次の行のコードを、コードセグメントの0番地から書き込むように指定する命令です。
*17: この辺は人によってポリシーが違います。ようするに、こうしなければならない必要は全くない(ォ |
先程からアドレスとか番地という言葉がでてきますが、概念的なものなのであまり悩まないで受け入れてください。アドレスとは、いわゆる住所の意味です。番地とは、具体的な住所の場所を数字で示したものです。番地の事をアドレスということもあります、ややこしいですが。コードセグメントのフラッシュメモリへアクセスする時に人間が理解しやすいよう、フラッシュメモリの記憶素子には、順番に数字が割りあてられています。そうすることでフラッシュメモリの中の位置を特定できるのですが、その様子が住所の意味する所に似ているので、アドレスと呼んでいるのです。たぶん(ォ 住所が分かれば地球上のどの場所なのか分かりますよね。それと同じです。
ところでコードセグメントがあるのですから、コードでないセグメントもあるのでしょうか。実はあります。今まで面倒なので黙っていました(ォ データセグメントとEEPROMセグメントいうものがAVRの中にあります。
データセグメントはその名の通り、データを読み書きするためのセグメントであり、各種専用レジスタやI/Oポート、SRAMなどがこの領域に割りあてられています。つまり、I/Oポートの入出力を行うには、この領域を読み書きすることになりますが、インクルードした設定ファイルでアドレスに名前が付けられているので、普通はデータセグメントのアドレスを気にする事はありません。
データセグメントに書き込んだデータは、プログラムで明示的に呼び出さない限り、勝手に読み込まれる事はありません。逆にコードセグメントへ書き込まれたコードは、電源を入れた時に$0000番地から読み込まれ、順次実行されて行きます。また、データセグメントにあるI/Oポートや各種レジスタ、SRAMなどは、電源を切ると内容が消えてしまうため、プログラマで予めデータを書き込んでおくことはできません。
EEPROMセグメントは、EEPROMのセグメントです。そのまんまです。AVRには、データを入れておくためのEEPROMが用意されており、EEPROMセグメントに割りあてられています。EEPROMはフラッシュメモリと同様に、電源を落としてもデータが消えないメモリの一つです。EEPROMセグメントにデータを書き込むには、プログラムと同様にプログラマで書き込む方法と、プログラムの中で、EEPROMセグメントにアクセスする命令を使う方法があります。コードセグメントには、プログラムから書き込むことはできませんが、EEPROMにはできるのです。よって、プログラム中で発生したデータを保存するような目的で使うことになります。
ちなみにデータセグメントとEEPROMセグメントの先頭アドレスは両方とも0です・・・・・
あかんやん!コードセグメントと一緒やん!! なんで怒られてるの?(ォ
コードセグメントと、データセグメント、EEPROMセグメントの先頭アドレスは、どれも0ですが、AVRの中では物理的に異なる番地です。物理的に異なるとは、アドレスバスが異なるという意味です。バスというのは道路を走っているバスと同じで、目的地の違うデータを共通の道で運ぶ機能を意味します。ここではアドレスバスの話なので、アドレスのデータ、即ち番地を運ぶ道の事になります。AVRはこのアドレスのデータを運ぶ道を2つ持っています。コードセグメント用のアドレスバスと、データセグメント用のアドレスバスです。また、アドレスを指定したならデータも運ぶ必要がありますよね。よってデータバスというものも当然ありまして、AVRはコード用とデータ用の2つのデータバスを持っています。つまり、アドレスバスとデータバスの組み合わせを2つ持っているのです。(*18) 2つ別々に持っていることで、コードとデータを同時に処理することができるようになっています。これを専門用語でハーバードアーキテクチャと言います。
*18: と言い切っていますが、実はこの辺はちょっと自信がナイ(ォ まぁそんな感じになっているとゆうことで (・w・ |
はてさて、セグメントは3つなのにアドレスバスは2つしかありませんね。実はEEPROMセグメントだけ他のセグメントと違っています。他の2つはMPUとアドレスバスで直接つながっているのですが、EEPROMセグメント(というかEEPROMそのもの)は、データセグメントのデータバスへ物理的にぶら下がっているのです。要するにI/Oポートへ外付けEEPROMがつながっているようなもので、EEPROMセグメントのアドレスは、実際にはEEPROM自体が持っているメモリのアドレスを指しているのです。よってアドレス指定ではMPUから直接アクセスできません。だったらどうすりゃいいのってことですが、EEPROMを制御するために用意されているレジスタを使ってデータを読み書きします。
ところで、アドレスが3種類あって、どれも最初は0から始まるのですが、では一体今どのセグメントに書きこもうとしているのでしょうか。さっきは、コードセグメントに書き込まれると言いましたが、それはデフォルト(規定値)がコードセグメントになっているからです。セグメントを切り替えるには、.DSEG 及び .ESEG 擬似命令を使用します。それぞれの擬似命令以降の記述がデータセグメント、EEPROMセグメントへ書き込まれるようになります。この時、コードセグメントと同じ命令を使うことはできません。EEPROMに書き込むデータを記述したり、SRAMのアドレスに名前を付けたりすることができます。コードセグメントに戻すには、.CSEG擬似命令を使用します。
2行目の説明終わり。やっと3行目に到達です。解説長すぎ(ォ 飽きずに行きましょう。
rjmp reset reti reti reti
さて、新たに3種類の命令が出てきました。順番に説明します。
まず rjmp です。これは、次に続くラベルへジャンプする命令です。ジャンプとは文字通りのジャンプで、そのラベルがある行に飛ぶということです。AVRのコードは、ジャンプなどの特別な制御をしない限り、上から順番に実行されていきます。逆に言うと、それらの特別な命令が無ければ、書いた順番にしか実行できないのです。それでは毎回同じ事しかできないので非常に不便ですね。
ジャンプの飛び先であるラベルは、好きな名前で(*19)好き場所に作る事ができます。今の例では reset というラベルにジャンプするようになっていますが、これは私がresetというラベルを作成したからであって、必ずしもこの名前にする必要はありません。しかし、いくら好きな名前をつけてよいと言っても、意味のない名前を付けるのは止めましょう。今の例ではresetとなっていますので、多分リセットの処理をする場所にジャンプするのだな、となんとなく分かるでしょう。これがもし sakura とか karen とかであったなら何が何やら分からないでしょう。ていうかここはツッコミどころです(ォ
*19: ただし、命令で使われているものと同じ名称にはできません。 |
プログラムとは不思議な物で、自分で書いたコードのはずなのに、時間が経ってから改めて読むと、どうゆう動きをするコードなのか分からなくなっている場合が多々あります。こんな時に、ラベルなどが意味のない名前になっていたら必ず後悔します。俺はアホかと(ォ そうならない為に、名前はちょっとだけ考えて付けるようにしましょう。ちなみに、ラベルの文字はアルファベットしか使えません。
一応、もう一度確認しておきますと、rjmpは指定したラベルが存在する行へジャンプする命令です。resetは私が好きにつけたラベルであって、AVRの命令ではありません。
次に進みます。同じ命令が3行並んでいますね。非常に無駄っぽいですね(ォ retiとは、割り込みから復帰するためのコードです。割り込みとは、文字通りそのままの機能で、プログラムの流れの途中に割り込んで行われる処理のことです。AT90S1200は4種類の割り込みが使えます。リセット、INT0、タイマ/カウンタ、アナログコンパレータの4つです。
AVRがリセットされると、I/Oレジスタの状態が初期化されて、コードセグメントの$0000番地から再実行されます。リセットはプログラムがどのように動いているかに関わらずいつでも行える、つまり割り込みです。ただし、初期化されるということは、当然ながらリセットがかかったときに実行していた処理へ戻ることはできません。
次のINT0は外部入力割り込みで、AT90S1200ではI/OポートのうちのPD2にだけこの機能があります。その他の入力は、I/Oポートの状態を調べる命令が実行されなければ、ONしているのかOFFなのかを知る事はできません。しかし、INT0割り込みを使った場合、コードのどの行を実行していたとしても、PD2の入力状態が変化したことを即座に知る事ができます。
タイマ/カウンタは、AVRに内蔵されているタイマが設定した時間になった時、又はカウンタが設定した値までカウントアップしたときに呼び出される割り込みです。
アナログコンパレータは、AVRに内蔵されているアナログコンパレータの状態が変化した時に呼び出される割り込みです。だんだん説明がそっけなくなってきました(ォ
もうちょっとだけ詳しい説明。INT0はそのまんまなのでいいですかね。タイマは、要するにタイマーの事です(ォ AVRのクロック周波数(を何分の一かしたもの)を基準にして、クロックを数えることで時間を計ることができます。カウンタは、外部入力のうちPD4だけが使える機能で、入力の状態が変化した回数を数える事ができます。タイマ/カウンタは内部的には同じもので、カウントする対象がクロックか、外部入力かの違いだけです。アナログコンパレータは、アナログ信号の大きさを比較する事ができる機能です。2つのアナログ量(*20)を比較し、どちらの方が大きいか小さいかというデジタル値として変換することができます。これもPB0とPB1の組み合わせにだけ備わった機能です。
簡単に説明しましたが、AT90S1200は、これら4つの割り込みを使用する事ができます。もちろん使わないように設定する事もできます。そして、これらの割り込みが実際に実行された時には、予め決められたコードセグメントのアドレス(*21)へ処理がジャンプします。ジャンプ先のアドレスは、リセットは$0000、INT0は$0001、タイマ/カウンタは$0002、アナログコンパレータは$0003です。アドレスが1ずつ増えていますが、アドレス1つで1ワードのフラッシュメモリを使います。AVRの命令は全て1ワード(=2バイト)の容量を必要とするので、1つの命令毎にアドレスが1つ増えて行きます。
*20: 電位、電圧のこと。 |
*21: 割り込みベクタと呼ぶ。 |
ちょっと脱線しますが、世の中の1ワードは必ず2バイトという訳ではありません。MPU(CPU)が違えば1ワードのサイズも変わります。AVRの仕様が2バイトで1ワードになっているというだけです。
さて、ここまで分かった所で話を最初に戻します。もう一回コードを眺めてください。
.org $0000 rjmp reset (リセット ) reti (INT0 ) reti (タイマ/カウンタ ) reti (アナログコンパレータ)
.orgは、コードセグメントのアドレスを決める擬似命令でした。よってアドレス$0000からコードを書き込む事になります。そしてすぐ下に命令が4つ並んでいますので、それぞれ $0000、$0001、$0002、$0003 へ命令が書き込まれる事になります。もう気付かれたかと思いますが、割り込みが実行された時、このアドレスにジャンプしてくるのでしたね。つまり、このコードは割り込みが起きた時の動作を記述していたのです。リセットがかかった時には、rjmpでresetラベルのある場所へジャンプします。これをやらないと、すぐ下の命令が実行されてしまうからです。その下の3つの割り込みは、今回は使用しません。よって割り込みから復帰する命令retiを書いておきます。こうすれば割り込みがかかったとしてもなにもせず、割り込みがかかった時に実行していた元のアドレスへ、すぐに復帰します。
でも実のところ、今回は割り込みを使うように設定しませんので、理屈で言うと、これらの割り込みが実行されることはありません。よってretiのところに別の命令を書いても、普通に実行されるでしょう。ついでに言うと、リセットされた場合は必ず$0000から実行されるので、retiしない場合は、rjmpでresetまでジャンプする必要もなくなります。更に、.orgで指定しなかった場合、デフォルトではコードセグメントの$0000から始まることになっており、またマクロ以外の擬似命令は、フラッシュメモリに書き込まれる命令ではないので、ここで .org $0000を記述しなくても、.includeでアドレスがずれたりすることはありません。更に更に、これ以降の記述で出てくるレジスタ名を、データセグメントのアドレスで直接指定するならば、最初の.includeさえ記述する必要がありません。よって今まで長々と説明してきましたが、ここまでのコードを全く書かなくても実は動くのでした。
・・・・・・もう誰も信じられない(ォ
しかし、そんなことは特に必要がない限り(*22)やらないようにしましょう。理由は2つ。まず、割り込みによってジャンプするアドレス(割り込みベクタ)に、割り込み処理以外の目的で命令を書くことはルール違反です。いやそんなルールはどこにも書かれてないのですが、ルール違反です(ォ プログラムは他人が読みやすく、理解しやすいことを考えて書くことが最も大切なことです。他人が理解しやすいということは、時間がたって忘れた頃に自分が読んでも理解しやすいということです。ですから、割り込みベクタに他の処理を書くなんてことはやらないでいただきたい。私からのお願いだ!(ォ
*22: フラッシュメモリがあと2ワード足りないんジャー! とか。 |
理解のしやすさを基準とすれば、割り込みを使わないのだとしても使っていないことが分かるコードを書くのが、正しいコーディングと言えるでしょう。今回の例では、リセット後は即座にresetラベルのあるアドレスへジャンプしています。よってリセットの処理をするのだなと分かります。また他の割り込みベクタは、reti命令ですぐさま元の処理へ戻るように記述しているので、これらは使わないのだとすぐ分かるのです。
もう一つの理由。理由というか言いがかりというか(ォ 割り込みを使わない設定にした場合、確かに論理的にはそのアドレスへジャンプしてくることはないのですが、悲しいかな論理が通じないことがあるのが電子回路の世界です。ノイズが乗ったとか、電源電圧が低くなったとか、とにかくあらゆる理由で意図しない動作をしてくれる可能性があります。よって、割り込みを使わないようにしていても、割り込みの起きてしまう可能性が、全くゼロとは言いきれないのです。そこで、コーディングする側としては、割り込みが起きてしまっても大丈夫なコードを書くという対応をとります。つまり、使わない割り込みベクタにretiを書いておくのです。こうすることで、もし何かの間違いで割り込みがかかってしまっても、すぐに元に戻るので本来の期待した動作をしてくれるでしょう。(*23)
*23: もっとも、設定してない割り込みが起きてしまうような状態で、他のプログラムがまともに動いてくれるとは思えませんけどね(ォ |
ということで、ここまでの記述はどんなコードを書くにしても、必要なコードだったということになります。決して無駄なことではないのです。よかった、本当によかった(ォ
どんなコードでも必要ということは、毎回同じような記述をするということです。覚えてしまうか、以前に書いたコードからコピーして使いましょう。リセット以外の割り込みを使用する場合は、リセットと同じような書き方になります。割り込みの処理をするためのコードを別の所に書いてからラベルを決め、割り込みベクタにそのラベルへジャンプする命令を書きます。ただし、ジャンプ先の最後でretiするのをお忘れなく。そうしないと元のアドレスへ復帰しません。
割り込みの説明はここで終わりです。とっとと次に行きます。
reset: ldi R16,0b11111110
ついに出てきました。reset: とあるのがラベルです。リセット割り込みで、このラベルがある場所へジャンプしてくるのでした。ラベルは行の先頭から書き始めて、一番右にコロン( : ) を付けます。これがラベルの書き方です。ラベルの名前はプログラムの中で同じものが重複しないように自分で決めます。
そして、新しい命令が2つ出てきました。ldi は汎用(*24)レジスタに即値を入れる命令です。即値を入れるとは、要するに書いた数字をそのまま入れるということです。即値ではない数字は変数です。レジスタの中に入っている数字のことです。汎用レジスタとは、AVRの内部にある記憶素子です。汎用ということで、どんな意味で、どんな値を入れるかは自由です。
汎用でないレジスタは、AVRではI/O(*25)レジスタと呼びます。I/Oレジスタは、どの割り込みを使うか、タイマ/カウンタの値、I/Oポートの設定や現在の状態、などといったような、予め動作の決められているレジスタです。よってI/Oレジスタへ勝手に意味のない値を入れることはやめましょう。そんなことをしても訳のわからない動きをされて悩むのがオチです。
AVRのレジスタの大きさは全て1バイト(=8ビット)になっています。またここでも細かい話になりますが、1バイトが8ビットなのはAVRがそうなっているというだけの話で、他のMPUでは必ずしもそうとは限りません。
話を戻して、R16というのが汎用レジスタの名前になります。汎用レジスタにはR0〜R31までの名前が付いています。AVRには汎用レジスタが、32個(!)もあり、どれでも同じようにデータを格納し、演算処理を行えます。ただし、即値を扱う命令は、R16〜R31にしか使えません。ldi は即値を扱う命令なのでR16を指定しました。R0〜R15のどれかを指定した場合はエラーになります。また、AT90S1200ではR30とR31の組を、インデックスレジスタ(*26)として使用することができます。その他の機能はまったく同等だと思ってかまいません。特に、全ての汎用レジスタで演算処理を行えるのはすごいことだと思います。
*24: 「はんよう」です。その昔、「ぼんよう」と読まれて腰が砕けた事が(ォ |
*25: あいおー と読む。Input / Outputの略。 |
*26: レジスタをアドレスポインタとして使うこと。難しい話になるので各自で調べてください(ォ |
演算処理用のレジスタを専門用語でアキュムレータと言いますが、例えばPICマイコンではアキュムレータが1つしかありません。つまりレジスタに何らかの演算をしたい場合は、アキュムレータにいちいちコピーして演算してから、元のレジスタに戻さないといけないわけです。その点についてはAVRの方が便利にできていると思います。まぁどんなことにも一長一短があるので、どっちが良いとかそうゆう話はここではしません。
なんかまた余談が多いですけど(ォ R16に続くのが即値になります。即値ということは、これは数字です。暗号ではありません(ォ 最初に0bとあるのは、2進数であることを示しています。出ました2進数、義務教育で習っていない第2弾。2進数の詳しい説明をここですると、またまた話が長くなってしまうので、残念ながら割愛させていただきます。ですので極めて簡単に説明させていただきます。
2進数で使う数字は0と1の2つのみです。さすが2進数というだけはあります。2つだけであらゆる数を表現しようというわけです。ところでデジタル回路では、2つの状態しか取り扱わないということを覚えているでしょうか。H、L、ON、OFF、0、1・・・あるじゃないですか0と1が(笑) そうなのです、デジタル回路は2つの状態しか扱わないので、同じく2つの数字しか使わない2進数とはとても似ています。つまり、2進数を使うことでデジタル回路が表現しやすく、また理解しやすくなるのです。そうなると、2進数を使うときは、数字として何かを数えることに使うのではなく、2つの状態がHかLか、ONかOFFか、0か1かを表現するために使う方が便利そうです。
ちょっと回り道します。またかよ(ォ 汎用レジスタの大きさは1バイトだと前に説明しました。AVRの1バイトは8ビットです。ビットとはそのものずばり、2進数の1桁という意味です。そうです、知らぬ間に2進数を使っていたのです。ということは、1バイトには8桁の2進数を入れることができる、ということになります。8桁の2進数で0を表現すると00000000です。何進数であっても0は0なのです。これにさっきのルールで2進数を意味する0bを先頭に付けると、0b00000000となります。なんだか問題にしている即値と似てきました。
問題の即値をもう一度眺めてみましょう。0b11111110。これは1桁目が0で、それ以外は全部1である8桁の2進数だとなんとなく分かっていただけるでしょうか。1ビット目が0(OFF)、2〜8ビット目が1(ON)、これを、汎用レジスタR16に入れようとしていたのです。何かの数字を入れたいのではなく、ON、OFFの状態をR16に入れたいのだということです。何のON、OFFなのかは、次の命令を読むことでわかります。
ということで次に行きます。
out DDRB,R16
out命令は、汎用レジスタの内容をI/Oレジスタへ出力するための命令です。ここで、DDRBはI/Oレジスタの一つです。R16は汎用レジスタでしたね。しかもついさっき即値で内容を設定したばかりです。
DDRBは何をするためのI/Oレジスタかというと、I/OポートBの方向を決めるためのレジスタです。方向とは入力として使うのか、出力として使うのかということです。DDRBの各ビットが0の時は入力、1の時は出力になります。DDRBの1ビット目〜8ビット目が、PB0〜PB7に対応します。数字が1個ずれているのでややこしいですね。よってビットを数えるときは0ビット目〜7ビット目と考えるようにしましょう。
そして、このレジスタにR16の値を入れるということは、さっきの即値はポートBの方向を表していたのです。out命令は残念ながら即値を指定することができないので、一旦、汎用レジスタへ値を設定する必要があります。もう一度、さっきの即値を眺めてみましょう。0b11111110。1は出力、0は入力でしたね。よって、out命令によってPB0は入力、PB1〜PB7が出力に設定されることがわかりました。
恒例のちょっと脱線その1。DDRBレジスタは、実際にはデータセグメントのアドレス$17にあるI/Oレジスタです。よってDDRBと書く代わりに$17と書いても同じ動作をします。逆に、DDRBと表記できるのは、.include擬似命令で読みこんだ1200def.incの中で、$17にDDRBと名前を付けているからであって、もし1200def.incをインクルードしていないならDDRBと書くとエラーになります。
ちょっと脱線その2。2進数は数字なので、もちろん10進数や16進数に変換することができます。試しにやってみますと、0b11111110は10進数では254、16進数では$feとなります。これらは全て同じ数字なので、例えば、
reset: ldi R16,0b11111110 out DDRB,R16 reset: ldi R16,254 out DDRB,R16 reset: ldi R16,$fe out DDRB,R16
これらは全て同じ意味のコードになります。つまり、PB0は入力、PB1〜PB7が出力に設定されるのですが、10進数と16進数では直感的にわかりませんよね。2進数の威力がわかっていただけたでしょうか。
話をI/Oポートに戻します。かなり前の方になりますが、もう一度回路図を見ていただけますでしょうか。めんどくさい場合は思い出してください(ォ PB0に押ボタン、PB1とPB2にLEDを付けたのでしたね。言うまでもなく、押しボタンは入力、LEDは出力で使うのですが、さっきやったPB0〜PB7の設定と方向が合っているでしょうか。ちなみに、PB3〜PB7には何もつないでいないので、方向はどちらでもかまいません。確認できましたか? どうやら合っているようなので、次に進みましょう。
sbi PORTB,0 sbi PORTB,1
なにやら同じ命令が2つ並んでいます。後ろの数字が0と1になっています。また2進数でしょうか。残念でした(ォ 先頭に0bが付いていないので10進数ですね。
sbi命令は、I/Oレジスタの指定したビットを1にする命令です。後ろの数字でI/Oレジスタの何ビット目かを指定します、AVRのレジスタの大きさは全て8ビットなので、0〜7の数字になります。ここでは2進数を使うより10進数を使った方が分かりやすいですね。PORTBとはそのまんまで、I/OポートBへの出力を設定するI/Oレジスタです。これも1200def.incの中で名前を付けられたものであり、実際はデータセグメントのアドレス$18です。まとめると、1つ目の命令でPB0の出力を1に、2つ目でPB1の出力を1にしています。PORTBの初期値は0になっているので、その他のビットは全て0になります。
さて、ちょっと気になりませんでしょうか。PB1にはLEDがつながっていて、方向を出力に設定しました。しかし、PB0には押ボタンをつなげて、しかも入力方向に設定したはずです。なんでそのPB0に出力しているのでしょうか。別に間違ったわけではありませんよ。実は、入力方向に設定したI/Oポートへ1を出力すると、その入力をプルアップすることができるのです。久しぶりに出てきましたプルアップ、まさか忘れていませんよね(ォ よって、PB0は入力なのでプルアップされます。PB1は出力方向に設定されているので、そのものずばり出力が1です。PB2への命令はないので、PB2はPORTBの初期値である0です。整理すると、PB0は入力でプルアップ、PB1は出力1、PB2は出力0に設定されました。
ということで最後のコードですが
loop: rjmp loop
rjmp命令は既にリセットの所で使っています。これはloopラベルのある場所にジャンプする命令ですね。loopラベルのある場所は、なんと!この命令そのものじゃないですか。これはどのような動きになるのかというと、自分自信にジャンプしているので、AVRに電源が入っている限り、リセットがかかるまでずっとこのコードを実行しつづけることになります。いわゆる無限ループというやつです。最後まで終わったので、あとは余計なことをせずにここで止まっていろということです。
もし、この命令がなかったらどうなるでしょうか。その場合、AVRは次のアドレスにある命令を実行しようとします。次って言ったって何も命令がないじゃないかと思いますが、我々がコードを書いていないだけであって、AVRのフラッシュメモリがなくなるわけではありません。よって、フラッシュメモリには何らかのコードが書き込まれていますので、それが実行されてしまいます。実際には、AVRへ書き込むときにプログラマがチップ消去してくれるので、何もしない命令が書き込まれているはずですが、"はず"では困るので、普通は不明なコードを実行しないようにプログラミングします。そのために今回は、無限ループで先に進まないようにしているのです。また、このように書くことで、コードがここで終わっているのだと分かるようにする目的もあります。
さて、よ〜〜〜〜やっと全てのコードを読み終わりました。お疲れ様でした。でもまだまだ続くのですよ(ォ
・穴埋めコラムその2 今回は終了位置以降に何も書いていないのですが、ジャンプ先が他にもある場合などでは、プログラムの終了位置がコードの一番下にあるとは限りません。その場合、プログラムした人がここで終わりと勝手に思っていても、AVRは次のアドレスをどんどん実行してしまいます。きちんと終わりのコードを書かないと、ジャンプ先のコードを勝手に実行されてしまうのです。 AVRが実行するアドレスは、プログラムカウンタ(以下 PC)という特殊なレジスタに入っています。命令が実行されると、PCの値が自動的に1つ増やされ、次に実行するアドレスを指し示すようになっています。PCのサイズは無限ではありませんので、値が増えていくとそのうちオーバーフローして0に戻ります。今回のコードで最後の無限ループがなかった場合、アドレス$0000からのコードを繰り返す動作をするでしょう。ちなみに、ジャンプ命令の動作は、PCの値を書き換えることで実現されています。 |
コードが最後まで実行されたところで、PB0は入力でプルアップ、PB1は出力1、PB2は出力0に設定されたのでした。さて、この状態で回路はどのような状態になっているでしょうか。このプログラムは、PB2につなげたLED2を点灯させると最初に書きました。はたしてそうなるのでしょうか。まず、PB0は入力に設定されています。押ボタンがつながっていますが、ボタンをいじらなければ当然なにも起きません。もし押したとしても、押したことを調べるコードを書いていないので、AVRはボタンが押されているかどうか分かりません。よって押した場合でもなにも起きません。つぎにPB1ですが、出力は1になっています。AVRは正論理で動いているので、出力を1に設定した場合、I/Oポートがハイレベルになります。つまり電源電圧の高い方、+5Vが出力されます。PB2は逆に出力が0なので、ローレベルの0Vが出力されることになります。
では、どちらのLEDが点灯するでしょうか。出力を1にした方が点くだろうって? 回路をよく見ましょう。LEDは三角の尖った方に電流がながれるのですよね。つまり、尖っている方をローレベル、尖っていない方をハイレベルにしたときに点灯するのです。そして、今は尖っている方がAVRの方を向いています。なぜかというと、AVRはシンク電流の方が多く流せるので、電流が流れ込む方向にLEDの向きを設計したのでした。よって、AVRの出力をローレベルにしたときにLEDが点灯します。ということで、ローレベルになっているPB2のLED2が点灯し、PB1のLED1は消灯することになります。
お疲れ様でした! これでこのプログラムの説明は本当に全て終わりました。どうだったでしょうか。難しかったでしょうか。それとも簡単過ぎて退屈だったでしょうか。個人的には回りくどくてあんまり読みたくない類の文章だと思うのですが(ォ 分かりやすくしようとか、難しい表現をなるべく使わないようにしたらそうなってしまったので、その点はご了承ください。
それでは次に行きましょうか。次ってなんやの! さっきのプログラムのタイトルをお忘れでしょうか。"(1) LEDを点灯するプログラム"です。(1)ということは(2)も当然あるのです。それに出力の後に入力をやるとちゃんと言っています。観念していただきたい(ォ しかし安心してください。初期設定や割り込みベクタの処理のやり方は、私達はもう既に知っているのです。あとは入力の制御をするコードを追加するだけです。軽く流す感じで行きましょう
(2) 押ボタンの状態によって、点灯するLEDを切換えるプログラム
.include "1200def.inc" .org $0000 rjmp reset reti reti reti reset: ldi R16,0b11111110 out DDRB,R16 sbi PORTB,0 main: sbis PinB,0 rjmp btn_on sbi PORTB,1 cbi PORTB,2 rjmp main btn_on: cbi PORTB,1 sbi PORTB,2 rjmp main
今度は最初に全体を眺めてみましょう。最初の方は、さっきまでさんざんっぱら説明したプログラムと同じですね。リセットされて、入出力の方向が設定されて、PB0がプルアップされるまでは全く同じであると分かりますでしょうか。そして、まだ知らない命令が2つあるようですね。sbis命令とcbi命令です。cbi命令はsbi命令となんとなく似ていますね。また、sbis命令にはPinBというのが出てきます。これもI/Oレジスタかな?となんとなく分かるようになっていれば、AVRのことがだんだんと分かってきた証拠です。
では、詳しく見て行きましょう。最初に全体を眺めたので、そんなに難しくないと感じているはずです。リセットされて、入出力の方向が設定されて、PB0がプルアップされるまでは、もうお分かりですよね。次にmainラベルがあり、新しいsbis命令が出てきました。
sbisは、I/Oポートの入力を調べ、指定したビットが1であった場合、すぐ下の一行をスキップする命令です。スキップするとは、すぐ下のそのまた下の命令にジャンプするということです。PinBとはI/Oレジスタの一つで、I/OポートBの入力状態が入っています。0は0ビット目、すなわちPB0を指定しています。よってこの命令により、PB0の入力状態が調べられて、1であったら次の命令をスキップ、0であったら次の命令を実行することになります。ここで注意しなければならないことは、PB0をプルアップしたことです。プルアップしたために、押ボタンを押していないときには、常にハイレベル、すなわち1が入力されています。押ボタンを押すと、0が入力されます。プルアップすると見かけ上の負論理になるのでした。よって、押ボタンを押していない場合には、次のrjmp命令がスキップされて、更にその下の命令が実行されます。押ボタンを押していた場合にはbtn_onラベルへジャンプします。
結局これは何をしているのかというと、入力の状態に応じて、処理を分岐しているのです。AVRの命令は、基本的に上から順番に実行されていくのでした。途中でジャンプ命令があった場合は、順番を変えることができますが、単に順番が変わっただけであり、これだけでは毎回同じ動作をすることに変わりありません。そこでsbis命令のような条件分岐命令が必要となるのです。分岐の条件はいろいろあって、他にも汎用レジスタのビット状態や、汎用レジスタ同士を比較した結果など、30種類以上用意されています。これにより、さまざまな動作をさせることができるのです。
さて、押ボタンの状態によって処理を分岐させることができました。ところで今回のプログラムのタイトルは、"(2)押ボタンの状態によって、点灯するLEDを切換えるプログラム"です。"押ボタンの状態によって"まではできたので、この先は"点灯するLEDを切換える"コードがあるはずですね。まず、押ボタンが押されていない時の処理を見ましょう。押ボタンが押されていない場合には、rjmp btn_on の一行がスキップされるのでした。そうすると、
sbi PORTB,1 cbi PORTB,2 rjmp main
が実行されることになります。処理の最後でmainラベルにジャンプするので、さっきの分岐命令からまた実行されることになります。つづいて押ボタンが押された時の処理も先に見てしまいましょう。押ボタンが押されていた場合には、btn_onラベルのある行へジャンプするのでした。
btn_on: cbi PORTB,1 sbi PORTB,2 rjmp main
よく見ると、押ボタンを押していないときの処理とほとんど同じです。違うのはsbi命令とcbi命令が入れ替わっていることだけです。sbi命令は、I/Oポートの指定したビットを1にする命令でした。cbi命令は新しい命令ですが、sbi命令の逆で、I/Oポートの指定したビットを0にする命令です。
よって、押ボタンが押されていない場合には、PB1の出力が1に、PB2の出力が0になります。逆に押ボタンが押されている場合は、PB1の出力が0に、PB2の出力が1になります。LEDはAVRの出力がローレベルになったときに点灯するのでしたね。よって、押ボタンを押していない時にはLED2が、押している場合にはLED1が点灯するようになりました。 どちらの場合でも、出力を切換えたあとはmainラベルへジャンプしているので、押ボタンの状態を調べてLEDを切換える動作を、ひたすら繰り返すことになります。
おお〜 どうですか、もう理解できましたよ。最初のややこしいところさえ分かってしまえば、あとはなんてことないのですよ〜。というわけで、コードの説明はこのくらいにしておきます。
今回使ったAVRの命令は、AVRが本来もっている命令の中の、ほんのちょっとだけを使ったにすぎません。AT90S1200には89個(!)の命令があります。89個もあるといっても、機能別に分けると4種類の命令しかありません。算術、論理命令。分岐命令。データ転送命令。ビット操作、ビットテスト命令の4種類です。今回は、算術、論理命令だけは使用していませんが、要するに足し算等のことなので、命令の内容は分かると思います。その他の3種類には、今回使用した命令が含まれていますので、同じような命令だと考えていただければ難しく考える必要はないかと思います。全ての命令は、レディオテクニカさんの日本語マニュアルに書いてありますので、調べてみてください。
はっきり言って、まだ足の先を突っ込んだだけで熱いと騒いでいる程度ですが、とにかく動くであろうプログラムを作成しました。このプログラムを実際に動かすためには、アセンブルしてAVRへ転送できる形式に変換しなければなりません。
ということで、次はAVR Studioでのアセンブルです。
・穴埋めコラムその3 もちろん、まだまだいろいろいじってみたいという人は、どんどん改造してみましょう。試しにLEDをピカピカ点滅するようにしてみてはいかがでしょうか? 今回使った命令だけでは難しいので、私なら、新たにnop、dec、brneの3つの命令を加えてコーディングします。 ヒントとして、AVRは1クロックで1命令を実行できます。今回は4MHzのセラロックを使っているので、1つの命令を実行するのに0.25マイクロ秒かかります。逆にいうと、0.25マイクロ秒しかかからないので(0.25秒の百万分の一!)、こんな速さでLEDをON、OFFしても、人間の目には点灯しっぱなしに見えてしまいます。よって、点灯と消灯の間で一定時間待つプログラムが必要です。 いろいろやり方が考えられますが、一番単純なのは、何もしない命令を一定回数実行することです。例えば100ミリ秒待つには、0.25マイクロ秒の命令を40万回( ! )実行すればよいのです。何もしない命令なので時間だけが過ぎて行きます。もう一つのヒントは、汎用レジスタは1バイトなので、10進数で0〜255まで、256通りの数字を入れられます。さて、どんなアルゴリズムにすれば実現できるでしょうか。考えてみてください。 |
AVRにプログラムを転送するには、書いたコードをAVR Studioでアセンブルしなければなりません。そこで、まずAVR Studioでアセンブルするまでの使い方を簡単に説明します。その前にAVR Studioをインストールしておかなければなりませんが、インストールの方法は特に説明しません。インストーラが付いているので適当にマウスのボタンを押していればインストールできるでしょう。また、ここで説明するのはAVR Studio3.56です。4.07は操作画面の見た目や使い方が微妙に異なります。
ということで、AVR Studioを起動してみますと、こんな感じの画面が表示されます。
そして、メニューからProject→Newを選ぶと、新しいプロジェクトを作成するためのダイアログが表示されます。
Projectにはプロジェクト名を自分で決めて入力します。ちなみに日本語はつかえません。とりあえず今回は、AVRtestとします。Locationはプロジェクトを保存するフォルダを指定します。任意(*27)のフォルダを指定してください。もう一度出てくるProject(*28)は、AVR Assemblerを選択します。設定し終わったらOKを押してください。
*27: なんでもよいと言う意味だが、すでに存在するフォルダでなければならない。 |
*28: なんで同じなのかは不明(ォ |
すると、こんな感じでProjectウィンドウが開きます。そしてもう一度メニューを操作し、Project→CreateNewFileを選んでください。
Nameに作成するアセンブラファイル名を入力します。とりあえずtest.asm としましょう。Locationはプロジェクトと同じ場所にしておきます。Add to projectのチェックは付けたままにして、OKを押してください。すると、Projectウィンドウに、test.asmが追加されます。ここで、もう一度メニューから、Project→Project Settingsを選んでください。
Assembler locationはこのままでOKです。Assember entry fileには、先ほど追加したtest.asmが選択されているはずですので、これもそのままでOKです。ここで重要なのは、Output fileをIntel Intellec 8/MDS(Intel Hex)に変更することです。後述のプログラマでAVRに書き込むには、Intel Hex形式でアセンブルしなければなりません。変更すると、もう一つのOutput Fileにはhexが入力されることを確認してください。Wrap relative jumpsはON、List fileはlstのままにしておいて、OKを押します。
更にもう一つやっておかなければならないことがあります。AVR Studioをダウンロードしたときに、AVR000.ZIPもダウンロードしたことを覚えていますでしょうか。AVR000.ZIPは圧縮ファイルなので、解凍するといくつかのファイルが展開されます。その中の一つに、1200def.incが含まれています。コードの中でインクルードしている設定ファイルです。これを、test.asmで指定したLocation(フォルダ)にコピーしておきます。こうしないと、アセンブルの時に1200def.incが見つからず、エラーとなります。
ここまでで初期設定は終わりです。test.asmのウィンドウが開いていると思いますので、そこにプログラムを書いて行きます。今回は既にメモ帳でコーディングしていますので、"(2)押ボタンの状態によって、点灯するLEDを切換えるプログラム"をコピーしてください。Windowsですから、もちろんコピー&ペーストが使えますよ。
書き終わったら上書き保存します。そして、メニューからProject→Assembleを選んでください。エラーが無ければ次のようなウィンドウが表示されて、test.asmと同じフォルダに、アセンブルされたtest.hexファイルができているはずです。
ここまでがアセンブルの手順です。では早速AVRに書きこんでみましょう!
と言いたいところなのですが、はたしてこのプログラムは本当に思った通りに動くのでしょうか? いまさら何を言うんだこいつは(ォ まぁあせらないで聞いてください。
今回のプログラムは、ほんの十数行で短いですし、動きの内容も細かく確認してきましたので、いまアセンブルしたプログラムをいきなりAVRへ転送しても、おそらく一発で思い通りの動きをするでしょう。しかし、通常はいきなりAVRへ転送するなんてことはしません。なぜならプログラムには間違いがつきものだからです。どんなにこれで完璧!と思っていても、いざ動かしてみるまでは間違いに気づかないものです。理由は簡単で、プログラミングは目的の動作をさせるコードのほかに、目的以外の動作をさせないコードも書かなければいけないからです。そして、目的以外の動作(*29)をさせないためには、それに気づかなければコードを書けません。この気づくことがとても難しいのです。Windowsなんかはアップデートと称して、しょっちゅうプログラムの更新をしていますが、ほとんどが不具合の修正で、新機能の追加なんかは全然ないですよね。あれは結局、プログラムが大きすぎて、目的以外の動作をしてしまうことを見つけきれないために、あとから修正しまくっているわけです。
*29: いわゆるバグってやつです。 |
ということで、じゃあどうすればバグを見つけられるのって話ですが、一番簡単な見つけ方は、とりあえず動かしてみることです。おいおい、さっき転送するなって言ったじゃん。いや、転送しないで動かしてみてください。禅問答ではありません(ォ AVR Studioを使えばAVRに転送しなくても動かしてみることができるのです。AVR Studioにはアセンブラの機能の他に、AVRシミュレータの機能があります。というよりも、このAVRシミュレータがあるからこそAVR Studioを使うのです。AVRシミュレータとは、AVRをパソコン上で再現してしまうソフトのことです。これを使えば、プログラムをAVRにいちいち転送しなくても、動作の確認ができます。また、AVRに転送して動かしたのでは分からないことも確認できます。例えば、プログラムを停止しながら一行ずつ実行することや、任意の行で動作を一時停止することで、その時点での各種レジスタの内容を確認したり、また強制的にレジスタの値を変えて、動きの変化を確認したりできるのです。こうすることで、プログラムの動作をパソコン上で予め確認することができるのです。
それではせっかくなので、AVRシミュレータを使った動作確認の練習をしてみましょう。AVRシミュレータを起動するには、アセンブルした後、メニューから Project → Build and run を選びます。すると、次のようなダイアログが開きます。
DeviceでAT90S1200を選択すると、Memoryの値は自動的に正しく書き換わります。Frequencyはクロック周波数のことです。今回は4MHzのセラロックを使用していますので、4.000000を選択します。選択し終わったらOKを押してください。
そうしますと、まずアセンブルが行われ、続いてコード窓の欄外に矢印が表示されます。矢印のある場所は、アドレス$0000です。この状態は、アドレス$0000の命令を実行しようとして一時停止しているところです。
矢印のある命令はまだ実行されていません。命令を実行して先に進むには、ツールバーのRun、またはTrace Intoのアイコンをクリックします。(メニューやファンクションキーからでも同じ操作ができます。)
アイコンの説明
Runを押すとプログラムが実行されます。しかし、実行中はレジスタの表示などが更新されないので、下記のブレークポイントを設定してから使います。
Trace Intoは、一時停止しながら一行ずつ実行します。通常はこれを使って、レジスタ等の内容を見ながら動作の確認をしましょう。
Toggle breakpointは、カーソルのある行にブレークポイントを設定することができます。ブレークとは、一時停止のことです。Runした時に、ブレークポイントが設定されている行で実行が一時停止されます。例えば、既に初期化のコードが確認済みの場合、その初期化コードが終わるところにブレークポイントを設定してRunします。すると、初期化のコードが実行された後、設定した場所で自動的に止まるので便利です。
Resetは、Build and Runをしたときと同じ状態から、再実行したいときに使います。
さて、実行のやり方がわかったので、次は汎用レジスタの確認をしてみましょう。メニューからView → Registersを選んでください。すると、Registersウィンドウが開きます。
汎用レジスタR0〜R31の内容が表示されています。残念ながら表示は16進数です。数字の先頭の0xはアセンブラの$と同じ意味です。試しにTrace Intoを1回押してみてください。resetラベルの行に移動しましたね。更にもう一度Trace Intoを押すと、ldi命令によってR16に即値が代入されます。R16の値が変更されたので、表示が赤くなりました。0b11111110は$FEと同じなので、0xFEと表示されるはずです。
ところで、R16以外には何も値を入れていないのですが、0になっていないものがあります。なんででしょう。汎用レジスタの内容はリセットで初期化されないからです。初期化されないので何らかの意味のない値が入っています。よって、リセットしても0が入っているとは限りません。試しにResetアイコンを押してみてください。R16は0にならないはずです。実際にAVRに転送して実行しても、同じことが起きます。リセットしたら0が入っている、な〜んて思ってコーディングしていると痛い目に会います。
次にI/Oポートの確認をしましょう。メニューからView →New IO Viewを選んでください。すると、IOウィンドウが開きます。何がNewなのかは知りません(ォ
このウィンドウで、I/Oレジスタの内容が確認できます。PortB Data、Data Direction、Input Pinsは、それぞれPORTB、DDRB、PinBのことです。値が1のビットにチェックが付きます。
今、R16の内容をDDRBに入れ、PORTBの0ビット目を1にしました。どうやらそのようになっているみたいですね。ただし、PinBは何も設定されていません。0ビット目をプルアップしたので、本来ならPinBの0ビット目にもチェックがつくはずなのですが、残念ながらAVR Studioはそこまでは面倒みてくれないようです。プルアップしなかった場合でも、AVRシミュレータで入力の操作をするには、全て手動で強制的にチェックを入れなければなりません。操作は超簡単、変更したいチェックボックスをマウスでクリックするだけです。早速、Input Pinsの0ビット目にチェックを入れましょう。
ここまでで、初期化のコードが実行されました。次からは押ボタンの状態によってLEDを切換えるコードです。今、PinBの0ビット目を手動で1にしたので、押ボタンを押していない状態ですね。この時は、LED2を点灯させるので、PortB Dataの1ビット目が1、2ビット目が0に設定されるはずです。残念ながら実際のLEDまではシミュレーションしてくれないので、あくまでビットの確認をすることしかできません。では、mainラベルに戻るまでTrace Intoで一行ずつ実行してください。
するとこうなりました。PortB Dataは予定通りに動いたようですが、Input Pinsの1ビット目が勝手に1になっています。一瞬疑問に思いますが、AVRの仕様です。I/Oポートが出力方向の時に1を出力すると、入力も1に設定されます。これはAVRがPinBレジスタを直接設定しているためで、I/Oポートの電圧を読み取っているわけではありません。入力方向のときと違うのはその点です。
次は、押ボタンを押したときの動作を確認しましょう。入力を変えるので、Input Pinsの0ビット目のチェックを外してください。これで押ボタンを押した状態になりました。それでは、またmainラベルに戻るまでTrace Intoで一行ずつ実行してください。
さっきと逆で、PortB Dataの1ビット目が0、2ビット目が1に設定されるはずです。
ついでにブレークポイントも使ってみましょう。mainラベルの行にカーソルを移動して、Toggle breakpointアイコンをクリックしてください。すると、欄外に赤い四角が表示されます。これが、ブレークポイントのマークです。もう一度Toggle breakpointすれば消えます。
ブレークポイントを設定した状態で、今度はRunしてみましょう。すると、Runを押してもブレークポイントの所で止まったままに見えますが、実際は、Runしてからブレークポイントに戻るまでの命令が実行されているのです。試しに、押ボタンの状態を変えてからRunしてみてください。さっきと同じ出力の変化が確認できると思います。
機能限定で説明しましたが、AVRシミュレータの練習はこのくらいにしましょう。
どうやらプログラムは良さそうですね。ついに、プログラムをAVRへ書き込むときがやってまいりました。AVRへプログラムを書き込むにはプログラマが必要です。次からはプログラマの説明をします。
いよいよプログラマが必要になるのですが、えるむに公開されているプログラマ(*30)を製作しましょう。ページに詳しく書かれているので、ここではあまり説明しません。掲載されている回路図も難しい記号は使われていないので、見れば理解できると思います。
*30: http://elm-chan.org/works/avrx/report.html えるむではAVRライタと呼んでいます。 |
LPTポートとはパソコンのパラレルポート、又はプリンタポートのことです。普通はD-Sub25ピンのメスコネクタになっています。COMポートとはシリアルポートのことです。普通はD-Sub9ピンのオスコネクタになっています。また、プログラムを転送するためのソフトも同じ所で公開されていますので、製作するプログラマに合ったソフトをダウンロードしましょう。ソフトの詳しい使い方は、中に入っているドキュメントをよく読んでください。
この図は、一番簡単に作れるパラレルポート版プログラマの回路図です。コネクタと抵抗だけです。とっても簡単ですね。6ピンのヘッダーは、AVR側のISPピンと、ピンの名称が合うように配線しましょう。D-Sub25Pオスコネクタのピン番号は、実物のコネクタをよく見ると、ピンの所に小さく書いてあります。
私が使っているプログラマはこれです。えるむのプログラマを参考にした、COMポート接続のプログラマです。これにAVRを乗っけるだけで書き込みできるようにしました。ゼロプレッシャーソケットを使ってAVRを簡単に取り外せるようにしています。また、ISPでも書き込みできるように、ケーブルをつなぐコネクタも付けています。
さぁ、プログラマができましたら、さっそく書き込んでみましょう。MS-DOSをさわったことのない人には未知の領域でしょうが、やることは簡単なので難しがらないでください。基本は2つです。まず、書き込むhexファイルとプログラマ(パラレルポート版ならavrxs.com)を同じフォルダに入れること。そしてカレントフォルダを、その2つのファイルが入っているフォルダへ移動することです。移動するにはcdコマンドを使います。
書き込み終わりましたでしょうか。エラーが表示された場合は、プログラマの結線などをもう一度確かめてください。それと、AVRに電源が入っていなければ書き込みができません。
エラーがなく正常に書き込まれても、まだあせってはいけません。プログラマからはリセット信号が出ているので、プログラマをつないでいる間、AVRはプログラムを実行しません。プログラマを外した瞬間にリセット信号がなくなり、コードの$0000から実行されます。
(2)のプログラムを動かした場合は次の写真のようになるはずです。どうでしょうか、思ったように動きましたでしょうか。
ハードからソフトまで長々と書いてきましたが、最後は目標通り、実際に動かすところまでいきました。マイコンなんて難しくて自分には分からないと思っていた方、いかがでしたでしょうか。やっぱり難しいと思ってしまった方には、私の説明が下手だったということで、ごめんなさい。しかし、他のマイコンに比べたら、AVRは本当に理解しやすいマイコンですので、せっかくですからもうちょっとがんばってみてください。そして、この本を読んで、マイコン遊びに少しでも興味を持たれたならば、明日にでも秋葉原へ行って部品を買い揃えてみてはいかがでしょうか。自分で作ったものが思い通りに動くということは、とても面白いことです。また、本を読むだけでなく、実際に作ってみないと分からない面白さもたくさんあります。特に、普段から電子回路を趣味としていないのであれば、今回のように比較的単純な回路でも、おそらく最初は予定通りに動かないでしょう。動くようになるまで少なからず悩み、苦労すると思います。趣味でわざわざ苦労することもないように思えますが、結局のところ、あれこれ考えながら苦労して作っている最中が、なんといっても一番楽しい時間なのです。
そうした苦労を楽しく乗り越えて、見事に動かすことができたなら、おめでとうございます! あなたはAVR初心者にレベルアップしました(^^ まぁ、I/OのON、OFFができるようになっただけなのですけれども、デジタル回路はON、OFFのみが全ての状態なので、デジタルで制御する各種機器やIC等をあなたは動かすことができるようになったのです。LEDを点灯させられるようになったということは、そうゆうことなのです。各種機器を動かすために、どの順番、どのタイミングでI/OをON、OFFさせればよいかは、各種機器に必ずある仕様書を読めばわかります。仕様書は買ったときに付いてくるでしょうし、インターネットで検索しても見つけることができるでしょう。
脱初心者を目指すためにこれからやらなければならないことは、動かしたい機器を自らの興味をもって見つけだすこと。その仕様書をきちんと理解できるようになること。そして、それを制御できるように、AVRのことをもっと理解することです。この本では、AVRについてまだまだ説明していないことがたくさんあります。知らない命令はまだ何十個もありますし、割り込みもまだ使っていません。そもそも、たくさんあるレジスタの全貌すら説明していません。それらを使えば、もっと自由自在にAVRを動かせるようになります。使い方は、マニュアルに全て書いてあることなのですが、やっと初心者になれた我々には、まだ理解しづらいところもあるでしょう。それについては、次の機会に話したいと思います。
みなさん、すっげ〜AVRマイコンは楽しいですよ!
おくづけ
リンクはてけとうに |