屋外ディスプレイは,LEDが配置されている表示ユニット36個と,表示ユニットを制御する統轄ユニット1個で成り立っています.表示ユニットにはこれまでよく使っているPIC16F1936を使ってしまいましたが,多くのパターンを記憶させるためにRAMの大きなPIC16F1716に変えました.基本的に同じようなピンアサインをであることもあり,マイコンノ置き換えにはそれほど問題はないだろうと考えていたのですが,I2Cで結構ハマってしまいました.その一番の原因はPICのPPSでした.ここではPPSについて分かったことを書いておきます.

PIC16F1716には,ピンに機能を可変に割り当てることのできるPPS(Peripheral Pin Select)が備わっています.古いPICでは,各ピンの機能は固定されており,たとえばI2Cであれば決まったピンからSCLとSDAが割り振られていて,プリント基板の設計をするとき引き回すのが少し大変でした.一方,PPSが付いていますと好きなピンに機能を割り当てることができるのです.

正直に言って,PIC16F1716にこの機能が備わっていることを知らずにいました.このため,PIC16F1936よりROMサイズが大きくなったのがPIC16F1716程度の認識しかありませんでした.まずは,既に作成していたPIC16F1936のためのI2C通信プログラムの一部を変更したものを示します.このプログラムでは正しく動きません.

/* I2Cの7ビットアドレス */
#define I2C_ADDRESS (0x11)

/* I2Cの初期化 */
void IIC_initialize() {
    /* デジタルピンとする */
    ANSC3 = 0;
    ANSC4 = 0;

    /* SCLを入力端子とする */
    TRISC3 = 1;
    /* SDAを入力端子とする */
    TRISC4 = 1;
    
    /* クロックストレッチを行わない */
    SEN = 0;

    /* 標準速度モード */
    SMP = 1;
    
    /* I2Cアドレスを設定する */
    SSPADD = (I2C_ADDRESS << 1);

    /* アドレスマスクを設定する */
    SSPMSK = 0xFE;

    /* I2Cに関する状態を初期化する */
    SSPSTAT = 0x00;

    /* 書き込み時のコリジョンをクリアする  */
    WCOL = 0;
    /* オーバフローを示すフラグをクリアする */
    SSPOV = 0;
    /* クロックストレッチをリリースする */
    CKP = 1;
    /* I2Cをスレーブモード,7ビットアドレス */
    SSPM3 = 0;
    SSPM2 = 1;
    SSPM1 = 1;
    SSPM0 = 0;

    /* SDAホールド時間のを300[ns]とする */
    SDAHT = 1;

    /* アドレスホールドを禁止する */
    AHEN = 0;
    /* データホールドを禁止する */
    DHEN = 0;

    /* データ受信時の割り込みを許可する */
    SSP1IE = 1;

    /* 受信データを初期化しておく */
    SSPBUF = 0x00;

    /* MSSPを使用する  */
    SSPEN =1;

}

PIC16F1716では,I2Cの割り当てられているRC3およびRC4はアナログ端子としても利用できるので,それらをデジタル端子にするために7,8行目でANSC3=0; ANSC4=0;としています.また,PIC16F1936と同様に11,13行目でSCLとSDA端子を入力端子としています.このようにプログラミングしてみましたが,正しく動作してくれませんでした.具体的には,アドレスを受信してくれるのですが,ACKを返しませんでした.

そこでいろいろと調べてたどり着いた結論がPPSでした.PIC16F1716のデータシートをよく読んでいくと,このマイコンにはPPSが搭載されており,ピンの機能を変えることができます.逆にいえば,PPSの設定してあげないと各端子の機能が正しく動作してくれないのです.

PPSの概念を示すブロック図を下に示します.入力と出力で変更するレジスタが異なっており,図の左側にあるabsPPSが入力端子に関するレジスタ,右側にあるPA0PPSが出力に関するレジスタを表します.たとえば調歩同期通信のRX端子をRC3から入力させたい場合,入力端子ですからRXPPSレジスタを0b10011と変更すればよく,TX端子をRB3に設定したい場合,出力端子ですからRB3PPSレジスタを0b10100と変更すればよいのです.なお,レジスタに設定する値はすべてデータシート134~135ページに記載されています.

名称未設定 1
(出典: PIC16(L)F1713/6 データシート)

今回,I2Cの端子であるSCLとSDAは,入出力両方あり得ますので,それぞれ2個のレジスタを変更しなければなりません.以上のことから,下に示すプログラムになりました.なお,PPS関連のレジスタを変更するときには,必ず割り込み停止とPPSLOCKEDを解除しておく必要があることに留意して下さい.

/* I2Cの初期化 */
void IIC_initialize() {
    /* デジタルピンとする */
    ANSC3 = 0;
    ANSC4 = 0;

    /* 割り込みをできないようにする */
    GIE = 0;

    /* PPSのロックを解除する */
    PPSLOCKED = 0;

    /* RC3をSCLにする */
    SSPCLKPPS = 0b10011;
    /* RC3をSCLにする */
    RC3PPS = 0b10000;

    /* RC4をSDAにする */
    SSPDATPPS = 0b10100;
    /* RC4をSDAにする */
    RC4PPS = 0b10001;

    /* SSPのロックを設定する */
    PPSLOCKED = 1;

    /* 割り込みを許可する */
    GIE = 1;

    /* SCLを入力端子とする */
    TRISC3 = 1;
    /* SDAを入力端子とする */
    TRISC4 = 1;

    /* (以下略) */

}