dポイントプレゼントキャンペーン実施中!

PIC16F648Aのタイマ0割り込みによって定期的に1-Wireデジタル温度計のDS18B20と通信させるプログラムを作ろうとしています。DS18B20の通信ポートとはPICのRB7ポートと繋がっており、4.7kΩの抵抗によってプルアップされています。PICには内部クロックを使って4MHzで動作させています。

DS18B20に「1」を書き込むプログラムですが、初め入力の設定でHighにしてある状態から、出力のLowに切り替え、2μsだけ待ったあと、また入力に切り替えてHighにするプログラムを組んでいます。

しかし、その波形をオシロスコープで見てみると、Lowである時間が約35μsと異様に長くなっていて困っています。この時間はたとえ待ち時間を1μsにしても10μsにしても、待ち時間プログラムでなくNOP1回にしても変わりません。

ネットで調べたりもしましたが、このような事例は僕が調べた限りでは載っていませんでした。これだけの質問内容ではご回答しにくかったら申し訳ありませんが、誰かわかる方ご教授いただけると助かります。

質問者からの補足コメント

  • うーん・・・

    省略や改変のあるプログラムとなってしまうので申し訳ありませんが、下記にプログラムを載せておきます。

    #include <xc.h>
    #pragma config FOSC = INTOSCIO
    #pragma config WDTE = OFF
    #pragma config PWRTE = ON
    #pragma config MCLRE = ON
    #pragma config BOREN = OFF
    #pragma config LVP = OFF
    #pragma config CPD = OFF
    #pragma config CP = OFF
    #define _XTAL_FREQ 4000000
    int icount;
    void ds18b20_read(void); // DS18B20と通信をする
    void onewire_write(int data);

      補足日時:2016/03/07 13:18
  • うーん・・・

    void main(void){
    di();
    // 76543210
    T1CON=0b00110001; // bit0:TMR1ON 1 タイマ1の動作をする
    // bit1:TMR1CS 0 内部クロック
    // bit2:^T1SYNC 0  内部クロック同期
    // bit3:T1OSCEN 0 外部発振回路不使用
    // bit4:T1CKPS0 1 プリスケーラ (8)
    // bit5:T1CKPS1 1 (同上)
    // bit6:なし 0
    // bit7:なし 0
    // 76543210
    TRISA=0b11100000;
    TRISB=0b10000000;
    PORTA=PORTB=0;

      補足日時:2016/03/07 13:21
  • うーん・・・

    TMR1IF=0;
    TMR1IE=1;
    PEIE=1;
    GIE=1;
    while(1);
    }
    void interrupt ipt(void){
    if(TMR1IF){
    TMR1IF=0;
    if(icount++>3){
    icount=0;
    ds18b20_read(); // DS18B20と通信をする
    }
    return;
    }
    }
    void ds18b20_read(void){ // DS18B20と通信をする
    di();
    TRISB=PORTB=0; // リセットパルス
    __delay_us( 500 );
    TRISB=0b10000000;

      補足日時:2016/03/07 13:23
  • うーん・・・

    __delay_us(70);
    if(PORTB==0b10000000){ // 存在パルスの確認
    TMR1IE=1;
    PEIE=1;
    GIE=1;
    return;
    }
    __delay_us( 500 );
    onewire_write(0xCC); // SKIP ROMコマンド
    onewire_write(0x44); // CONVERT Tコマンド
    }
    void onewire_write(int data){ // DS18B20にデータを書き込む
    int count;
    for(count=0;count<8;count++){
    if((data>>count)&0x01){ // 「1」を書き込む

      補足日時:2016/03/07 13:27
  • うーん・・・

    TRISB=PORTB=0;
    __delay_us(2);
    TRISB=0b10000000;
    __delay_us(70);
    }
    else{ // 「0」を書き込む
    TRISB=PORTB=0;
    __delay_us(70);
    TRISB=0b10000000;
    __delay_us(2);
    }
    }
    }

      補足日時:2016/03/07 13:29

A 回答 (4件)

私もPICのことは全く知りませんし、レジスタの説明がないのでプログラムも斜め読みですが、…。



一つ言えることは、一般的に高々4MHzのCPUクロックで1µsクラスの分解能を持たせるのは無理ではないかと思います。
4MHzは1クロック0.25µsであり、1CPU命令0.25µsで実行していると考えられるので、
コンパイルの結果1行が4CPU命令とかになった場合、容易に1µsの遅延が入ります。
PICの周辺機能のアーキテクチャのことはよく解りませんが、IOレジスタのリードライトに数クロックかかる場合もあります。
if文でIOポートレジスタにアクセスしているようですが、レジスタアクセスとテスト判定、ジャンプなどで数クロック消費していると思います。
これだけで1μs位は容易に遅延します。

