Posts Tagged with "Design"

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.

ゲームFSMとサウンドFSMの連携

posted by sakurai on June 5, 2020 #271

Ultra96においてBSVで開発

元々Verilog版では、コマンドバッファに書き込むだけで特に何もしなくても動作していました。今回BSVで再設計する際に、サウンドを4chとし、取りこぼしを避けるために考えたのがサウンドキュー(FIFO)でした。

図%%.1
図271.1 サウンドキュー

これはVivadoのFIFOジェネレータで作成したため、最小段数でもかなり深く1024段程度となっています。 実験したところ、確かに取りこぼしは無いのですが、一方、サウンドがゲームとズレて行き、まるでサウンドレコーダのような動作になってしまいました。そのため、FIFOを1段に修正しました。FIFOジェネレータでは1段のFIFOは作成できないのでVerilogで記述しました。1段のためFIFOと呼ぶのはおかしいのでコマンドバッファと呼ぶことにします。

コマンドバッファには、ゲームFSMからコマンドが来たことを示すフラグemptyを設け、書き込むと!emptyとなるようにします。サウンドFSMからは!emptyの時に新たにコマンドが来たと判断し、コマンドを読んだ後にemptyに変更します。

図%%.2
図271.2 1段バッファに変更

Artyボード移植後

Ultra96ではこれで動作していたのですが、Artyボードに移植後に自機増加音が無視されることに気づきました。サウンドFSMが取りこぼしているのだと推測し、再考すると、ゲームFSMが不必要に速いことに気づきました。DSOを接続して調べたところ、96.4%がウェイトだと判明したので、これを1MHzに落としたところ、動作するようでした。

ところが実験すると、依然として自機増加音(コマンドNo.9)が無視されるようです。そこで、ILAを接続して、

  • サウンドコマンド
  • サウンドFSMステート
  • コマンドバッファemtpy
  • サウンドFSM内部フラグ(fNO9)

を観測しました。最後の内部フラグfNO9は自機増加音がプリエンプトされないように割込みを禁止するためのフラグで、コマンドNo.9を受け付けた際にTrueになる信号です。

図%%.3
図271.3 ILA波形(NG)
図271.3はゲームFSMクロックを2MHzとして取得したものですが、フラグfNO9がTrueにならず、コマンドNo.9を無視しています。その原因は、サウンドFSMが受け取る前に次のコマンドNo.4を上書きしているためです。

従って、コマンドの書き込みの際にemptyである場合のみ書き込み、!emptyの場合は捨てる処理を行います(図271.4のマゼンタ矢印の処理を追加)。

図%%.4
図271.4 両側でemptyを確認するように修正
このように修正したところ、No.9の次のコマンドが!empty(=buffer full)のため捨てられることにより、図271.5のように受け付けられるようになりました。
図%%.5
図271.5 ILA波形(OK)
FIFOではないので、原理上取りこぼしは防げないものの、実用上これで動作するようです。

左矢前のブログ 次のブログ右矢

posted by sakurai on June 4, 2020 #270

QSPIフラッシュへの書き込み

通常ではVivadoからPROGRAM AND DEBUG⇒Open Hardware Manager⇒Open Target⇒Auto Connectとし、Program DeviceによりJTAG経由でFPGAにビットストリームを焼きこみます。しかしながらこれだと電源断によりFPGAのSRAM内容が消えてしまいます。また、FPGAプログラミング用のPCが常に必要です。オンボードFlashにデータを焼きこめばPCを持ち運ぶ必要がなく、電源onでアプリケーションが立ち上がるため、Flashのプログラミングを行います。

binファイルの作成

最初にFlashへ書き込むデータファイルであるbinファイルを用意します。これは、Tools⇒Setting(歯車マーク)⇒Project Settings⇒Bitstream画面で行います。この画面を開くと複数のチェックボックスが表示されます。その中の、-bin_file*のチェックボックスにチェックします。

