標準TTLだけ(!)でCPUをつくろう!(組立てキットです!)
(ホントは74HC、CMOSなんだけど…)
[第410回]

●PIC18F4550のメモリアクセスについて説明するつもりでしたが…

本日は大晦日です。
本当にあっという間の1年でした。
2009年も本日限り。
で、メモリアクセスについて、本年の締めくくりとして、ばたばたっと片付けてしまって、ではよいお年をお迎えくださいませ、という調子でやっつけてしまおうと思ったのですけれど…。

前回、前々回を読み直してみますと、いやあ、そういうわけにはいかんなあ、という感じなのですねえ。
ここで、ばたばたっと片付けてしまったら、まるでよくある類の、読んだけど結局何も身につかないという「技術入門書」と同じじゃないの。

前回は、「こんなもの読む必要はありません」のひとことで片付けてしまいましたけれど、そういうことでは、いかんでしょう。
やっぱりまじめな読者諸氏におかれましては、「読むな」と言われてもやっぱり読みたい、読んでみよう、と思われるのが人情の常。
で、読んでみようとするけれど、たいがいは「うう。わからん」ということになります。
そうすると、欲求不満になってしまいますので、そういう精神状態で新年を迎えていただくのはよろしくありません。

くそみそにけなしていましたけれど、よくよく落ち着いてPIC18F4550のData Sheetを読み直してみますと、それほどけなさなくてはならないほどひどいシステムではないではないか、いや、逆になかなかよく考えてあるなあ、と思い始めたところでもあります。
そうしますと、ここはまたまた道草ついでに、ひとつ徹底的にカイボーしてしまおうではないか、ということになってしまうのですよねえ(もう藪の中というよりも、富士の樹海みたいなもので、果たして生きているうちに抜け出せますかどうか)。

こうなると、もう大晦日もお正月もない、俗世を離れて、技術三昧でまいりましょう。
なのですけれど、そうは言ったものの、そこは小人の悲しさ、浮世を離脱してしまうことはとてもかないまへん(そんなことをするとほんとうに死んでしまいまする)。

で、あらためて腰を据えてかかりたいと思い直したメモリアクセスについては、次回以降(つまり年が明けてからということであります)に致しまして、今回は本年の締めくくりといたしまして(どこが締めくくりじゃあ)、別のテーマでお話をいたします。

●C or アセンブラ ? in PIC

口は災いの門とか申しまして、言わなくてもいいことを不用意に発言したりするものですから、あとでフォローを余儀なくされることになってしまいます。
「PICでCなどを使うお方の気が知れない」などという過激な発言を続けてまいりますと、「いまどきこんな時代錯誤にはつきあっておれん」と思われて、ついには貴重な読者様を失ってしまうことにもなりかねません。

んでも、事実は事実として、伝えなければいけませぬ。
そうなると、要はフォローです。
言いっぱなしは一番いけませぬ。

●そもそもCとは一体なにものぞ?

そこんところをわかっていらっしゃらないでCをお使いの方が世間には余りに多いように思われます。
ああ。CでもC++でもVisualC++でも同じです。
面倒なのでCの眷属一族郎党を全部ひっくるめて、Cと称します。

ことにCでPICプログラムを解説していらっしゃる方々などは、本当にわかっていらっしゃるのかなあ、と疑問に思ってしまいます。
ああ。Microchip社の面々もおんなじかあ。いや、こりゃ弱ったなぁ。引っ込みがつかんではないか。
でもでもこれだけ分厚いData Sheetを作成していらっしゃるところを見ますと、本当はMicrochip社もそこんところは十分に承知の上、と推測しております。今のところは。

でも多少危険な兆候は見受けられます。
Microchip社供給のPICのサンプルプログラムがみごとにCばっかりになっておりまする。
こうなるってえと、詳細にわたって解説しておりましたPICのData Sheetが全て消え去って、Cでの記述に統一されてしまう日もそう遠くではないのかも、と危惧の念を抱かざるをえません。

何が間違っているのか、何が危険なのか。
それについて、説明をいたします。

●Cは高級言語です

そもそもCとは一体なにものぞ。
Cとは高級言語でありまする。
高級言語とは、通常低次の言語である機械語(アセンブラを含む)に対比して用いられる言葉であります。
んなことは知ってるよぉ。

あ。まあまあ。落ち着いて。
ん?知っていらっしゃる?
本当に?