さらに__delay_usとかいう関数をコールしていますが、この処理にもかなり時間がかかると思います。
関数コールにはプログラムカウンタやレジスタの待避、引数のプッシュとか重い動作が関わるので、
軽く数μs~数十µsとかは遅延すると思います。
なので、コールとリターンで20μs~数十μsレベルの遅延は考慮すべきです。
ゆえに35μsは想定内ではないかと思います。
すなわち、__delay_us関数で数十μs以下の時間調整は困難だと思います。

もう一点、コンパイルの最適化はどのようにしているのか書かれていませんが、高度な最適化をしているとNOPは削除されかもしれません。
なので、NOPの挿入で確認しようというのは適切ではありません。

これらの観点についてコンパイラがどのようなコードを出力したかアセンブラレベルで注意深く検討する必要があります。


後、考えられる点としては、PICのRB7なるポートのドライブ能力がDS18B20のキャパシタをフルスイングするだけの能力があるのかという点も調べる必要があるかもしれません。
    • good
    • 1
この回答へのお礼

ご回答ありがとうございました。
いろいろと問題があることに気づくことができました。ありがとうございました。

お礼日時:2016/03/08 12:26

小生コンパイラはやったことはありませんがPICのアセンブラは経験があります。


ご質問の症状は経験がありませんが、可能性がありそうな点がいくつか思い当たります。

1.クロックは4MHzとのことですが、内部発振周波数ですか? φ/4が4MHzですか?
内部発振が4MHzではシステムクロック(φ/4)は1HMzのはず。ならば 1命令=1μSec になます。完全マシン語でギリギリの速度ですね。コンパイラではまず無理と思いますが。

2.コンパイラは一般に1行済むごとにAcc(PICではW?)を退避してシステムの進行を確認しているはず。そんな仕様のコンパイラではないですか? 当然退避、復旧に時間を要しますね。少なくともこの部分はマシン語でないと無理と思います。

3.割り込みを使っていますね。このポート制御の前後では割り込みは禁止にしていますか?
このCPUのハードウェアは調べていませんが一部のチップではどのピンからでも割り込みが掛けられます。出力に指定しておいてもレベルが変化するとそのエッジで割り込みが掛るものがあります。こんな割込みは使ってないですよね?
    • good
    • 1
この回答へのお礼

ご回答ありがとうございました。
1. クロック周波数である4MHzは内部発振周波数のことです。ご指摘の通り、コンパイラだとマイクロ秒単位の制御は難しいことがわかりました。ありがとうございました。
2. CコンパイラとしてMPLAB XC8 Compilerを使用しました。コンパイラの仕様に関して勉強不足で、1行済むごとにシステムの進行を確認しているのかどうかわからないので申し訳ありませんが、もしそのような仕様ならば、そのためにもマシン語を使う必要があることがわかりました。
3. 確かに割り込みを禁止することを忘れていました。PIC16F648AはポートBに割り込みを掛けられるそうです。その可能性も含めて改善していきたいと思います。

お礼日時:2016/03/21 12:59

答えは既に出ていますが、一応PICを知っている者として補足しておきますと、


PICは1命令に4クロック掛かります。
ですから2μsは2命令です。
またIOレジスタの操作自体は1命令ですが、ワーキングレジスタにロードしてから書き込む必要があるのとバンク切り替えのせいで、3~6命令は掛かるかと思います。
PIC16F648Aでマイクロ秒単位の制御をしたいならアセンブラで書くしかありません。
アセンブラを覚える気がないのであれば、32MHzで動作するPIC16F1823などを使えばなんとかなるかもしれません。
なおポートのドライブ能力はPICはそれなりに高いので大丈夫でしょう。別のPIC(10F200)ですが直結でDS18B20を使ったことがあります。
    • good
    • 1
この回答へのお礼

ご回答ありがとうございました。
PIC16F648Aをマイクロ秒単位で制御するプログラムを、C言語で書くことの難しさを知ることができました。
回路の設計を変えたくないこともあるので、一度アセンブラでやってみたいと思います。

お礼日時:2016/03/20 23:40

PICですか、


知っているのは名前だけですが...
>この時間はたとえ待ち時間を1μsにしても10μsにしても、待ち時間プログラムでなくNOP1回にしても変わりません。
ということですから、まず疑うのはプログラムでは?
プログラムを載せて皆さんに聞いては?
    • good
    • 0
この回答へのお礼

ご回答ありがとうございました。
わかりました。省略や改変のあるプログラムとなってしまうので申し訳ありませんが、補足コメントの方にプログラムを載せておきます。

お礼日時:2016/03/07 13:06

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!