Top Page [◀◀]  2   3   4   5   6   7   8   9   ... [▶▶Last Page

自作タイマ関数で時々起動しない時がある

他のスレッドで「H8/36094:IRQ0の処理が起動しないことがある」というのを見かけましたが

SH7670で下記のようなソースで自作関数を作成しています

ソースここから→

///////////////////////////////////////////////////////////////////////
//タイマを設定する
TIME_PROC tproc[]={
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
};
BOOL bWaitTimer=FALSE;
BOOL WaitTimer(int msec,void (*pc)())
{
int i;
CMT.CMSTR.BIT.STR1=0;//タイマ停止
for(i=0; i<10; i++){
if(tproc[i].msec ==0){
tproc[i].msec =msec;
tproc[i].pc =pc;
//1m Sec タイマ開始
if(!bWaitTimer){
bWaitTimer=TRUE;
cpu_ms1_start //タイマを開始させるマクロ
}
CMT.CMSTR.BIT.STR1=1;//タイマ開始
return TRUE;
}
}
//登録できない
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////////
//1mSインターバルタイマ
void cmi1_(void)
{
BOOL flg;
int i;
void (*pc)();
CMT.CMCSR1.BIT.CMF &= 0;
CMT.CMSTR.BIT.STR1=0;
flg=FALSE;
for(i=0; i<10; i++){
if(tproc[i].msec > 0){
tproc[i].msec--;
if(tproc[i].msec == 0){
pc = tproc[i].pc;
tproc[i].pc=0;
pc();
}
}
if(tproc[i].msec > 0)flg=TRUE;
}
if(!flg){
bWaitTimer=FALSE;
return;
}
CMT.CMCSR1.WORD=0;
CMT.CMCNT1.WORD=0;
CMT.CMCOR1.WORD=1000;
CMT.CMCSR1.BIT.CKS=1;//11:Pφ/512
CMT.CMCSR1.BIT.CMIE=1;
CMT.CMSTR.BIT.STR1=1;
cpu_ms1 ++;
}
///////////////////////////////////////////////////////////////////////
//タイマ関数 end
///////////////////////////////////////////////////////////////////////

→ソースここまで、これを使用するには

void test(void)

{

}

に飛ばしたい場合に

WaitTimer(100, &test );

等とすると、100ミリ秒後にtest()が実行されるという仕組みですが

どうやらたまに実行されない場合があるということで、なにが原因なのか思案しています

アドバイスお願いできませんでしょうか?

  • cmi1_(1msインターバルの割り込み関数)が常時動作しているとしたら、
    WaitTimer関数での処理中に割り込まれると、状態がおかしくなる可能性があります。
    WaitTimer関数での処理中は割り込み禁止にした方が良いと思います。
  • In reply to Higetaka:

    Higetakaさん、アドバイスありがとうございます
    遅延関数登録後にはWaitTimer関数が常時動作いたします、これを止めますとミリ秒カウントしなくなるのではと思いますが?というのがその中でtproc[i].msec--;if(tproc[i].msec == 0){をやっていまして割り込み中にマイナスしていき0になった時に遅延関数実行して登録削除のようにしています。
  • In reply to IKUZO:

    わわいです
    なんかいろいろツッコミどころが多そうなコードではありますが。。
    その関数が実行されないときって、他のタイマ関数は実行されるんでしょうか?
  • In reply to IKUZO:

    > 遅延関数登録後にはWaitTimer関数が常時動作いたします
    常時動作するのはcmi1_関数の事ですよね。

    その後にWaitTimer関数による登録が何回も発生するとして、
    問題が生じそうなケースを考えてみました。
    ---
    BOOL WaitTimer(int msec,void (*pc)())
    {
      CMT.CMSTR.BIT.STR1=0;//タイマ停止
      ★ この後で割り込みが発生する事があります。
      タイマを停止しても、直前に割り込み要因が成立していれば、
      割り込みは発生します。cmi1_関数が実行されるとタイマは再び
      動作し始めます。そしてこの後の処理でも割り込みががんがん
      発生する可能性があります。タイマ登録した関数の処理時間が
      合計で1msを超えれば、数回の割り込みがこの後で発生する
      かもしれません。

      if(tproc[i].msec ==0){
        ★ ここで割り込み禁止にしたい。
          tprocテーブル操作に関して、割り込みとの競合を避ける。
        tproc[i].msec =msec;
        ★ ここでmsecを0以外にした途端、割り込みでtprocが操作されることがある。
        tproc[i].pc =pc;
        //1m Sec タイマ開始
        if(!bWaitTimer){
          bWaitTimer=TRUE;
          cpu_ms1_start //タイマを開始させるマクロ
        }
        CMT.CMSTR.BIT.STR1=1;//タイマ開始
        ★ ここで割り込み許可
        return TRUE;
      }
  • cmi1_() の中の

    CMT.CMCSR1.BIT.CMF &= 0;
    

    は単純に & は不要でしょう。

    cpu_ms1_start //タイマを開始させるマクロ
    

    の内容が不明ですが、これが原因である可能性も考えられますね。

  • In reply to わわい:

    わわいです
    まずはツッコミどころをいくつか。
    ・タイマ割り込み関数内でタイマの初期化している
    fujitaさんも言ってますが、
    >cpu_ms1_start //タイマを開始させるマクロ
    これもおそらくタイマの初期化してませんか?
    そこらへんで割り込みが輻輳してトチ狂ってると予想されます
    ・タイムアウト関数の実行時間が不明、また、タイムアウト関数が割り込み内で実行されている
    まー、ここらへん、いかにも初心者あるあるのコードなんですが、とりあえず現時点での問題はないんでしょうけど、これから色々タイマ関数を追加していく過程でコケるのが目に見えるようです
    割り込み関数とメインループとの輻輳をあえて大量生産するようなコードはちと感心しません。
    また、割り込み関数の実行時間が不明、割り込み内でのスタック消費が不明、ってのはいかにも危ういですねー
    ココらへんでトラブルと、フツーのデバッグでは行き詰まるのは目に見えてます
    タイムアウト関数の実行はメインループ内でしましょうよw
    #まー、タイムアウトで関数を実行させるって自体が、ちとアレですが
  • In reply to わわい:

    わわいさん
    「他のタイマ関数」そうですね、ウオッチドッグタイマ等は実行されています。
  • In reply to Higetaka:

    Higetakaさん
    ご丁寧に説明してくださり、一時的に「割り込み禁止」ということだったんですね、
    「タイマを停止しても、直前に割り込み要因が成立していれば、
      割り込みは発生します。cmi1_関数が実行されるとタイマは再び
      動作し始めます。そしてこの後の処理でも割り込みががんがん
      発生する可能性があります。タイマ登録した関数の処理時間が
      合計で1msを超えれば、数回の割り込みがこの後で発生する
      かもしれません。」
    なるほど、そうですね、この現象が発生するのは、複数登録している場合に限らず発生します、例えば極端にTIME_PROC tproc[]を廃止してシングルにしても不都合が発生するのを確認しています。

  • In reply to fujita nozomu:

    fujita nozomuさん
    いつもお世話になります、cpu_ms1_startのマクロでございますが
    #define cpu_ms1_start \
    CMT.CMSTR.BIT.STR1=0;\
    CMT.CMCSR1.WORD=0;\
    CMT.CMCNT1.WORD=0;\
    CMT.CMCOR1.WORD=1000;\
    CMT.CMCSR1.BIT.CKS=1;\
    CMT.CMCSR1.BIT.CMIE=1;\
    CMT.CMSTR.BIT.STR1=1;
    のようなものです、
    CMT.CMCSR1.BIT.CMFについてはマニュアル記載では次のように説明しています、
    7 CMF 0 R/(W)* コンペアマッチフラグ
    CMCNT とCMCOR の値が一致したか否かを示すフラグです。
    0:CMCNT とCMCOR の値は不一致
    [クリア条件]
    • CMF=1 を読み出し後、CMF に0 を書き込んだとき
    1:CMCNT とCMCOR の値が一致
    のように書いてあったのと、割り込み要因はクリアした方が良いと思ったのか、実際にしないといけなかったのか定かではありませんが、昔からの流れで読みだした後に0を設定するようなことしています、(0:CMCNT とCMCOR の値は不一致)ですからCMCNT=0でクリアされるので必要ありませんよね。
  • In reply to わわい:

    わわいさん
    C言語の初心者で、自分にしては、なかなかいい発想だと思ったのですが、いざ使ってみると信頼性が全くなくて使用できずにいます「割り込み関数の実行時間が不明、割り込み内でのスタック消費が不明、ってのはいかにも危ういですねー」まーそれは割り込み中というのは承知してますから、遅延関数で実行されるのはLEDを消すためにポートをLOWにするとか、実行フラグをFALSEにするとかの後処理です、何かのトリガーのために使用したりです、でも絶対の信頼性がないとダメなので、そういうものを作りたいわけです、WindowsのAPIのtimer関数のようなものを目指したのですが、やっぱり自分で作らないとだめですよね。
  • In reply to IKUZO:

    わわいです
    >遅延関数で実行されるのはLEDを消すためにポートをLOWにするとか、実行フラグをFALSEにするとかの後処理です
    で、あればいいんでしょうけどねえ。
    そういう自分のコードの中の約束事をずっと覚えておけばいいんです。そこの関数を登録するときに、ここは割り込み内だから、変数を使うときはメインルーチンとの輻輳を気にしなければならない、スタックを使いすぎないように、時間がかかる処理はだめ、かんたんな処理しか登録できない、と。
    まあ、それで今組んでるやつに関しては大丈夫かもしれないです。きちんと動いているならそれでOKOK。

    しかし、半年たって修正の必要が出たときにそれをキチンと覚えておけるか、とか、会社に新人が入ってきて、そのコードを引き継いだときにどーなるか、
    あるいは別の仕事で、ああ、タイマルーチンは前に組んだこれあるからそのままもってきたらいいぢゃん、ああ、ちゃんと動くし、わざわざ組まなくてラッキやねー
    とかなんとか考えていくと、なんか暗いストーリーしか思い浮かばないですねー

  • In reply to IKUZO:

    現状のコードでは
    「合計で1msを超えれば、数回の割り込みがこの後で発生するかもしれません。」
    というのは、ないかもしれないと思えてきました。
    (割込み側でタイマを停止してから関数呼び出しをしているので)

    今のコードの延長でも、資源管理をしっかりすれば、なんとかなると思いますよ。
    管理するべき資源はタイマ(CMT)とテーブル(tproc)です。
    WaitTimerは急に割り込まれても大丈夫なように割り込み禁止でガードをかけるのがポイントです。
    (但し、禁止期間はなるべく短くしたいものです。他の緊急な割り込みが遅れたりする事があるので)

    今の延長で私がデザインするとしたら、以下のような疑似コードにします。

    // IKUZOさんのコードとほぼ同じ
    // 但し、CMTの制御はStartだけ行う。
    WaitTimer()
    {
      ★ タイマ停止は行わない
      for (i) {
        if (tproc[i]が未使用) {
          ★ テーブル&CMT制御は割り込みに邪魔されないようにする。
          disable_irq
          テーブル登録
          if (CMT停止中) {
            CMT Start
          }
          enable_irq
        }
      }
      ★ タイマ再開は行わない
    }

    // IKUZOさんのコードとほぼ同じ
    // 但し、CMTの制御はStopだけ行う。
    cmi1_()
    {
      ★ タイマ停止は行わない
      waiting = false;
      for (i) {
        if (tproc[i].msec > 0) {
          tproc[i].msec--;
          if(tproc[i].msec == 0){
            func呼び出し
          }
        }
        if (tproc[i].msec > 0)
          waiting = true;
      }
      ★ 有効な登録がなければ停止する
      if (!waiting) {
        CMT Stop
      }
      ★ タイマ再開は行わない
    }

  • In reply to わわい:

    わわいさん
    なにか別のいい方法て、ご存知ないでしょうか?
  • In reply to Higetaka:

    Higetakaさん
    いろいろ考えていただき、感謝します、ご提案の内容を早速組み込んで評価してみたいと思います。
  • In reply to IKUZO:

    わわいです
    8bitCPUの時代から使っているタイマルーチン群です
    1msのタイマ割り込みルーチンの中では32bit変数をインクリメントさせているだけです
    timeout_set関数で、タイムアウト時間を設定し、timeout_chk関数で、timeout_setの戻り値を与えてやれば、タイムアウト時間経過していればtrueを返します。
    まあ、32bitCPUではこんなシンプルな形となりますが、16bitCPUや8bitCPUでは、tickcnt 変数の読み込みのところで一工夫必要となります。
    これで、tickcnt インクリメントでオーバーフローする場合、timeout_setでオーバーフローする場合、など、いろいろどうなるか考えてみてください。


    #include <stdint.h>
    // 32bit整数
    typedef uint32_t TIMER_T;

    volatile TIMER_T tickcnt;

    // 1ms タイマ割り込みルーチン
    void INT_TIMER1MS(void)
    {
    tickcnt++;
    // その他の処理
    }


    // タイマ設定
    // time_ms :タイマ設定値(*ms)
    // 戻り値:タイマ値
    TIMER_T timeout_set(int time_ms)
    {
    TIMER_T res;
     res=tickcnt;
     res+=time_ms;
     if(res==0) res++;
     return res;
    }

    // タイムアウト判定
    // timer : タイマ値
    // 戻り値:タイム・アウトしていれば true
    int timeout_chk( TIMER_T timer)
    {
     if(timer==0) return 0;

     timer -= timeout_set(0);
     return ((int32_t)timer)<=0;
    }

Top Page [◀◀]  2   3   4   5   6   7   8   9   ... [▶▶Last Page