歩き遍路 平成19年春(J1) 記録の準備
SPIでUSB( 5)
 SPI でH8/3067と拡張I/O MCP23S08を接続

 PIC18F4520は使い易いマイコンですが、RAM容量や命令実行速度を意識するようになってきました。そこで、電子工作でよく使われているルネサス社のH8/3067を使うことにしました。
 ここでは「SPIでUSB( 1)」と同じ実験をH8/3067で行います。

●ハードとソフトの構成


 ●ハードの構成を示します。
 ・左は「SPIでUSB( 1)」の実験で使ったMCP23S08を実装したSPI拡張I/Oボードです。
 ・右は、秋月電子のAkiH8/3067とスイッチとLEDを実装したマイコンのボードです。




 ●ソフトウエアは、イエローソフト社のCコンパイラとデバッガを使います。
 

●AkiH8/3067とスイッチとLEDを実装したマイコンのボード

 マイコンボードの回路図です。AkiH8/3067を中心にスイッチとLEDを実装しています。左のLCDは使用していません。AkiH8/3067ボードは20MHzの水晶と32KBの拡張SRAMを使っています。




●SPIマスタのプログラム

 SPIマスタのプログラムを次に示します。このプログラムは、PIC18F4520プログラムのSPI部分をH8/3067用に変更しました。
 プログラムの動きは、 H8/3067に接続してある4個のスイッチを読み取りSPIでMCP23S08に送りLEDを制御します。
 次に、MCP23S08に接続してある4個のスイッチをSPIで読み取り、H8/3067に接続してある4個のLEDを制御します。
 
 イエローソフト社のCコンパイラでは、I/Oポート定義はユーザーで定義するようになっているのでここではプログラムファイルで定義しています。
 SPIマスタはSCI2を使ってハードウエアで実現しました。SPIクロックは1MHzです。ビットスワップ関数はMSBとLSBを入替える関数です。

/**************************************************************
*
* AkiH8/3067のSCI2を使ってMCP23S08とSPI接続
*
* AkiH8/3067接続のSWでMCP23S08接続のLEDを制御
* MCP23S08接続のSWでAkiH8/3067接続のLEDを制御
*
***************************************************************

/** I N C L U D E S *******************************************/
#include <sysio.h>


#define P4DR (*((volatile unsigned char *)(0xffffd3)))
#define P4DDR (*((volatile unsigned char *)(0xfee003)))
#define P7DR (*((volatile unsigned char *)(0xffffd6)))
#define PADR (*((volatile unsigned char *)(0xffffd9)))
#define PADDR (*((volatile unsigned char *)(0xfee009)))
#define PBDR (*((volatile unsigned char *)(0xffffda)))
#define PBDDR (*((volatile unsigned char *)(0xfee00a)))

#define SMR2 (*((volatile unsigned char *)(0xffffc0)))
#define BRR2 (*((volatile unsigned char *)(0xffffc1)))
#define SCR2 (*((volatile unsigned char *)(0xffffc2)))
#define TDR2 (*((volatile unsigned char *)(0xffffc3)))
#define SSR2 (*((volatile unsigned char *)(0xffffc4)))
#define RDR2 (*((volatile unsigned char *)(0xffffc5)))
#define SCMR2 (*((volatile unsigned char *)(0xffffc6)))
// ********************
// ***** 時間遅延関数
void TD10CY(void){
char i,Tmp;

for (i=0; i<10; i++) {
Tmp = i; // ダミー動作
}
}

