Q質問

現在電子工作をしており、それにPIC16F84Aを使っています。
正直アセンブリはよくわからないので、C言語でプログラムを作っています。
コンパイラはPICCLITEで、LEDの点灯・消灯に関するプログラムです。

機能としては
・RA0に入力があったときRB0につないであるLEDが消灯しているならLEDを点灯。
・RA0に入力があったときRB0につないであるLEDが点灯しているならLEDを消灯。
・RA1に入力があったときLEDが点灯しているなら5秒後にLEDを消灯。
というものを目指しています。

1つ目、2つ目の項目はif文で簡単に実現できましたが、割り込みがうまくいかず、
消えている状態でRA1に入力を入れたときなぜか5秒後に点灯してしまいます。
だからRB0の出力を逆にしてみたんですがうまくいかず・・・。

ハード的には、RB0には反対側から5Vをかけ、
RB0=0の時は点灯
RB0=1の時は消灯
としています。
またセラロックは10MHzのものを使用しています。

現在のプログラムとしては
#include "pic.h"

#define XTAL_FREQ 10MHZ
#define MHZ*1000

void DelayUs(unsigned char cnt){ //時間待ち関数
 unsigned char i;
 i=(cnt)/(12MHZ/(XTAL_FREQ))|1;
 while(--i!=0) continue;
}

void DelayMs(unsigned int cnt){ //時間待ち関数
 unsigned char i;
 do{
  i=4;
  do{
   DelayUs(250);
  }while(--i);
 }while(--cnt);
}

int cnt,SW;
//SWが0なら消灯
//SWが1なら点灯

void interrupt isr(void){ //割り込み関数
 if(T0IF==1){
  T0IF=0;
  cnt--;
 }
 if(cnt==0){
  RB0=1; //消灯
  SW=0;
  cnt=190;
  T0IE=0;
  GIE=0;
 }
}

main()
{
 TRISA=0xFF; //入出力設定
 TRISB=0x00;
 PORTA=0x00;
 PORTB=0x00;
 SW=0;
 OPTION=0x87; //プリスケーラの設定
 TMR0=0x00;
 T0IF=0;
 T0IE=1;
 cnt=190;
 while(1){
  if(RA0==1){
   DelayMs(60); //チャタリング防止
   if(RA0==1){
    if(SW==0){
     RB0=0;
     SW=1;
    }
   else{
    RB0=1;
    SW=0;
   }
  }
 }
 if(RA1==1){
  DelayMs(60); //チャタリング防止
   if(RA1==1 && SW==1){
    TMR0=0;
    T0IF=0;
    T0IE=1; //割り込み許可
    GIE=1; //全体割り込み許可
   }
  }
 }
}

