PICでUSBを!(知識ゼロからのスタートです)
PIC18F14K50のUSB機能を100%自前のソフトで制御する試みです。しかもアセンブラで!
当記事は2009年12月から「TTLでCPUをつくろう!」というタイトルの もとにほとんど毎日連載をしてきたものを再編集したものです。 2011.7.9

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

PICとのつきあいはもうずいぶん長いのですが、今まで使ってきたのはPIC16FXXというナンバーのものばかりでした。
PIC18FXXは初めてさわります。
PIC18FXXはPIC16FXXに比べるとハードウェア、ソフトウェアの機能がかなり拡張されていることがわかりました。
USBを扱うためには、その拡張されたハード、ソフトについて理解する必要がありそうです。
ということでまずはPIC18F4550の機能を理解することからエントリーいたします。

[第24回]

●プログラムミスがありました

前回、PICの直接アドレッシングの説明をするために書いたプログラムにミスがありました。
今日になってから、アセンブラによって出力された機械語コードのリストをながめていて、プログラムミスに気がつきました。
これがそのソースプログラムでした。


どこが間違っていたかといいますと、真中あたりにある
bcf STATUS,0;bank 0
が間違いだったのです。

PIC16F88のデータメモリが4つのメモリバンクに分かれていることは、すでに説明しました。
そのバンクを選択するのに、PIC18F4550ではBSRレジスタを使うのですが、PIC16F88ではSTATUSレジスタを使います。
こちらがPIC16F88のSTATUSレジスタの説明です。


[出典]Microchip社PIC16F88Data Sheet

PIC16F88ではSTATUS registerのbit5とbit6によってバンクを切り換えています。
ですから本当はbit5だけではなくてbit6もセット、リセットしなければならないのですが、今回のプログラムではバンク0とバンク1しか使わなかったことと、リセットによってSTATUSレジスタのビット5とビット6は’0’になることから、今回のプログラムでは、Bポートを出力に設定するため、バンク1にあるTRISBに00を書くときに、STATUSのbit5を’1’にし、そのあとでポートにデータを出力するため、バンク0にあるPORTBがアクセスできるように、STATUSのbit5を’0’にするつもりでした。
ところがそこのところをうっかりして、bit5ではなくてbit0をクリアしていたのです。

ということは、ずっとバンク1が選択されていたことになります。
すると当然PORTBに出力はできなかったはずです。
じつはこの部分のプログラムミスは、前回お見せしたプログラムだけではなくて、はじめにパルス出力プログラムのサンプルとして書いたものがもとになっていて、[第18回]の最初のパルス出力プログラムも[第19回]のプログラム変更をした2本のプログラムも、全部同じところが間違っていました。

でも、そうすると、当然PORTBにはアクセスできませんから、パルスを出力することはできなかったはずです。
PIC18F4550のアセンブラは「かしこいアセンブラ」なので、勝手に都合よくパラメータを付加してくれましたが、PIC16F88の場合にはそういう芸当はできません。
機械語に変換されたリストを眺めてみても、STATUSのビットを勝手にセット、リセットしたりはしていないようです。
すると、アクセスできないはずのPORTBから、なぜパルスが出力されたのでしょうか?

これはこれで問題です。
たまたま今回はプログラムにミスがあったのですが、そのミスが無視される形で、期待した通りの結果が得られました。
しかし、CPUは忠実な僕(しもべ)であるべきなので、たとえご主人さまが間違った命令を下したとしても、それにさからうことは許されません。

むむ、それともウチのPIC16F88は、ひょっとすると、自ら考える知能を獲得してしまったのか?
こ、こういうものは直ちに破壊してしまわなければ、未来からターミネータが攻撃してくるかも…。

んなわけは、ありませんでした。
ターミネータの妄想と戦いながら、必死で考えていましたら、なんともあっけなく謎が解けてしまいました。
いやあ、よかった、よかった。
正月早々、てっきりターミネータと戦わねばならぬのか、と思ってしまったじゃありませんかぁ(おお、こわぁ)。

プログラムのミスのために、本来は選択されるべきバンク0が選択されず、バンク1が選択されていたことは間違いがありません。
当然、
xorwf PORTB
という命令も、PORTBをアクセスすることはできません。
ではどこをアクセスしていたのでしょう?

ここでぜひとも思い出していただきたいのが、前回の「直接アドレッシング」の説明です。
xorwf PORTB
のようにアセンブラで書くと、PORTBをアクセスして当たり前だと思ってしまうのですが、じつは、
xorwf 06
と書いたのと同じことなのでした。
このように書くと、むむ、本当にこれでPORTBをアクセスするのだろうか?とちょっと自信がゆらいできてしまいます。

前回もお見せしましたが、PIC16F88のメモリマップです。

[出典]Microchip社PIC16F88Data Sheet

PORTBはアドレス06にあります。メモリバンク0です。
その同じ位置のメモリバンク1には何があるでしょうか?
TRISBです。アドレス86にあります。
TRISBはPORTBの向きを設定するためのレジスタです。
TRISBは、今回のプログラムでも使っています。
前回お見せしたアセンブラによって出力された機械語コードのリストをもう一度見てみましょう。



PORTBを出力に設定するために、TRISBに0を書き込んでいます。
その部分は、アドレス0009にあります。
movwf TRISBの機械語コードは0086になっています。

PORTBに値を出力するための、movwf PORTBも、機械語コードは0086です。
この’86’はTRISBのアドレスの86ではなくて、もともとmovwfの機械語コードの下位8ビットのbit7は1なのです。bit6〜bit0に対象になるメモリアドレスが入ります(前回[第23回]参照)。
メモリアドレス部分は7ビットしかありませんから、TRISBの最上位ビットの’1’は無視されてしまうため、movwf PORTBもmovwf TRISBも全く同じ機械語コードになってしまうのです。
(問題のパルス出力部分は、movwf PORTBではなくて、xorwf PORTBなのですが、理屈は全く同じです)

この場合PORTBに出力するか、TRISBに出力するかを決定しているのが、STATUSのビット5、ビット6なのです。
では、なぜTRISBにしかアクセスできなかったはずなのに、パルスが出力されたのでしょうか?

TRISBはPORTBの入力、出力の向きを決定します。
TRISBのビットに’1’を書き込むとPORTBの対応するビットの向きが入力になり、’0’を書き込むとPORTBのそのビットは出力になります。
PORTBの入力に設定されたビットの出力はハイインピーダンスになります。
抵抗でプルアップしていますから、見かけ上は’1’が出力されたように見えます。
PORTBの出力に設定されたビットはいつも’0’が出力されるわけではありませんが、power−onしたとき、たまたま出力ラッチがクリアされていると、’0’が出力されます。
つまり、’1’と’0’が交互に出力されていたのではなくて、「入力」と「出力」に向きが交互に切り替わっていて、たまたま出力が’0’であるとパルスが出力されているように見えたのです。

どうりで、今回のPIC16F88の動きはおかしい、と思っていました。
ときどきpower onのタイミングによってパルスが出力されないことがあったり、そもそもData Sheetには「オープンドレイン」とは書いてないにもかかわらず、どの出力ビットも’1’出力がハイインピーダンスだったのです。

何日かぶりに、やっと、納得。でしたけれど、まあ、ほんとに、正月からなんともみっともないお話になってしまいました(恥)。
CPUをつくろう!第416回(2010.1.8upload)を再編集

PICでUSBを![第24回]
2011.7.9upload

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