SSブログ
English Version

レトロマイコンZ80ボードの構想(その5)ピンアサイン [Z80]

 前回の記事で書いたように PIC24FJ64GA004 のDIP化変換基板が出来たのでピンサインの検討とSPIの簡単な実験を行ってみました。

 「レトロマイコンZ80ボードの構想(その2)」の記事で書いたように今回は128KBytesの外部メモリも制御するので44pinのPIC24FJ64GA004でもピンが不足します。

 従って外部メモリとSPI等、同時に使うことがないものは同じピンに重ねてアサインすることになります。
 現時点でのピンアサインの構想をまとめたものが下表になります。外部メモリが外部ピンを大食いしている状態です w
 左端のカラムには使用済みの場合に自動でマークが表示されます(全て使用済みの状態)。

PIC24FJ64GA004 Pin Assign

★2018/01/30 変更 Z80とのI/Fが抜けていたので追記しました。
★2018/02/25 変更 Z80のアドレスとデータを追記
★2018/03/03 変更版をその11に記載

 SDカード制御のための SPI についても軽い実験をしてみました。
 'A'と'B'の文字コードを送信しているだけのものですがSPIのモード等はSDカードに合わせたつもりです。
 SPIのチップセレクト(PPS_SS1OUT)はRP出力にアサインしても想定した信号が出ない(ピンとしては入力状態のままになっている)ので少し悩みましたがmasterモードの時はSPIのCSはI/Oを使って自分で制御する必要があるみたいです(複数のslaveを接続する場合を考えれば当然のことかも)

 今回のZ80ボードではクロックを16MHzか8MHzにする予定なので信号の確認作業を効率化するために最大100MHzサンプリング(3入力時)の16chのロジアナを購入しました ^^
 SPIでの送信時の画面サンプルが下のキャプチャです。

ロジアナ画面(SPI送信)


 このような実験の際には手軽にソース変更ができ、コンパイル&フラッシュメモリへの書込みがオンチップで瞬時に完了する picle言語の環境が威力を発揮します。(ちょっと宣伝w)

 実験で作ったpicleソースは下記のとおりです。
 今回はソースをhtmlに成形してみました。(私はエディタではタブサイズを4にしているのでコメントのインデントが少し乱れています)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# SPI test for PIC24FJ64GA004 with picle
#  Ver 0.01 by skyriver 2018/01/29

var SPISTAT,SPIBUF;
var _RPINR20,RPOR1;
var _REG,AD1PCFG;
var TRISA,LATA;


proc TmWait( n ) {
	var i;
	for (; n ; n=n-1) {
		i = 123 + i;
	}
}

		
proc init() {
	SPISTAT = $0240;	# SPI1 status
	SPIBUF = $0248;
	RPOR1 = $06c2;
	AD1PCFG = $032c;
	AD1PCFG[0] = $ffff;	# I/O
	TRISA = $02c0;
	LATA = TRISA + 4;

	SPISTAT[1] = $0039;	# SPI1CON1 prescale2:2,prescale1:16 = 32:1
	SPISTAT[2] = 0;		# SPI1CON2
	SPISTAT[0] = $8000;

	_RPINR20 = $06a8;
	_RPINR20[0] = 18;		# SDI -> RP18
	RPOR1[0] = $0708;		# CLK -> RP2, SDO ->RP3
	TRISA[0] = TRISA[0] & $fffb;
	LATA[0] = $0004;
}


proc main() {
	var c,i;
	init();
	c = 'A';
	for (i=0; i<2; i=i+1 ) {
		TmWait( 20 );
		LATA[0] = 0;
		SPIBUF[0] = c;
		c = c + 1;
		if ( c > 'Z' ) { 
			c = 'A';
		}
		TmWait( 18 );
		LATA[0] = $0004;
	}
}


 因みにXC16コンパイラ用に書き直すと次のようになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// SPI test
// Ver 0.01 by skyriver 2018/01/29

#include <xc.h>
#include <spi.h>
#include <pps.h>
#include <libpic30.h>

 
#define FCY 32000000UL
#define SPI1_CS LATAbits.LATA2


// CONFIG2
#pragma config  POSCMOD   = NONE,\
                I2C1SEL   = PRI,\
                IOL1WAY   = ON,\
                OSCIOFNC  = ON,\
                FCKSM     = CSDCMD,\
                FNOSC     = FRC,\
                SOSCSEL   = SOSC,\
                WUTSEL    = LEG,\
                IESO      = OFF
 
// CONFIG1
#pragma config  WDTPS     = PS1,\
                FWPSA     = PR128,\
                WINDIS    = OFF,\
                FWDTEN    = OFF,\
                ICS       = PGx1,\
                GWRP      = OFF,\
                GCP       = OFF,\
                JTAGEN    = OFF

void delay_us( unsigned int usec )
{
	for( ; usec ; usec-- ) {
		asm("repeat #9" );
		asm( "nop" );
	}
}