インターネット上での割り込みのプログラムをいくつか見てみたのですが、
どれもすべて動作を終えて、あとはwhile(1)で割り込みを待つだけ
というプログラムばかりでした。
僕のは基本的にRA0の入力によってLEDを点灯・消灯させつつ
RA1の入力で割り込みを開始。
5秒後に消えたら割り込みを禁止する。
という仕様にしたいのですが・・・・・・。
1ヶ月ぐらいやってますがうまくいきません。
良ければご回答をお願いします。

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

  • お早いご回答ありがとうございます。

    >セオリーは「メインでポートを監視する為の無限ループを組まない事」です。

    >つまり
    >>どれもすべて動作を終えて、あとはwhile(1)で割り込みを待つだけ
    >というプログラムにするのが常道なのです。
    そーだったんですか・・・・。

    とりあえず、このプログラムの通りやってみました、が、
    なかなかうまくいきません(汗
    最初にRA0につながれているスイッチを押したらそのままLEDがつきっぱなしになっちゃって・・・。
    スイッチを押している間消えています。
    あと割り込みもうまくいかなかったです・・・。
    なかなか消えてくれません。

    とりあえずプログラムは
    #include "pic.h"

    int a; //直前のRA0の状態
    int cnt; //秒カウンタ
    int LED; //LEDの状態

    main()
    {
    /*初期化始まり
     TRISA=0xFF;
     TRISB=0x00;
     PORTA=0x00;
     PORTB=0x00;

     a=0;
     cnt=0;
     LED=0;
    */初期化終わり

     OPTION=0x87; //割り込みの設定
     TMR0=0x00;
     T0IF=0;
     T0IE=1; //割り込み許可
     GIE=1; //全体割り込み許可
     while(1) {;}
    }

    void interrupt isr(void)
    {
     if (a != RA0) {
      a = RA0;
      if (RA0==1) {
       RB0=0;
       LED=1;
       cnt=0; //RA1の入力後の5秒間にRA0が変化したらチャラにする
      } else {
       RB0=1;
       LED=0;
       cnt=0; //RA1の入力後の5秒間にRA0が変化したらチャラにする
      }
     } //※1
     if (cnt==0) { //通常時
      if ((LED==1) && (RA1==1)) { //消灯時はRA1は無視
       cnt++; //5秒待ち状態に入る
      }
     } else { //5秒待ち状態に入っている時
      cnt++;
      if (cnt>=500) { //1ミリ秒×500=5秒
       RB0=1;
       LED=0; //LEDを消す
       cnt=0; //5秒待ち状態から通常状態に戻る
      }
     }
     T0IE=1; //割り込みを再許可する
     GIE=1;
    }

    ※1…カッコが1つ足りなかったのでおそらくここだと思います。

    という感じになっています。

    No.1の回答に寄せられた補足コメントです。 補足日時:2008/01/07 20:54 通報する

A 回答 (2件)

訂正

  if ((LEDの状態==0) && (RA1==1)) {//消灯時はRA1は無視

  if ((LEDの状態==1) && (RA1==1)) {//消灯時はRA1は無視
の間違い

    • good
    • 0

>消えている状態でRA1に入力を入れたときなぜか5秒後に点灯してしまいます。
点灯するでしょうね。そういうプログラムになってますから。

>・RA1に入力があったときLEDが点灯しているなら5秒後にLEDを消灯。
>・RA0に入力があったときRB0につないであるLEDが消灯しているならLEDを点灯。
RA1に入力があり、5秒後にLEDを消灯した次の瞬間に、RA0に入力があったら「LEDが消灯しているのでLEDを点灯」する事になります。

こういう場合には、普通
・入力ポートに変化があったら割り込みがかかる
・タイマーにより割り込みがかかる
と言うハード構成にします。

セオリーは「メインでポートを監視する為の無限ループを組まない事」です。

つまり
>どれもすべて動作を終えて、あとはwhile(1)で割り込みを待つだけ
というプログラムにするのが常道なのです。

なので、ネットでさがすと、そういうプログラムばかり見付かる筈です。

以下のような作り方をしましょう。

グローバル変数は「直前のRA0の状態」と「秒カウンタ」と「LEDの状態」を用意します。

int 直前のRA0の状態;
int 秒カウンタ;
int LEDの状態;

main()
{
 ハードの初期化;
 直前のRA0の状態=0;
 秒カウンタ=0;
 LEDの状態=0;
 割り込み許可;
 while(1) {;}
}

void interrupt isr(void)
{
 if (直前のRA0の状態 != 今のRA0) {
  直前のRA0の状態 = 今のRA0;
  if (RA0==1) {
   LED点灯;
   LEDの状態=1;
   秒カウンタ=0;//RA1の入力後の5秒間にRA0が変化したらチャラにする
  } else {
   LED消灯;
   LEDの状態=0;
   秒カウンタ=0;//RA1の入力後の5秒間にRA0が変化したらチャラにする
 }
 if (秒カウンタ==0) {//通常時
  if ((LEDの状態==0) && (RA1==1)) {//消灯時はRA1は無視
   秒カウンタ++;//5秒待ち状態に入る
  }  
 } else {//5秒待ち状態に入っている時
  秒カウンタ++;
  if (秒カウンタ>=500) {//1ミリ秒×500=5秒
   LED消灯;
   LEDの状態=0;//LEDを消す
   秒カウンタ=0;//5秒待ち状態から通常状態に戻る
  }
 }
 割り込みを再許可する
}

割り込み間隔を1ミリ秒にしているのは、チャタリング防止の意味も兼ねていて、エッジノイズやパルスなど、1ミリ秒より短い変化は拾いません。

    • good
    • 0

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

新しく質問する

このQ&Aを見た人が検索しているワード


このカテゴリの人気Q&Aランキング

おすすめ情報