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

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

目次

PPS機能とは?

突然「PPS機能」なんて用語がでてきて、どこ? I2C通信は?、という感じですが、I2Cプログラム作成の前にPPSの理解が必要になりますのでお付き合いください。

PICマイコンにはいろいろな機能モジュールが内蔵されていますよね。応用編ではPWMなどのPICマイコンに内蔵されている機能をいろいろと使ってタイマー機能を追加しました。実践編では、これからPIC16F18857に搭載されているI2C通信モジュールを使用してプログラムを作成していきます。

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

モジュール ピン
I2C通信モジュール1のSCL RC3
I2C通信モジュール1のSDA RC4
I2C通信モジュール2のSCL RB1
I2C通信モジュール2のSDA RB2

多くのPICマイコンでは、この機能モジュールのピンの割り当てを変更することができるようになっています。この機能のことを「PPS」機能と呼んでいます。「PPS」は「Peripheral Pin Select」の略で、「周辺機能のピン選択」という意味です。意味合いとしては「内蔵機能モジュールのピン割り当て機能」という感じです。

実践編ではI2C通信モジュール1を使用することにしますが、このモジュールのデフォルトの割り当てピンは、SCLがRC3、SDAがRC4となっています(上の表参照)。作成した回路図は、SCLがRC3、SDAがRC2に接続していますので、割り当てピンを変更する必要があります。

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

PPSの使い方概要

機能モジュールは以下のように入力部と出力部を持っています。

Pic practice 39 periipheral

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

Pic practice 39 pps assign input

Pic practice 39 pps assign output

なお、図中では任意のピンと記載していますが、機能モジュールにより設定できないピンもありますので注意してください。詳しい内容はこの記事の後半で説明します。

PICマイコンのI2C機能モジュールは、一つの機能モジュールではなく、I2C通信のSCLのモジュールとSDAのモジュールの2つの機能モジュールから構成されています。そのため、以下のように、SCLとSDAのそれぞれに対して、入力ピンと出力ピンを割り当てます。

Pic practice 39 i2c pps assign

次にPPSの設定の流れを説明します。PPSのピン割り当て機能はデフォルトではロックされています。そのため、以下のようにロックを解除してからPPSの設定を行い、設定後再度ロックします。

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

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

1. PPSロック解除

PPSのロックを解除するには、PPSLOCKレジスタのPPSLOCKEDビットを0に設定します。ただ、このビットを変更するには、おまじないが必要になります。

そのおまじないとは、ちょっと不思議なのですがPPSLOCKレジスタに0x55を代入した後、0xAAを代入します。2進数では0b01010101を代入した後に0b10101010を代入することになります。「アリババと40人の盗賊」の話に「開け、ゴマ」という呪文が出てきますが、そんな感じでしょうか。同じレジスタに0x55、0xAAという順番に代入するとPPSLOCKEDビットを変更することができるようになります。

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

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

2. 機能モジュールの入力部とピンの割り当て

PPSロックの解除ができたら、機能モジュールの入力部にピンを割り当てます。割り当て方は回路図から以下のようになります。

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

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

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

それでは、モジュール名とピン名はどのような名称になるのかデータシートの読み方を説明します。

以下の表は、入力部の設定を行うための機能モジュール名の表です。この表は機能モジュール名の他に、デフォルトで割り当てられているピン、設定可能なピンの表になっています。

Pic practice 39 input pps name

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

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

というように記載することになります。

次にピン名です。ピン名は文字列ではなく数値で指定します。以下は入力部の割り当てピンを設定するための各ピンの設定値の表です。

Pic practice 39 input pin name

表中、赤枠で囲んだ部分がRC2とRC3の設定値です。RC2は0x12、RC3は0x13を設定することになります。

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

SSP1DATPPS = 0x12;   // SDA入力部をRC2に設定する
SSP1CLKPPS = 0x13;   // SCL入力部をRC3に設定する

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

次にI2C通信モジュール1のSDA、SCLの出力部の設定を行います。先ほどの入力部と同様、以下のように設定することになります。

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

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

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

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

ピン名PPS

例えば、RC2であれば「RC2PPS」、RC3であれば「RC3PPS」と記述します。ここまでの説明で、出力部の設定は以下のようになります。

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

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

以下の表は、出力部の設定を行うための表です。この表は機能名とその設定値が書かれています。またデフォルトで割り当てられているピンも記載されていますが、入力部の表と同じ内容になります。

Pic practice 39 output rxypps name

I2C通信モジュール1のSDAとSCLは赤枠で囲んである部分にあるように、それぞれ0x15、0x14です。プログラムでは以下のように設定することになります。

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

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

4. 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 新規投稿
通知の設定
通知タイミング
guest
5 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
aaa
aaa
2 年 前

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

aaa
aaa
返信  管理者
2 年 前

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

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
2 年 前

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

目次