PIC18のI2C以降


8

プロジェクトでは、3つのPIC(2つのスレーブPIC18F4620、1つのマスターPIC18F46K22)でI2Cバスを介して通信したいと考えています。後で、さらに多くのスレーブが追加される可能性があります(EEPROM、SRAMなど)。C18コンパイラを使用して、これらのPICのコードをCで記述しています。私はインターネットをよく見て回りましたが、(M)SSP周辺機器を処理するためのライブラリーが見つかりませんでした。I2Cモードの(M)SSPペリフェラルの両方のPICのデータシートを読みましたが、バスのインターフェース方法を見つけることができませんでした。

だから私はマスタースレーブのライブラリが必要です。

何がお勧めですか?どこかにそのような図書館はありますか?コンパイラーに組み込まれていますか?はい、組み込まれていますか?ネット上のどこかに良いチュートリアルがありますか?


2
私は数か月前に同様の問題を抱えていました。あなたはそれらについてここで読むことができます。ここに、I ^ 2Cで動作するC18のライブラリがありますが、欠けている大きな点が1つあります。適切なレジスタに書き込むことにより、バス速度を手動で設定する必要があり、ライブラリのドキュメントのどこにも記載されていません。
AndrejaKo 2013年

ありがとう、それは役に立ちました!ただし、スレーブ部分ではなく、マスター部分のみを実行しました。

ええ、私は当時スレーブで作業する必要がなかったので、スレーブの例はありません。ごめんなさい。
AndrejaKo 2013年

2
いいえ、それは大丈夫です、それはマスターパートに役立ちました!:-)

ポートANSELC = 0のアナログも無効にしてください。

回答:


22

マイクロチップはこれについてのアプリケーションノートを書きました:

  • I2Cスレーブの実装に関するAN734
  • I2Cマスターの実装に関するAN735
  • 環境監視用のネットワークプロトコルの設定に関するより理論的なAN736もありますが、このプロジェクトでは必要ありません。

アプリケーションノートはASMで動作していますが、Cに簡単に移植できます。

マイクロチップ社の無料のC18およびXC8コンパイラには、I2C関数があります。これらの詳細については、コンパイラライブラリのドキュメントのセクション2.4をご覧ください。ここにいくつかのクイックスタート情報があります:

セットアップ

すでにマイクロチップ社のC18またはXC8コンパイラを使用しています。どちらにもI2C関数が組み込まれています。それらを使用するには、以下を含める必要がありますi2c.h

#include i2c.h

ソースコードを見たい場合は、ここで見つけることができます:

  • C18ヘッダー: installation_path/vx.xx/h/i2c.h
  • C18ソース: installation_path/vx.xx/src/pmc_common/i2c/
  • XC8ヘッダー: installation_path/vx.xx/include/plib/i2c.h
  • XC8ソース: installation_path/vx.xx/sources/pic18/plib/i2c/

ドキュメントで/i2c/は、関数が配置されているフォルダー内のどのファイルにあるかを確認できます。

接続を開く

マイクロチップ社のMSSPモジュールに精通している場合は、最初に初期化する必要があることがわかります。OpenI2C関数を使用して、MSSPポートでI2C接続を開くことができます。これは、次のように定義されています。

void OpenI2C (unsigned char sync_mode, unsigned char slew);

を使用sync_modeすると、デバイスがマスターであるかスレーブであるかを選択でき、スレーブである場合は、10ビットアドレスと7ビットアドレスのどちらを使用するかを選択できます。ほとんどの場合、特に小さなアプリケーションでは7ビットが使用されます。オプションは次のsync_modeとおりです。

  • SLAVE_7 -スレーブモード、7ビットアドレス
  • SLAVE_10 -スレーブモード、10ビットアドレス
  • MASTER -マスターモード

ではslew、デバイスでスルーレートを使用するかどうかを選択できます。ここにあるものの詳細:I2Cのスルーレートとは何ですか?

2つのMSSPモジュール

PIC18F46K22のように、2つのMSSPモジュールを備えたデバイスには特別なものがあります。これらには、モジュール1用とモジュール2用の2つの関数セットがあります。たとえば、の代わりにおよびOpenI2C()OpenI2C1()ありopenI2C2()ます。

さて、あなたはそれをすべてセットアップし、接続を開きました。次に、いくつかの例を見てみましょう。

マスター書き込み例

I2Cプロトコルに精通している場合、一般的なマスター書き込みシーケンスは次のようになります。

Master : START | ADDR+W |     | DATA |     | DATA |     | ... | DATA |     | STOP
Slave  :       |        | ACK |      | ACK |      | ACK | ... |      | ACK |