では機械語に対して高級言語という場合そもそもどこがどう違うのか。
なぜに機械語ではなくて、高級言語なのか?
そこのところからの理解が肝要でありまする。

機械語が面倒極まりないことは周知のことであります。
そのために、アセンブラが開発されました。
それでいいじゃないの?

それでよくはなかったのです。
昔のコンピュータはみごとにハードに依存していました(はずです)。

●機械語は?

たとえば、IBMのコンピュータで開発した機械語(アセンブラ)のプログラムは、他社のコンピュータでは実行できません。
それは当たり前のことです。
それだけではなくて、同じIBMのコンピュータの間でも、技術の進歩にしたがって、新しい回路を採用すると、以前の回路で通っていた機械語のプログラムが実行できなくなってしまいます。

互換性を維持しようとすると、いつまても旧式の回路をひきずることになってしまいます。
ちなみに、一世を風靡したNECのPC9801シリーズは、初期の8086CPUのPC9801との機械語レベルでの完全互換を維持するために、その後の286、386CPUを搭載したシリーズにおいても、ボード内に8086(注記)を実装していて、ディップスイッチで切り換えて実行できるようになっていた、と記憶しております。
[2011.5.4注記]当記事をお読みいただいた方からメールをいただきました。
286、386を搭載した9801Vシリーズに実装していたのは8086ではなくてV30です、とのご指摘をいただきました。
V30はNECが開発した8086上位互換の16bitCMOS・CPUです。
ご指摘いただき有難うございました。
ここに謹んで訂正させていただきます。(注記ここまで)

さすが、NECであります。それでこそ、あっぱれ日の丸メーカーであります。
今のNECはどうだか知りませんけれど。
ほんと、Mi○softのビ○イツ君には、カネ儲けばっかりに専念しないで、NECのツメのアカでも煎じて飲め、と言ってやりたいですぅ。

ああ。また脱線してしまいました。
本題に戻ります。
それはしかし、大変なことであります。
いまやソフトウェアを抜きにしてはコンピュータは全く成り立ちません。ソフトウェアの重要性は日々増大してきています。
そのソフトウェアが初期のコンピュータのように、個々のマシンの設計に深く依存しているということでは、応用技術の開発に致命的な影響を与えてしまいます。

●高級言語とは?

もうおわかりいただけたと思います。
高級言語はそういうハードウェアとのしがらみを断つために考え出された言語だったのです(と思いますよ)。
したがって優れて洗練された高級言語はハードウェアを選ばない。
ソースプログラムレベルでは、どういうハードウェアの上でも実行できる互換性を維持していることが、その存在の基本的な条件であります(ここんとこ、非常に重要ですから、しっかりマークしておいてくださいませ)。
このルールによって、あらゆるソフトウェアがハードウェアにかかわることなく、完全な互換性を獲得したことになります(これはいうならばバーチャルなシステムであります)。

そこで次に必要不可欠になってきますのが、そのソースプログラムを個々のマシンの機械語に正しく翻訳してくれるコンパイラです。
そうすると、せっかく苦労して作り上げた高級言語なのだけれども、今度は新しいマシンを開発するたびに、コンパイラ(つまり機械語への翻訳プログラム)も個々に用意しなければならない、ということになります。
それを助けるために、OS(オペレーションシステム)なるものも考え出されたのでありますけれど、基本的に、コンパイラが機械語に翻訳しなければならない、という役目がOSによって変わったわけではありません。
しかしOSについては余談の余談になってしまいますから、ここでは触れません。

ともかく、高級言語というものは、要するに、ハードウェアのしがらみから完全に開放されるためのものである、ということについては、どちら様にも異存はなかろうかと思います。
でも、実際のところ、Cをお使いの皆様方は、そこんところをついうっかりと失念なさっているのでは、と思います。

●それではPICのプログラムをCで書くということは…

よろしいでしょうか。
Cはハードウェアに依存しない高級言語である。
よろしいですね。

では、ここで問題です。
PICのプログラムをハードウェアに依存しないで、書くことが可能でしょうか?
いや、たとえ可能だとしても、そんなプログラムになんの意味があるのでしょうか?

だって、ハードに依存しないことが建前の言語を使って、どうやってPICの何番ピンの、ポート何とかのビットからH信号を出力せよ、とか、何番ピンから信号を入力せよ、とかというプログラムを書くのでしょう?

