#include <stdio.h>
#include <stdlib.h>
#include <p32xxxx.h>

typedef unsigned char byte;

#pragma config FNOSC = PRIPLL // 発信源 = 主外部発振器
#pragma config POSCMOD = XT // 主発振の方法　= 外部高精度発振器を使う
#pragma config FPLLIDIV = DIV_2 // PLL入力分数 = x1/2
#pragma config FPLLMUL = MUL_20 // PLL逓倍比 = x20
#pragma config FPLLODIV = DIV_2 // PLL出力分数 = x1/2
#pragma config FPBDIV = DIV_2 //周辺モジュールクロック倍数 : x1/2

// レジスタに登録するアドレスは7bitの数値を1bit左にずらしたものになる
#define MCP23017_ADDR (0x20 << 1)

void IdleI2C(void)
{
    //　スタート、待機、受信、ACK、送信レジスタがクリアされるまで待機する
    while(I2C2CONbits.SEN || I2C2CONbits.PEN || I2C2CONbits.RSEN || I2C2CONbits.RCEN || I2C2CONbits.ACKEN || I2C2STATbits.TRSTAT);
}

unsigned int WriteI2C(unsigned char data)
{
    I2C2TRN = data;
    
    if(I2C2STATbits.IWCOL) {
        // 通信の衝突が発生したなら0を返す
        return 0;  
    }else{
         while(I2C2STATbits.TBF);      // 送信バッファが空になるのを待機
        IdleI2C();
        return 1;
    }
}

void send(byte reg, byte val)
{
    // 開始フラグ
    I2C2CONbits.SEN = 1;
      
    IdleI2C();    
    
    WriteI2C(MCP23017_ADDR);  
    WriteI2C(reg);    
    WriteI2C(val);
    
    // 停止状態にして送信を完了
    I2C2CONbits.PEN = 1; 
    
    IdleI2C();

    
    /*
    // データ受信のプログラム例(※汎用例です)
    I2C2CONbits.SEN = 1;
    IdleI2C();
    WriteI2C(SLAVE_ADDR);
    WriteI2C(reg);    
    WriteI2C(val);

    // 信号を途切れさせず、連続してデータを送りたいときの例
    I2C2CONbits.RSEN = 1; // リピートスタート
    IdleI2C();
    WriteI2C(SLAVE_ADDR | 0x1);  // 0x1はスレーブから受信を促すために必要
    WriteI2C(val);
    
    // データ読み出し
    I2C2CONbits.RCEN = 1;     // 受信モードに移行
    while(I2C2CONbits.RCEN) ; // 受信が完了すると0になるのでそれまで待機
    I2C2STATbits.I2COV = 0;   // バッファあふれのエラーフラグをリセット
    res = I2C2RCV;            // 受信したデータを格納
     
    I2C2CONbits.PEN = 1; 
    IdleI2C1();
    */
}

int main()   
{   
    int i, toggle = 0xFF;
    ANSELB = 0;
    
    // B2,B3を入力モードにし、プルアップ抵抗を有効にする
    TRISBbits.TRISB2 = 1;
    TRISBbits.TRISB3 = 1;
    CNPUBbits.CNPUB2 = 1;
    CNPUBbits.CNPUB3 = 1;
    
    // 100kHzモード
    I2C2CONbits.DISSLW = 1;
    I2C2BRG = 0x0C5;
    
    // I2C2を有効にする
    I2C2CONbits.ON = 1;
    
    send(0x00, 0x00);  // PortAをすべて出力にする命令
   
    while(1){
        send(0x12, toggle);  // PortAのGPIOをすべてON/OFFにする命令
        toggle = ~toggle;
        for(i=0;i<1000000;i++);
    }
   
    return 0;   
}   