PIC16F887の周辺

16F887は16F877,16F877Aの改良版である。40pin DIP packageが秋月で250円(16F877Aは400円)である。秋月やADWINのPICライターは16F887には対応していない。


むしろ、そんな余計な改良をして
・バイナリの互換性がなくなった
・対応しているPICライターが少ない
となるのであれば、改良しないままのほうが嬉しかったのだが…。


今回はそんなPIC16F887の周辺について書いてみたい。


昨日書いたPIC用のシリアル通信のプログラムだが、PIC16F887でもそのまま動いた。


テストしていてわかったのだが、128以上の値を送ると'?'(0x3f)に化けてしまうのだ。


早速、MPLABのSIM機能が役に立つ時が来た!と思った。MPLABにはUSARTのデバッグ機能があって、ファイルから入力文字列を食わせることが出来るのだが、どうもこれが通常文字しか対応していないようだ。'\0'やら'\8'やらを食わせてデバッグしたいのだが。全然使えねぇ…。


私は255(0xff)が0x3fに化けるもんだから、てっきり上位2bitが欠落しているのかと勘違いして、通信設定の見直しをしたり、ノイズ対策をしたりと余計なことをいろいろしてしまった。


実は、.NET Framework2.0以降で追加されたSerialPortクラスは、defaultではencodingはASCIIである。つまり7bitしか受け付けず、8bit目が立っているデータが送られてくると勝手に '?'(0x3f)へencodeしてしまうのであった。


これを解決するにはencodingは28591(ISO_8859_1)を指定すれば良い。

serial.Encoding = System.Text.Encoding.GetEncoding(28591);

・明日から使える無駄知識デバッグノウハウ
文字をstreamなどから受け取って'?'に化けてたらencodingがASCIIになってますよ


それで問題の16F887だが、16F877Aのバイナリがそのまま動くかと言えば、動かない。 簡単なLEDちかちかすら動かない。何故かと言えば、I/Oピンがdefaultではanalogモードになっているからだ。

// PIC16F877,PIC16F877Aのとき
ADCON1 = 0x7;

データシートを見るとどうもADCON1の機能が変わってしまっているようだ。16F887ではデジタルI/Oとして使いたければ次のように設定する。

// PIC16F887のとき
ANSEL = 0;
ANSELH = 0;

私にはdefaultでアナログモードになっているのが信じられない。これではバイナリ互換性がなくなってしまう。常識的にはdefaultではデジタルI/Oにしておくべきだろう。そうすればデジタルI/Oとしてピンを使っているプログラムはバイナリそのままで動く。


877→887でアナログ機能が拡張されたから、アナログ機能を利用しているプログラムがそのまま動かないのはいいとして、デジタルI/Oとしてしか使っていないプログラムがそのまま動かないというのは狂気の沙汰としか言いようがない。


PICはポートの設定もTRISレジスタは1が入力で0が出力である。defaultでは入力になっていて欲しいから、0x00でメモリ全域をクリアしたときに入力となるようにTRISは0が入力となっているべきだ。


私にはPICは頭のおかしい人が作っているとしか思えない。ひとりのソフトウェア開発者としてそう思う。何かハード的な理由があるのかどうかは知らない。


さて、PIC16F887には内蔵RC発振回路が搭載されている。これが凄く便利な代物で、ある程度自分の望むクロックに設定することが可能である。

// 内蔵クロックの粗調整
// <6:4> = 111( 8MHz), 110( 4MHz), 101( 2MHz), 100( 1MHz)
// 011(500kHz), 010(250kHz), 001(125kHz), 000(31.25kHz)
OSCCON = 0b01110000; // 8MHzに設定

// 内蔵クロックの微調整
// 下位6bitを2の補数で指定する
// 100000(最低)〜00000(0%)〜011111(最高)

// 7.3728 MHz付近に設定する
OSCTUNE = 54;
// 私が調べてみたら48〜59の時のみ
// 56kbpsで正しく通信できたので54ぐらいではないかと思う。

このように8MHz±1MHz の範囲で調整することが出来る。6bitで指定しているので64段階。64段階で2MHzの幅なので、31.25kHzのstepで調整できる。(実際はそんなにリニアな特性にはなっていないと思うが)


上の例では、7.3728MHzに設定している。この周波数だと、シリアル通信時の誤差率が0%になるからである。試してみたところ56kbpsでもうまく通信できた。115.2kbpsのほうはOSCTUNEの値をどう調整しても通信できなかった。これは、速度的に足りてないことによるものではないかと思う。(何せPIC16シリーズは4 clockで1命令しか実行出来ないので…)


あと、7.3728MHzは8MHz-31.25kHz×20.07なので64-20.07 ≒ 44がOSCCONが設定すべき値のはずである。でも上に書いてある通り、44では正しく通信できなかった。16F88のほうでは、OSCCONに42を設定してうまく通信できたと書いてあるサイトもあって*1、これが16F887と16F88の違いによるものなのか、個体差なのか何なのか私にはわからない。とりあえず16F887を70個買ってみたので明日実験してみようと思う。


室温によるRC発振の時の周波数の揺らぎは±2.5%以内のはずだが、8MHzの2.5%と言うと200kHzであり、200kHz/31.25kHz = 6.4である。OSCTUNE(これが周波数に対してリニアな特性を持っていると仮定して)の値は±6.4だけずれても正しく通信が出来なければ内蔵RC発振回路を用いてシリアル通信をするべきではないような気もする。


そう考えると56kbpsはちょっと危ない。せいぜい38400bpsにすべきだとは思う。(詳しく測定したわけでもなく、専門家でもないのでよくわからない)