SSブログ
English Version

Z80GALの構想(その5)タイマ割込みによるシリアル通信 [Z80]

 前回の記事で書いたようにGAME言語を使ったモニタができて開発に必要な環境が整ってきたので今回はタイマ割込みを使ったシリアル通信の実現に関して書いてみます。

 結論から先に書くと今回のタイマ割込みによるシリアル通信の実装により、バックグランドでの受信が可能となり、目標の一つであるUART用のICを使わないということが達成できました(Z80GALの設計ポリシーは他のマイコンに制御されない「最小限のIC構成」のZ80ボードの実現です)

 これまでは「Z80GALの構想(その3)ブレッドボードでの仮組」の記事に書いたようにソフトウェアでタイミングを計ったシリアル通信(開発環境を整えるための仮の実装)を使っていましたが、キーセンスができないため、GAME言語でリスト出力時の途中停止等ができませんでした。またソースをコピペする場合でもTeraTermで 1ms/char のウェイトを入れていました。
 今回、タイマ割込み処理化したことでリスト表示を途中で停止できるようになり、0ms/char の設定でもソースのコピペで取りこぼしが発生しないようになりました(ライン毎のウェイトは余裕を見て50ms/lineで設定)。

 それではタイマ割込みによるシリアル通信について以下に概要をメモしておきます。

  1. 割込み信号源
     この連載の初回「Z80GALの構想」で書いたように当初はGALでタイマ割込み信号を生成しようと試みましたが、GALのリソースをかなり使ってしまうのでPIC12F683を割込み信号の生成と水晶発振(20MHz)用バッファとして使うことにしました。
     8ピンなので74HCU04等で発信させるよりもハードがコンパクトにまとまります。

     Z80の割込みモードは最も単純なモード1(割込みエントリーアドレスが38H固定)を使いました。
     汎用性を持たせるため

    • タイマ割込み信号は疑似的にオープンドレイン出力とし、ワイヤードORで他の割込みも後付け可能
    • タイマ割込み信号の状態をGAL経由でZ80から読込み可能

    としています。

     Z80側から割込み要求をクリアできればいいのですがGALのリソースを節約するためにタイマ割込み要求は一定時間でクリアされるようにしました。
     このため、割込み処理先頭でタイマ割込み要求を確認でき、割込み処理を抜ける時点では要求信号がクリアされるように調整する必要があります。

     下図は割込み発生時のシンクロ画面で黄色(ch1)がタイマ割込み要求信号で水色(ch2)が割込み処理時間を確認するために割込み処置中に出力しているSDO信号(SD用に用意したビット出力)です。
     タイマ割込み要求信号(黄色)は割込み処理先頭ではアクティブで割込みを抜ける時点ではインアクティブになっています。

    タイマ割込み信号と割込み処理時間

     この時の割込み処理は下のリストのようにPush/popだけのダミー処理です。

    上図確認時の割込み処理
    ;************************************* ; timer int width test ; Ver 0.01 2020/10/31 by skyriver ;************************************* ;*** Z80GAL I/O address *** 0080 adBASE EQU 080H ; I/O base address 00E0 adRAM EQU (adBASE + 60H) ; select memory RD D0=1:RAM, 0:ROM 00C0 adSdCs EQU (adBASE + 40H) ; set SD's CS D0=1:active 00A0 AdSd EQU (adBASE + 20H) ; write D0:SDO D1:CLK, read D0:SDI D1:CS 0080 AdSeri EQU (adBASE + 00H) ; serial D0 write:Tx read:Rx 0009 RGETCAD EQU 0008H + 1 ; getc address 000D RKBHTAD EQU 000CH + 1 ; kbhit address 0011 RPUTCAD EQU 0010H + 1 ; putc address 0039 TINTADR EQU 0038H + 1 ; timer interrupt jump address .Z80 0000' ASEG ORG 5000H 5000 F3 START: DI 5001 ED 56 IM 1 ; set int mode 5003 21 500C LD HL,TINT 5006 22 0039 LD (TINTADR),HL 5009 FB EI 500A 18 FE JR $ 500C F5 TINT: PUSH AF ; 11 500D 3E 01 LD A,1 ; 7 500F D3 A0 OUT (AdSd),A ; 11 5011 C5 PUSH BC ; 11 5012 D5 PUSH DE ; 11 5013 E5 PUSH HL ; 11 5014 E1 POP HL ; 10 5015 D1 POP DE ; 10 5016 C1 POP BC ; 10 5017 AF XOR A ; 4 5018 D3 A0 OUT (AdSd),A ; 11 501A F1 POP AF ; 10 501B FB EI ; 4 501C C9 RET ; 10 END

     PIC12F683のソースも貼っておきます。コンパイラはCC5X C Compilerを使用しています。

    PIC12F683のソース
    /* Clock & IntReq signal genelator for Z80 board * PIC12F683 * Ver 0.01 made by skyriver 2020/10/20 * Ver 0.02 by skyriver 2020/10/25 * * timer int req(GPO0) manage like open draon. * * output timer int status to GP1 */ #include <INT16CXX.H> // define config bit assign(PIC12F683) #define conf_FCMEN (1<<11) // Fail-Safe Clock Monitor Enable #define conf_IESO (1<<10) // Internal External Switchover bit #define conf_BODEN1 (1<<9) // Brown-out Detect Selection bit #define conf_BODEN0 (1<<8) // Brown-out Detect Selection bit #define conf_CPD_ (1<<7) // Data code Protection(0:enable) #define conf_CP_ (1<<6) // Code Protection(0:enable) #define conf_MCLRE (1<<5) // GP3/MCLR_ pin function secet bit(1:MCLR) #define conf_PWRTEN_ (1<<4) // Power-up Timer Enablr bit(0:enable) #define conf_WDTEN (1<<3) // Watchdog Timer enable bit(1:enable) #define conf_FOSC_RC 0b111 #define conf_FOSC_RCIO 0b110 #define conf_FOSC_INTOSC 0b101 #define conf_FOSC_INTOSCIO 0b100 #define conf_FOSC_EC 0b011 #define conf_FOSC_HS 0b010 #define conf_FOSC_XT 0b001 #define conf_FOSC_LP 0b000 #pragma config = conf_CPD_ | conf_CP_ | conf_FOSC_HS /* initialize H/W and workarea */ void init( void ) { GPIO = 0b00000000; // GP0:IntReq WPU = 0b00000000; // 1:Pull-up enable TRISIO = 0b00000001; // 0:output, 1:input CMCON0 = 0b00000111; // Comparator off CMCON1 = 0b00000011; // comp output is asynchronization VRCON = 0b00001000; // Vref is off ANSEL = 0b00000000; // 0:dig, 1:ana OSCCON = 0b01110000; // external clock OPTION = 0b00000010; // not PortB_PullUp PreScaleToTMR0(1/8) // 1000000/(9600*3) = 34.72[uSec] // 20MHz : 1/20[usec] // division 1000000/(9600*3)/(1/20) = 694.4 -> 1/4 : 173.61 T2CON = 0b0000100; // Timer1 on,pre/post scaler 1:1 PR2 = 172; PCON = 0b00000000; } void main( void ) { init(); while ( 1 ) { while ( TMR2IF == 0 ) {} TRISIO.0 = 0; // set timer reqest to 0 GPIO = 2; // GPIO.0 = 0, GOPI.1 = 1 nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); #if 0 nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); #endif TRISIO.0 = 1; // set input mode(external pull up) GPIO = 0; // set timer int status to 0 TMR2IF = 0; } }


  2. シリアル通信処理
     通信速度は9600bspとし、タイマ割込みは通信速度の3倍で掛けています。
     受信時のスタートビットをエッジ割込みで拾えればタイマ割込み間隔は通信速度と同じでいいのですが、タイマ割込みだけで処理するためにスタートビットを3倍速でサンプリングするようにしました。

     このため、タイマ割込みの間隔は34.7us(=1000000/(9600x3))になるので20MHz動作のZ80でも悠長なコードは書けません^^

     下図が'A'を受信してエコーバックしている際のサンプル波形です。上側のTx信号がタイマ割込みシリアル通信の出力信号で1bit幅が104usになっています。
     9600bpsの1bt幅は104.17us(=1000000/9600)なので想定通りであることが確認できました。

    タイマ割込みシリアル通信サンプル波形


  3. 直近の予定
     次はいよいよソフトウェアによるSPIインターフェースの実装です。この部分は「レトロマイコン86ボードの構想(その23)16MHz動作実験3」でも評価中の 5V <--> 3.3V のレベル変換の実験も必要になります。


★追記 2020/11/01 {
 タイマ割込み処理の速度への影響が気になったので「MSX-DOS上でのGAME言語のベンチマーク」の記事で書いたベンチマークで確かめてみました。

 GAME言語インタープリタでの実行時間は

   従来のソフトシリアル  : 33秒
   タイマ割込みシリアル時 : 44秒 43秒 ※若干高速化

でタイマ割込み処理により3割(1.30=43/33)程度、遅くなっています。

 CP/M-80 GAME80 Interpreter(Pic24CPM Z80:16MHz)が52秒なのでクロック比例計算すると20MHzの場合は41.6秒になりますが「従来のソフトシリアル」ではキー入力チェックがダミー(OR A:RET)なので速いのだと思います。
 タイマ割込みシリアルではキー入力チェックがバッファ内受信データのカウンタチェックだけなのでそれなりに速いですね^^

 尚、処理速度にはあまり関係ないですが、タイマ割込みシリアルの場合、シリアル送信も割込みで処理しています(ソフトシリアル送信はタイマ割込みによるジッタがでて使えなかった)。
}



[TOP] [ 前へ ] 連載記事 [ 次へ ]
nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。