図%%.1
図270.1 bin_fileにチェック

これを行ってから、通常どおりPROGRAM AND DEBUG⇒Generate Bitstreamを実施するとWrite Bitstreamが完了しますが、同時にbinファイルが生成されています。場所はbitファイルと同じところで'プロジェクト/プロジェクト.runs/impl_1/'です。

binファイルの焼きこみ

binファイルができたら、Add Configuration Memoryにより、Add Configuration Memory Device画面が開きます。Flashデバイスの選択が可能なので、この中で"s25fl128sxxxxx0"を選択します。Search窓にs25fl128を入力すれば、候補が3つ現れますがその真ん中です。

図%%.2
図270.2 FLASHデバイスの選択
選択したらOKをクリックします。するとプログラミングが始まり、30秒程でプログラミングが完了します。

実行

リファレンスマニュアルにはJP1でプログラミングモードが決まるとあります。JP1の位置がどちらでもJTAGからは書き込めるとのことです。初期状態はJP1はショートで、SPI-FLASHのモードとなっており、そのまま電源のOFF⇒ONでSpace Invadersが立ち上がりました。

図%%.3
図270.3 JTAG接続なしにSpace Invadersが動作

左矢前のブログ 次のブログ右矢

posted by sakurai on June 3, 2020 #269

Arty A7-35ボードの購入

DigilentからArty A7-35ボード(魚拓)を購入しました。このボードはUltra96と比べて本体が約半額と安いだけでなく、(弊社開発の)PMOD変換ボードも不要なので、最も安くSpace Invadersを動かすことができます。

必要な周辺

Space Invadersを動作させるには、Artyボードの他に必要なものは以下のとおりです。

Arty A7-35ボードへの移植

除算器を引き算に変換

FPGAの世代や遅延、容量は違うものの、基本的には同様に動作するはずです。ところが、一部動作がおかしかったので修正しました。まず、除算器にバグがあるようなので引き算方式に修正しました。スコアを表示する箇所において、各桁表示のため1000、100、10で割る場合がありますが、1000で割った商を誤ることがあるようです。除算をやめ、引けなくなるまで1000、100、10を引く方式に変更したところ、回路規模も小さくなり正常に動作するようになりました。

FSM clockを1/10に変更

ゲームFSMクロックを10MHzで設計し、96.4%がウェイトだと判明したので、FSMクロックを1MHzに落としました。自機増加音が無視されることがあるので、クロックを落としたのですが、原因は異なっていました(後述)。

60Hzクロックの生成

この修正により、FSMの待ち時間が影響を受けます。1tick=60HzのタイミングをとるのにFSMクロック数を数えていましたが、FSMクロックの周波数が変わるため、外部から60Hzを入力するように修正します。60Hzクロックは、上記FSMクロックである1MHzクロックをバイナリカウンタで\$411B回カウントすることで生成します。さらにFSM内での60Hzクロックとの同期は以下のように行います。countはtick(=16.67msec)の何倍待たせるかを示す引数です。

     repeat(pack(extend(count))) seq
        await(tick == 0);
        await(tick == 1);
     endseq

60Hzクロックの"L"を待ち、もし"L"であれば次に"H"を待つようにします。これにより60Hzの立ち上がりに同期して動作することになります。

このように変更した結果、FSMの処理時間は10倍の約5msecに増加し、60Hzの周期16.67msecの約30%になりました。図269.2の黄線が60Hzクロック、青線がそれによる実行(Hでウエイト中、Lで実行中)を示します。

図%%.8
図269.8 青線が"H"でウェイト中

左矢前のブログ 次のブログ右矢

FM-7 Z80カードの調査 (3)

posted by sakurai on June 2, 2020 #268

引き続き、Z80コネクタへの信号を解説します。また、Z80CPUの高速化に伴い、Z80⇒6809切り替え時にDRAMのRASプリチャージタイム不足が判明したため、その対策も取っています。

