第39回 PICマイコンのPPS機能

I2C通信プログラムを作成する前に、PICマイコンの「PPS機能」の使い方を説明します。

目次

PPS機能とは?

突然「PPS機能」なんて用語がでてきて、 I2C通信はどこいった?、という感じですよね。

今回製作したブレッドボードの回路を制御するには、I2Cプログラム作成の前にPPS機能の理解が必要になりますのでお付き合いください。


PICマイコンにはいろいろな機能モジュールが内蔵されていますよ。

例えば応用編シリーズでは、PWMやADコンバータなどのPICマイコンに内蔵されている機能を使ってタイマーを製作しました。

今回は、PIC16F18857に搭載されている「I2C通信モジュール」を使用してプログラムを作成していきます。

この「I2C通信モジュール」とは、複雑なI2C通信をハードウェアで実現してくれるものです。

ところで、このI2C通信モジュールは、PICマイコンのピンに接続されていて、クロック信号(SCL)とデータ信号(SDA)を制御します。

I2C通信モジュールのクロック信号とデータ信号は、デフォルトで特定のピンが割り当てられています。

例えば、PIC16F18857には2個のI2C通信モジュールが内蔵されていますが、デフォルトでは以下のようにピンが割り当てられています。

モジュール信号デフォルトピン
I2C通信モジュール1クロック信号RC3
データ信号RC4
I2C通信モジュール2クロック信号RB1
データ信号RB2

実は、このピンの割り当ては変更することができるようになっています

このピンの割り当てを変更する機能のことを「PPS機能」と呼んでいます。

PPS」は「Peripheral Pin Select」の略で、日本語では「機能のピン選択」という意味です。意味合いとしては「内蔵機能モジュールのピン割り当て機能」という感じです。


実践編ではI2C通信モジュール1を使用することにしますが、このモジュールのデフォルトの割り当てピンは、SCLがRC3、SDAがRC4となっています。

作成した回路図は、SCLがRC3、SDAがRC2に接続していますので、割り当てピンを変更する必要があります。

そこで、I2C通信プログラムの作成に入る前にPPSの使い方を確認します。

PPSの使い方概要

I2C通信モジュールを含めて、PICマイコン内部の機能モジュールは、次のように入力部と出力部を持っています。

機能モジュールのピンの割り当ては、以下のように1つの機能モジュールに対して入力部と出力部のピンの割り当てを行います。

上野図では任意のピンに接続できるように記載していますが、実際には機能モジュールによっては割り当てできないピンもありますので注意してください。詳しい内容はこの記事の後半で説明します。


次に、I2C通信モジュールを詳しく見ていきます。

PICマイコンのI2C機能モジュールは、一つの機能モジュールではなく、I2C通信の「SCL(クロック信号)のモジュール」と「SCL(データ信号)のモジュール」の2つの機能モジュールから構成されています。

まとめると、次のようにSDAモジュールとSDAモジュールのそれぞれに対して、入力ピンと出力ピンを割り当てます。(なんだかややこしいですね…)


PPS機能のプログラム

これから、PPS機能を使って上の図のようにI2C通信モジュール1のピン割り当てを行います。

最初にPPSの設定の流れを確認しておきます。

PPSのピン割り当て機能はデフォルトではロックされています。

そのため、以下のようにPPS機能のロックを解除してからPPSの設定を行い、設定後再度ロックするようになっています。

  1. PPS機能のロック解除
  2. 機能モジュール入力部のピンの割り当て
  3. 機能モジュール出力部のピンの割り当て
  4. PPS機能のロック

それでは、この番号順に対応するプログラムを順番に説明します。

❶ PPS機能のロック解除

PPSのロックを解除するには、PPSLOCKレジスタのPPSLOCKEDビットを0に設定します。

この変更は、プログラムではPPSLOCKbits.PPSLOCKED = 0;と書けば良さそうですが、このよう変更することはできないようになっています。

この変更するには、おまじないが必要になります。