void init()
{
	_RCDIV = 0;	//PostScaler   ( 0 : 32MHz   1 : 16MHz )
	OpenSPI1(
        // config1
		ENABLE_SCK_PIN &	// enable SPICLK
		ENABLE_SDO_PIN &	// enable SDO
		SPI_MODE8_ON &      // set 8bit mode
		SPI_SMP_OFF &
		SPI_CKE_OFF &
		SLAVE_ENABLE_OFF &	// set slave SS off
		CLK_POL_ACTIVE_HIGH &	// set CLK to Active High
		MASTER_ENABLE_ON &      // set master mode
		SEC_PRESCAL_2_1 &
		PRI_PRESCAL_16_1,

		FRAME_ENABLE_OFF,
 
		SPI_ENABLE &
		SPI_IDLE_CON &
		SPI_RX_OVFLOW_CLR
	);
	PPSInput( PPS_SDI1, PPS_RP18 );
	PPSOutput( PPS_RP3, PPS_SDO1 );
	PPSOutput( PPS_RP2, PPS_SCK1OUT );
//	PPSOutput( PPS_RP17, PPS_SS1OUT );	// not effect in master mode
	TRISAbits.TRISA2 = 0;		// set output
	SPI1_CS = 1;	// set unselected
}


int main(void)
{
	init();
	delay_us( 50 );
	SPI1_CS = 0;	// set selected
	_SPI1IF = 0;	// clear Interrupt flag
	WriteSPI1('A');
	delay_us( 18 );
	SPI1_CS = 1;
	for(;;);
	return( 0 );
}


★2018/01/31 追記
 実験中に次の2つの問題が発生したのでメモしておきます。
  1. SDカードとの接続
     インターネット上の情報では MCU と SDカードのSDI及びSDOをそれぞれ同じ名称同士をつなぐように書いてあるものが多い?ですが、PIC24FJの場合はテレコに繋ぐ必要がありました。

  2. アイドル時のクロック状態
     参考にしたwebページではアイドル時にクロックが low 状態だったのでそれに合わせましたが、次に問題が発生しました。

    • web上にある情報に従いクロック立上りでラッチ(サンプリング)できるタイミングでコマンドを送信してもSDカードが反応しない。立下りでラッチするように送信するとSDカードが反応する。

     別のサイトの情報を見るとアイドル状態でクロックは high になっているタイムチャートを見つけたのでアイドル時にクロックを high になるように変更したところ、立上りラッチのタイミングでコマンドを送信するとSDカードが反応するようになりました。

 SDカードに cmd0 送信後、応答(0x01)を受けるまでのロジアナ画面を貼っておきます。

CMD0送信シーケンス


 picleソースのリスト表示と実行結果も付けておきます。クロックは333kにしていますが、500kでも動作しました。
 (html化するとiPhoneのChromeでは行番号が崩れて表示されたので今回は従来通りの貼り方にします)
★2018/02/03 リスト表示部がスクロールするようにしてみました。

:l 1:# SPI test to connect SD card for PIC24FJ64GA004 with picle 2:# Ver 0.01 by skyriver 2018/01/31 3: 4:var SPISTAT,SPIBUF; 5:var _RPINR20,RPOR0; 6:var _REG,AD1PCFG; 7:var TRISA,LATA; 8: 9:var _Buf,Bcnt; 10: 11: 12:proc init() { 13: SPISTAT = $0240; # SPI1 status 14: SPIBUF = $0248; 15: RPOR0 = $06c0; 16: AD1PCFG = $032c; 17: AD1PCFG[0] = $ffff; # I/O 18: TRISA = $02c0; 19: LATA = TRISA + 4; 20: 21:# SPISTAT[1] = $0079; # SPI1CON1 prescale2:2,prescale1:16 = 32:1 500k 22: SPISTAT[1] = $0075; # SPI1CON1 prescale2:3,prescale1:16 = 48:1 333k 23: SPISTAT[2] = 0; # SPI1CON2 24: SPISTAT[0] = $8000; 25: 26: _RPINR20 = $06a8; 27: _RPINR20[0] = 18; # SDI -> RP18 28: RPOR0[0] = $0708; # CLK -> RP0, SDO ->RP1 29: TRISA[0] = TRISA[0] & $fffb; 30: LATA[0] = $0004; 31: 32: _Buf = Array_(0); 33:} 34: 35: 36:proc SpiRcv() { 37: if ( SPISTAT[0]&1 ) { 38: _Buf[ Bcnt ] = SPIBUF[0]; 39: Bcnt = Bcnt + 1; 40: } 41:} 42: 43:proc SpiSnd( dat ) { 44: while ( SPISTAT[0]&2 ) { 45: SpiRcv(); 46: } 47: SPIBUF[0] = dat; 48:} 49: 50:# wait about 2us * n 51:proc TmWait( n ) { 52: var i; 53: for (; n ; n=n-1) { 54:# i = 123 + i; 55: SpiRcv(); 56: } 57:} 58: 59: 60:proc main() { 61: var i; 62: 63: init(); 64: 65: TmWait( 50 ); 66: LATA[0] = 0; 67: SpiSnd( $40 ); 68: SpiSnd( $00 ); 69: SpiSnd( $00 ); 70: SpiSnd( $00 ); 71: SpiSnd( $00 ); 72: SpiSnd( $95 ); 73: SpiSnd( $ff ); 74: SpiSnd( $ff ); 75: TmWait( 30 ); 76: 77: LATA[0] = $0004; 78: for ( i = 0; i<Bcnt; i=i+1 ) { 79: PrnHexB_( _Buf[i] ); 80: PrnStr_( " " ); 81: } 82: PrnStr_("status:"); 83: PrnHex_( SPISTAT[0] ); 84:} :run FF FF FF FF FF FF FF 01 status:8000 :