EB

EBはZ80のMREQに相当する信号のため、前稿でのライト信号(=$\text{R/}\overline{\text{W}}$, M5 5pin)とCPUの$\overline{\text{RD}}$の負論理ORを取り、EBとしています。信号名は6809のE信号のバッファしたもの(E Buffered)から来ています。

QB

QBは立ち上がりはアドレス有効を示す信号です。また、6809の場合はQBの立下りでデータ確定しますが、Z80は$\overline{\text{WR}}$の立ち下がりでデータ確定します。そのため$\overline{\text{WR}}$の反転をQBとして使用する場合、その考慮が本体DRAM側で必要となります。その理由からFM-7メインボードのDRAMの回路図では、Z80と6809でQBの論理を反転させています。信号名は6809のQ信号のバッファしたもの(Q Buffered)から来ています。

RWB

RWBは、前述のライト信号を後縁を広げてRWBとしています。またデータバスバッファのDIRも後縁を広げて制御しています。この理由は図268.3でも示されているように、ライトサイクル時にZ80からのデータの切れが悪く、MREQがネゲートされていてもまだ出ているように見えるためです。信号名は6809のRW信号のバッファしたもの(RW Buffered)から来ています。

Z80W

しばしば負論理信号と誤解されますが、この信号は正論理の”Z80 wait”の意味であり、すなわち正論理の6809動作中を示す信号です。つまり$\text{6809/}\overline{\text{Z80}}$を表します。waitをバーと読み替えれば理解が容易です。

DRAM RASプリチャージタイム不足

基本的には上記の回路で動作しますが、一点、Z80から6809に戻る時に問題があります。それはZ80のEBにより\$FD05に0をライトすると$\text{G/}\overline{\text{H}}$が1となり、すぐに6809からQが出力されます。そのため、RASのプリチャージタイム$\text{t}_\text{RP}$が不足となり、結果としてDRAM化けが起こります。これを解決するために、Z80から\$FD05にライトする場合には、D0を0とし、EB、QBを出力せずに、6809側のEでライトし、CPUを切り替えるようにしています。

システムバスのEB及びQBが出力されないため、DRAMへのRASが出力されません。従って、RASプリチャージタイム不足が起きず、DRAMデータ化けが防止できます。

本来であれば、このようなトリッキーなことをせずZ80カードがやっているように、6809側で切り替え信号を遅延させてバスアクセスを行えば良いはずですが、時期的に本体側の回路変更ができなかったのかもしれません。

I/O掲載のZ80カード

アーカイブサイトに1985年9月号の月刊I/Oが保存されていました。その中に「FM-7/New7/77 IC9個でできる! Z80カードの製作」という記事がありました。IC9個は簡易回路とのことで、動作が不安定のようです。一方、コンパチ回路は基本的に富士通純正回路のコピーであり、ゲートを組み替えているだけに見受けられます。従って回路は正しく動作するはずですが、記事中に2点誤りがあります。

  • Z80Wが負論理となっている⇒Z80Wは正論理です。記事中では負論理のバーがついていますが、Z80Wは正論理の”Z80 wait”の意味であり、正論理の6809動作中を示す信号です。上記のように$\text{6809/}\overline{\text{Z80}}$を表します。筆者はこの$\overline{\text{Z80}}$を$\overline{\text{Z80W}}$と誤認したのかもしれません。ただしこれは表記上だけの問題であり、回路への実害はありません。
  • Z80カード側ではCPU(6809)のHALTと誤認する恐れがある⇒BA/BSだけで判断するとDMA(リフレッシュ)の場合と誤認するため、6809が動作中でないことを表すZ80Wとの論理をとっており、誤認しません。そもそも6809がHALT中にはDMAはかかりません。これも回路解釈の誤りだけで、回路への実害はありません。

左矢前のブログ 次のブログ右矢

FM-7 Z80カードの調査 (2)

