PIC18F14K50の基本動作から応用プログラムまでを学びます。

ホーム
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
    
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
MSSP I2C

18F14K50のMaster Synchronous Serial Port (MSSP) を使うと周辺機器と簡単にシリアル通信を行うことができます。MSSPは、SPIとI2Cという2種類の方式をサポートしていまが、ここでは、I2Cを利用したEEPROMとのインターフェースだけを取り上げます。このため、I2C マスターモードが対象です。

*注意*
SCL SDAの2ピンはオープンドレインの回路にする必要があるため、TRISレジスタで入力モードに設定します。
 SCL : RB6 (pin11) 
 SDA : RB4 (pin13)

高低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への設定値

I2Cのプログラム例

64KbitsEEPROMメモリ24LC64に読み書きするプログラムを作成しました。読み出したデータの表示や書込みデータ値を指示するのにUARTの入出力を利用しています。PICkit2をUARTツールとして接続し制御している画面を示します。

<使用できるコマンド例>

R \r \n
現番地から64番地分を16進表示
(電源ON直後は、0000番地)

W0000 22 \r \n
0000番地に22を書込み

R0000 \r \n
0000番地から64番地分を16進表示

<回路図>

<プログラム>

/***************************************************************
 *    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;       // 送信
}