そのおまじないとは、PPSLOCKレジスタに一度0x55を代入したあと、すぐに0xAAを代入します。(2進数では0b01010101を代入した後に0b10101010を代入することになります)

ちょっと不思議なプログラムを書くことになりますが、「アリババと40人の盗賊」の「開け、ゴマ」という呪文のような感じでしょうかね。

同じレジスタに0x550xAAという順番に代入するとPPSLOCKEDビットを変更することができるようになります。

PPSロック解除のプログラムは以下のようになります。

PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0;

❷ 機能モジュール入力のピンの割り当て

PPS機能のロックが解除できたら、次は機能モジュール入力部のピンを割り当て設定です。

ここでは、次の部分を設定することになります。(何を設定しているのか迷子にならないように注意していただければと思います)

今回製作したブレッドボードの接続では、割り当て方は次のようになります。(上の図と比較してみてください)

モジュール割り当てピン
I2C通信モジュール1のSDA入力部RC2
I2C通信モジュール1のSCL入力部RC3

この設定は、プログラムでは次のフォーマットで書きます。

[機能モジュール名] = [ピン名];

この「機能モジュール名」と「ピン名」はデータシートに記載されています。

今回はPIC16F18857のデータシートを解読していきますが、他の型番のPICマイコンでも同じ読み方になります。

最初に「機能モジュール名」部分の書き方、次に「ピン名」部分の書き方を説明します。

機能モジュール名の書き方

次の表は、「機能モジュールの入力部」の設定を行うための「機能モジュール名」の表です。

この表は機能モジュール名の他に、デフォルトで割り当てられているピン、設定可能なピンが書かれています。

(Microship社PIC16F18857データシートより引用)

I2C通信モジュール1の機能モジュール名は、赤枠で囲んだ部分に記載されています。

I2C通信モジュール1のSDAとSCLの入力部の名前は「SSP1DATPPS」、SCLは「SSP1CLKPPS」です。(かなりややこしくなってきましたね…)

ここまでの内容をまとめると、I2C通信モジュールのSDAとSCLの入力部のピンの割り当ては次のようになります。

SSP1DATPPS = [RC2のピン名];
SSP1CLKPPS = [RC3のピン名];

ピン名の書き方

次に「ピン名」の書き方です。

ピン名は文字列ではなく数値で指定します。

次の表は、機能モジュールの入力部の割り当てピンを設定するための各ピンの設定値の表です。

(Microship社PIC16F18857データシートより引用)

表中、赤枠で囲んだ部分がRC2ピンとRC3ピンの値です。

RC2ピンは0x12、RC3ピンは0x13を設定することになります。

まとめると、機能モジュールの入力部の設定は次のようになります。

// I2C通信モジュール1の入力部設定
SSP1DATPPS = 0x12;   // SDA入力部をRC2に設定する
SSP1CLKPPS = 0x13;   // SCL入力部をRC3に設定する

❸ 機能モジュール出力部のピンの割り当て

次にI2C通信モジュール1のSDA、SCLの出力部の設定を行います。

ここでは、次の部分を設定することになります。

先ほどの入力部と同じく、ピンの割り当ては次のようになります。

モジュール割り当てピン
I2C通信モジュール1のSDA出力部RC2
I2C通信モジュール1のSCL出力部RC3

この設定は、プログラムでは次のフォーマットで書きます。(先ほどの入力部とは逆の設定のイメージです)

[ピン名] = [機能モジュール名];

それでは、「ピン名」部分と「機能モジュール名」部分の書き方を確認していきましょう!

ピン名の書き方

ピン名はフォーマットが決まっていて、以下のように記述します。

ピン名PPS

ちょっとわかりづらい説明ですね。少し具体的に確認しましょう。

例えば、RC2であればピン名はRC2PPS、RC3であればRC3PPSと記述します。

ここまでの説明で、出力部の設定は次のようになります。

RC2PPS = [SDAのモジュール名];
RC3PPS = [SCLのモジュール名];