posted by sakurai on June 1, 2020 #267

ライト時のデータバス衝突

FM-8もFM-7も、オプションのZ80カード上のデータバスバッファには、双方向バッファ74LS245を使用しています。245ではDIRにより方向制御を行いますが、6809の場合方向制御は$\text{R/}\overline{\text{W}}$信号であり、ノーマリリードです。ノーマリリードとは、ライト時以外は常にリード方向、すなわち周辺からCPU方向にバスバッファの方向を向ける制御方式です。Z80にも似たような信号であるライト時を示す$\overline{\text{WR}}$があるため、FM-8のZ80カードではこれをデータバスバッファのDIRに接続していました。

ところが、図267.1を見るとわかるように、$\overline{\text{WR}}$はストローブ信号であり、データバスバッファの方向制御に使用すると、

  • T1のクロックの立下りからT2のクロックの立下りまで(1クロックのデータセットアップタイム)
  • T3のクロックの立下りから立上がりまでの間(0.5クロックのデータホールドタイム)

の2つの期間において、CPUはデータを出力しているにも関わらず、バッファはリード方向となっています。すなわち、Z80と245の間でバスが衝突します。

図%%.1
図267.1 メモリリードライトサイクルの波形

通常、Z80システムではデータバスバッファのDIRには$\overline{\text{RD}}$を用います。すなわち、Z80はノーマリライトバスアーキテクチャです。

バス衝突の解消

まず、データバスバッファのDIRに$\overline{\text{RD}}$を用いれば、CPUとバスバッファの間のバス衝突は解消できます。FM-7のZ80カードではこの方式をとりました。

次に問題になるのがシステムバスのRWBです。データバスバッファ方向制御が$\text{R/}\overline{\text{W}}$であることから、同じ信号をシステムバスに出力するのが最も容易ですが、一方、これはノーマリライトのバスアーキテクチャであることを意味します。従って、システムバスアーキテクチャの変更にはリスクがあるため、無理やりノーマリリードの信号を作り出しています。

まずM18.4によりリフレッシュでないメモリ要求信号(!RFSH & MREQ)を作成します。

図%%.2
図267.2 M18周りの回路

これはリードとライトの両方でアサートされるため、次の図のように$\overline{\text{RD}}$信号で打ち消し、ライト信号を取り出します(M19.8)。これは同時変化の信号の論理を取っているため、M5のDFFを用いてクロックで叩きます。これが基本的に$\text{R/}\overline{\text{W}}$となります。

図%%.3
図267.3 M5周りの回路
バッファの方向制御に関して、CPUバスバッファ、システムバスバッファの両方ともにアナログ遅延を入れ後縁を伸ばしていますが、これはデータホールドタイムの保証目的だったと思います。バス容量によるホールドタイムを考えれば不要かもしれません。

バス衝突の確認

上記のとおり、バスの方向制御を作成しましたが、残るのはデータバスバッファ(ノーマリライト)と周辺(ノーマリリード)の間でのバスアーキテクチャの食い違いであり、この間でのバス衝突です。以下ではこれを確認します。バッファの衝突の可能性があるのは、

  • 周辺バッファはリード方向
  • データバスバッファはライト方向

であるから、下図において、M5.5($\text{R/}\overline{\text{W}}$)=Hのときです。さらにEB=Hの時に周辺からデータが出力されます。一方、データバスバッファは$\overline{\text{RD}}$=Hの際に周辺方向になるため、これらをANDすれば、リードサイクルのEBの後縁及び、ライトサイクルのEBの後縁のみであり、DCパスは無いことが確認できました。

図%%.4
図267.4 メモリリードライトサイクルの波形

左矢前のブログ 次のブログ右矢

FM-7 Z80カードの調査

posted by sakurai on May 29, 2020 #266

FM-7のZ80カード

FM-7のZ80カードは基本的にはFM-8のZ80カードの継承ですが、数点の改良があります。