最初に、START条件を送信します。これを電話を取り上げることを検討してください。次に、アドレスを書き込みビットで-番号をダイヤルします。この時点で、送信されたアドレスを持つスレーブは、自分が呼び出されていることを認識しています。彼は謝辞( "Hello")を送信します。これで、マスターデバイスはデータを送信できます-彼は話し始めます。彼は任意の量のバイトを送信します。各バイトの後、スレーブは受信したデータにACKを送信する必要があります(「はい、あなたの言う通りです」)。マスターデバイスが通信を終了すると、STOP状態でハングアップします。

Cでは、マスター書き込みシーケンスはマスターに対して次のようになります。

IdleI2C();                         // Wait until the bus is idle
StartI2C();                        // Send START condition
IdleI2C();                         // Wait for the end of the START condition
WriteI2C( slave_address & 0xfe );  // Send address with R/W cleared for write
IdleI2C();                         // Wait for ACK
WriteI2C( data[0] );               // Write first byte of data
IdleI2C();                         // Wait for ACK
// ...
WriteI2C( data[n] );               // Write nth byte of data
IdleI2C();                         // Wait for ACK
StopI2C();                         // Hang up, send STOP condition

マスター読み取りの例

マスターの読み取りシーケンスは、書き込みシーケンスとは少し異なります。

Master : START | ADDR+R |     |      | ACK |      | ACK | ... |      | NACK | STOP
Slave  :       |        | ACK | DATA |     | DATA |     | ... | DATA |      |

この場合も、マスターが通話を開始し、番号をダイヤルします。しかし、彼は今、情報を入手したいと考えています。スレーブは最初に呼び出しに応答し、次に会話を開始します(データを送信します)。マスターは、十分な情報が得られるまで、すべてのバイトを確認します。次に、Not-ACKを送信し、STOP条件で電話を切ります。

Cでは、マスターパーツの場合は次のようになります。

IdleI2C();                         // Wait until the bus is idle
StartI2C();                        // Send START condition
IdleI2C();                         // Wait for the end of the START condition
WriteI2C( slave_address | 0x01 );  // Send address with R/W set for read
IdleI2C();                         // Wait for ACK
data[0] = ReadI2C();               // Read first byte of data
AckI2C();                          // Send ACK
// ...
data[n] = ReadI2C();               // Read nth byte of data
NotAckI2C();                       // Send NACK
StopI2C();                         // Hang up, send STOP condition

スレーブコード

スレーブの場合は、割り込みサービスルーチンまたはISRを使用するのが最適です。マイクロコントローラーをセットアップして、アドレスが呼び出されたときに割り込みを受け取ることができます。そうすれば、バスを常にチェックする必要がなくなります。

まず、割り込みの基本を設定しましょう。割り込みを有効にし、ISRを追加する必要があります。PIC18には、高と低の2つのレベルの割り込みがあることが重要です。I2C呼び出しに応答することが非常に重要であるため、I2Cを高優先度割り込みとして設定します。私たちがやろうとしていることは次のとおりです:

  • 割り込みが(別の割り込みではなく)SSP割り込みである場合のSSP ISRの書き込み
  • 割り込みの優先度が高い場合に備えて、一般的な優先度の高いISRを記述します。この関数は、発生した割り込みの種類を確認し、適切なサブISR(たとえば、SSP ISR)を呼び出す必要があります。
  • GOTO高優先度割り込みベクターの汎用ISRに命令を追加します。一般的なISRは多くの場合大きすぎるため、ベクターに直接配置することはできません。

次にコード例を示します。

// Function prototypes for the high priority ISRs
void highPriorityISR(void);

// Function prototype for the SSP ISR
void SSPISR(void);

// This is the code for at the high priority vector
#pragma code high_vector=0x08
void interrupt_at_high_vector(void) { _asm GOTO highPriorityISR _endasm }
#pragma code

// The actual high priority ISR
#pragma interrupt highPriorityISR
void highPriorityISR() {
    if (PIR1bits.SSPIF) {        // Check for SSP interrupt
        SSPISR();            // It is an SSP interrupt, call the SSP ISR
        PIR1bits.SSPIF = 0;  // Clear the interrupt flag
    }
    return;
}

// This is the actual SSP ISR
void SSPISR(void) {
    // We'll add code later on
}

次に行うことは、チップの初期化時に優先度の高い割り込みを有効にすることです。これは、いくつかの簡単なレジスタ操作によって実行できます。

RCONbits.IPEN = 1;          // Enable interrupt priorities
INTCON &= 0x3f;             // Globally enable interrupts
PIE1bits.SSPIE = 1;         // Enable SSP interrupt
IPR1bits.SSPIP = 1;         // Set SSP interrupt priority to high

これで、割り込みが機能しました。これを実装している場合は、今すぐ確認します。SSPISR()SSP割り込みが発生したときにLEDの点滅を開始する基本を記述します。