私の知る限りでは、C(C++でもよろしい)のstdioでもなんでもよろしいが(スタンダードIOと言っているにもかかわらず)、マシンに直結する、IN、OUT命令は存在しないはずなのですよねえ。
そしたらPICのプログラムなど、書けるはずはないじゃありませんか。
どだい、やろうとしていることが無理なのですよ。
もう完全に分裂しておりまする。

Microchip社もそこんところはわかっていて、だからPICのためだけの(はやいはなしがそこんとこだけはめちゃめちゃ互換性無視のムリムリの)Cコンパイラを用意しています。
んじゃあ、どこがCなのかといいますと、Cの機能にPORTBだとか、TRISBだとかの機能名が扱えるようになって、Cコンパイラですう、ということのようなのですよねえ。

そうすると、それを使えばPICについて知らなくてもプログラムを書くことができるようになるのか、というと、そういうわけにはいかんでしょうよ。

そもそもPORTBというものが何物で、そのビットがPICなんたらの何番ピンにつながっているのか、というような基本的な情報は知らないで済ますわけにはいかないでしょう。
いかないけれども、ひょっとすると、そこんところも、ヘッダーファイルにしてインクルードしてしまって、たとえばoutpb1(n)をCALLするとPORTBのnビットから1を出力しますよ、outpb0(n)をCALLすると0を出力しますよ、というふうにしてしまいますと、これはもうかなりバーチャルに近くなってきます。

んでも、そういうことでよいのか?本当にそれでいいのか?
と、どうしても考えてしまうのですよね。
え。皆様はどうお思いになられますでしょうか。

●PICにはアセンブラがあるじゃありませんか

そんな面倒なことをしなくても、PICアセンブラで
bsf PORTB,n
と書けば済むじゃありませんか。

オーバーヘッドなど気にすることは全くありません。
実クロックで実行してくれます。

ああ。そうです。
高級言語のもつ致命的な欠点として、オーバーヘッドがあります。
コンパイラはそれをなんとか回避しようと努力しますけれど、所詮機械語(=アセンブラ)の敵ではありません。
コンピュータの世界ではオーバーヘッドとは、ある処理をするのにかかってしまう処理時間(とりわけそれがかかり過ぎるときに用いる)のことをいいます。

でも、それってPICのプログラムでは致命的なのではありませんか?
ま。そのことについては、あとでまた説明することにいたしましょう。

たとえばPICのC18コンパイラを使うことにして、通常のCコンパイラのみの知識しか持っていない方が、C18コンパイラを使うとしたら、最初にまず、CONFIGの設定だとか、PORTの設定だとかをどうすればいいの?というところでつかえてしまうはずです。
だって、それは一般のCの関与しない(すべきではない)事柄なのですから、知らないのが当たり前です。

そうすると、C18コンパイラの説明書を読んで、関数を探すことになります。
ひょっとすると、メインの命令部分には書いてなくて、インクルードするヘッダーファイルにあったりして…。
なんで、そういう不毛としかいいようのない努力をせにゃあならんのか?

たとえばさきほどの
bsf PORTB,n
のbsfという命令は、PICのData SheetのInstruction Setを見ればちゃんと書いてあります。


[出典]Microchip社 PIC16F88Data Sheet

簡潔にして明快、もうそのまんまじゃありませんか。
なんでこういうすぐに使える簡単なものを使おうとしないで、わざわざ回り道をして、Cなんぞを使おうとするのでしょう?
しかも設定ファイルだとか、ヘッダーファイルだとか、前準備が大変なものですから、結局のところ深く踏み込もうとはしないで、どなたかがお作りになったサンプルプログラムをいただいて、そのなかで関数のパラメータだけをちょこちょこっと直して、それで、
できた、できた、先生、PICのプログラムができました。
って、それって違うでしょ。
何にもわかってないのと違います?

せっかくPICという、手軽にハードとソフトの両方が体験できる素材がありながら、なんでわざわざCなどを使ってバーチャルにしてしまうのか、私にはとんと合点がいきませんです。

●アセンブラは難しいか?

だって、アセンブラは難しいから…。

そんなことはないです。
それは一般に流布されたデマにすぎませんです。
アセンブラくらい簡単なものはありませんですよお。

あ。ま。それはちょいとオーバートークでした。
今後の「舌禍」を避けるためにも、一応、「PICに限っては」つう但し書きをつけておくことにいたします。