図%%.1
図266.1 FM-7 Z80カード回路図( 「FM‐7 F‐BASIC 解析マニュアル (フェーズ II)」より引用)
  • 高速化
  • DRAMリフレッシュ方式の変更
  • ライト時のデータバス衝突の解消
  • 切り替え時のDRAM RASプリチャージタイム不足の解消

高速化

FM-8のZ80が2.4MHz動作だったのに比べて、FM-7本体の高速化に合わせてZ80Aを採用し、4MHz動作になっています。ただしM1サイクルが通常のメモリリードライトサイクルに比べて厳しいため、ウエイトをかけています。なお、タイムチャートはここのオンラインツールで作成しました。

図%%.2
図266.2 M1サイクルの波形

$\overline{\text{RFSH}}$リフレッシュ

M1で重要なのが*REFCKの生成です。前稿でリフレッシュタイミングを示しまたが、Z80でも同様に出力する必要があります。DRAMのタイミング要求に合うように、MREQからリフレッシュ時のみを取り出し(M18.10)、前縁を削り*REFCKとして出力しています。


左矢前のブログ 次のブログ右矢

FM-7 ROM吸出し器の改版 (10)

posted by sakurai on May 28, 2020 #265

実験&PICプログラムの修正

組み立てたボードを図265.1に示します。

図%%.1
図265.1 FM-7 ROM抜出し器改版
実験したところ、リフレッシュがかかりっぱなし("L")となってしまいました。これはPICから出力する*REFCKが常にONになっていたためでした。本来はZ80W==Lの時のみ出力するようなトライステート制御を行う必要があります。PICにはトライステート出力は無いので、Z80W==Hの時に入力、Z80W==Lの時に出力とします。割込みハンドラを、この方向制御で挟むことにより次のように修正します。
void TMR2_DefaultInterruptHandler(void){
    if (Z80W_GetValue() == 0) {
        XREFCK_SetDigitalOutput();
        XREFCK_SetHigh();
        RFREQ_SetHigh();
        while (RFGNT_GetValue() == 0);
        XREFCK_SetLow();  XREFCK_SetHigh();
        XREFCK_SetLow();  XREFCK_SetHigh();
        XREFCK_SetLow();  XREFCK_SetHigh();
        XREFCK_SetLow();  XREFCK_SetHigh();
        RFREQ_SetLow();
        XREFCK_SetDigitalInput();
    }
}

実験

62.5 usecに4発のリフレッシュを実験しました。リフレッシュ期間は3.8 usecであったので、Arduinoに割込みをかけた場合はほとんど100%がリフレッシュであったのに比べて、PICによるリフレッシュ時間の割合は6%に激減しました。

さらに125 usecに8発を試してみます。図265.2、3にその場合のリフレッシュの波形を示します。PICを用いることで125 usec(8発)ではリフレッシュの割合を4.6%まで下げる事ができました。

図%%.2
図265.2 複数回のリフレッシュ(上:*REFCK、下:RFREQ)
図265.2より、青色線で示されるRFREQの周期は125 usであることが読み取れます。このRFREQのH幅が約5.8 usecであることから、オーバヘッドは4.6%となります。

図%%.3
図265.3 1回のリフレッシュ(上:*REFCK、下:RFREQ)
図265.3上より黄色線で示される*REFCKが8発観測されます。負のパルス幅は約240 nsであり、タイミング規格を満足しています。ArduinoとのDRAMアクセス競合が起きている場合に、通常約5.8 usecであるRFREQが時々伸びていることが分かります。

結論

実験結果としては、熱はかけていないものの、180 secまでノーエラーで経過しており、リフレッシュ回路は問題ないものと判断します。

表265.1にリフレッシュ回数とオーバヘッドを示します。

