---
---
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)