どのくらい、PICアセンブラが簡単なのか、例で示してみることにいたします。
例題です。
PORTBのビット0から最短の幅のパルスを出力しなさい。

これはよくあるシチュエーションです。
書き込みパルスでも制御パルスでもなんでもいいですけれども、ある周波数のパルスを出力したい、というのは出力制御の基本中の基本です。

MYCPU80のTK80応用プログラムでも、出力パルスの周波数を変えることで、音階を出力するというのがありましたですねえ。
さて、Cならどうやりますでしょう?
私は知りません。
んなもの考える気にもなりませんから。

●PICのサンプルプログラムです

PICは何でもよいのですが、PIC16F88を使ってみることにしましょう。
PIC16F88は内部クロック発振モードという便利な機能があって、外にクリスタルを取りつけなくても、configの設定でMax8MHzのクロックを自前で供給できますから、回路が実に簡単になります。
こんな感じです。

さすがにこれはちょいと乱暴ですけれど、とりあえず動きます。

ねえ。ハードはこんなに簡単ですのに、このハードを動かすのに、Cコンパイラを使うんですかあ。
アセンブラを使いましょうよ。

で。アセンブラのソースプログラムです。


[注記]このプログラムにはミスがあります([第416回]参照)。

CONFIGのところはおまじないみたいなものです。
でも詳細を知りたければ、PIC16F88Data Sheetにはちゃんと書いてあります。


[出典]Microchip社PIC16F88Data Sheet

各設定値の意味、詳細についてもData Sheetに説明があります(巻末の索引で記載ページがわかります)。
アセンブラのConfigの表記にはちょいとクセがありますが、たとえば_CP_OFFとか_LVP_OFFというような表記は、MPLABをダウンロードするとついてくる、infファイル(上のプログラム例ではp16f88.inf)を読めば見当がつきます。

こういうことはアセンブラを使うから見えてくることで、Cコンパイラではよくわからない部分でしょう。

さて、アセンブラのソースリストの、bsf STATUS,5からbcf STATUS,5までのところは、ポートの向き(INかOUTか)とかCPUクロックの設定などをしているところで、PICを扱うからには、Cであろうとなかろうと、ここは避けては通れないところですし、その内容についても、当然知っていなくてはなりません。
で、ここも、Cを通じて理解するのが簡単か、それともData Sheetから直接理解するのが簡単か、ということですけれど、いかがでしょうか?

たとえば特殊レジスタSTATUSのビット5を1にすれば、メモリバンク1が選択され、それを0にすればバンク0が選択される、ということもData Sheetを読めばわかるのです。
またOSCCONに6Cを与えれば、内部発振クロックが4MHzになることも、Data Sheetを読めばわかりますし、TRISBの’0’を書き込んだビットに対応するPORTBのビットの向きが出力になるということも、それから、OSCCONやTRISBはバンク1にあって、PORTBはバンク0にあるということも、全部Data Sheetを読めばわかるのです。

ところでCコンパイラだと、いったいどこを読んだらそれらを理解することができるのでしょうか?
よくわからんから、結局サンプルプログラムをそのままコンパイルすることになってしまうのではありませんか?
それじゃあ何もわからんのと同じではありませんか?
私は、そういうことが言いたいのですよ。

さて、プログラムの実行部分です。
loopからgoto loopまでを繰り返しているだけです。
たったこれだけです。
PICのプログラムをご存知ではない方でも、8080やZ80の機械語(アセンブラ)がおわかりになる方でしたら、なんとなく読めるような気がしませんか?
その通り。読めるのですよ。
PICのアセンブラはちょいとクセがあって評判は余りよろしくないようですけれど、それでもCよりは相当マシだと思いますよ、私は。
movf PORTB,wはPORTBをワーキングレジスタwにmoveせよという命令ですし、xorlw 01は、そのwの値と01のxorを計算せよという命令ですし、movwf PORTBはwレジスタの値をPOTBにmoveせよ、という命令です。
ほらね。そのまんまじゃありませんか。
実に簡単でしょう。

それだけのプログラムで動いてしまうのですよ。
この通りです。

上のプログラムを実行中の写真です。
ジャノ目基板は裏側しか見えませんが、PIC16F88以外には抵抗とコンデンサしかありません。
ミノムシクリップで電源を供給しています。
ミニクリップはオシロのプローブにつないでいます。

●アセンブラならパルス幅が計算できるのです