表265.1 PICによるリフレッシュ回数とオーバヘッド
周期[usec] リフレッシュ回数 リフレッシュ時間[usec] オーバヘッド[%]
15.625 1 2.3 14.72
31.25 2 2.8 8.96
62.5 4 3.8 6.08
125 8 5.8 4.64
250 16 9.8 3.92
500 32 17.8 3.56
1,000 64 33.8 3.38
2,000 128 65.8 3.29

基本的にはリフレッシュ回数を増やせばサイクルが伸びるため、リフレッシュのオーバヘッド割合は一定ですが、一方、アービトレーションのオーバヘッド割合は減少するため、トータルでは減少します。このように8回辺りが妥当だと思われます。

左矢前のブログ 次のブログ右矢

FM-7 ROM吸出し器の改版 (9)

posted by sakurai on May 27, 2020 #264

Arduino側のアクセス修正

前稿にあるとおりプログラムします。新たにprologue()及びepilogue()を作成して、PICと待ち合わせを行います。

void prologue() {
  while (digitalRead(RFREQ) == HIGH);
  digitalWrite(RFGNT, LOW);
}
void epilogue() {
  digitalWrite(RFGNT, HIGH);
}

従来のアクセスの前後に挟んでアクセスコントロールを行います。例えば、readByte()という、あるアドレスから1バイトを読み出す関数の、

    setAddress(address);  // アドレスをセット
    d_pin_input(); // データピンを入力方向
    DIR_Read(); // R/Wをread
    data = readData(); // データを入力

この本体4行に対して、今作成したリフレッシュ排他制御で挟みます。

    prologue(); // リフレッシュを禁止
    setAddress(address);
    d_pin_input();
    DIR_Read();
    data = readData();
    epilogue(); // リフレッシュ許可

さらに、6809動作と競合しないように、トライステート制御で挟みます。

if (digitalRead(Z80W)==LOW) {
    ポート出力動作;
  } else {
    a_pin_input();
    d_pin_input();
  }

これを全ての出力ポートアクセスに被せることで、バスバッファを3個省略しています。以上から、次のようなプログラムを作成しました。

unsigned char readByte(unsigned address) {
  unsigned char data;
  if (digitalRead(Z80W)==LOW) {
    prologue();
    setAddress(address);
    d_pin_input();
    DIR_Read();
    data = readData();
    epilogue();
    return data;
  } else {
    a_pin_input();
    d_pin_input();
  }
}

左矢前のブログ 次のブログ右矢

FM-7 ROM吸出し器の改版 (8)

posted by sakurai on May 26, 2020 #263

タイマーの設定

今回はTimer2を用いて割り込みをかけるので、Timer2の設定を行います。表261.1にある組み合わせであればどれでも良いのですが、ここでは62.5 usecの割込みを入れ、割込みルーチン内で4回の*REFCKを発行するものとします。

まず、図263.1のようにモジュール設定でIntterrupt moduleを選択し(①)、右の画面でTMR2をイネーブル(②)とします。

図%%.1
図263.1 Interrupt module

最後にタイマーの設定を行います。モジュール設定でTMR2を選択(①)します。周期に62.5 usと記入(②)します。プリスケーラ、ポストスケーラとも1:1(③)とします。割込み何回に一回コールバックするかを設定(④)します。

図%%.2
図263.2 TMR2 module

コード生成

