---
---
18F14K50のMaster Synchronous Serial Port (MSSP) を使うと周辺機器と簡単にシリアル通信を行うことができます。MSSPは、SPIとI2Cという2種類の方式をサポートしていまが、ここでは、I2Cを利用したEEPROMとのインターフェースだけを取り上げます。このため、I2C マスターモードが対象です。
高低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 |
| PIR2 | OSCFIF | C1IF | C2IF | EEIF | BCLIF | USBIF | TMR3IF | CCP2IF |
| PIE2 | OSCFIE | C1IE | C2IE | EEIE | BCLIE | USBIE | TMR3IE | CCP2IE |
| IPR2 | OSCFIP | C1IP | C2IP | EEIP | BCLIP | USBIP | TMR3IP | CCP2IP |
| SSPCON1 | WCOL | SSPOV | SSPEN | CKP | SSPM<3:0> | |||
| SSPCON2 | GCEN | ACKSTAT | ACKDT | ACKEN | RCEN | PEN | RSEN | SEN |
| SSPCON3 | ACKTIM | PCIE | SCIE | BOEN | SDAHT | SBCDE | AHEN | DHEN |
| 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 | ||||
| レジスタ | BIT | 名 | 説明 | 1 | 0 |
|---|---|---|---|---|---|
| RCON | 7 | IPEN | 2レベルのインタラプト優先度 | 使用 | 不 |
| INTCON | 7 | GIEH | 高優先度インタラプトを使用許可 | 許可 | 不 |
| 6 | GIEL | 低優先度インタラプトを使用許可 | 許可 | 不 | |
| PIR1 | 3 | SSPIF | SSP フラグ | 発生 | 待ち |
| PIE1 | 3 | SSPIE | SSP インタラプトを使用許可 | 許可 | 不 |
| IPR1 | 3 | SSPIP | SSP インタラプト優先度 | 高 | 低 |
| PIR2 | 3 | BCLIF | バス衝突 フラグ | 発生 | 待ち |
| PIE2 | 3 | BCLIE | バス衝突 インタラプトを使用許可 | 許可 | 不 |
| IPR2 | 3 | BCLIP | バス衝突 インタラプト優先度 | 高 | 低 |
| SSPCON1 | 7 | WCOL | 送信時バス衝突 | 発生 | なし |
| 6 | SSPOV | 受信時SSPBUFオーバーラン | 発生 | なし | |
| 5 | SSPEN | MSSPの動作許可 | 許可 | 不 | |
| 4 | CKP | 同期 | 非同期 | ||
| 3 | SSPM3 | SSPM ModeSelect Bits:: I2C マスターモードのときは、 SSPM <3 : 0> = 1000 をセットする。 |
|||
| 2 | SSPM2 | ||||
| 1 | SSPM1 | ||||
| 0 | SSPM0 | ||||
| SSPCON2 | 7 | GCEN | General Cell Add (Slaveのみ) | ||
| 6 | ACKSTAT | ACKを受信 | 未 | 受信 | |
| 5 | ACKDT | NACK/ACK指示 | NACK | ACK | |
| 4 | ACKEN | ACKDTを送信開始 | 開始 | 待ち | |
| 3 | RCEN | 受信開始 | 開始 | 待ち | |
| 2 | PEN | STOP シーケンス開始 | 開始 | 待ち | |
| 1 | PSEN | RepeatSTART シーケンス開始 | 開始 | 待ち | |
| 0 | SEN | START シーケンス開始 | 開始 | 待ち | |
| SSPSTAT | 7 | SMP | バス速度指定 | 100k | 400k |
| 6 | CKE | クロックエッジ指定 (SPIモードのみ) | |||
| 5 | D/A | Data/Address 表示 (Slaveモードのみ) | |||
| 4 | P | STOP ビット状況 (Read Only) | 検出 | 未 | |
| 3 | S | START ビット状況 (Read Only) | 検出 | 未 | |
| 2 | R/W | 送信状況 (Read Only) | 送信中 | 待ち | |
| 1 | US | Update Address (Slaveモードのみ) | |||
| 0 | BF | SSPBUF状況 (Read Only) | フル | 空 | |
| I2C クロック周波数 | Fosc | ||
|---|---|---|---|
| 48MHz | 16MHz | 4MHz | |
| 400 KHz | 0x1D | 0x18 | --- |
| 100 KHz | 0x77 | 0x63 | 0x09 |
I2Cクロック周波数と、SSPADDへの設定値
64KbitsEEPROMメモリ24LC64に読み書きするプログラムを作成しました。読み出したデータの表示や書込みデータ値を指示するのにUARTの入出力を利用しています。PICkit2をUARTツールとして接続し制御している画面を示します。
<使用できるコマンド例>
<回路図>
<プログラム>
/***************************************************************
* I2C シリアルEEPROM(24LC64)をUART経由で読書テスト
* コマンド例 R0000\r\n :0000番地から64番地分を16進表示
* R\r\n :現番地から64番地分を16進表示
* W00A0 33\r\n:00A0番地に33を書込み
*
* OSC 内部クロック 4MHz Language: MPLABX XC8
* Target: PIC18F14K50
* Copyright (c) 2014 iwamoto All Rights Reserved
***************************************************************/
#include <xc.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#define _XTAL_FREQ 4000000 // XTAL_FREQ は4MHz
// -------------- I2C関連定義 --------------
// 各シーケンスの終了を待って復帰
// I2C特殊 condition
#define i2c_Idle() while ((SSPCON2 & 0x1F) | (SSPSTATbits.R_W))
#define i2c_Start() SSPCON2bits.SEN=1;while(SSPCON2bits.SEN)
#define i2c_Stop() SSPCON2bits.PEN=1;while(SSPCON2bits.PEN)
#define i2c_Restart() SSPCON2bits.RSEN=1;while(SSPCON2bits.RSEN)
#define i2c_Ack() SSPCON2bits.ACKDT=0;SSPCON2bits.ACKEN=1;while(SSPCON2bits.ACKEN)
#define i2c_NAck() SSPCON2bits.ACKDT=1;SSPCON2bits.ACKEN=1;while(SSPCON2bits.ACKEN)
#define BuffMax 20 // UART Rx暫定バッファMAX容量
// -------------- プロトタイプ --------------------------
char i2c_Write(char data);
char i2c_Read( void );
char i2c_EEPRomRead( int addr );
void i2c_EEPRomWrite( int addr, char data);
void ReadMem(void); // 「R」Cmdの処理
void WriteMem(void); // 「W」Cmdの処理
//-------------- コンフィグレーション ------------------------
#pragma config FOSC = IRC // 内部クロック
#pragma config USBDIV = OFF, CPUDIV = NOCLKDIV
#pragma config IESO = OFF, FCMEN = OFF, PLLEN = ON
#pragma config BORV = 30, BOREN = OFF, PWRTEN = OFF
#pragma config WDTPS = 32768, WDTEN = OFF
#pragma config MCLRE = OFF, HFOFST = OFF, XINST = OFF
#pragma config BBSIZ = OFF, LVP = OFF, STVREN = ON
#pragma config CP1 = OFF, CP0 = OFF, CPD = OFF, CPB = OFF
#pragma config WRT1 = OFF, WRT0 = OFF, WRTB = OFF, WRTC = OFF
#pragma config EBTR1 = OFF, EBTR0 = OFF, EBTRB = OFF
//
// -------------- 共通変数 定数 -----------------------------
int memadd = 0; // EEPROM読み書きアドレス
char TempBuf[BuffMax]; // UART Rx暫定バッファ
char PtrBuf = 0; // UART Rx読込みポインタ
char RxBuf[BuffMax]; // UART Rx受信バッファ
char RxSize = 0; // UART Rx受信文字数
//----------------------高優先割込み------------------------
void interrupt Hi_Isr(void){
char RxData;
if(PIR1bits.RCIF){ // ------- UART (PC)からの受信
if(RCSTA1bits.OERR){ // Overrunエラーなら
RCSTA1bits.CREN=0; // エラークリア
RCSTA1bits.CREN=1; // 再度UARTをON
}else{
RxData = RCREG; // 割込フラッグクリア
TempBuf[PtrBuf++] = RxData; // データ格納
if(RxData == 0x0A){ // コマンド終了か
memcpy(RxBuf, TempBuf, PtrBuf); // バッファコピー
RxSize = PtrBuf; // 受信バイト数保存
PtrBuf = 0; // ポインタリセット
}
if(PtrBuf >= BuffMax) PtrBuf = 0; // 容量超えはすべて廃棄
}
} // UART RxIF}
}
//****** メイン関数 ************************
void main(void){
OSCCON = 0b01010010; // 内部クロック4Mhz
PORTA = 0x00;
PORTB = 0x00;
PORTC = 0x00;
TRISA = 0b11111111; //PortA すべて入力
TRISB = 0b11111111; //PortB すべて入力
TRISC = 0b11111111; //PortC すべて入力
ANSEL = 0b00000000; //すべてデジタル
ANSELH = 0b00000000;
// I2Cモジュールを初期化 ---------------------------------------------
SSPCON1 = 0b00001000; // I2C Master modeにする
SSPCON2 = 0x00; // PowerOn初期値にする
SSPSTAT = 0b10000000; // スルーレート制御はOff @100k
SSPADD = 9; // クロックの設定 100k@4MHz
SSPCON1bits.SSPEN = 1; // SSP 有効にする
// UARTを初期化 --------------------------------------------------
RCSTA = 0b10010000; // UART送受信を有効
TXSTA = 0b00100100; // 8Bit非同期送受信
BAUDCON = 0b00001000; // HI-16Bitボーレート
SPBRGH = 0; // 9600
SPBRG = 103;
IPR1bits.RCIP = 1; // 受信高優先割込
PIE1bits.RCIE = 1; // 受信割込み許可
RCONbits.IPEN=1; // 2段階の割込みに設定
INTCONbits.GIEH=1; // 高優先割込みを許可
INTCONbits.GIEL=0; // 低優先割込みを禁止
__delay_ms(100); // システム安定までの時間
// ---------------- 電源ONの開始メッセージ --------------------------
printf("R[0000] -- Read memory : W[0000],[00] -- Write memory\r\n");
while(1){
if(RxSize){ // コマンドを受信したか
switch(RxBuf[0]){ // 文字をテストする
case 'R': //「R」Cmdの処理
ReadMem(); //
break; //
case 'W': //「W」Cmdの処理
WriteMem(); //
break; //
default: // 上記 Cmd以外は、
break; // 受信文字を廃棄
}
RxSize=0;
}
}
}
/********************** ReadMem **************************
* [R]コマンドの直後の文字を検査し、
* Hex文字なら、その文字列をEEPROMのアドレスとする
* Hex文字でないなら、記録してあるアドレスを使用する
* 64アドレス分4行表示したら、記録アドレスを更新し、終了
**********************************************************/
void ReadMem(void){
char i,mdata,data;
if(isxdigit(RxBuf[1])){ //「R」直後がHex文字なら
memadd = strtol(RxBuf+1, NULL, 16); // 新しいメモリアドレスとする
}
for(i=0;i<4;i++){ // 16番地を1行に、4行分表示
printf(" %04X",memadd); // 番地を表示
for(mdata=0;mdata<16;mdata++){ // 16番地分を横方向に順に表示
data = i2c_EEPRomRead(memadd + mdata); // メモリ内容の読出
printf(" %02X",data); // データ間のスペース
}
printf("\r\n"); // 1行終了、復帰改行
memadd += 16;
}
}
/********************** WriteMem **************************
* [W]コマンドの直後の文字列をEEPROMのアドレスとする
* アドレスとスペースで区切られた次のHex文字列を
* 書込みデータとしEEPROMに書き込む
**********************************************************/
void WriteMem(void){
char data;
char * end;
memadd = strtol(RxBuf+1, &end, 16); //「W」直後からアドレス取得
data = strtol(end+1, NULL, 16); // 書込みデータ取得
i2c_EEPRomWrite(memadd, data); // EEPROMに書込み
}
/********************************************************
* i2c Library Master Mode Only
* Target PIC18F14K50
* 2014/09/27 initial issue
*********************************************************/
char i2c_Write(char data){
SSPBUF = data; // データセット
PIR1bits.SSPIF = 0; // 終了フラグクリア
while(!PIR1bits.SSPIF); // 送信終了待ち
return SSPCON2bits.ACKSTAT; // ACKなら「0」で復帰
}
char i2c_Read( void )
{
SSPCON2bits.RCEN = 1; // データ受信を指示
while ( !SSPSTATbits.BF ); // 8ビット受信の完了を待つ
return SSPBUF; // 受信データで復帰
}
/**************************************************************
* EEPROM(24LC64)から、1バイトを読み込む
* チップセレクトは、A0=1,A1=0,A2=0 として
* スレーブアドレスは、「A2」
**************************************************************/
char i2c_EEPRomRead( int addr ){
char data;
i2c_Idle(); // busアイドルの確認
i2c_Start(); // Start condition
i2c_Write(0xA2); // アドレス送信
i2c_Write(addr >> 8); // 制御コード送信
i2c_Write(addr & 0xFF); // データ送信
i2c_Restart(); // ReStart condition
i2c_Write(0xA3); // 制御コード送信
data=i2c_Read(); // データ送信
i2c_NAck();
i2c_Stop(); // Stop condition
return data;
}
/**************************************************************
* EEPROM(24LC64)に、1バイトを書き込む
* チップセレクトは、A0=1,A1=0,A2=0 として
* スレーブアドレスは、「A2」
**************************************************************/
void i2c_EEPRomWrite( int addr, char data ){
i2c_Idle(); // busアイドルの確認
i2c_Start(); // Start condition
i2c_Write(0xA2); // アドレス送信
i2c_Write(addr >> 8); // 制御コード送信
i2c_Write(addr & 0xFF); // データ送信
i2c_Write(data); // データ送信
i2c_Stop(); // Stop condition
}
/********************************************************
* printf を UART で使用するための指定
*********************************************************/
void putch(char data) {
while( ! TXIF) // 送信バッファが
continue; // 空になるのを待ち
TXREG = data; // 送信
}
*注意*
SCL SDAの2ピンはオープンドレインの回路にする必要があるため、TRISレジスタで入力モードに設定します。
SCL : RB6 (pin11)
SDA : RB4 (pin13)