さて、あなたはあなたの割り込みを機能させました。次に、SSPISR()関数の実際のコードを記述します。しかし、最初にいくつかの理論。5つの異なるI2C割り込みタイプを区別します。

  1. マスター書き込み、最後のバイトはアドレス
  2. マスター書き込み、最後のバイトはデータでした
  3. マスター読み取り、最後のバイトはアドレスでした
  4. マスター読み取り、最後のバイトはデータでした
  5. NACK:送信終了

SSPSTATレジスタのビットを確認することで、現在の状態を確認できます。このレジスタは、I2Cモードでは次のようになります(未使用または無関係なビットは省略されます)。

  • ビット5:D / NOT A:データ/非アドレス:最後のバイトがデータの場合はセット、最後のバイトがアドレスの場合はクリア
  • ビット4:P:ストップビット:STOP条件が最後に発生した場合に設定されます(アクティブな操作がない場合)。
  • ビット3:S:開始ビット:START条件が最後に発生した場合に設定されます(アクティブな操作がある場合)。
  • ビット2:R / NOT W:読み取り/書き込みなし:操作がマスター読み取りの場合はセット、操作がマスター書き込みの場合はクリア
  • ビット0:BF:バッファフル:SSPBUFFレジスタにデータがある場合はセット、そうでない場合はクリア

このデータを使用すると、I2Cモジュールの状態を確認する方法を簡単に確認できます。

State | Operation | Last byte | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 0
------+-----------+-----------+-------+-------+-------+-------+-------
1     | M write   | address   |   0   |   0   |   1   |   0   |   1
2     | M write   | data      |   1   |   0   |   1   |   0   |   1
3     | M read    | address   |   0   |   0   |   1   |   1   |   0
4     | M read    | data      |   1   |   0   |   1   |   1   |   0
5     | none      | -         |   ?   |   ?   |   ?   |   ?   |   ?

ソフトウェアでは、デフォルトとして状態5を使用するのが最適です。これは、他の状態の要件が満たされていない場合に想定されます。この方法では、スレーブがNACKに応答しないため、何が起こっているのかわからない場合は応答しません。

とにかく、コードを見てみましょう:

void SSPISR(void) {
    unsigned char temp, data;

    temp = SSPSTAT & 0x2d;
    if ((temp ^ 0x09) == 0x00) {            // 1: write operation, last byte was address
        data = ReadI2C();
        // Do something with data, or just return
    } else if ((temp ^ 0x29) == 0x00) {     // 2: write operation, last byte was data
        data = ReadI2C();
        // Do something with data, or just return
    } else if ((temp ^ 0x0c) == 0x00) {     // 3: read operation, last byte was address
        // Do something, then write something to I2C
        WriteI2C(0x00);
    } else if ((temp ^ 0x2c) == 0x00) {     // 4: read operation, last byte was data
        // Do something, then write something to I2C
        WriteI2C(0x00);
    } else {                                // 5: slave logic reset by NACK from master
        // Don't do anything, clear a buffer, reset, whatever
    }
}

どのような割り込みタイプがあるかを確認するために、ビットマスクを使用してSSPSTATレジスタを確認する方法(最初にANDを0x2dとって、有用なビットのみが表示されるようにする)を確認できます。

割り込みに応答するときに送信または実行する必要があることを見つけるのはあなたの仕事です。それはアプリケーションによって異なります。

参考文献

繰り返しますが、マイクロチップがI2Cについて書いたアプリケーションノートについても触れておきます。

  • I2Cスレーブの実装に関するAN734
  • I2Cマスターの実装に関するAN735
  • 環境監視用のネットワークプロトコルの設定に関するAN736

コンパイラライブラリのドキュメントがあります:コンパイラライブラリドキュメント

自分で設定する場合は、(M)SSPセクションのチップのデータシートでI2C通信を確認してください。マスターパーツにはPIC18F46K22、スレーブパーツにはPIC18F4620を使用しました。


3

まず、XC8コンパイラーが最新のものであるという理由だけで変更することをお勧めします。利用可能な周辺機器ライブラリがありますが、私はそれらをあまり使用していません。詳細とドキュメントについては、マイクロチップのWebサイトを確認してください。

さて、私はPIC16Fと古いMicrohipミッドレンジコンパイラ(Hi-Techコンパイラであった可能性があります)でずっと以前に使用したI2C eeprom通信用の非常に古い基本ルーチンをいくつか持っていますが、うまく機能すると思いますPIC18では、周辺機器は同じだと思うので。とにかく、それがすべて異なっていれば、すぐにわかるでしょう。
それらは温度ロガープロジェクトで使用されるより大きなファイルの一部だったので、すぐに他のすべての関連しない機能を取り除き、以下のファイルとして保存しました。そのため、少し混乱した可能性がありますが、何が必要かについていくつかのアイデアを得ることができます(それはうまくいくかもしれませんが、あなたは決して知りません;-))

