PIC24FJシリーズ SPI 誤動作
<カテゴリー:PICマイコン>
アンテナアナライザーのLCDドライブ用に使用していたPIC24FJ64GA004のROMエリアがほぼフルになりましたので、128Kに換える事にしました。 選んだマイコンは、将来USB機能やSDカードを組み込むかも知れないとの考えから、PIC24FJ128GB106という品番のICにしました。
左の画像が、128Kの64pin TQPF品に入れ替えた実装基板です。 変換基板の大きさが、44pinのときより小さくなっている関係で、以外とすっきり配線できました。
回路図的には、44pinの時と同じ構成で、一部i/oの割り当てを変更しただけで、さっそく動作確認です。 ところが、LCDの表示は問題なく表示されますが、コントロールマイコンとの通信がうまくいきません。 周波数表示はでたらめで時々暴走します。 UART通信のプログラムが異常なのかと、色々試しましたがうまくいきません。 じたばたしている内に、通信もうまくいかないのに、さらに、26バイトの伝送に15m秒くらいかかっている事が判り、LCDの表示速度も遅くなっている事に気付きました。 ボーレート38.4Kでは不足みたいです。 56K以上にアップする案もありましたが、 キー入力をADに変更して、ラッチアップの対策もできましたので、残りのUP,DOWNのキーもADに変更する事によりi/oポートが2本余ります。 この際、通信方式もi/o 1本のUARTからi/o 3本のSPIへ変更する事にしました。
しかし、これが、またまた、訳の判らない誤動作に悩まされる事になってしまいました。
時々、伝送に成功しますが、一度リセットをかけると、伝送データがでたらめになります。 約2週間悩んだ末、判った結論は、またまた、伝送路のリンギングによるマイコンのラッチアップでした。 ただし、ラッチアップの影響が小さい為、時々まともに動作するのは、プログラムの性ではないかと、ああでもない、こうでもないとやっていたのがいけなかったようです。 ふと、ハードが原因ではないかと、SPIのクロックやデータの波形をチェックしてみました。
上の画像は左が対策前の、クロック(下)とデータ(上)です。 右の画像は対策後の波形です。 対策前は、クロックにも、データにもリンギングのオーバーシュートやアンダーシュートが発生し、いずれも時々0.5Vを超えています。 これが伝送データがでたらめになる原因でした。 対策内容は、各ラインに1KΩの直列抵抗を挿入し、リンギングの発生をおさえますが、抵抗だけでは不十分でしたので、GNDとの間に68Pのコンデンサも追加しました。 波形がきれいになって暴走の確率はかなり減少しました。
しかし、時々、電源ON直後に暴走します。 ただ、基板のGNDラインを指でつかむとか、オシロのGND端子へ接続すると完璧に直ります。 このような症状の原因はノイズです。 ふたつのマイコンのうち、どちらで暴走しているか確認したところ、コントロール側の24FV32KA302が暴走しています。 さらに、このマイコンの入力関係をひとつづつ殺して確かめても暴走するときは暴走します。 最後にたどりついたところはMCLR端子でした。 この部分についてデータシートを再確認したところ、推奨回路はMCLR端子にシリーズに100から470Ωを入れろと書かれていますが、8bit時の延長で抵抗は入れてありませんでした。 そこで、470Ωを追加し、10回くらいのPOWER ON/OFFでは暴走が起こらない事を確認できました。 しかし、頻度は少ないですが、たまに誤動作します。 このマイコンのデータシートには、さらに0.1μFでGNDへ落とせと書いてあります。 さっそく0.1μFのコンデンサを追加したところ暴走が起こらなくなりました。 しかし、PICKIT3の説明書では、MCLRラインにコンデンサを接続するなと書いてあります。 試にこのラインに0.1μFを接続したままで、フラッシュメモリーの書き換えを行ってみましたが、問題なく書き換えが出来ました。 もし、書き換えがうまくいかない時は、コンデンサを外し、アナライザーが完成したら、コンデンサを追加する事にして、コンデンサは追加したままにしています。
LCDドライブ用の24FJ128GB106のMCLRラインにはまだシリーズ抵抗は入っていませんので、抵抗だけは追加する事にしました。
このSPI通信は、PIC24FV32KA302をマスターとして、PIC24FJ128GB106をスレーブとした一方通行のシリアル通信です。 クロックは約156KHzとし、マスター側はワード(16bit)伝送で割込みなし、全17ワードを連続送信し、その間、CEとしてLの信号を送ります。 スレーブ側は、CE信号がLになったらこれを割込みで捉え、受信バッファのインデックスを0にした後、1ワード受信する度に割込みを発生させ、17ワードのデータをバッファにストアさせます。 CE信号を送ったり、CEを受けてインデックスを0にする動作以外、すべて、データシートにかかれた説明通りの設定です。
上の画像は、下のパルスがSPIのクロック(SCK)で、上のパルスが受信割込みが発生したときのダミー信号です。 1ワード受信する度に割込みが発生し、先頭ワードのインデックスを0番として順次17ワードを受信バッファに格納していきます。
送信側のPIC24FV32KA302のmain内の初期設定と送信ファンクションは以下です。
main () {
・・・・・・・・・
SPI1CON1 = 0b0000011000111100;//マスターモード クロック1/64
SPI1CON2 = 0b0000000000000000;
SPI1STATbits.SPIROV = 0;
SPI1STATbits.SPIEN = 1;//SPI1有効化
・・・・・・・・・・・・・
送信処理
void sendDATA() {
unsigned char i;
SPIFlg = 0;//CE信号をL
__delay_us(20);
for (i=0;i <17;i++) {
while(SPI1STATbits.SPITBF);
SPI1BUF = TXData[i];
}
SPIFlg = 1;//CEをH
}
受信側PIC24FJ128GB106の初期設定と受信割込みルーチン
main() {
・・・・・・・・・・・・・・・
RPINR0bits.INT1R = 21;//INT1をRP21に割り当て(RG6)
RPINR20bits.SCK1R = 26;//SCK1INをRP26に割り当て(RG7)
RPINR20bits.SDI1R = 19;//SS1INをRP19に割り当て(RG8)
SPI1BUF = 0x0;//SPIバッファークリアー
IFS0bits.SPI1IF = 0;//SPI割込みフラグクリアー
IEC0bits.SPI1IE = 1;//SPI割込み有効
IPC2bits.SPI1IP = 3;//SPI割込み優先レベル3
SPI1CON1 = 0b000011000000000;//SDO使用せず。スレーブモード
SPI1CON2 = 0b000000000000000;//フレーム未使用
SPI1STATbits.SISEL = 1;
SPI1STATbits.SPIEN = 1;//SPI1有効化
INTCON1 = 0b000000000000000;
INTCON2 = 0b000000000000010;//INT1 ネガティブエッジ
IFS1bits.INT1IF = 0;// INT1 割込みフラグクリアー
IEC1bits.INT1IE =1;//INT1割込み許可
IPC5bits.INT1IP = 3;//INT1割込み優先レベル3
・・・・・・・・・・・・・
CE割込み処理
void __attribute__((interrupt, no_auto_psv)) _INT1Interrupt(void) {
IFS1bits.INT1IF = 0; //割り込みフラグ解除
RXindex = 0;
}
SPI受信割込み処理
void __attribute__((interrupt, no_auto_psv)) _SPI1Interrupt(void) {
unsigned int i;
// LED=1;
IEC0bits.SPI1IE = 0;//受信割込み禁止
IFS0bits.SPI1IF = 0; //受信割り込みフラグ解除
i = SPI1BUF;
RXdata[RXindex] = i;
RXindex++;
if (RXindex > 16) {
RXreadyflag = 1;//全データ受信完了フラグ
RXindex = 0;
}
IEC0bits.SPI1IE = 1;//受信割込み許可
// LED=0;
}