これで設定が完了したので、Generateでコード生成を行います。様々なCソースやヘッダファイルが生成されます。例えば、device.cが生成され、その内容は、

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection Bits->INTOSC oscillator: I/O function on CLKIN pin
#pragma config WDTE = OFF    // Watchdog Timer Enable->WDT disabled
#pragma config PWRTE = OFF    // Power-up Timer Enable->PWRT disabled
#pragma config MCLRE = ON    // MCLR Pin Function Select->MCLR/VPP pin function is MCLR
#pragma config CP = OFF    // Flash Program Memory Code Protection->Program memory code protection is disabled
#pragma config BOREN = ON    // Brown-out Reset Enable->Brown-out Reset enabled
#pragma config CLKOUTEN = OFF    // Clock Out Enable->CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin
// CONFIG2
#pragma config WRT = OFF    // Flash Memory Self-Write Protection->Write protection off
#pragma config STVREN = ON    // Stack Overflow/Underflow Reset Enable->Stack Overflow or Underflow will cause a Reset
#pragma config BORV = LO    // Brown-out Reset Voltage Selection->Brown-out Reset Voltage (Vbor), low trip point selected.
#pragma config LPBOR = OFF    // Low-Power Brown Out Reset->Low-Power BOR is disabled
#pragma config LVP = ON    // Low-Voltage Programming Enable->Low-voltage programming enabled

となっています。次にinterrupt_manager.cの内容は、

#include "interrupt_manager.h"
#include "mcc.h"
void __interrupt() INTERRUPT_InterruptManager (void)
{
    // interrupt handler
    if(INTCONbits.PEIE == 1)
    {
        if(PIE1bits.TMR2IE == 1 && PIR1bits.TMR2IF == 1)
        {
            TMR2_ISR();
        } 
        else
        {
            //Unhandled Interrupt
        }
    }      
    else
    {
        //Unhandled Interrupt
    }
}

のように、TMR2_ISR()を呼び出しています。mcc.cは各種初期化関数の集合であり、

#include "mcc.h"
void SYSTEM_Initialize(void)
{
    PIN_MANAGER_Initialize();
    OSCILLATOR_Initialize();
    WDT_Initialize();
    TMR2_Initialize();
}
void OSCILLATOR_Initialize(void)
{
    // SCS FOSC; IRCF 16MHz_HF; 
    OSCCON = 0x78;
    // SBOREN disabled; BORFS disabled; 
    BORCON = 0x00;
}
void WDT_Initialize(void)
{
    // WDTPS 1:65536; SWDTEN OFF; 
    WDTCON = 0x16;
}

となっています。pin_manager.cは、

#include "pin_manager.h"
void PIN_MANAGER_Initialize(void)
{
    /**
    LATx registers
    */
    LATA = 0x00;
    /**
    TRISx registers
    */
    TRISA = 0x32;
    /**
    ANSELx registers
    */
    ANSELA = 0x00;
    /**
    WPUx registers
    */
    WPUA = 0x00;
    OPTION_REGbits.nWPUEN = 1;
    /**
    APFCONx registers
    */
    APFCON = 0x00;
}

