■ PIC間 SPI 通信
SPI(Serial Peripheral Interface)通信はマイコン間やマイコンと外付け部品(ADコンバータ、EEPROMなど)でよく
つかわれる同期式全二重シリアル通信です。機器間にはMasterとSlaveの関係ありMasterが送受信の信号すべてを
制御します。接続は機器のクロック端子SCK同士及びMasterのSDOとSlaveのSDI、MasterのSDIとSlaveのSDO端子を
それぞれつないで通信がおこなわれます。PICにはこのSPI(及びI2C)通信が簡単にできるようにMSSP(Master Synchronus
Serial Port)と呼ばれるレジスタ回路が内臓されたものがあります。MSSPを利用すると送信用バッファレジスタにデータを
セットすると自動的に送信されたり、また受信用バッファーレジスタにデータがセットされると制御レジスタにフラグがたったり
しますので、クロックレベルの通信ソフト設計から開放されます。
C言語を使うと更に簡単になります。
記載要領 | 記載例(CCSコンパイラの場合) |
MasterなのかSlaveなのか、クロックの検出エッジ方向(立上りor立下り)、同期クロックの速度をPICのクロックにたいしてどれだけ分周するかといった設定をおこないます | setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED ); setup_spi(SPI_SLAVE | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED); |
送信バッファーにデータSendDataを書き込むと自動的に送信されます | spi_write(SendData); |
受信バッファーのデータを変数ReceiveDataに読み込むと同時に送信バッファーにある送信データSendDataを送信します | ReceiveData = spi_read(SendData); |
受信バッファに受信データが受信完了となると1を返す。未完了時は0を返えします | spi_data_is_in() |
SPI割込みが発生するとこのあとの関数が実行されます | #INT_SSP |
SPI通信に関しては以下の3つの例を紹介します。
(1)PIC間送受信1(割込みなし 1文字毎の送受信 Masterのみ液晶付 ) <CCS編> <dsPIC編>
(2)PIC間送受信2(SPI割込み有 1文字毎の送受信) <CCS編> <dsPIC編>
(3)PIC間送受信3(外部割込み有 文字列の送受信 Master/Slave共液晶付)
以下のプログラム例の中にある液晶表示器制御ライブラリ 1llcd_lib.cは 後閑哲也さんが設計されたものです。
(1)PIC間送受信1(割込みなし 1文字毎の送受信 Masterのみ液晶付)
<CCS編>
<試作品仕様>
・ 送信データとして0〜255までを順次MasterからSlaveにSPIインターフェースをつかい送信すること。
・ Slave側では受信したデータをそのまま送信データとしてMaster側へすぐに送信すること。
・ 液晶の1行目にはMasterから送信したデータを表示すること。
・ 液晶の2行目にはMasterが受信したデータを表示すること。
・ 割込みは使いわないこと。
<試作品回路図> (→回路図のPDFファイル)
PIC18F452 ×2個をつかった場合の回路図を以下に示します。
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品が多々写っています
<プログラム例>
//*********************************************************************************** // Master側のプログラム // // SPI通信 : 0〜255の整数を順次SLAVE側に送信する // 送信データを液晶の1行目に表示する // 受信データを液晶の2行目に表示する //*********************************************************************************** #include <18F452.h> #use delay(clock=40000000) #FUSES EC,PUT,NOWDT,BROWNOUT,BORV42,NOPROTECT,NOLVP #use fast_io(D) //////// Port define and link LCD library #define mode 0 // 液晶 #define input_x input_D #define output_x output_D #define set_tris_x set_tris_D #define rs PIN_D2 //chip select #define stb PIN_D0 //strobe #include <1lcd_lib.c> main(){ unsigned int data_from_Master = 0,data_from_Slave; // setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED ); lcd_init(); lcd_cmd(0b00001100); // カーソル:OFF ブリンク:OFF lcd_clear(); printf(lcd_data,"SPI start!!"); delay_ms(2000); lcd_clear(); while(1) { spi_write(data_from_Master); //送信データをSPIバッファーに書き込む data_from_Slave = spi_read(); //SPIバッファーにある送信データを送信すると同時に受信データを変数に取り込む if(data_from_Master >= 255)data_from_Master = 0; lcd_clear(); //液晶表示 液晶クリア printf(lcd_data,"send_data=%u",data_from_Master); //1行目表示 lcd_cmd(0xC0); // 2行目の先頭へ printf(lcd_data,"receive_data=%u",data_from_Slave); //2行目表示 data_from_Master++; //送信データをインクリメントする delay_ms(2000); } return 0; } //--------------------------------------------------------------------------- //****************************************************** //インクルードファイル 1lcd_lib.c //このファイルは後閑哲也さんが設計されたものです //****************************************************** /////////////////////////////////////////////// // 液晶表示器制御ライブラリ // 内蔵関数は以下 // lcd_init() ----- 初期化 // lcd_cmd(cmd) ----- コマンド出力 // lcd_data(chr) ----- 1文字表示出力 // lcd_clear() ----- 全消去 //////// データ出力サブ関数 void lcd_out(int code, int flag) { output_x((code & 0xF0) | (input_x() & 0x0F)); if (flag == 0) output_high(rs); //表示データの場合 else output_low(rs); //コマンドデータの場合 delay_cycles(4); //NOP 1 output_high(stb); //strobe out delay_cycles(8); //NOP 2 output_low(stb); //reset strobe } //////// 1文字表示関数 void lcd_data(int asci) { lcd_out(asci, 0); //上位4ビット出力 lcd_out(asci<<4, 0); //下位4ビット出力 delay_us(50); //50μsec待ち } /////// コマンド出力関数 void lcd_cmd(int cmd) { lcd_out(cmd, 1); //上位4ビット出力 lcd_out(cmd<<4, 1); //下位4ビット出力 delay_ms(2); //2msec待ち } /////// 全消去関数 void lcd_clear() { lcd_cmd(0x01); //初期化コマンド出力 delay_ms(15); //15msec待ち } /////// 初期化関数 void lcd_init() { set_tris_x(mode); //モードセット delay_ms(15); lcd_out(0x30, 1); //8bit mode set delay_ms(5); lcd_out(0x30, 1); //8bit mode set delay_ms(1); lcd_out(0x30, 1); //8bit mode set delay_ms(1); lcd_out(0x20, 1); //4bit mode set delay_ms(1); lcd_cmd(0x2E); //DL=0 4bit mode lcd_cmd(0x08); //display off C=D=B=0 lcd_cmd(0x0D); //display on C=D=1 B=0 lcd_cmd(0x06); //entry I/D=1 S=0 lcd_cmd(0x02); //cursor home } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- //******************************************************************************************** // SLAVE側 // SPI通信 : 受信したデータと同じデータをそのまま送信データとしてMASTERに返す //******************************************************************************************** #include <18F452.h> #use delay(clock=40000000) #FUSES EC,NOWDT,,PUT,BROWNOUT,BORV42,NOPROTECT,NOLVP unsigned int data_from_Master,data_from_Slave; main(){ setup_spi(SPI_SLAVE | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED); // スレーブ側 検出エッジ:L→H 搬送クロック:CPUクロックの1/16 SS端子:使用せず while(1) { if(spi_data_is_in() == 1) //受信データの受信が完了していれば { data_from_Master = spi_read(); //受信データを変数にセットすると同時に送信バッファーにある送信データを送信する data_from_Slave = data_from_Master; //受信したデータを送信データの変数にセット //受信したデータをMasterにそのまま返す spi_write(data_from_Slave); // 送信データを送信バッファーに書き込む } } return 0; }
<動作結果>
送信データ 91より ひとつ少ない90が受信データとして表示されています。これは MasterがデータをSlaveに送信すると同時にSlaveからのデータを受信するためひとつ前の送信データに対する応答がデータとしてMasterで受信されているためです。
(1)PIC間送受信1(割込みなし 1文字毎の送受信 Masterのみ液晶付)
<C30 dsPIC編> dsPIC30F2012 〜 dsPIC30F4013間通信
<試作品仕様>
・ 送信データとして0〜255までを1秒毎にインクリメントして順次MasterからSlaveにSPIインターフェースをつかい
送信すること。
・ Slave側では受信したデータをそのまま送信データとしてMaster側へすぐに送信すること。
・ 液晶の1行目にはMasterから送信したデータを表示すること。
・ 液晶の2行目にはMasterが受信したデータを表示すること。
・ 割込みは使いわないこと。
<試作品回路図>
Master側にdsPIC30F2012をSlave側にdsPIC30F4013を使った場合を下記に示します(→回路図のPDFファイル)
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品が多々写っています
<プログラム例> ///****************************************************************************************/ // <MASTER側 プログラム> // SPI通信 : 1秒毎に整数をインクリメントしてSLAVEに送信する // 送信したデータと受信したデータを液晶に表示する // dsPIC30F2012 //*****************************************************************************************/ // MPLAB プロジェクトへの要追加ファイル // Linker Scriptフォルダ p30f2012.gld // Library Filesフォルダ lib30F2012-coff.a #include "p30f2012.h" #include <stdio.h> #include "spi.h" #include "1lcd_lib_C30.h" _FOSC(CSW_FSCM_OFF & // クロック切り替えなし、フェースセイフクロックモニタなし XT_PLL8 //外部発振子:10MHz、PLL:8倍 → システムクロック周波数=10×8=80MHz //4,8,16倍が選択可 ); _FWDT(WDT_OFF); _FBORPOR(PBOR_ON & //ブラウンアウトリセット機能:ON BORV_42 & //ブラウンアウト電圧:4.2V PWRT_64 & //パワーオンリセットタイマ64msec MCLR_EN //MCLR機能:ON ); _FGS(CODE_PROT_OFF); //コードプロテクト:OFF void delay_ms(unsigned int); char Ver = '1'; char* str; char Buf[17]; //文字列のバッファー用レジスタ void delay_ms(unsigned int N) //ウェイト関数 { __delay32(Clock/4000*N); } /// メイン関数 int main(void) { unsigned int data_from_Master = 0,data_from_Slave; // TRISB = 0; //all out TRISC = 0; //all out TRISF = 0b0000000000000100; //RF2:in 他out CloseSPI1(); ConfigIntSPI1(SPI_INT_DIS & SPI_INT_PRI_6); //割込みなし OpenSPI1( // SPI1CONレジスタとSPI1STATレジスタの設定 FRAME_ENABLE_OFF & //フレーム制御なし<Frame SPI support Enable/Disable:FRMEN> FRAME_SYNC_INPUT & //フレーム制御時の入出力方向<Frame Sync Pulse direction control:SPIFSD> ENABLE_SDO_PIN & //SDOピンを汎用ピンとしてではなくSPI制御用として使用 <SDO Pin Control bit:DISSDO> SPI_MODE16_ON & //送受信単位:16ビット (8ビット→ SPI_MODE16_OFF)<Word/Byte Communication mode:MODE16> SPI_SMP_ON & //データ入力サンプルフェーズ:データ出力時間の終了時に入力データをサンプルする<SPI Data Input Sample phase:SMP> SPI_CKE_OFF & //クロックエッジ選択:IDLE 状態からアクテイブクロック状態に遷移するエッジでシリアル出力データが変化CKE=0<SPI Clock Edge Select:CKE> // SLAVE_SELECT_ENABLE_OFF & //SS ピン有効化:SSピン使用せず<SPI Slave Select enable:SSEN> CLK_POL_ACTIVE_LOW & //クロック極性選択ビット:アクティブ状態Low(CKP=1)<SPI Clock polarity select:CKP> //SDカードはクロックの立ち上がりでデータを取り込む → CKE=0 CKP=1 MASTER_ENABLE_ON & //マスターモード有効化:マスターモードに設定<SPI Mode Select bit:MSTEN> SEC_PRESCAL_4_1 & //2次プリスケール選択: 1:1<Secondary Prescale select:SPRE2-SPRE0> PRI_PRESCAL_16_1, //1次プリスケール選択: 1:1<Primary Prescale select : PRE1-PRE0> //SPIクロック周波数=Fosc × 1/4 ÷ (1次プリスケーラ×2次プリスケーラ) // = 80MHz × 1/4 ÷ ( 16 × 4) = 312.5 KHz < 400KHz(SDカード認識時のクロック) SPI_ENABLE & //SPI有効無効設定: 有効<SPI Enable/Disable:SPIEN> SPI_IDLE_CON & //アイドル時の動作設定: 動作<SPI Idle mode Operation:SPISIDL> SPI_RX_OVFLOW_CLR); //オーバーフロー発生時フラグクリア:クリア<Clear Receive Overflow Flag bit:SPIROV> lcd_init(); // LCD初期化 lcd_cmd(0b00001100); // カーソル:OFF ブリンク:OFF lcd_clear(); // 全消去 sprintf(Buf,"SPI %c",Ver); //arguementがないと遅い C30のバグ? lcd_str(Buf); //液晶表示 delay_ms(1000); while(1) //タイマ割込みを待つ { putcSPI1(data_from_Master); //SLAVEにデータ送信 while(SPI1STATbits.SPITBF); //送信完了確認:SPI1送信バッファーが空になるまで待つ while(!SPI1STATbits.SPIRBF); //SPI1STATレジスタのSPIRBFビットが1になるまで待つ // while(!DataRdySPI1()); // 同上(受信データ有無を確認して受信データがなければ待つ) data_from_Slave = getcSPI1(); //SLAVEからのデータを取得 if(data_from_Master >= 255)data_from_Master = 0; lcd_clear(); //液晶表示 液晶クリア sprintf(Buf,"dataFrom2012=%u",data_from_Master); lcd_str(Buf); //1行目液晶表示 lcd_cmd(0xC0); //2行目の先頭へ sprintf(Buf,"dataFrom4013=%u",data_from_Slave); lcd_str(Buf); //2行目の表示 data_from_Master++; //送信データをインクリメントする delay_ms(1000); } CloseSPI1(); return 0; } //********************************************************************************************** //インクルードファイル 1lcd_lib_C30.h //このファイルは後閑哲也さんが設計されたCCSコンパイラ用液晶表示ライブラリ 1lcd_lib.cをもとに、 //C30コンパイラ対応、及び分散ポート対応等でに変更したものです。 //********************************************************************************************** #include "p30f2012.h" #define Clock 80000000 // 単位はHzで指定 // LCDポート設定 #define lcd_port_DB7 LATBbits.LATB7 //LCDのDB7(14番ピン)に接続されるPIC側ポート番号設定 #define lcd_port_DB6 LATBbits.LATB6 //LCDのDB6(13番ピン)に接続されるPIC側ポート番号設定 #define lcd_port_DB5 LATBbits.LATB5 //LCDのDB5(12番ピン)に接続されるPIC側ポート番号設定 #define lcd_port_DB4 LATBbits.LATB4 //LCDのDB4(11番ピン)に接続されつPIC側ポート番号設定 #define lcd_stb LATFbits.LATF4 //LCDのstb(6番ピン)に接続されるPIC側ポート番号設定 #define lcd_rs LATFbits.LATF5 //LCDのrs(4番ピン)に接続されるPIC側ポート番号設定 void lcd_out(char code, char flag); void lcd_data(char asci); void lcd_cmd(char cmd); void lcd_clear(void); void lcd_init(void); void lcd_str(char *str); //************************************************************************************************* //インクルードファイル 1lcd_lib_C30.c //このファイルは後閑哲也さんが設計されたCCSコンパイラ用液晶表示ライブラリ 1lcd_lib.cをもとに、 //C30コンパイラ対応、分散ポート対応等で変更したものです。 //************************************************************************************************* /////////////////////////////////////////////// // 液晶表示器制御ライブラリ for C30コンパイラー // 内蔵関数は以下 // lcd_init() ----- 初期化 // lcd_cmd(cmd) ----- コマンド出力 // lcd_data(chr) ----- 1文字表示出力 // lcd_clear() ----- 全消去 // lcd_str(str*) ----- 文字列表示 ////////////////////////////////////////////// #include "1lcd_lib_C30.h" unsigned int _1usec; // 1μsec待つに必要なウェイト回数 unsigned int _50usec; //50μsec待つに必要なウェイト回数 unsigned long N_msec; // 1msec待つに必要なウェイト回数 //////// データ出力サブ関数 void lcd_out(char code, char flag) { if(code & 0b10000000)lcd_port_DB7 = 1; //LCDのDB7への出力セット else lcd_port_DB7 = 0; if(code & 0b01000000)lcd_port_DB6 = 1; //LCDのDB6への出力セット else lcd_port_DB6 = 0; if(code & 0b00100000)lcd_port_DB5 = 1; //LCDのDB5への出力セット else lcd_port_DB5 = 0; if(code & 0b00010000)lcd_port_DB4 = 1; //LCDのDB4への出力セット else lcd_port_DB4 = 0; if (flag == 0) lcd_rs = 1; // 表示データの場合 else lcd_rs = 0; // コマンドデータの場合 __delay32(_1usec); //1μsecウェイト lcd_stb = 1; // strobe(E) ON (Enable) __delay32(_1usec); // 1μsec : strobe信号の幅 lcd_stb = 0; // reset strobe } //////// 1文字表示関数 void lcd_data(char asci) { lcd_out(asci, 0); // 上位4ビット出力 lcd_out(asci<<4, 0); // 下位4ビット出力 __delay32(_50usec); //50μsecウェイト } /////// コマンド出力関数 void lcd_cmd(char cmd) { lcd_out(cmd, 1); // 上位4ビット出力 lcd_out(cmd<<4, 1); // 下位4ビット出力 if((cmd & 0x03) != 0) // clear Homeの場合 __delay32(2*N_msec); // 2msec待ち else __delay32(_50usec); //50μsecウェイト } /////// 全消去関数 void lcd_clear(void) { lcd_cmd(0x01); // 初期化コマンド出力 // __delay32(15*N_msec); //15msecウェイト } /////// 文字列出力関数 void lcd_str(char* str) { while(*str) //文字列終端の '\0'を検出するまで { lcd_data(*str); // 1文字表示 str++; //ポインタをインクリメント } } /////// 初期化関数 void lcd_init(void) { _1usec =(unsigned int)( Clock / 4000000); // 1μsecに要するウェイト回数 //__delay32(N) : Nが11以下の場合でも11回ウェイト _50usec = (unsigned int)(Clock / 4000000 * 50); //50μescに要するウェイト回数 N_msec = (unsigned long int)(Clock / 4000); // 1msecに要するウェイト回数 // = Clock / 4000000*1000 __delay32(20*N_msec); //20msecウェイト lcd_out(0x30, 1); // 8bit mode set __delay32(5*N_msec); //5msecウェイト lcd_out(0x30, 1); // 8bit mode set __delay32(N_msec); //1msecウェイト lcd_out(0x30, 1); // 8bit mode set __delay32(N_msec); //1msecウェイト lcd_out(0x20, 1); // 4bit mode set __delay32(N_msec); //1msecウェイト lcd_cmd(0x2E); // DL=0 4bit mode lcd_cmd(0x08); // display off C=D=B=0 lcd_cmd(0x0D); // display on C=D=1 B=0 lcd_cmd(0x06); // entry I/D=1 S=0 lcd_cmd(0x02); // cursor home } //---------------------------------------------------------------------------------------------- //************************************************************************************************************************ // <SLAVE側 プログラム> // SPI通信 : ・ 受信したデータと同じデータをそのまま送信データとしてMASTERに返す // ・ 割込みなし // dsPIC30F4013 //************************************************************************************************************************ //MPLABのプロジェクトで下記のファイル要追加 //Library Filesフォルダ : lib30F4013-coff.a //Linker Scriptフォルダ : p30f4013.gld #include <spi.h> _FOSC(CSW_FSCM_OFF & XT_PLL8); // (10MHz)x8=80MHz _FWDT(WDT_OFF); _FBORPOR(PBOR_ON & BORV_20 & PWRT_64 & MCLR_EN); _FGS(CODE_PROT_OFF); int main(void) { unsigned int data_from_Master,data_from_Slave; TRISF = 0b0000000001000100; //RF2(SDI)、RF6(SCK)ピンのみ入力 CloseSPI1(); ConfigIntSPI1(SPI_INT_DIS & SPI_INT_PRI_6); //割込みなし OpenSPI1( // SPI1CONレジスタとSPI1STATレジスタの設定 FRAME_ENABLE_OFF & //フレーム制御なし<Frame SPI support Enable/Disable:FRMEN> FRAME_SYNC_INPUT & //フレーム制御時の入出力方向<Frame Sync Pulse direction control:SPIFSD> ENABLE_SDO_PIN & //SDOピンを汎用ピンとしてではなくSPI制御用として使用 <SDO Pin Control bit:DISSDO> SPI_MODE16_ON & //送受信単位:16ビット (8ビット→ SPI_MODE16_OFF)<Word/Byte Communication mode:MODE16> SPI_SMP_ON & //データ入力サンプルフェーズ:データ出力時間の終了時に入力データをサンプルする<SPI Data Input Sample phase:SMP> SPI_CKE_OFF & //クロックエッジ選択:IDLE 状態からアクテイブクロック状態に遷移するエッジでシリアル出力データが変化CKE=0<SPI Clock Edge Select:CKE> // SLAVE_SELECT_ENABLE_OFF & //SS ピン有効化:SSピン使用せず<SPI Slave Select enable:SSEN> CLK_POL_ACTIVE_LOW & //クロック極性選択ビット:アクティブ状態Low(CKP=1)<SPI Clock polarity select:CKP> //SLAVEはクロックの立ち上がりでデータを取り込む → CKE=0 CKP=1 MASTER_ENABLE_OFF & //マスターモード無効化:SLAVEモードに設定<SPI Mode Select bit:MSTEN> SEC_PRESCAL_4_1 & //2次プリスケール選択: 1:1<Secondary Prescale select:SPRE2-SPRE0> PRI_PRESCAL_16_1, //1次プリスケール選択: 1:1<Primary Prescale select : PRE1-PRE0> //SPIクロック周波数=Fosc × 1/4 ÷ (1次プリスケーラ×2次プリスケーラ) // = 80MHz × 1/4 ÷ ( 16 × 4) = 312.5 KHz < 400KHz(SDカード認識時のクロック) SPI_ENABLE & //SPI有効無効設定: 有効<SPI Enable/Disable:SPIEN> SPI_IDLE_CON & //アイドル時の動作設定: 動作<SPI Idle mode Operation:SPISIDL> SPI_RX_OVFLOW_CLR); //オーバーフロー発生時フラグクリア:クリア<Clear Receive Overflow Flag bit:SPIROV> while(1) { while(!SPI1STATbits.SPIRBF); //SPI1STATレジスタのSPIRBFビットが1になるまで待つ // while(!DataRdySPI1()); // → 受信データ有無を確認して受信データがなければ待つ data_from_Master = getcSPI1(); //MASTERらのデータを取得 data_from_Slave = data_from_Master; // putcSPI1(data_from_Slave); //MASTERにデータ送信 while(SPI1STATbits.SPITBF); //送信完了確認:SPI1送信バッファーが空になるまで待つ } CloseSPI1(); return 0; }
<動作結果>
送信データ 84より ひとつ少ない83が受信データとして表示されています。これは MasterがデータをSlaveに送信すると同時にSlaveからのデータを受信するためひとつ前の送信データに対する応答がデータとしてMasterで受信されているためです。
(2)PIC間送受信2(SPI割込み有 1文字毎の送受信 )
<CCS編>
(1)と同じ仕様をSPI用に用意されているSPI割込みを使って実現してみました。
<試作品仕様><試作品回路図><試作品外観><動作結果><Master側のプログラム>は(1)と全く同じです。
<プログラム例> //****************************************************************************************** // SLAVE側 // SPI通信 : 受信したデータと同じデータをそのまま送信データとしてMASTERに返す //****************************************************************************************** #include <18F452.h> #use delay(clock=40000000) #FUSES EC,NOWDT,,PUT,BROWNOUT,BORV42,NOPROTECT,NOLVP unsigned int data_from_Master,data_from_Slave; #INT_SSP //データを受信すると割込みが発生 void sspx() { data_from_Master = spi_read(); //受信データを変数にセットすると同時に送信バッファーにある送信データを送信する data_from_Slave = data_from_Master; //受信したデータを送信データの変数にセット //受信したデータをMasterにそのまま返す spi_write(data_from_Slave); // 送信データを送信バッファーに書き込む } main(){ setup_spi(SPI_SLAVE | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED); // スレーブ側 検出エッジ:L→H 搬送クロック:CPUクロックの1/16 SS端子:使用せず enable_interrupts(INT_SSP); //割込み許可 enable_interrupts(GLOBAL); while(1) { } return 0; } //--------------------------------------------------------------------------- //************************************** //インクルードファイル 1lcd_lib.c //このファイルは後閑哲也さんが設計されたものです //************************************** /////////////////////////////////////////////// // 液晶表示器制御ライブラリ // 内蔵関数は以下 // lcd_init() ----- 初期化 // lcd_cmd(cmd) ----- コマンド出力 // lcd_data(chr) ----- 1文字表示出力 // lcd_clear() ----- 全消去 //////// データ出力サブ関数 void lcd_out(int code, int flag) { output_x((code & 0xF0) | (input_x() & 0x0F)); if (flag == 0) output_high(rs); //表示データの場合 else output_low(rs); //コマンドデータの場合 delay_cycles(4); //NOP 1 output_high(stb); //strobe out delay_cycles(8); //NOP 2 output_low(stb); //reset strobe } //////// 1文字表示関数 void lcd_data(int asci) { lcd_out(asci, 0); //上位4ビット出力 lcd_out(asci<<4, 0); //下位4ビット出力 delay_us(50); //50μsec待ち } /////// コマンド出力関数 void lcd_cmd(int cmd) { lcd_out(cmd, 1); //上位4ビット出力 lcd_out(cmd<<4, 1); //下位4ビット出力 delay_ms(2); //2msec待ち } /////// 全消去関数 void lcd_clear() { lcd_cmd(0x01); //初期化コマンド出力 delay_ms(15); //15msec待ち } /////// 初期化関数 void lcd_init() { set_tris_x(mode); //モードセット delay_ms(15); lcd_out(0x30, 1); //8bit mode set delay_ms(5); lcd_out(0x30, 1); //8bit mode set delay_ms(1); lcd_out(0x30, 1); //8bit mode set delay_ms(1); lcd_out(0x20, 1); //4bit mode set delay_ms(1); lcd_cmd(0x2E); //DL=0 4bit mode lcd_cmd(0x08); //display off C=D=B=0 lcd_cmd(0x0D); //display on C=D=1 B=0 lcd_cmd(0x06); //entry I/D=1 S=0 lcd_cmd(0x02); //cursor home }
(2)PIC間送受信2(SPI割込み有 1文字毎の送受信 )
<C30 dsPIC編>
<試作品仕様>
・ 送信データとして0〜255までを1秒毎にインクリメントして順次MasterからSlaveにSPIインターフェースをつかい
送信すること。
・ Slave側では受信したデータをそのまま送信データとしてMaster側へすぐに送信すること。
・ マスタ側の液晶の1行目にはMasterから送信したデータを、液晶の2行目にはMasterが受信したデータを
表示すること。
・ Slave側はSPI割込みを使うこと。
・ スレーブ側の液晶の1行目にはMasterから受信したデータを、液晶の2行目にはMasterへ送信したデータを
表示すること。
<試作品回路図>
Master側にdsPIC30F2012をSlave側にdsPIC30F4013を使った場合を下記に示します(→回路図のPDFファイル)
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品が多々写っています
<プログラム例> ///***********************************************************************/ // <MASTER側 プログラム> // SPI通信 : 2秒毎に整数を +2 インクリメントしてSLAVEに送信する // 受信にはSPI割込みを使用する // 送信したデータと受信したデータを液晶に表示する //* Master : dsPIC30F2012 Slave : dsPIC30F4013 //***********************************************************************/ // MPLAB プロジェクトへの要追加ファイル // Linker Scriptフォルダ p30f2012.gld // Library Filesフォルダ lib30F2012-coff.a #include "p30f2012.h" #include <stdio.h> #include <stdlib.h> #include "spi.h" #include "1lcd_lib_C30.h" _FOSC(CSW_FSCM_OFF & // クロック切り替えなし、フェースセイフクロックモニタなし XT_PLL8 //外部発振子:10MHz、PLL:8倍 → システムクロック周波数=10×8=80MHz //4,8,16倍が選択可 ); _FWDT(WDT_OFF); _FBORPOR(PBOR_ON & //ブラウンアウトリセット機能:ON BORV_42 & //ブラウンアウト電圧:4.2V PWRT_64 & //パワーオンリセットタイマ64msec MCLR_EN //MCLR機能:ON ); _FGS(CODE_PROT_OFF); //コードプロテクト:OFF void delay_ms(unsigned int); char Ver = '1'; char Buf[17]; //文字列のバッファー用レジスタ int data_from_Master,data_from_Slave; void delay_ms(unsigned int N) //ウェイト関数 { __delay32(Clock/4000*N); } void _ISR _SPI1Interrupt(void) //SPI割込み発生 { IFS0bits.SPI1IF = 0; //割込みフラグクリア if(SPI1STATbits.SPIRBF == 1) //SPI1STATレジスタのSPIRBFビットが1ならば data_from_Slave =getcSPI1(); //SLAVEからのデータを取得 } /// メイン関数 int main(void) { TRISB = 0; //all out TRISC = 0; //all out TRISF = 0b0000000000000100; //RF2:in 他out CloseSPI1(); ConfigIntSPI1(SPI_INT_EN & SPI_INT_PRI_6); //割込あり 優先度6 OpenSPI1( // SPI1CONレジスタとSPI1STATレジスタの設定 FRAME_ENABLE_OFF & //フレーム制御なし<Frame SPI support Enable/Disable:FRMEN> FRAME_SYNC_INPUT & //フレーム制御時の入出力方向<Frame Sync Pulse direction control:SPIFSD> ENABLE_SDO_PIN & //SDOピンを汎用ピンとしてではなくSPI制御用として使用 <SDO Pin Control bit:DISSDO> SPI_MODE16_ON & //送受信単位:16ビット (8ビット→ SPI_MODE16_OFF)<Word/Byte Communication mode:MODE16> SPI_SMP_ON & //データ入力サンプルフェーズ:データ出力時間の終了時に入力データをサンプルする<SPI Data Input Sample phase:SMP> SPI_CKE_OFF & //クロックエッジ選択:IDLE 状態からアクテイブクロック状態に遷移するエッジでシリアル出力データが変化CKE=0<SPI Clock Edge Select:CKE> // SLAVE_SELECT_ENABLE_OFF & //SS ピン有効化:SSピン使用せず<SPI Slave Select enable:SSEN> CLK_POL_ACTIVE_LOW & //クロック極性選択ビット:アクティブ状態Low(CKP=1)<SPI Clock polarity select:CKP> //SDカードはクロックの立ち上がりでデータを取り込む → CKE=0 CKP=1 MASTER_ENABLE_ON & //マスターモード有効化:マスターモードに設定<SPI Mode Select bit:MSTEN> SEC_PRESCAL_4_1 & //2次プリスケール選択: 1:1<Secondary Prescale select:SPRE2-SPRE0> PRI_PRESCAL_16_1, //1次プリスケール選択: 1:1<Primary Prescale select : PRE1-PRE0> //SPIクロック周波数=Fosc × 1/4 ÷ (1次プリスケーラ×2次プリスケーラ) // = 80MHz × 1/4 ÷ ( 16 × 4) = 312.5 KHz < 400KHz(SDカード認識時のクロック) SPI_ENABLE & //SPI有効無効設定: 有効<SPI Enable/Disable:SPIEN> SPI_IDLE_CON & //アイドル時の動作設定: 動作<SPI Idle mode Operation:SPISIDL> SPI_RX_OVFLOW_CLR); //オーバーフロー発生時フラグクリア:クリア<Clear Receive Overflow Flag bit:SPIROV> lcd_init(); // LCD初期化 lcd_cmd(0b00001100); // カーソル:OFF ブリンク:OFF lcd_clear(); // 全消去 sprintf(Buf,"SPI %c",Ver); //arguementがないと遅い C30のバグ? lcd_str(Buf); //液晶表示 delay_ms(1000); EnableIntSPI1; //割込み許可 while(1) //タイマ割込みを待つ { putcSPI1(data_from_Master); //SLAVEにデータ送信 lcd_clear(); //液晶表示 液晶クリア sprintf(Buf,"from Master=%d",data_from_Master); lcd_str(Buf); //1行目液晶表示 lcd_cmd(0xC0); //2行目の先頭へ sprintf(Buf,"from Slave=%d",data_from_Slave); lcd_str(Buf); //2行目の表示 delay_ms(2000); data_from_Master = data_from_Master + 2; } CloseSPI1(); return 0; } //********************************************************************************************** //インクルードファイル 1lcd_lib_C30.h //このファイルは後閑哲也さんが設計されたCCSコンパイラ用液晶表示ライブラリ 1lcd_lib.cをもとに、 //C30コンパイラ対応、及び分散ポート対応等でに変更したものです。 //********************************************************************************************** #include "p30f2012.h" #define Clock 80000000 // 単位はHzで指定 // LCDポート設定 #define lcd_port_DB7 LATBbits.LATB7 //LCDのDB7(14番ピン)に接続されるPIC側ポート番号設定 #define lcd_port_DB6 LATBbits.LATB6 //LCDのDB6(13番ピン)に接続されるPIC側ポート番号設定 #define lcd_port_DB5 LATBbits.LATB5 //LCDのDB5(12番ピン)に接続されるPIC側ポート番号設定 #define lcd_port_DB4 LATBbits.LATB4 //LCDのDB4(11番ピン)に接続されつPIC側ポート番号設定 #define lcd_stb LATFbits.LATF4 //LCDのstb(6番ピン)に接続されるPIC側ポート番号設定 #define lcd_rs LATFbits.LATF5 //LCDのrs(4番ピン)に接続されるPIC側ポート番号設定 void lcd_out(char code, char flag); void lcd_data(char asci); void lcd_cmd(char cmd); void lcd_clear(void); void lcd_init(void); void lcd_str(char *str); //******************************************************************************************** //インクルードファイル 1lcd_lib_C30.c //このファイルは後閑哲也さんが設計されたCCSコンパイラ用液晶表示ライブラリ 1lcd_lib.cをもとに、 //C30コンパイラ対応、分散ポート対応等で変更したものです。 //******************************************************************************************** /////////////////////////////////////////////// // 液晶表示器制御ライブラリ for C30コンパイラー // 内蔵関数は以下 // lcd_init() ----- 初期化 // lcd_cmd(cmd) ----- コマンド出力 // lcd_data(chr) ----- 1文字表示出力 // lcd_clear() ----- 全消去 // lcd_str(str*) ----- 文字列表示 ////////////////////////////////////////////// #include "1lcd_lib_C30.h" unsigned int _1usec; // 1μsec待つに必要なウェイト回数 unsigned int _50usec; //50μsec待つに必要なウェイト回数 unsigned long N_msec; // 1msec待つに必要なウェイト回数 //////// データ出力サブ関数 void lcd_out(char code, char flag) { if(code & 0b10000000)lcd_port_DB7 = 1; //LCDのDB7への出力セット else lcd_port_DB7 = 0; if(code & 0b01000000)lcd_port_DB6 = 1; //LCDのDB6への出力セット else lcd_port_DB6 = 0; if(code & 0b00100000)lcd_port_DB5 = 1; //LCDのDB5への出力セット else lcd_port_DB5 = 0; if(code & 0b00010000)lcd_port_DB4 = 1; //LCDのDB4への出力セット else lcd_port_DB4 = 0; if (flag == 0) lcd_rs = 1; // 表示データの場合 else lcd_rs = 0; // コマンドデータの場合 __delay32(_1usec); //1μsecウェイト lcd_stb = 1; // strobe(E) ON (Enable) __delay32(_1usec); // 1μsec : strobe信号の幅 lcd_stb = 0; // reset strobe } //////// 1文字表示関数 void lcd_data(char asci) { lcd_out(asci, 0); // 上位4ビット出力 lcd_out(asci<<4, 0); // 下位4ビット出力 __delay32(_50usec); //50μsecウェイト } /////// コマンド出力関数 void lcd_cmd(char cmd) { lcd_out(cmd, 1); // 上位4ビット出力 lcd_out(cmd<<4, 1); // 下位4ビット出力 if((cmd & 0x03) != 0) // clear Homeの場合 __delay32(2*N_msec); // 2msec待ち else __delay32(_50usec); //50μsecウェイト } /////// 全消去関数 void lcd_clear(void) { lcd_cmd(0x01); // 初期化コマンド出力 // __delay32(15*N_msec); //15msecウェイト } /////// 文字列出力関数 void lcd_str(char* str) { while(*str) //文字列終端の '\0'を検出するまで { lcd_data(*str); // 1文字表示 str++; //ポインタをインクリメント } } /////// 初期化関数 void lcd_init(void) { _1usec =(unsigned int)( Clock / 4000000); // 1μsecに要するウェイト回数 //__delay32(N) : Nが11以下の場合でも11回ウェイト _50usec = (unsigned int)(Clock / 4000000 * 50); //50μescに要するウェイト回数 N_msec = (unsigned long int)(Clock / 4000); // 1msecに要するウェイト回数 // = Clock / 4000000*1000 __delay32(20*N_msec); //20msecウェイト lcd_out(0x30, 1); // 8bit mode set __delay32(5*N_msec); //5msecウェイト lcd_out(0x30, 1); // 8bit mode set __delay32(N_msec); //1msecウェイト lcd_out(0x30, 1); // 8bit mode set __delay32(N_msec); //1msecウェイト lcd_out(0x20, 1); // 4bit mode set __delay32(N_msec); //1msecウェイト lcd_cmd(0x2E); // DL=0 4bit mode lcd_cmd(0x08); // display off C=D=B=0 lcd_cmd(0x0D); // display on C=D=1 B=0 lcd_cmd(0x06); // entry I/D=1 S=0 lcd_cmd(0x02); // cursor home } //************************************************************************************ // <SLAVE側 プログラム> // SPI通信 : 受信したデータと同じデータをそのまま送信データとしてMASTERに返す // dsPIC30F4013 //************************************************************************************ //MPLABのプロジェクトで下記のファイル要追加 //Library Filesフォルダ : lib30F4013-coff.a //Linker Scriptフォルダ : p30f4013.gld #include "p30f4013.h" #include <spi.h> #include <stdio.h> #include <stdlib.h> #include "1lcd_lib_C30.h" _FOSC(CSW_FSCM_OFF & XT_PLL8); // (10MHz)x8=80MHz _FWDT(WDT_OFF); _FBORPOR(PBOR_ON & BORV_20 & PWRT_64 & MCLR_EN); _FGS(CODE_PROT_OFF); void delay_ms(unsigned int); int Ver = 1; char Buf[17]; int data_from_Master,data_from_Slave; void delay_ms(unsigned int N) { __delay32(Clock/4000*N); } void _ISR _SPI1Interrupt(void) //SPI割込み発生 { int dumy; IFS0bits.SPI1IF = 0; //割込みフラグクリア if(SPI1STATbits.SPIRBF == 1) //SPI1STATレジスタのSPIRBFビットが1ならば data_from_Master =getcSPI1(); //SLAVEからのデータを取得 data_from_Slave = data_from_Master; putcSPI1(data_from_Slave); //SLAVEにデータ送信 } int main(void) { TRISB = 0; TRISF = 0b0000000001000100; //RF2(SDI)、RF6(SCK)ピンのみ入力 CloseSPI1(); ConfigIntSPI1(SPI_INT_EN & SPI_INT_PRI_6); //割込あり 優先度6 OpenSPI1( // SPI1CONレジスタとSPI1STATレジスタの設定 FRAME_ENABLE_OFF & //フレーム制御なし<Frame SPI support Enable/Disable:FRMEN> FRAME_SYNC_INPUT & //フレーム制御時の入出力方向<Frame Sync Pulse direction control:SPIFSD> ENABLE_SDO_PIN & //SDOピンを汎用ピンとしてではなくSPI制御用として使用 <SDO Pin Control bit:DISSDO> SPI_MODE16_ON & //送受信単位:16ビット (8ビット→ SPI_MODE16_OFF)<Word/Byte Communication mode:MODE16> SPI_SMP_ON & //データ入力サンプルフェーズ:データ出力時間の終了時に入力データをサンプルする<SPI Data Input Sample phase:SMP> SPI_CKE_OFF & //クロックエッジ選択:IDLE 状態からアクテイブクロック状態に遷移するエッジでシリアル出力データが変化CKE=0<SPI Clock Edge Select:CKE> // SLAVE_SELECT_ENABLE_OFF &//SS ピン有効化:SSピン使用せず<SPI Slave Select enable:SSEN> CLK_POL_ACTIVE_LOW &//クロック極性選択ビット:アクティブ状態Low(CKP=1)<SPI Clock polarity select:CKP> //SLAVEはクロックの立ち上がりでデータを取り込む → CKE=0 CKP=1 MASTER_ENABLE_OFF & //マスターモード無効化:SLAVEモードに設定<SPI Mode Select bit:MSTEN> SEC_PRESCAL_4_1 & //2次プリスケール選択: 1:1<Secondary Prescale select:SPRE2-SPRE0> PRI_PRESCAL_16_1, //1次プリスケール選択: 1:1<Primary Prescale select : PRE1-PRE0> //SPIクロック周波数=Fosc × 1/4 ÷ (1次プリスケーラ×2次プリスケーラ) // = 80MHz × 1/4 ÷ ( 16 × 4) = 312.5 KHz < 400KHz(SDカード認識時のクロック) SPI_ENABLE & //SPI有効無効設定: 有効<SPI Enable/Disable:SPIEN> SPI_IDLE_CON & //アイドル時の動作設定: 動作<SPI Idle mode Operation:SPISIDL> SPI_RX_OVFLOW_CLR); //オーバーフロー発生時フラグクリア:クリア<Clear Receive Overflow Flag bit:SPIROV> lcd_init(); // LCD初期化 lcd_cmd(0b00001100); // カーソル:OFF ブリンク:OFF lcd_clear(); // 全消去 sprintf(Buf,"SPI Start !!%d",Ver); //arguementがないと遅い C30のバグ? lcd_str(Buf); //液晶表示 delay_ms(1000); EnableIntSPI1; //割込み許可 while(1) { lcd_clear(); //液晶表示 液晶クリア sprintf(Buf,"Receive=%d",data_from_Master); lcd_str(Buf); //1行目液晶表示 lcd_cmd(0xC0); //2行目の先頭へ sprintf(Buf,"Send=%d",data_from_Slave); lcd_str(Buf); //2行目の表示 delay_ms(200); } CloseSPI1(); return 0; } //********************************************************************************************* //インクルードファイル 1lcd_lib_C30.h //このファイルは後閑哲也さんが設計されたCCSコンパイラ用液晶表示ライブラリ 1lcd_lib.cをもとに、 //C30コンパイラ対応、及び分散ポート対応等でに変更したものです。 //********************************************************************************************* #include "p30f4013.h" #define Clock 80000000 // 単位はHzで指定 // LCDポート設定 #define lcd_port_DB7 LATBbits.LATB12 //LCDのDB7(14番ピン)に接続されるPIC側ポート番号設定 #define lcd_port_DB6 LATBbits.LATB11 //LCDのDB6(13番ピン)に接続されるPIC側ポート番号設定 #define lcd_port_DB5 LATBbits.LATB10 //LCDのDB5(12番ピン)に接続されるPIC側ポート番号設定 #define lcd_port_DB4 LATBbits.LATB9 //LCDのDB4(11番ピン)に接続されつPIC側ポート番号設定 #define lcd_stb LATFbits.LATF0 //LCDのstb(6番ピン)に接続されるPIC側ポート番号設定 #define lcd_rs LATFbits.LATF1 //LCDのrs(4番ピン)に接続されるPIC側ポート番号設定 void lcd_out(char code, char flag); void lcd_data(char asci); void lcd_cmd(char cmd); void lcd_clear(void); void lcd_init(void); void lcd_str(char *str); //******************************************************************************************** //インクルードファイル 1lcd_lib_C30.c //このファイルは後閑哲也さんが設計されたCCSコンパイラ用液晶表示ライブラリ 1lcd_lib.cをもとに、 //C30コンパイラ対応、分散ポート対応等で変更したものです。 //******************************************************************************************** /////////////////////////////////////////////// // 液晶表示器制御ライブラリ for C30コンパイラー // 内蔵関数は以下 // lcd_init() ----- 初期化 // lcd_cmd(cmd) ----- コマンド出力 // lcd_data(chr) ----- 1文字表示出力 // lcd_clear() ----- 全消去 // lcd_str(str*) ----- 文字列表示 ////////////////////////////////////////////// #include "1lcd_lib_C30.h" unsigned int _1usec; // 1μsec待つに必要なウェイト回数 unsigned int _50usec; //50μsec待つに必要なウェイト回数 unsigned long N_msec; // 1msec待つに必要なウェイト回数 //////// データ出力サブ関数 void lcd_out(char code, char flag) { if(code & 0b10000000)lcd_port_DB7 = 1; //LCDのDB7への出力セット else lcd_port_DB7 = 0; if(code & 0b01000000)lcd_port_DB6 = 1; //LCDのDB6への出力セット else lcd_port_DB6 = 0; if(code & 0b00100000)lcd_port_DB5 = 1; //LCDのDB5への出力セット else lcd_port_DB5 = 0; if(code & 0b00010000)lcd_port_DB4 = 1; //LCDのDB4への出力セット else lcd_port_DB4 = 0; if (flag == 0) lcd_rs = 1; // 表示データの場合 else lcd_rs = 0; // コマンドデータの場合 __delay32(_1usec); //1μsecウェイト lcd_stb = 1; // strobe(E) ON (Enable) __delay32(_1usec); // 1μsec : strobe信号の幅 lcd_stb = 0; // reset strobe } //////// 1文字表示関数 void lcd_data(char asci) { lcd_out(asci, 0); // 上位4ビット出力 lcd_out(asci<<4, 0); // 下位4ビット出力 __delay32(_50usec); //50μsecウェイト } /////// コマンド出力関数 void lcd_cmd(char cmd) { lcd_out(cmd, 1); // 上位4ビット出力 lcd_out(cmd<<4, 1); // 下位4ビット出力 if((cmd & 0x03) != 0) // clear Homeの場合 __delay32(2*N_msec); // 2msec待ち else __delay32(_50usec); //50μsecウェイト } /////// 全消去関数 void lcd_clear(void) { lcd_cmd(0x01); // 初期化コマンド出力 // __delay32(15*N_msec); //15msecウェイト } /////// 文字列出力関数 void lcd_str(char* str) { while(*str) //文字列終端の '\0'を検出するまで { lcd_data(*str); // 1文字表示 str++; //ポインタをインクリメント } } /////// 初期化関数 void lcd_init(void) { _1usec =(unsigned int)( Clock / 4000000); // 1μsecに要するウェイト回数 //__delay32(N) : Nが11以下の場合でも11回ウェイト _50usec = (unsigned int)(Clock / 4000000 * 50); //50μescに要するウェイト回数 N_msec = (unsigned long int)(Clock / 4000); // 1msecに要するウェイト回数 // = Clock / 4000000*1000 __delay32(20*N_msec); //20msecウェイト lcd_out(0x30, 1); // 8bit mode set __delay32(5*N_msec); //5msecウェイト lcd_out(0x30, 1); // 8bit mode set __delay32(N_msec); //1msecウェイト lcd_out(0x30, 1); // 8bit mode set __delay32(N_msec); //1msecウェイト lcd_out(0x20, 1); // 4bit mode set __delay32(N_msec); //1msecウェイト lcd_cmd(0x2E); // DL=0 4bit mode lcd_cmd(0x08); // display off C=D=B=0 lcd_cmd(0x0D); // display on C=D=1 B=0 lcd_cmd(0x06); // entry I/D=1 S=0 lcd_cmd(0x02); // cursor home }
<動作結果>
以下の写真で 上側の液晶がMaster側(dsPIC30F2012)Cに接続されている液晶で 下側液晶がSlave側(dsPIC30F4013)に
接続されている液晶です。送信データ 216より 2少ない214が受信データとして表示されています。これは Masterが
データをSlaveに送信すると同時にSlaveからのデータを受信するためひとつ前の送信データに対する応答がデータとして
Masterで受信されているためです。 送信データは +2づつインクリメントされています。
(3)PIC間の送受信3(外部割込み有 文字列の送受信 Master/Slave共液晶付)
液晶が接続された場合などのようにSlave側の処理時間が長い場合は、外部割込みによりMasterとSlaveの間で
同期をとって送信する方法があります。以下にその例を示します。
<試作品仕様>
★ Master側
・”How are you ? ” と ”Who is it ? ”を5秒毎に交互にSlaveにおくる
・送信のタイミングはRD1端子からパルスを発生し、SlaveのINT1から外部割込みをかける
・液晶の上段には送信した文字を、また下段には受信した文字を表示する
★ Slave側
・Masterから”How are you ? ”が送られてきた場合は”I am Fine”を ”Who is it ?”が送られてきた場合は
”I am PIC18F452”を返信する
・受信中は受信した直後の文字を文字列の配列番号と共に表示する。
例 How are you ? の文字列を受信中の場合
Hを受信した時は str[0]=H
wを受信した時は str[2]=w
を液晶上段に表示すること
・受信が完了したら上段に受信文字を下段には送信文字を表示すること
<試作品回路図>
PIC18F452×2をつかった場合の回路図を以下に示します。(→回路図のPDFファイル)
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品が多々写っています
<プログラム例>
信号の送受信はアスキーコードの制御文字をつかって以下のようにおこなわれています。
@ 送信側から受信側に受信準備OKかの問合わせ ENQ(Enquiry 0x05)信号を送信する。
A 送信側は受信側より受信準備完了の肯定応答 ACK(Acknowledge 0x06)信号を受信してからデータを送信する。
B データの先頭には送信データヘッダーとしてSTX(Start of Text 0x02)を またデータの最後尾には送信データ
フッターとして ETX(End of Text 0x03)をつけて送信する
//*********************************************************************************** // Master側のプログラム // // SPI通信 : "How are you ?" と "Who is it ?" を交互に送信する。 // 液晶の上段に送信文字を 下段に受信文字を表示する //*********************************************************************************** #include <18F452.h> #include <stdlib.h> #use delay(clock=40000000) #FUSES EC,PUT,NOWDT,BROWNOUT,BORV42,NOPROTECT,NOLVP #define STX 0x02 // STX #define ETX 0x03 // ETX #define ENQ 0x05 // ENQ #define ACK 0x06 // ACK #define NAK 0x15 // NAK #define ESC 0x1b // ESC #use fast_io(D) //////// Port define and link LCD library #define mode 0 // 液晶 #define input_x input_D #define output_x output_D #define set_tris_x set_tris_D #define rs PIN_D2 //chip select #define stb PIN_D0 //strobe #include <1lcd_lib.c> int j =0,dumy = 0,IN; char str1[] = "How are you ?"; char str2[] = "Who is it ?"; char strX[]; char str_receive[17]; void ext_int_pulse() //外部割込みパルス生成 { output_low(PIN_D1); // SlaveのINT1に割込みを発生させるべくパルスを出力する delay_us(100); //パルス幅:100μsec output_high(PIN_D1); } void Send_str(char* str[]) // 文字列を送信 { int i,len; len = strlen(str); for(i = 0; i < len; i++) //文字の数だけ送信 { spi_write(*str); //1文字SPI送信 str++; //ポインタを1つ進める delay_ms(500); //Slaveの液晶に送信した文字がよく表示されるようにゆっくりおくる } } void Send_strx(char* str[]) //送信データヘッダー、送信データフッタ-を含む文字列の送信 { spi_write(STX); //STX: 送信データヘッダー delay_ms(1); Send_str(str); spi_write(ETX); //ETX: 送信データフッター delay_ms(1); } void Sendx() // 送信関数 { int IN; do // Slaveの送信準備完了を待つ { spi_write(ENQ); delay_us(10); IN = spi_read(); }while(IN != ACK); if(j == 0) // str1[] = "How are you ?" の送信 { j = 1; strX = str1; Send_strx(str1); } else // str2[] = "Who is it ?" の送信 { j = 0; strX = str2; Send_strx(str2); } } void Receivex() // 受信関数 { int i,IN; do //Slaveの送信準備が完了するまで待つ { spi_write(ENQ); delay_us(10); IN = spi_read(); }while(IN != ACK); do //Slaveからの送信ヘッダーSTX(0x02)を待つ { spi_write(dumy); delay_us(10); IN = spi_read(); }while(IN != STX); i = 0; do //Slaveからのデータを受信 { spi_write(dumy); delay_us(10); IN = spi_read(); //SPIバッファーにある送信データを送信すると同時に受信データを変数に取り込む str_receive[i] = IN; i++; }while(IN != ETX); str_receive[i-1] = '\0'; } void LCD() // 液晶表示 { lcd_clear(); //液晶表示 液晶クリア printf(lcd_data,strX); //1行目に送信データを表示 lcd_cmd(0xC0); // 2行目の先頭へ printf(lcd_data,str_receive); //2行目に受信データを表示 } main(){ setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED ); output_high(PIN_D1); lcd_init(); lcd_cmd(0b00001100); // カーソル:OFF ブリンク:OFF lcd_clear(); printf(lcd_data,"SPI start!!"); delay_ms(2000); lcd_clear(); spi_write(0x00); // バッファークリア while(1) { Ext_int_pulse(); //SlaveのINT1から外部割込みをかける Sendx(); //データの送信 Receivex(); //データの受信 LCD(); //液晶表示 delay_ms(5000); } return 0; } //------------------------------------------------------------------------------------------------- //************************************** //インクルードファイル 1lcd_lib.c //このファイルは後閑哲也さんが設計されたものです //************************************** /////////////////////////////////////////////// // 液晶表示器制御ライブラリ // 内蔵関数は以下 // lcd_init() ----- 初期化 // lcd_cmd(cmd) ----- コマンド出力 // lcd_data(chr) ----- 1文字表示出力 // lcd_clear() ----- 全消去 //////// データ出力サブ関数 void lcd_out(int code, int flag) { output_x((code & 0xF0) | (input_x() & 0x0F)); if (flag == 0) output_high(rs); //表示データの場合 else output_low(rs); //コマンドデータの場合 delay_cycles(4); //NOP 1 output_high(stb); //strobe out delay_cycles(8); //NOP 2 output_low(stb); //reset strobe } //////// 1文字表示関数 void lcd_data(int asci) { lcd_out(asci, 0); //上位4ビット出力 lcd_out(asci<<4, 0); //下位4ビット出力 delay_us(50); //50μsec待ち } /////// コマンド出力関数 void lcd_cmd(int cmd) { lcd_out(cmd, 1); //上位4ビット出力 lcd_out(cmd<<4, 1); //下位4ビット出力 delay_ms(2); //2msec待ち } /////// 全消去関数 void lcd_clear() { lcd_cmd(0x01); //初期化コマンド出力 delay_ms(15); //15msec待ち } /////// 初期化関数 void lcd_init() { set_tris_x(mode); //モードセット delay_ms(15); lcd_out(0x30, 1); //8bit mode set delay_ms(5); lcd_out(0x30, 1); //8bit mode set delay_ms(1); lcd_out(0x30, 1); //8bit mode set delay_ms(1); lcd_out(0x20, 1); //4bit mode set delay_ms(1); lcd_cmd(0x2E); //DL=0 4bit mode lcd_cmd(0x08); //display off C=D=B=0 lcd_cmd(0x0D); //display on C=D=1 B=0 lcd_cmd(0x06); //entry I/D=1 S=0 lcd_cmd(0x02); //cursor home } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------ //*********************************************************************** // SLAVE側 // SPI通信 : "How are you ?" を 受信したら "I am Fine"を返信する // "Who is it ?" を受信したら " I am PIC18F452" を返信する // 液晶の上段に受信文字を、下段に送信文字を表示する。 // また、受信中は1文字づつ上段に表示する。 //*********************************************************************** #include <18F452.h> #include "string.h" #use delay(clock=40000000) #FUSES EC,NOWDT,,PUT,BROWNOUT,BORV42,NOPROTECT,NOLVP #define STX 0x02 // STX #define ETX 0x03 // ETX #define ENQ 0x05 // ENQ #define ACK 0x06 // ACK #define NAK 0x15 // NAK #define ESC 0x1b // ESC #use fast_io(D) //////// Port define and link LCD library #define mode 0 // 液晶 #define input_x input_D #define output_x output_D #define set_tris_x set_tris_D #define rs PIN_D2 //chip select #define stb PIN_D0 //strobe #include <1lcd_lib.c> char str1[] = "How are you ?"; char str2[] = "Who is it ?"; char str10[] = "I am Fine"; char str20[] = "I am PIC18F452"; char str[17]; char strx[]; char SendData[18]; void Receivex() //受信関数 { int i,IN; delay_ms(10); do //ENQを待つ { if(SPI_DATA_IS_IN() == 1)IN = spi_read(); }while(IN != ENQ); delay_us(10); spi_write(ACK); //ENQがきたらACKを送信する do //送信ヘッダー:STX(0x02)の検出 { if(SPI_DATA_IS_IN() == 1)IN = spi_read(); }while(IN != STX); i = 0; do // データの読込み { if(SPI_DATA_IS_IN() == 1) { str[i] = spi_read(); //受信データを変数に取り込む lcd_clear(); printf(lcd_data,"str[%d]=%c",i,str[i]); //受信した文字を1文字液晶に表示 i++; } }while(str[i-1] != ETX); str[i-1] = '\0'; //文字列に変換 lcd_clear(); printf(lcd_data,str); } void Select() // 受信データに対して送信データを選択 { if(strcmp(str,str1) == 0)strx = str10; //How are you ? --> I am Fine else if(strcmp(str,str2) == 0)strx = str20; // Who is it ? --> I am PIC18F452 else strx = str; lcd_cmd(0xC0); // 2行目の先頭へ printf(lcd_data,strx); } void Sendx() //送信関数 { int i,length,IN; //送信順に再配置 length = strlen(strx); SendData[0] = STX; //文字列の先頭に送信ヘッダーを設定 for(i = 1; i <= length; i++)SendData[i] = strx[i-1]; SendData[length +1] = ETX; //文字列の最後に送信フッターを設定 do //ENQを待つ { if(SPI_DATA_IS_IN() == 1)IN = spi_read(); }while(IN != ENQ); delay_us(10); spi_write(ACK); //ENQがきたらACKを送信する i = 0; do // 1文字づつ送信 { if(SPI_DATA_IS_IN() == 1) { spi_write(SendData[i]); i++; } }while( i != (length + 2) ); } #INT_EXT1 void EXTX() // RB1端子からの外部割り込みで実行 { Receivex(); Select(); Sendx(); } main(){ setup_spi(SPI_SLAVE | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED); // スレーブ側 検出エッジ:L→H 搬送クロック:CPUクロックの1/16 SS端子:使用せず ext_int_edge( 1, H_TO_L); // Set up PIC18 EXT1 //RB1(INT1)ポートが highからlowに変化した時割込みがかかるように設定 lcd_init(); lcd_cmd(0b00001100); // カーソル:OFF ブリンク:OFF lcd_clear(); printf(lcd_data,"SPI Slave!!"); lcd_cmd(0xC0); // 2行目の先頭へ printf(lcd_data,"EXT INT Start"); delay_ms(500); lcd_clear(); enable_interrupts(INT_EXT1); enable_interrupts(GLOBAL); while(1) // 何もしないで割込みを待つ { } return 0; } //--------------------------------------------------------------------------- //************************************** //インクルードファイル 1lcd_lib.c //このファイルは後閑哲也さんが設計されたものです //************************************** /////////////////////////////////////////////// // 液晶表示器制御ライブラリ // 内蔵関数は以下 // lcd_init() ----- 初期化 // lcd_cmd(cmd) ----- コマンド出力 // lcd_data(chr) ----- 1文字表示出力 // lcd_clear() ----- 全消去 //////// データ出力サブ関数 void lcd_out(int code, int flag) { output_x((code & 0xF0) | (input_x() & 0x0F)); if (flag == 0) output_high(rs); //表示データの場合 else output_low(rs); //コマンドデータの場合 delay_cycles(4); //NOP 1 output_high(stb); //strobe out delay_cycles(8); //NOP 2 output_low(stb); //reset strobe } //////// 1文字表示関数 void lcd_data(int asci) { lcd_out(asci, 0); //上位4ビット出力 lcd_out(asci<<4, 0); //下位4ビット出力 delay_us(50); //50μsec待ち } /////// コマンド出力関数 void lcd_cmd(int cmd) { lcd_out(cmd, 1); //上位4ビット出力 lcd_out(cmd<<4, 1); //下位4ビット出力 delay_ms(2); //2msec待ち } /////// 全消去関数 void lcd_clear() { lcd_cmd(0x01); //初期化コマンド出力 delay_ms(15); //15msec待ち } /////// 初期化関数 void lcd_init() { set_tris_x(mode); //モードセット delay_ms(15); lcd_out(0x30, 1); //8bit mode set delay_ms(5); lcd_out(0x30, 1); //8bit mode set delay_ms(1); lcd_out(0x30, 1); //8bit mode set delay_ms(1); lcd_out(0x20, 1); //4bit mode set delay_ms(1); lcd_cmd(0x2E); //DL=0 4bit mode lcd_cmd(0x08); //display off C=D=B=0 lcd_cmd(0x0D); //display on C=D=1 B=0 lcd_cmd(0x06); //entry I/D=1 S=0 lcd_cmd(0x02); //cursor home } //---------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------
<動作結果>
・ Masterより ”How are you ?”を送信した結果 Slaveより ”I am Fine”の返信がされた時の液晶画面が<写真1>に
表示されています。
<Master側の液晶> <Slave側の液晶>
<写真 1>
・ Masterより ”Who is it ?” を送信した結果 Slaveより ” I am PIC18F452 ”の返信がされた時の液晶画面が<写真2>に
表示されています。
<Masterga側の液晶> <Slave側の液晶>
<写真2>
・ 文字列を受信中のSlave側の液晶画面が<写真3>に表示されています。この写真は”Who is it ? ”の ” t ”が受信された
直後の写真です。 ”Who is it ? ” は
str[0]=W
str[1]=h
str[2]=o
str[3]=
str[4]=i
str[5]=s
str[6]=
str[7]=i
str[8]=t
str[9]=
str[10]=?
str[11]=
の 11個の文字にわけて送信されてきます。
<Slave側の液晶>
<写真3>