void TD10CYx(char data){
char i;

for (i=0; i<data; i++) {
TD10CY(); //時間待ち
}
}
// ***** ビットスワップ関数
char BitSwap(char data){
char Temp = 0x00;

if(data & 0x80) Temp = Temp + 0x01;
if(data & 0x40) Temp = Temp + 0x02;
if(data & 0x20) Temp = Temp + 0x04;
if(data & 0x10) Temp = Temp + 0x08;
if(data & 0x08) Temp = Temp + 0x10;
if(data & 0x04) Temp = Temp + 0x20;
if(data & 0x02) Temp = Temp + 0x40;
if(data & 0x01) Temp = Temp + 0x80;
return(Temp); //ビットスワップデータ戻り
}
// ***** SPI送受信関数 SSピン制御無し
char SpiTxRx(char data){
char Temp;

Temp = SSR2; //シリアルステータスレジスタ読出し
while((Temp & 0x80)== 0x00) { //送信バッファ空か
TD10CY(); //時間待ち
Temp = SSR2; //シリアルステータスレジスタ読出し
}

Temp = BitSwap(data); //送信データをビットスワップ
TDR2 = Temp; //送信データ設定
SSR2 = 0x00; //送信指令

Temp = SSR2; //シリアルステータスレジスタ読出し
while((Temp & 0x40)== 0x00) { //受信完了化
Temp = SSR2; //シリアルステータスレジスタ読出し
}

data = RDR2; //受信データ読み出し
Temp = BitSwap(data); //送信データをビットスワップ
return(Temp); //受信データ戻り
}
// ********************
// ***** メイン
void main()
{
char tempData;
char tempData2;
char tempData3;

_ei(); /* 割込み許可 */

//ポートデータ初期値設定
P4DR = 0xFF; // データ
PADR = 0xFF; // データ
PBDR = 0xFF; // データ

//ポート方向設定 TPxDDR 0 = IN, 1 = OUT
P4DDR = 0xFF; // bit7-0は出力
PADDR = 0x0F; // bit7-4は入力、 bit3-0はLED出力
PBDDR = 0xFF; // SPIポート

// *** SCI2初期設定 Spiマスター、クロック:1Mbps
SCR2 = 0x00; // 送受信不許可、クロック出力:シリアルコントロールレジスタ
SMR2 = 0x80; // クロック同期モード:シリアルモードレジスタ
BRR2 = 0x04; // 1Mbps@20MHz:ビットレートレジスタ
TD10CY();
SCR2 = 0x30; // 送受信許可、クロック出力:シリアルコントロールレジスタ


// ************ H8のSPIは、LSB先頭なのでデータを逆に設定している。
// *** MCP23S08の方向Reg設定 Bit7-4:Input,3-0:Output
PBDR = (P4DR & ~0x10); //SSピンLow


SpiTxRx(0x40); //SPI送受信 Writeオペコード
SpiTxRx(0x00); //SPI送受信 方向RegAdrs
SpiTxRx(0xF0); //SPI送受信 1:Input,0:Output
PBDR = (PBDR | 0x10); //SSピンHigh
TD10CYx(100);

/// メインループ
while(1)
{
/// スイッチを読込みPSI通信でデバイスに送信
tempData= P7DR; // スイッチ状態を読込み
PBDR = (PBDR & ~0x10); //SSピンLow
SpiTxRx(0x40); //SPI送受信 オペコード
SpiTxRx(0x0A); //SPI送受信 ラッチRegAdrs
SpiTxRx(tempData); //SPI送受信 スイッチData
PBDR = (PBDR | 0x10); //SSピンHigh
TD10CY();

/// デバイスからPSI通信でスイッチを受信して、LEDに表示
PBDR = (PBDR & ~0x10); //SSピンLow
SpiTxRx(0x41); //SPI送受信 オペコード
SpiTxRx(0x09); //SPI送受信 GPIORegAdrs
tempData2 = SpiTxRx(tempData); //SPI送受信 スイッチData
PBDR = (PBDR | 0x10); //SSピンHigh

PADR = tempData2>> 4; //LEDデータ抽出

TD10CYx(5);

}//END of while(1)
}//END of main




●プログラムの実行と結果

 問題無くスイッチを操作するとLEDは動きました。 SPIバスの波形です。



・上:送信データ(MOSI)
 データは0x40 です。
 クロックの立上がり時点では安定しています。



・下:クロック(SCK)
 H8/3067のSCI2のクロック出力です。
 ボーレートジェネレータに設定した1MHzのクロックが出ています。






●困った事

 H8がハードウエアでSPIをサポートか? ハードウエアマニュアルやいろいろ調べたのですが明確な記述がありません。ハードウエアマニュアルにはクロック同期モードの記述はあるのですがSPIと明記していません。
 
 ●H8のハードウエアSPIのクロックは?
 H8のクロック同期モードのSCIで出力されるクロックは、待機時はハイレベル固定です。
 MCP23S08のクロックの要求をハードウエアマニュアルで調べたら待機時はローレベル固定になっています。良く見ると、点線で Mode1.1 は待機時はハイレベル固定もあります。

 MCP23S08のクロックは、クロックの立上がり時点が重要なのだと思えます。

 
 ●H8のハードウエアSPIのデータ出力は?
 H8のクロック同期モードのSCIで出力されるデータは、LSBファースト固定です。
 MCP23S08のデータの要求は、MSBファースト固定です。困りました。

 2つのポートを使ってハードウエア的にビットスワップしてはと工作を始めたのですが、今回はプログラム的に解決しようと考えなおしました。
 プログラムのシフト命令を使ってビットスワップしたら処理時間がかかります。
 テーブルを使ったら? データは8ビットなので256バイトでできる。H8は十分メモリが有る。しかし、テーブルの検証に時間がかかりそうだし。

 処理時間も我慢ができそうだし、検証も確実に可能な if 文を使った次のビットスワップ関数を使うことにします。


// ***** ビットスワップ関数
char BitSwap(char data){
char Temp = 0x00;

if(data &amp; 0x80) Temp = Temp + 0x01;
if(data &amp; 0x40) Temp = Temp + 0x02;
if(data &amp; 0x20) Temp = Temp + 0x04;
if(data &amp; 0x10) Temp = Temp + 0x08;
if(data &amp; 0x08) Temp = Temp + 0x10;
if(data &amp; 0x04) Temp = Temp + 0x20;
if(data &amp; 0x02) Temp = Temp + 0x40;
if(data &amp; 0x01) Temp = Temp + 0x80;
return(Temp); //ビットスワップデータ戻り
}


ホームへ戻る