★2018/02/02 追記
 SPIでのSDカードとの通信ですが、データ送信時のクロックに対応した受信データ(ない場合は0xff)を確認しながらコマンド送信しようとすると1バイト送信後、1バイト受信するまで待つことになります。
 このため送信データと次の送信データの間に受信データチェックのための時間分、クロックが停止する無駄な時間が発生してしまいます。
(上記の追記では逐次受信フラグをチェックしています・・最終実装としては割込みで受信するイメージ)

 しかしSDカードとの通信は半二重なのでコマンド送信終了してから受信バイトが来るはずなのでコマンド送信中は受信フラグを気にせず、SDカードからの受信応答を読む時に0xffを送信しながら受信フラグをチェックし、受信があったらCSをdisableにするのが実装も簡単で通信時間も短くなります。(CSをdisableにした後に約1バイト分クロックが出続きますが悪影響はないと思う)
 CS信号をdisableにするタイミングも気にしなくて良くなりました。

 下図はこのような処理にした場合のロジアナで観測した波形です。(クロックを500MHに上げています)

通信簡略化でのロジアナ波形


 picleでのソースは下記のようになります。
 かなりすっきりしたソースになりました。

:l 1:# SPI test to connect SD card for PIC24FJ64GA004 with picle 2:# Ver 0.02 by skyriver 2018/02/02 3: 4:var SPISTAT,SPIBUF; 5:var _RPINR20,RPOR0; 6:var _REG,AD1PCFG; 7:var TRISA,LATA; 8: 9:var _Buf,Bcnt; 10: 11: 12:proc init() { 13: SPISTAT = $0240; # SPI1 status 14: SPIBUF = $0248; 15: RPOR0 = $06c0; 16: AD1PCFG = $032c; 17: AD1PCFG[0] = $ffff; # I/O 18: TRISA = $02c0; 19: LATA = TRISA + 4; 20: 21: SPISTAT[1] = $0079; # SPI1CON1 prescale2:2,prescale1:16 = 32:1 500k 22:# SPISTAT[1] = $0075; # SPI1CON1 prescale2:3,prescale1:16 = 48:1 333k 23: SPISTAT[2] = 0; # SPI1CON2 24: SPISTAT[0] = $8000; 25: 26: _RPINR20 = $06a8; 27: _RPINR20[0] = 2; # SDI -> RP2 28: RPOR0[0] = $0708; # CLK -> RP0, SDO ->RP1 29: TRISA[0] = TRISA[0] & $fffb; 30: LATA[0] = $0004; 31:} 32: 33: 34:# wait about 1us * n 35:proc TmWait( n ) { 36: var i; 37: for (; n ; n=n-1) { 38: i = 123 + i; 39: } 40:} 41: 42: 43:func SpiRcv() { 44: return = $ff; 45: SPISTAT[0] = $8000; # clear over flow flag 46: do { 47: if ( SPISTAT[0]&1 ) { 48: return = SPIBUF[0]; 49: } 50: else if ( (SPISTAT[0]&2 ) = 0 ) { 51: SPIBUF[0] = $ff; 52: } 53: } while ( return = $ff ); 54:} 55: 56: 57:proc SpiSnd( dat ) { 58: while ( SPISTAT[0]&2 ) {} 59: SPIBUF[0] = dat; 60:} 61: 62: 63:proc main() { 64: var ans; 65: 66: init(); 67: 68: TmWait( 30 ); 69: LATA[0] = 0; 70: SpiSnd( $40 ); 71: SpiSnd( $00 ); 72: SpiSnd( $00 ); 73: SpiSnd( $00 ); 74: SpiSnd( $00 ); 75: SpiSnd( $95 ); 76: ans = SpiRcv(); 77: LATA[0] = $0004; 78: 79: if ( ans = 1 ) { 80: PrnStr_( "ok" ); 81: } else { 82: PrnStr_( "Ng:" ); 83: PrnHexB_( ans ); 84: } 85: PrnStr_( " status:" ); 86: PrnHex_( SPISTAT[0] ); 87:} :run ok status:8001 :




[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(2) 
共通テーマ:趣味・カルチャー