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回でリセットする
} // ここまで繰り返し
}