機能モジュール名の書き方

次に機能モジュール名の設定です。

モジュール名は数値で指定することになります。

以下の表は、機能モジュールの出力部の設定を行うための表です。

この表は機能名とその設定値が書かれています。またデフォルトで割り当てられているピンも記載されています。

(Microship社PIC16F18857データシートより引用)

I2C通信モジュール1のSDAとSCLは赤枠で囲んである部分で、それぞれ0x150x14です。

プログラムでは次のように設定することになります。

RC3PPS = 0x14;   // RC3をSCLに設定
RC2PPS = 0x15;   // RC2をSDAに設定

これで入力部と出力部の設定が完了しました!

❹ PPS機能のロック

PPSの設定が終わったら、PPSを変更できないようにロックします。

ロックするにはPPSLOCKEDビットに1をセットすればOKですが、ロック解除の時と同様に呪文が必要になります。

PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 1;

I2C通信機能モジュールのPPS設定まとめ

今までの設定をまとめると以下のようになります。

// PPS設定ロックの解除
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0;

// I2C通信モジュール1のSCL、SDA入力部のピン割り当て
SSP1DATPPS = 0x12;   // SDA入力部をRC2に設定する
SSP1CLKPPS = 0x13;   // SCL入力部をRC3に設定する

// I2C通信モジュール1のSCL、SDA出力部のピン割り当て
RC3PPS = 0x14;   // RC3をSCLに設定
RC2PPS = 0x15;   // RC2をSDAに設定

// PPS設定ロック
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 1;

これでブレッドボード回路に合ったI2C通信モジュールのピン割り当てができました!

次回はI2C通信モジュールを使用するプログラムを作成していきます。

更新履歴

日付内容
2018.6.10新規投稿
2025.8.10説明内容補足
通知の設定
通知タイミング
guest
11 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
gosan
gosan
3 月 前

情報大変有り難う御ございました。
確認してみます。
(i2cで相手から来るデーターが32bytも有るので苦慮しておりました)
インクリメントの方もテストしてみます。
大変有り難うございました。

gosan
gosan
4 月 前

ご連絡大変ありがとうございました。
ご指摘に場所のコメント分修正しておきます。
ポート設定ですが、下記のようにセットされています
 movlw B’11111111′
 movwf TRISB
 clrf PORTB

 movlw B’00011000′
 movwf TRISC
 clrf PORTC

movlw B’00000000′
movwf ANSELB
movwf ANSELC

あともう一つ伺いたいのですが、ユーザーメモリー番地をプログラム上でインクリメントさせて行く方法は有るのでしょうか?
例として
_dat_bak1 EQU 30h
_dat_bak2 EQU 31h
_dat_bak3 EQU 32h
_dat_bak4 EQU 33h
_dat_bak*を30hからデーターの値を入力開始し、アドレス値にデーター入れたら、次の値31h→32hとインクリメントさせる方法。
何せ初心者な物でご伝授の程よろしくお願い申し上げます。

gosan
gosan
4 月 前

初めてご連絡致します。
PIC126F18857を使って液晶コントロールしておりますが、i2cのポートプロテクトロックで原因不明の状況が発生して困っておりますので、ご伝授下さい。
状況
PPSLOCK解除した後、ポートの各設定を行い終了後に、改めてPPSLOCKするとi2c出力しません。
但し、PPSLOCKを解除したままプログラムを走らせると、特に問題なく動きます。
(原因不明です)
?PPS設定ロックを解除したままでプログラムを作成しても問題ないのでしょうか?

以下が設定内容です。
;///////// PPS設定ロックの解除
  BANKSEL PPSLOCK
  MOVLW 0x55
  MOVWF PPSLOCK
  MOVLW 0xAA
  MOVWF PPSLOCK
  bcf PPSLOCK,PPSLOCKED
;ロック解除

  BANKSEL SSP1DATPPS ;BK=62
  movlw 0x14   ;
  movwf SSP1DATPPS   ;SDA入力部をRC2に設定する   
  movlw 0x13   ;
  movwf SSP1CLKPPS   ;SCL入力部をRC3に設定する

;///////// I2c通信(液晶用)モジュール1のSCL、SDA出力部のピン割り当て
  BANKSEL RC3PPS
  movlw 0x14   ;
  movwf RC3PPS   ;RC3をSCLに設定
  movlw 0x15
  movwf RC4PPS   ;RC4をSDAに設定
;///////// ここまで

;///////// I2c通信(温度用)モジュール2のSCL、SDA入力部のピン割り当て
  BANKSEL SSP2DATPPS ;BK=62
  movlw 0x0A   ;
  movwf SSP2DATPPS   ;SDA2入力部をRB2に設定する(SDA2にRB2ピン設定)   
  movlw 0x09   ;
  movwf SSP2CLKPPS   ;SCL2入力部をRB1に設定する(SCL2にRB1ピンを設定)

;///////// I2c通信(温度用)モジュール2のSCL、SDA出力部のピン割り当て   
  BANKSEL RB1PPS
  movlw 0x16   ;
  movwf RB1PPS   ;RB1をSCL2に設定(RB2ピンにSCL2設定)
  movlw 0x17
  movwf RB2PPS   ;RB2をSDA2に設定(RB2ピンにSDA2設定)
;///////// ここまで
   
  MOVLB 0x00 ;BK=0

;///////// PPS設定再ロック
  BANKSEL PPSLOCK
  MOVLW 0x55
  MOVWF PPSLOCK
  MOVLW 0xAA
  MOVWF PPSLOCK
  bsf PPSLOCK,PPSLOCKED
;ロック設定

以上よろしくお願い申し上げます。

aaa
aaa
3 年 前

お世話になります
RB1,RB2ピンでPPS機能を使う場合どの様な設定が必要になるのでしょうか?
RB1にSCL(クロック)、RB2にSDA(データ)を当てて
ピン属性を
 ANSELC = 0b00000000;
  ANSELB = 0b00000000;
  TRISC = 0b01001100;
  //RB1,RB2をIN
  TRISB = 0b00000110;
にして
SS1を全部SS2にしてみたけど駄目でした

aaa
aaa
返信  管理者
3 年 前

返信ありがとうございます

1)すみません、ピンの変更はしないのでPPSは関係なかったです

2)はこれでいいんですよね?
ANSELB = 0b00000000;
TRISB = 0b00000110;

3)SS1をSS2に置き換えるだけですよね?
  SSP2STAT = 0x80;
  SSP2CON1 = 0x28;
  SSP2CON3 = 0x00;
  SSP2ADD = 0x09;

void i2cProtocolStart() {
  SSP2IF = 0;
  SSP2CON2bits.SEN = 1;
  while (SSP2IF == 0) {}
  SSP2IF = 0;
  return;
}

void i2cProtocolStop() {
  SSP2IF = 0;
  SSP2CON2bits.PEN = 1;
  while (SSP2IF == 0) {}
  SSP2IF = 0;
  return;
}

void i2cProtocolSendData(uint8_t data) {
  SSP2IF = 0;
  SSP2BUF = data;
  while (SSP2IF == 0) {}
  SSP2IF = 0;   
  return;
}

デフォルト設定でのRB1,RB2のl2c(SSP)機能が反映してないようです
スタートコンディションのWhileで止まってしまいます
RB1,RB2でLED付けたりして接触不良も無さそうです

aaa
aaa
返信  aaa
3 年 前

追記
問題解決しました
PPSで設定したら機能が反映しました
 SSP2CLKPPS = 0x09;  
 SSP2DATPPS = 0x0A;  
 RB1PPS = 0x16;  
 RB2PPS = 0x17; 
これってピンの変更だけじゃなくて
モジュール機能使う場合にも必要になるんですね
ご迷惑かけました

目次