のように、ピン(ポート)の設定です。最後にtmr2.cはtimer2の各種関数の集合であり、左下のNavigatorウインドウに生成された関数一覧が確認できます。ここでは、

  • TMR2_CallBack()
  • TMR2_DefaultInterruptHandler()
  • TMR2_ISR()
  • TMR2_Initialize()
  • TMR2_InterruptHandler
  • TMR2_LoadPeriodRegister(uinit8_t periodVal)
  • TMR2_ReadTimer()
  • TMR2_SetInterruptHandler(void(*InterruptHandler)()
  • TMR2_StartTimer()
  • TMR2_StopTimer()
  • TMR2_WriteTimer(uint8_t timerVal)

が生成されました。interrupt_managerからTMR2_ISR()がコールされ、そこからTMR2_CallBack()がコールされ、そこではTMR_InterruptHandler()がコールされます。

ISR

以下のコメント部が示すように、TMR2_DefaultInterruptHandlerに割込み処理を書くようです。以下に内容を示します。

void TMR2_DefaultInterruptHandler(void){
    // add your TMR2 interrupt custom code
    // or set custom function using TMR2_SetInterruptHandler()
}

前稿での割込み処理内容は

もしZ80W=Lなら

  1. Rfreq=H、Xrefck=Hを出力 //リフレッシュ要求
  2. Rfgnt=Hを待つ
  3. Rfreq=H、Xrefck=Lを出力
  4. Rfreq=H、Xrefck=Hを出力 (3, 4を4回繰り返す)
  5. Rfreq=L、Xrefck=Hを出力
  6. リターン

であったので、そのままプログラムします。以下のピン名から始まる各種の関数はマクロとして、生成されたpin_manager.hで定義されています。

void TMR2_DefaultInterruptHandler(void){
    if (Z80W_GetValue() == 0) {
        XREFCK_SetHigh();
        RFREQ_SetHigh();
        while (RFGNT_GetValue() == 0);
        XREFCK_SetLow();  XREFCK_SetHigh();
        XREFCK_SetLow();  XREFCK_SetHigh();
        XREFCK_SetLow();  XREFCK_SetHigh();
        XREFCK_SetLow();  XREFCK_SetHigh();
        RFREQ_SetLow();
    }
}

TMR2_Initialize()の内容は、

void TMR2_Initialize(void)
{
    // Set TMR2 to the options selected in the User Interface
    // PR2 249; 
    PR2 = 0xF9;
    // TMR2 0; 
    TMR2 = 0x00;
    // Clearing IF flag before enabling the interrupt.
    PIR1bits.TMR2IF = 0;
    // Enabling TMR2 interrupt.
    PIE1bits.TMR2IE = 1;
    // Set Default Interrupt Handler
    TMR2_SetInterruptHandler(TMR2_DefaultInterruptHandler);
    // T2CKPS 1:1; T2OUTPS 1:1; TMR2ON on; 
    T2CON = 0x04;
}

のようになっています。また、main()は、

#include "mcc_generated_files/mcc.h"
/*
                         Main application
 */
void main(void)
{
    // initialize the device
    SYSTEM_Initialize();
    // When using interrupts, you need to set the Global and Peripheral Interrupt Enable bits
    // Use the following macros to:
    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();
    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();
    while (1)
    {
        // Add your application code
    }
}

のように、初期設定がされているため、以下のようにタイマーの開始を追加します。while ループ内の処理はありません。

    TMR2_StartTimer();

左矢前のブログ 次のブログ右矢

FM-7 ROM吸出し器の改版 (7)

posted by sakurai on May 25, 2020 #262

改版基板

JLCPCBにオーダーしていた基板が届きました。今回は製造に2.95日、配送が3.11日のほぼ6日で届きました。また、費用は基板が2 USD、配送料込で17.95 USDと格安でした。

図%%.1
図262.1 FM-7 Intruder V5のボード

PICの割込み処理

開発環境のプラグインであるMCC(MPLAB Code Configurator)をインストールし、MCCで設定していきます。いきなりソースコードを書くよりも分かりやすいためです。インストールや設定の仕方の参考となるサイトには例えばここがあります。

左側のProject ResourcesのSystem Module(①)を選択し、クロック周波数(②)やWDT(③)等を設定します。

  • Oscillator Select: INTOSC oscillator: I/O function on CLKIN pin
  • System Clock Select: FOSC
  • Internal Clock: 16MHz_HF
  • WDT: disabled

図%%.2
図262.2 System設定

次にポートの設定です。回路図では図262.3のようにポートをアサインしたので、これに基づいてプログラム上のポート設定を行います。

図%%.3
図262.3 PIC周りの回路図
ポート(ピン)の一覧です。
  • RA0: RFREQ (Output)
  • RA2: XREFCK (Output)
  • RA4: RFGNT (Input)
  • RA5: Z80W (Input)

図262.4のPin Module(①)を選択し、Grid View(②)にて、GPIOの方向を指定します。さらに、右上のスプレッドシートの表に信号名(③)を書きこみます。

図%%.4
図262.4 Pin設定

PIC12F1501の参考資料(魚拓)があったので、貼り付けておきます。ただし、MCCに全てを任せてしまったので、見ることはありませんでした。


左矢前のブログ 次のブログ右矢


ページ: