18F14K50のMaster Synchronous Serial Port (MSSP) を使うと周辺機器と簡単にシリアル通信を行うことができます。MSSPは、SPIとI2Cという2種類の方式をサポートしていまが、ここでは、SPIを利用したD/A変換器のインターフェースだけを取り上げます。このため、SPI マスターモードが対象です。 SCK(RB6) SDI(RB4) SDO(RC7) SS(RC6)の4ピンが対応しています。
SPIモードで使うときには、この4つのピンはの入出力をTRISレジスタで適切に設定しておく必要があります。
<TRISにセットする値> | ||||
---|---|---|---|---|
SPIモード | SCK TRISB6 |
SDI TRISB4 |
SDO TRISC7 |
SS TRISC6 |
マスター | 0 | 1 | 0 | - |
スレーブ | 1 | 1 | 0 | 1 |
SPIモードの機能を使用しないピンは、TRISをSPI機能の反対に設定することで、SPIでの設定と反対の汎用I/Oピンとして使用することができます。たとえば、SPIマスターモードで、送信専用としSPI入力がいらない場合、SDI(RB4)のTRISを出力[0]に設定すれば、汎用出力ピンとして使用することが可能です。
高低2レベルの優先度を使用するI2C に関連するレジスターを一覧で説明します。
レジスタ | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|---|
RCON | IPEN | SBOREN | — | RI | TO | PD | POR | BOR |
INTCON | GIE/GIEH | PEIE/GIEL | TMR0IE | INTIE | RABIE | TMR0IF | INT0IF | RABIF |
PIR1 | - | ADIF | RCIF | TXIF | SSP1IF | CCP1IF | TMR2IF | TMR1IF |
PIE1 | - | ADIE | RCIE | TXIE | SSP1IE | CCP1IE | TMR2IE | TMR1IE |
IPR1 | — | ADIP | RCIP | TXIP | SSPIP | CCP1IP | TMR2IP | TMR1IP |
SSPCON1 | WCOL | SSPOV | SSPEN | CKP | SSPM<3:0> | |||
SSPSTAT | SMP | CKE | D/A | P | S | WnR | UA | BF |
SSPADD | ADD<7:0> | |||||||
SSPBUF | MSSP Receive Buffer/Transmit Register | |||||||
TRISB | TRISB7 | TRISB6 | TRISB5 | TRISB4 | ||||
TRISC | TRISC7 | TRISC6 | TRISC5 | TRISC4 | TRISC3 | TRISC2 | TRISC1 | TRISC0 |
レジスタ | BIT | 名 | 説明 | 1 | 0 | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
RCON | 7 | IPEN | 2レベルのインタラプト優先度 | 使用 | 不 | ||||||||||||||
INTCON | 7 | GIEH | 高優先度インタラプトを使用許可 | 許可 | 不 | ||||||||||||||
6 | GIEL | 低優先度インタラプトを使用許可 | 許可 | 不 | |||||||||||||||
PIR1 | 3 | SSPIF | SSP フラグ | 発生 | 待ち | ||||||||||||||
PIE1 | 3 | SSPIE | SSP インタラプトを使用許可 | 許可 | 不 | ||||||||||||||
IPR1 | 3 | SSPIP | SSP インタラプト優先度 | 高 | 低 | ||||||||||||||
SSPCON1 | 7 | WCOL | 送信時バス衝突 | 発生 | なし | ||||||||||||||
6 | SSPOV | 受信時SSPBUFオーバーラン | 発生 | なし | |||||||||||||||
5 | SSPEN | MSSPの動作許可 | 許可 | 不 | |||||||||||||||
4 | CKP | クロックのアイドル状態を指定 | High | Low | |||||||||||||||
3 | SSPM3 | SSPM ModeSelect Bits:: SPIマスターモード
|
|||||||||||||||||
2 | SSPM2 | ||||||||||||||||||
1 | SSPM1 | ||||||||||||||||||
0 | SSPM0 | ||||||||||||||||||
SSPSTAT | 7 | SMP | 入力データサンプル時期 | 最後 | 途中 | ||||||||||||||
6 | CKE | クロックエッジ指定(A:active I:idle) | A→I | I→A | |||||||||||||||
0 | BF | SSPBUF状況 (Read Only) | フル | 空 |
<SPIバスモードに対する設定ビット値> | ||||
---|---|---|---|---|
バスモード | CKP | CKE | アイドル状態 | 送信タイミング |
MODE_00 | 0 | 1 | Low | クロックの↓ |
MODE_01 | 0 | 0 | Low | クロックの↑ |
MODE_10 | 1 | 1 | Hight | クロックの↑ |
MODE_11 | 1 | 0 | Hight | クロックの↓ |
SPIのクロックとデータ変化タイミングの相対的関係とデータを送信していないアイドル時のクロックの状況からSPIには4種類のバスモードがあります。
これらバスモードは、接続するデバイスに適合するように、SSPCON1 CKP と SSPSTAT CKE の2Bitで指定します(右表)。
各バスモードのクロックとデータの関係をロジックアナライザーで観察した波形を以下に示します。
Microchip社の12bitsSPI DACMCP4822を使い正弦波を出力するプログラムを作成しました。このDACは、16BitのデータをSPIで受け取り、その上位4bitは、DACの制御に、下位12bitが変換するDACのデータに割り当てられています。
PICは16MHzの内部発振クロックで動作し、Timer2は定期的に16KHzでカウントアップします。そのたびに正弦波の波高データをrom定数(wavAx)から読み出しSPIモジュールを介してDACに送信しています。 波高データは16対で360度になるよう設定されているため、1KHzの正弦波がDACから出力されます。DAC出力をオシロスコープで見るときれいな正弦波になっています。
<回路図>
<プログラム>
// File name: SPI // Description: SPI 16bits example // 正弦波周波数は // クロック16MHzで 1KHz、 4MHzで250Hz // // RC6 SPI_CS // RC7 SPISDO // RB6 SPISCK // Language: MPLABX XC8 // Target: PIC18F14K50 #include <xc.h> #define SPI_CS LATCbits.LATC6 //-------------- コンフィグレーション -------------------------------- #pragma config MCLRE = OFF #pragma config PWRTEN = OFF, BOREN = OFF, BORV = 30 #pragma config WDTEN = OFF, WDTPS = 32768 #pragma config STVREN = ON #pragma config FOSC = IRC // 内部クロック #pragma config PLLEN = ON, CPUDIV = NOCLKDIV, USBDIV = OFF #pragma config FCMEN = OFF, IESO = OFF, HFOFST = OFF #pragma config LVP = OFF, XINST = OFF, BBSIZ = OFF #pragma config CP0 = OFF, CP1 = OFF, CPB = OFF, CPD = OFF #pragma config WRT0 = OFF, WRT1 = OFF #pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF #pragma config EBTR0 = OFF, EBTR1 = OFF, EBTRB = OFF // ChA 正弦波定数 const unsigned char wavA1[] = { 0x37,0x3B,0x3D,0x3F,0x3F,0x3F,0x3D,0x3B, 0x37,0x34,0x32,0x30,0x30,0x30,0x32,0x34}; const unsigned char wavA2[] = { 0xFF,0x0E,0xA6,0x62,0xFE,0x62,0xA6,0x0E, 0xFF,0xF0,0x58,0x9C,0x00,0x9C,0x58,0xF0}; void main(void){ unsigned char var,wcnt; OSCCON = 0b01110010; // 内部クロック16Mhz ANSEL = 0b00000000; // デジタル ANSELH = 0b00000000; LATC = 0; // PortCのすべてのビットを「0」 SPI_CS = 1; // SPI_CSを「1」にする TRISCbits.TRISC6 = 0; // SPI_CSを出力に設定 TRISCbits.TRISC7 = 0; // SPISDOを出力に設定 TRISBbits.TRISB6 = 0; // SPISCKを出力に設定 SSPSTAT = 0b11000000; // FOSC_4, MODE_00, SMPEND SSPCON1 = 0b00100000; T2CON = 0b00000100; // Timer2 PS_1/1 POST_1/1 PR2 = 249; // Timer2 PR設定 wcnt = 0; while(1){ // 繰り返しループ while(! PIR1bits.TMR2IF); // Timer2 のタイムアップを待つ PIR1bits.TMR2IF = 0; // Timer2 フラッグをクリア SPI_CS = 0; // SPI_CSを「0」にする SSPBUF = wavA1[wcnt]; // MSBデータの送信を開始 while(!SSPSTATbits.BF); // 送信完了を待つ var = SSPBUF; // BFをリセットするため空読み SSPBUF = wavA2[wcnt]; // LSBデータの送信を開始 while(!SSPSTATbits.BF); // 送信完了を待つ var = SSPBUF; // BFをリセットするため空読み SPI_CS = 1; // SPI_CSを「1」にする if(++wcnt > 15)wcnt = 0; // 16回でリセットする } // ここまで繰り返し }