Nゲージの制御盤やらプラモデルの飾り台にPIC24で作ったWAVプレーヤーで音を出す基板を量産している・・が、音が出ない時も。試験用にアンプをモジュール化しておくことにした。
I2C通信・導入段階でドツボにハマる
PICマイコンを使ってポイントマシンを動作させたり信号の点灯制御を行うNゲージレイアウトを製作中。
複数のPICマイコンはI2C通信を使ってRaspberry Pi3で制御する予定。
Nゲージのレイアウトは3分割で、それぞれの台枠にPICマイコンがある制御盤を持っている。
そのため、Raspberry Pi3と各台枠のPICマイコン間のI2C通信はLANケーブルを使用して行う。
モニターの背面にとりつけたRaspberry Pi3の横にLANケーブル用のRJ45モジュラジャックを配置した基板を取り付けておいた。
写真には写っていないが、左端にはI2Cバス用双方向電圧レベル変換モジュールがある。
現段階ではI2C通信のマスターはRaspberry Pi3、スレーブは4種類ほどのPICマイコンを想定している。
直接Raspberry Pi3と通信するPICマイコンは4個ほどになる予定だが、もっと多くなる可能性もある。
また、PICマイコンは3.3Vのものの他に5Vのものもある。
従って、Raspberry Pi3のGPIOのポートからの電力供給ではすべてのI2C通信を稼働させるだけの容量はとれそうもないし、電圧レベルの変換も必要になる。
ということで、市販のI2Cバス用双方向電圧レベル変換モジュールを購入し、その基盤にアダプターから電力を供給することにした次第。
準備したモジュールは秋月電子で扱っているFXMA2102を使用したもの(青い基板)と、PCA9306を使用したもの(緑の基盤)の2種。
・・写真撮るんなら、基盤を穴の部分で切って後、ヤスリがけしてきれいにしとくんだった
(^^;
FXMA2102を使用した方は電源へのプルアップ抵抗部分は空きパターンとなって実装されていないので自分で実装する必要がある。
PCA9306を使用した方は電源へのプルアップ抵抗部分が実装されている。
=後々考えると=
写真にもあるように、この基盤を作った時点では3.3Vの電源はRaspberry Piからとり、5VはPICマイコン側のボードからとることを考え、I2Cのクロック、データ以外に電源用の配線も加えた4線で配線していた。
しかし、Raspberry Piから電源をとらなくなった時点で、何らかのことがきっかけでGNDの配線はいらない?と思い始めたのがドツボにハマる原因だったようだ。
事前準備
事前準備としてRaspberry Pi3でI2C通信ができるようにする設定を行っておく必要がある。
テストプログラム
まず、PICマイコンがスレーブとなって、マスターとするRaspberry Piから認識できるか確認してみる。
使用したのはPIC16F886。
電圧レベル変換モジュールはFXMA2102。
テストプログラムは次のような簡単なもの。
// PIC16F886 // I2C test // ピンアサイン // #1 MCLR // #2 RA0 // #3 RA1 // #4 RA2 // #5 RA3 // #6 RA4 // #7 RA5 // #8 VSS (GND) // #9 RA7 // #10 RA6 // #11 RC0 // #12 RC1 // #13 RC2 確認用LED --> 抵抗 --> GND // #14 RC3/SCK/SC I2C clock // #15 RC4/SDI/SDA I2C data // #16 RC5 // #17 RC6 // #18 RC7 // #19 VSS (GND) // #20 VDD +2.0V ~ +5.5V // #21 RB0 // #22 RB1 // #23 RB2 // #24 RB3 // #25 RB4 // #26 RB5 // #27 RB6/ICSPCLK // #28 RB7/ICSPDAT // コンパイラ XC8 #include <stdio.h> #include <stdlib.h> #include <xc.h> // PIC16F886 Configuration Bit Settings // 'C' source line config statements // CONFIG1 #pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) //#pragma config MCLRE = ON // RE3/MCLR pin function select bit (RE3/MCLR pin function is MCLR) #pragma config MCLRE = OFF // RE3/MCLR pin function select bit (RE3/MCLR pin function is digital input, MCLR internally tied to VDD) #pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled) #pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled) #pragma config BOREN = ON // Brown Out Reset Selection bits (BOR enabled) #pragma config IESO = ON // Internal External Switchover bit (Internal/External Switchover mode is enabled) #pragma config FCMEN = ON // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled) #pragma config LVP = OFF // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming) // CONFIG2 #pragma config BOR4V = BOR40V // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V) #pragma config WRT = OFF // Flash Program Memory Self Write Enable bits (Write protection off) // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. // クロック #define _XTAL_FREQ 8000000L // クロック 8MHz ボーレート、__delay_ms用 // 自分のアドレス #define I2C_ADDRESS 0x10 /* * メイン */ int main(int argc, char** argv) { OSCCONbits.IRCF =0b111; // IRCF<2:0>=111 内臓クロックは8MHz CM1CON0bits.C1ON =CM2CON0bits.C2ON = 0;// コンパレータオフ ANSEL = ANSELH = 0x00; // ポートI/Oはすべてデジタル TRISA = 0x00; //RAポート 全て出力 (0:出力 1:入力) TRISB = 0x00; //RBポート 全て出力 (0:出力 1:入力) TRISC = 0b00011000; //RCポート RC3,RC4入力、他は出力 (0:出力 1:入力) //SSP STATUS REGISTER SSPSTATbits.SMP = 1; //In I2 C Master or Slave mode: //1 = Slew rate control disabled for standard speed mode (100 kHz and 1 MHz) //SSPSTATbits.SMP = 0; //In I2 C Master or Slave mode: //0 = Slew rate control enabled for high speed mode (400 kHz) //SSPSTATbits.CKE = 0; //0 = Data transmitted on falling edge of SCK //SSP CONTROL REGISTER 1 SSPCONbits.WCOL = 1; //Slave mode:1 = The SSPBUF register is written while it is still transmitting the previous word (must be cleared in software) SSPCONbits.SSPOV = 1; //In I2 C mode:1 = A byte is received while the SSPBUF register is still holding the previous byte. SSPOV is a “don’t care” in Transmit //mode (must be cleared in software). SSPCONbits.SSPEN = 1; //1 = Enables the serial port and configures the SDA and SCL pins as the source of the serial port pins SSPCONbits.CKP = 1; //In I2 C Slave mode: //1 = Enable clock SSPCONbits.SSPM = 0b00000110; //0110 = I2C Slave mode, 7-bit address //SSP CONTROL REGISTER 2 SSPCON2bits.GCEN = 0; //0 = General call address disabled //SSPADD SSPADD = I2C_ADDRESS << 1; // 7bit address SSPIF = 0; // Clear MSSP interrupt flag SSPIE = 1; // I2C interrupt enable PEIE = 1; // Enable Peripheral interrupts GIE = 1; // Enable global interrupts RC2 = 1; // 確認用LED点灯 while(1){ __delay_ms(1000); // 1秒待ち RC2 ^=1; // 確認用LED点灯/消灯 }; }
回路の方も、Raspberry PiのGPIOのピンと電圧レベル変換モジュールを接続、また、PIC16F886のピンと電圧レベル変換モジュールを接続という単純なもの。
Raspberry PiのGPIO #3:I2Cデータ
Raspberry PiのGPIO #5:I2Cクロック
PIC16F886の#14:I2Cクロック
PIC16F886の#15:I2Cデータ
この状態でRaspberry Pi、PIC16F886の双方の電源を入れ、Raspberry Piで「i2cdetect」コマンドを実行。
PIC16F886のアドレスが表示されれば通信ができる状態にあることがわかる。
PIC16F886のアドレスは、ソースプログラムの中に記述してある。
// 自分のアドレス #define I2C_ADDRESS 0x10
で、実行してみると・・・
0x10の表示が・・ない
ダメじゃん・・
PICマイコン単体だとちゃんとプログラムが書き込まれているのか、電源がちゃんと接続されているのかすら分からない。
なもんで、今回はPIC16F886の#13にLEDと抵抗を繋げば1秒ごとに点滅するようにしてある。
LEDはちゃんと点滅しているのでPICマイコンは動作しているようだ。
何度かプログラムを修正しては実行することを繰り返したが、0x10ではなく、他のアドレスが表示されることがあった。
まったく通信ができていない状態ではないらしい。
結果的には、何度かアドレスが表示されたので、これはいけそうという感じだったのでハマりながらも作業を続けていった・・
一度も表示されなかったら・・市販の小さなチップに搭載されている温度計などの安いモジュールを買って、Raspberry Piが正しく動いているかなどの切り分けからやっていたと思う。
しかし、その後もでたらめなアドレスが表示されたり、何も表示されなかったりと安定した動作はしない。
電圧レベル変換モジュールもFXMA2102、PCA9306の双方で試したがダメ。
ちなみに、Raspberry Pi側は3.3V、PIC16F886側は5V。
ブレッドボードを使っての確認用の電源部分もガタが出始めたので作り直してみた。
茶色の基盤がその部分。
DC15Vアダプターから3.3Vの三端子レギュレータ、5Vの三端子レギュレータ、9Vの三端子レギュレータと直の電圧の4種類の電圧を得られる。
で、問題はどこにあるんじゃろ?
PCA9306を使用した電圧レベル変換モジュールはこんな感じ。
この図に3.3Vの電源と5Vの電源を追加したのが先ほど製作した基板と同じということになる。
作り直した電源部でテストしてみる
このように接続したのだが、ダメだった。
ネット上でいろいろ検索しながら、どこが悪いのか見当をつけてみる。
まず、電源。
今回の回路は電圧レベル変換モジュールのVREF1は3.3V、VREF2は5Vとしている。
海外のサイト(https://e2e.ti.com/support/interface/f/138/t/591249)に次のような記載があった。
Vref2からVref1への少量の漏れがあります。 Vref2プルアップ抵抗はこれを制限するのに役立ちますので、必ずその値を再確認してください。ただし、Vref2の抵抗が十分に大きい場合でも、Vref1を提供する電源が電流をシンクできない場合、Vref1の電圧が上昇する可能性があります。この場合、Vref1の大きなプルダウン抵抗(たとえば、270 kOhm)が役立つ。
試しに、VREF1の電圧を測ると3.8V近くあった。
手元にあった200kΩの抵抗でプルダウンしてみたところ、3.7V位に下がったがRaspberry Pi側では認識できなかった。
次にSDAの電圧。
国内のサイト(https://www.denshi.club/pc/raspi/i2c.html)に次のような記載があった。
SDAラインがLowレベルを満たしていないため、簡易的に、2.2k~3kΩでGNDにプルダウンする対応策がネット上で紹介されています。3kΩをつなぐと、3eに存在することが確認できます。
手元にあった3.3kΩの抵抗でプルダウンしてみたがRaspberry Pi側では認識できなかった。
VREF1ラインとSDAラインをプルダウンしたところ。
(ブレッドボード左下の抵抗。)
電源のみプルダウン、SDAのみプルダウン、両方プルダウン等のパターンを試したり、抵抗値を少し変えてみたりしたが、いずれも認識しないという結果に終わった。
MPLABのデバッガでデバッグしてみる
PICマイコンのSSP CONTROL REGISTER 1、SSP CONTROL REGISTER 2、SSPADDはどのようになっているのか気になって MPLABとPICKIT3を使ってデバッグしてみることにした。
値は設定したもの、想定したものが正しく入っているようだった。
が、このとき判明したのだが、
PICKIT3を回路に繋いでいる時は・・ お~、ちゃんと動いとる。
PICKIT3を回路から切り離すと・・ お~、ダメじゃん。
原因を調べなくては。
PICKIT3はMCLR、Clock、Data、Vdd、Vssと5本の線で回路と繋がっている。
で、1本ずつ切り離しながら試験していった。
最終的に分かったのはVss(GND)が回路につながっていると正しく動作するみたい。
VSS(GND)1本が繋がるのみで正しく動作する。
図にするとこんな感じになる。
一歩前進したが
ネット上でよく見かけるI2Cの接続に関する概念図。
この概念図の中に電圧レベル変換モジュールが入っているだけのハズなのだが。
そもそも、GNDはどうなるんじゃ!?
他人様のサイト、 第36回 I2C通信の考え方(1) 、第37回 I2C通信の考え方(2) をみて勉強したが・・
今回試験している環境はRaspberry Pi側の電源とブレッドボード上の電源は異なる。
しかも、Raspberry Pi側とブレッドボード上のGNDは接続されていない。
やっぱ、GND同士は繋がっていないといけない・・と思いながらもPICKIT3を繋いだ時の挙動が気になる。繋がってなくても動いとる。ハテ?
・・この事象がなければ素直にボード間のGNDを接続していたと思う。
PICKIT3はPCとUSBで接続されている。
もちろん、PCの電源のGNDはRaspberry Piの電源、ブレッドボードの電源のGNDなどとは接続されてはいない。
なのに、なぜPICKIT3のGNDがこの回路に繋がるだけでちゃんと動くようになるんじゃ!?
シロートは知らなくても良い世界なのか!?
ちなみに、PICKIT3にUSBから電源が供給されていないとこの回路はちゃんと動作しない。
最終的には
電圧レベル変換モジュールのGND、ブレッドボードのGNDを接続して確認したが・・動作しなかった 。
_| ̄|○ ガクッ
なんでじゃ・・ こうなりゃ、Raspberry PiのGNDも接続じゃ。
PICKIT3を繋いだ時はここは繋がっていなくても動作したのだが。
しかし、こうすることによってちゃんと動作することが確認できた。
なぜPICKIT3のGNDを繋げるだけで動作したのかという謎は一層深まった。
基盤の製作
動作が確認できたので、モニター裏のRaspberry Piの横に並べる電圧レベル変換回路の基盤を製作。
3.3V三端子レギュレータ、5V三端子レギュレータとPCA9306を使用した電圧レベル変換モジュール。
電源アダプタは切り離し可能。
その他にLANケーブル用のRJ45モジュラジャックを4個。
このモジュラジャックでNゲージのレイアウトの制御盤と結ばれることになる。
LANケーブル内の8本の心線の内訳は次のとおり。
・SCL1(3.3V系クロック)
・SDA1(3.3V系データ)
・GND
・Vref2(5V)
・SCL2(5V系クロック)
・SDA2(5V系データ)
・GND
他基板への接続用RJ45モジュラジャック
Nゲージレイアウトの台枠は3個、制御盤は4個。 常に4個の制御盤を繋いでいる訳ではない。
したがって、制御盤の電源はオフになっている時もあるし、別の場所に置いていることもあり得る。
電源の入っていないスレーブがあれば、そのSCLとSDAはハイインピーダンスにしておく必要があるらしい。
で、あればRJ45モジュラジャックを使って電源が入っていない制御盤自体を切り離してしまえ・・と考えた次第。
なお、現時点では、SCL、SDAってどの程度の電流が流れるんじゃ!?ということは未調査。
今回は手元にあった昔ながらの太いLANケーブルを使い、必要な長さに切って自分でコネクタをくっつけた。
ケーブルには0.5×4Pの表示が・・0.5mm 4対。
まぁ、5Vなんで大丈夫・・と信じているが。
実際にレイアウトに配線するときには調べてからやりましょうか。