メインヘッダーファイルが正しいことを確認し、ピンをチェック/変更して、それらが正しいI2Cペリフェラルピンであることを確認し、レジスタ名がHi-Techコンパイラのものである場合はレジスタ名が少し異なることを確認する必要があります。レジスタの命名規則について。

I2C.cファイル:

#include "I2C.h"
#include "delay.h"
#include <pic.h>

#define _XTAL_FREQ 20000000


void write_ext_eeprom(unsigned int address, unsigned char data)
 {
    unsigned char a0 = ((address & 0x8000) >> 14);  
    unsigned char msb = (address >> 8);
    unsigned char lsb = (address & 0x00FF);


   i2c_start();
   i2c_write(0xa0 | a0);
   i2c_write(msb);
   i2c_write(lsb);
   i2c_write(data);
   i2c_stop();
   DelayMs(11);
}

/******************************************************************************************/

unsigned char read_ext_eeprom(unsigned int address)
{
   unsigned char a0 = ((address & 0x8000) >> 14);  
   unsigned char data;
   unsigned char msb = (address >> 8);
   unsigned char lsb = (address & 0x00FF);

   i2c_start();
   i2c_write(0xa0 | a0);
   i2c_write(msb);
   i2c_write(lsb);
   i2c_repStart();
   i2c_write(0xa1 | a0);
   data=i2c_read(0);
   i2c_stop();
   return(data);
}

void i2c_init()
{
 TRISC3=1;           // set SCL and SDA pins as inputs
 TRISC4=1;

 SSPCON = 0x38;      // set I2C master mode
 SSPCON2 = 0x00;

 //SSPADD = 9;          // 500kHz bus with 20MHz xtal 
 SSPADD = 49;           // 100kHz bus with 20Mhz xtal

 CKE=0;     // use I2C levels      worked also with '0'
 SMP=1;     // disable slew rate control  worked also with '0'

 PSPIF=0;      // clear SSPIF interrupt flag
 BCLIF=0;      // clear bus collision flag
}

/******************************************************************************************/

void i2c_waitForIdle()
{
 while (( SSPCON2 & 0x1F ) | RW ) {}; // wait for idle and not writing
}

/******************************************************************************************/

void i2c_start()
{
 i2c_waitForIdle();
 SEN=1;
}

/******************************************************************************************/

void i2c_repStart()
{
 i2c_waitForIdle();
 RSEN=1;
}

/******************************************************************************************/

void i2c_stop()
{
 i2c_waitForIdle();
 PEN=1;
}

/******************************************************************************************/

int i2c_read( unsigned char ack )
{
 unsigned char i2cReadData;

 i2c_waitForIdle();

 RCEN=1;

 i2c_waitForIdle();

 i2cReadData = SSPBUF;

 i2c_waitForIdle();

 if ( ack )
  {
  ACKDT=0;
  }
 else
  {
  ACKDT=1;
  }
  ACKEN=1;               // send acknowledge sequence

 return( i2cReadData );
}

/******************************************************************************************/

unsigned char i2c_write( unsigned char i2cWriteData )
{
 i2c_waitForIdle();
 SSPBUF = i2cWriteData;
//if(ACKSTAT)
{
//while(ACKSTAT);
}
 return ( ! ACKSTAT  ); // function returns '1' if transmission is acknowledged
}

I2C.hヘッダーファイル:

extern void i2c_init();
extern void i2c_waitForIdle();
extern void i2c_start();
extern void i2c_repStart();
extern void i2c_stop();
extern int i2c_read( unsigned char ack );
extern unsigned char i2c_write( unsigned char i2cWriteData );

おかげで、それはマスターパートであり、実際にPIC18とおそらく同じです。また、コンパイラノートにも感謝します:-)少し尋ねると、いくつかのアプリケーションノートが得られました。

ボーレートジェネレータの設定に関するセクションを追加することを検討してください。通常、コードはSSP1ADD = ((_XTAL_FREQ/100000)/4)-1;1KHzなどのように見えます
Jesse Craig

1

XC8およびXC16コンパイラには、I2C用のライブラリが含まれています。

私が遭遇した問題は、ドキュメントがあまり良くないということです!マイクロチップ社のドキュメントの例を使用すると、運が悪くなります。マイクロチップ社のサポートでさえ、あなたを助けることはできません。私は自分で行ったことがある。

しばらく前にPIC24EP512GPシリーズマイクロコントローラーを操作しましたが、Microchipのドキュメントに記載されているように、ライブラリーは機能しませんでした。


ああ、それは残念です!それであなたは何をしたのですか?

残念ながら自分で即興。
Chetan Bhargava、2013年

1
他の人にも役立ちますか?見たい!
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.