これはもう、Cでは逆立ちしたって実現できない機能です。
だれかが作ったタイマールーチンなんてものをさがしてきてインクルードすればアバウトでしたらできないことはないでしょうけれど、これだけ正確にしかも高い周波数を得ることは、Cにはできない芸当でしょう。

今回のプログラム例ではPICのクロックはたかだか4MHzです。
PICの命令サイクルはCPUクロックの1/4です。
つまりCPUクロックが4MHzの場合、1命令は1μs単位で実行されます。
たいていの命令は1μsで実行されますが、2μsかかる命令もあります。
それでいて、オシロの波形からわかりますように、H=5μS、L=5μSですから100KHzの出力が得られているのです。
H出力もL出力も、わずか5命令サイクルで実行されているのです。
しかもCと違って、アセンブラなら、プログラムを書いたときから、そのパルス幅は約束されているのです。

movf、xorlw、movwfは1命令サイクルで実行され、gotoは2命令サイクルで実行されます。ですから1回の出力あたり5μsなのです。

いかがでしたでしょうか?
PICならアセンブラ、ということに納得いただけましたでしょうか。

●MPLABのバグ?

ところで、アセンブラをおすすめしておきながら、何なのですけれど、PIC16F88についてはMPLABにバグがあるようです。
私は普段はMPLAB IDE v7.31を使っているのですが、そこで気がついたバグは、念のため確認してみたところ v8.4でも同じでした。
ConfigureのSelect DeviceでPIC16F88を選んだうえで、PIC16F88のソースプログラム(上のプログラム例のような)をアセンブルすると、どういうわけか、configが間違っている、というエラーメッセージがでてしまいます。
デバイスを(ウソですけれど)PIC16F84とかPIC16F628にして、同じプログラムをアセンブルすると、「デバイスの設定が違ってるじゃないの」という警告メッセージが出ますけれど、正しく終了してHEXファイルが作成されます。

あ。私は面倒なことが嫌いですので、Projectの設定などは全部無視して、いきなりソースプログラムをOpenして(またはいきなりNew FileをOpenして、そこにソースプログラムを書いてしまって)、Project→QuickBuildで簡単一発アセンブルを済ませてしまっています。
そのことと、バグとは多分関係ないとは思いますが。

[2010.4.27注記]
バグではありませんでした。間違っていたのは、この私のほうでした。
PIC16F88のCONFIGの書き方が悪かったのです。
下のPICプログラマで表示されたメッセージも、そのCONFIGの書き方に関係していました。
詳しくは[第488回]をご参照ください。
[注記ここまで]

●なんとPICプログラマでもバグが?

秋月のPICプログラマで、今回のテストプログラムをPIC16F88に書き込もうとすると、気持ちの悪い警告メッセージが出ます。

こんな表示が出ます。


アセンブル後のリストで確認してみましたが、Configの機械語コードに間違いはありませんし、それはここで表示されている3F78に間違いありません(これはdefaultではなくて、あくまで上のテストプログラムのCONFIGで設定した値です)。

ここは無視して、書き込みをしてしまうと、何事も無くOKになります。

もちろん正しく実行できることは、さきほどお見せした写真の通りです。

●PIC16F88のPORTBについて

今回のテストプログラムを書いて試してみる過程で気がついたことがあります。
上の方でお見せした回路図で、RB0端子に10KΩのプルアップ抵抗がつけてあります。
これがないと出力が確認できません。
PIC16F88のData SheetのPORTBの説明では、どこにもオープンドレインである、とは書いてありません。
ただPORTBのブロック図をみると、いろいろな出力や入力やらが1つのピンに複合されていて、その回路の動作によってはオープンドレインになってしまうかも、と疑う余地は十分にあります。
このあたりも、ことPIC16F88については、かなり確認して使わなければ期待通りには動いてくれない、という可能性があるように思えます。
クリスタルなしで済むというあたりは、なかなかに捨てがたいICではあるのですが…。

ということで、本年はこれで終わりです。
今年一年、お付き合いいただき有難うございました。
来年も倍旧のお引き立てをお願いいたします。

私といたしましても、さすがに新年正月くらいはちょいとのんびりしたいと思いますので(そんなことを言いながら、きっと元旦から、USBの追及解明などをごそごそやったりして…)、新年の更新開始は正月明けから、ということになると思います。

皆様。よいお年をお迎えくださいませ。
2009.12.31upload
2010.4.27追記
2011.5.4注記

前へ
次へ
ホームページトップへ戻る