平成26年9月26日
実験環境の準備
PIC32マイコンを実際に動作させる環境を用意します。選択肢としては
- ブレッドボード+DIPパッケージマイコン(変換基板使用を含む)
- 各社のPIC32マイコン評価ボード
があります。
前回PIC24Fを使ってUSBドライバプログラムの動作検証をしたときには、
オプティマイズ社製のPIC24USB基板を使用しました。
非常に小型でUSBから給電できるのが便利でした。
今回も
同社製のPIC32基板を使用します。この基板に決めた理由は非常に安価なうえ余計な機能の付いていない点が魅力的です。
PIC24USB程ではないものの非常にコンパクトです。基本動作の検証が終わった後には、先にPIC24USBで動作検証したUSBプログラムを移植しますので、USBコネクタが実装されている基板の方が安心できます。
手っ取り早く動作検証するならマイクロチップ社の評価基板を購入すれば、メーカーのサンプルプログラムが変更なしに動作します。
が、それでは得られるものもありません。実際に自分で動かしてみる過程の中で、その製品に関する具体的な知識や詳細な理解が付いてきます。その意味ではマイコンと必要最低限のハードウェアというのは、ごく初期の動作検証にはうってつけです。程度の問題ですが、マイコンの初心者がターゲットなら全く違う判断になります。
基板が到着すると
指定された手順に従って半田付けを行います。この作業が面倒なら購入時、同時に依頼することで作業を代行してくれるサービスもあるようです。あとは、HPにあるとおりに動作確認を行います。私はUSBコネクタを接続してPCに正しく認識されるところまでしか検証しなかった。少なくともこれでCPUが正しく動作していることは判断できます。
プログラムの準備
動作検証用のプログラムを作成します。最初の動作は組み込みでは”LEDピカピカ”と相場が決まっています。今回はオシロスコープを使ってIOポートの出力値を確認する”IOポートバタバタ”で確認を行います。
今回使用するPIC32MX795F512Lというマイコンにはキャッシュ機能が搭載されているので、正確な命令の実行速度はキャッシュ機能に依存します。このため実行速度の検証は後回しにして、取りあえずIO出力が期待したとおりの動作をすることを先に確認します。
実際には完全にゼロからプログラムを書くには非常に多くの資料を読む必要があり、簡単ではありません。
ここは少しサボってオプティマイズ社のHPにあるサンプルプログラムを変更することで動作検証用プログラムとします。
こちらの画面の下側1/3ほどにある「Hello world.」サンプルソース一式をダウンロードします。このプログラムは旧来のC32 CコンパイラとMPLABの組み合わせで動作するプログラムのようです。今回はXC32
CコンパイラとMPLAB Xの組み合わせで行うので、内容を参考にするに留めます。
圧縮ファイルの中からmain.cを開いて中身を確認します。オプティマイズ社の許可が必要になりますのでソースリストは示せませんが、これから最低限必要な処理内容が分かります。
- Configビットの設定で右記事項記を設定する。 発信器の選択・外部発信器の発信モード・PLLによる周波数の倍率設定
- main関数の記述
従来のPICマイコンとほぼ同じ記述で問題ないようです。Configビットの設定内容はXC32 Cコンパイラの付属文書に説明があります。
動作仕様は下記のとおりとします。
- PIC32基板には外部発信として16MHz発信子が接続されているので、これを使用します。
- 動作周波数は最大80MHzまで可能だが、今回はまず20MHzで動作させる。この動作周波数なら内蔵フラッ不ROMへのアクセスがウエイトなしでも直接アクセスできる(フラッシュROMの0ウエイト動作は30MHzまで可能)。
- 出力するIOポートはアナログ入力との兼用でないポートDを使用する。アナログ兼用ポートはリセット直後はアナログ入力になるので別に設定が必要になる。
これをプログラムにすると下記になります。Config設定はウォッチドックタイマを禁止するFWDTENを除いて発信器の選択と周波数の設定です。詳細はデバイスのデータシートを参照してください。動作周波数は下記になります。
動作周波数 = 発信器周波数 / FPLLIDIV x FPLLMUL / FPLLODIV
#include <xc.h>
#pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_4, FPLLODIV = DIV_4, FWDTEN = OFF
#pragma config POSCMOD = HS, FNOSC = PRIPLL
int main(void)
{
TRISD = 0;
LATD=0;
for(;;){
LATD=0xffff;
LATD=0;
}
return 0;
}
アセンブルリストは下記になります。
!int main(void)
!{
! TRISD = 0;
0x9D000234: LUI V0, -16504
0x9D000238: SW ZERO, 24768(V0)
! LATD=0;
0x9D00023C: LUI V0, -16504
0x9D000240: SW ZERO, 24800(V0)
! for(;;){
! LATD=0xffff;
0x9D000244: ORI V1, ZERO, -1
0x9D000248: SW V1, 24800(V0) ここで出力が'1'になる
! LATD=0;
0x9D00024C: SW ZERO, 24800(V0) ここで出力が'0'になる
0x9D000250: J 0x9D000248
0x9D000254: NOP
ポートDの出力が'1'である時間は1命令(SW ZERO, 24800(V0))の命令実行に必要な時間のみであることが分かります。
このときのIOポートの出力を示します。期待通りに繰り返し波形として出力されています。これよりプログラムの設定は最低限正しいことが分かります。
キャッシュ機能の検証
次に動作速度についてみて見ると、ポートの出力値が'1'の期間は400ns程あります。CPUの動作周波数は20MHzですので期待値は1000ns/20で50nsです。明らかに長すぎる。キャッシュ機能を無視して動かしたので、キャッシュ機能に関連した設定が不適切な可能性が大です。
データシートのキャッシュ機能に関する記述を調べると、どうもキャッシュ機能は電源投入時フラッシュROMへのアクセスに対してウエイトを入れるようです。CHECONレジスタのPFMWSビットがそれで、デフォルトでは7ウエイトが入ります。そうすると本来の1クロックを合わせた合計8クロックとなり、先の'1'出力時間400nsと一致します。PFMWSビットを0ウエイトになるように設定を変えると期待通り50nsの'1'出力時間となりました。ここまでは希望通りの動作です。
ここで動作周波数を80MHzに変更したところ上手く動作せず、どうも暴走しているようです。データシートを読んでもキャッシュ機能は電源投入時には有効になっているように思えます。何が悪いのか?
(H26/09/28追記)
暴走する原因は単純なケアレスミスでした。
こちらをご覧ください。
(追記ここまで)