時間待ち

別スレッドで時間待ちについて盛り上がったので、私はこうしてます。のサンプルソースです。

コンペアマッチタイマを常時動作中を前提にしています。(1ms周期。CMT1.CMCOR=6249の場合)

CPU違い、設定違いは適度に調整してね。 関数が超短いっす。

-----------------------------------------------------------------------------------

typedef unsigned short int  ushort;

typedef unsigned int        uint;

typedef unsigned long int   ulong;

volatile static  ushort  Tm1;    // TMカウンタ(1ms ushort型)

volatile static  ulong      Tm1L;   // TMカウンタ(1ms ulong型)

コンペアマッチタイマを常時動作(例:1ms周期。CMT1.CMCOR=6249の場合)

//*******************************************************************************

// インターバル(1.0ms)割込み

void   Int_CMT1(void)               // 144 CMT CMI1

{

    Tm1++;                          // TMカウンタを無限に更新(ushort型)

//  Tm1L++;                         // TMカウンタを無限に更新(ulong型)

}

// ↑例では、割込みフラグのクリアを省略

 

//*******************************************************************************

// タイマ監視開始

void    Tm1_Start(ushort *ptm1)

{

    *ptm1 = Tm1;                    // タイマ開始時のTm1値

}

// タイマ経過時間演算

ushort Tm1_Check(ushort tm1)

{

    return((ushort)(Tm1 - tm1));    // 経過時間演算(1ms分解能)

}

//*******************************************************************************

// CMCNT利用:タイマ開始    分解能:160ns=0.16us=1ms/(CMT1.CMCOR+1)=1000us/6250

void    TmC_St(ushort *ptmC)

{

    *ptmC = CMT1.CMCNT;             // タイマ開始時の CMT1.CMCNT値

}

// CMCNT利用:タイマ経過時間演算

ushort TmC_Ck(ushort tmC)

{

    ushort  tmCnt = CMT1.CMCNT;

    if(tmCnt < tmC)

        tmCnt += 6250;              // 6250=CMT1.CMCOR +1

    return((ushort)(tmCnt - tmC));  // 経過時間演算(160ns分解能)

}

-----------------------------------------------------------------------------------

<使い方>

#define TIM_1S      1000            // 1000ms(1sec)

#define TIMC_10US   (1000/16)       // 10us (10us/0.16us)

#define TIMC_160NS  (16/16)         // 0.16us (160ns/0.16us)

static  ushort      tm1;            //

static  ushort      tm1C;           //

例1:1ms ~ max約65secの監視。(実用上 max30sec程度がベター)

 <監視開始時>

    Tm1_Start(&tm1);                // 1秒監視開始

 <経過時間判定時>

    if(TIM_1S < Tm1_Check(tm1))     // 1秒経過?

        return;                     // yes

   // 未経過の処理。他タスク等実施可

    // 再度、経過時間判定へ

例2:0.16us ~ max1ms未満の監視。(実用上 max500us程度がベター)

 <監視開始時>

    TmC_St(&tm1C);                  // 10us監視開始

 <経過時間判定時>

    if(TIMC_10US < TmC_Ck(tm1C))    // 10us経過?

        return;                     // yes   少々誤差有り

   // 未経過の処理。他タスク等実施可

    // 再度、経過時間判定へ

<わがまま>:もっと長い時間を監視したい

 ・ushort → ulong   max49日余りの監視が可 (1ms周期タイマ時)

 ・1ms周期タイマ → 10ms周期タイマ  

Parents
  • あれ~! 改行が消えてるぅ....

  • 読める気がしません

  • ご迷惑おかけしました。改行の消えは修正しました。

    max±分解能の誤差は、許容範囲と割り切りる必要があるかも。

    ご意見聞かせてください。

  • LEON さん

    私も前には1mS割り込みやってた時があります、1mS割り込みやるとどうしてもCPU時間が気になっています1/10は消費しないとは思うが、1/100ぐらいにはなりはしないかと、気兼ねします、計算するとごくわずかとは思われますが、私の場合10mSであれば許せるが、「1mSはちょっと」、と思います、割り込みは全ての動作に影響しますので、ご注意を、その結果割り込みではなくフリーランのカウンタ値を任意に確認する方法で行えばCPU時間の消費は0ですので動作に影響を与えません。

  • IKUZOさん

    ありがとうございます。たしかに、使用するCPU、システムの用途によっては、1ms周期割込みの処理が重荷に感じるものもあるでしょうね。

    で、割込み軽減策としてカウンタの無限インクリメント(0~0xFFFF,0~ 繰り返し)のみとし、変数の管理、計算、判定等は、割込みの外でやらせています。

    IKUZOさんと同様、要求機能、特性とのバランスを考慮して、周期時間(~数十ms)を設定しています。たまたま早い周期タイマを使用することが多く、それを利用しているだけです。

    割込み処理は最小限に。フリーランカウンタの利用。は、まさにその通りで同意です。

    昔、割込み内処理に依存傾向のお客様に対し、割込み処理最小限による動作比較の検証結果を提示し、苦労してようやく納得してもらえたっけ。

  • Former Member
    Former Member in reply to IKUZO

    私も、ほぼ毎回同様の処理を組み込んでいますね。

    どこかのタイマを1ms(or 10ms)割込として、割込内でインクリメント。

     #define GetTickCount()  Tm1

    として使用しています。

    最大±1msの誤差が出るのは仕方ないですが、私の場合はその精度で十分な処理しかしないもので。

    シリアルのタイムアウトとかLED点灯制御とか、UIのための時間管理などに使ってます。

    まあ厳密な時間管理には向きませんね。

    また、大抵は1ms割込内で1秒カウンタ(32bitのUnixTimeとして使用)も作っていて、

    RTC使用時などはtime.h互換関数を作って使っています。

  • つくしさんの GetTickCount() のマクロ化もいいですね。分かりやすく、コード、処理ステップも少なくて済みそうです。

    ただ、Tm1アクセス演算中に、割込みが発生、Tm1値が更新。タッチの差で異なるTm1値で演算。に注意ですかね。ま、その辺はぬかりなく対応されているでしょう。

    監視開始から判定部が高速でループ(数μs ~ 数十μs程度)していれば、-誤差は仕方ないとしても+誤差は少なくできそうです。ご指摘の通り、この程度の精度で十分な用途向けですね。

    開始、判定処理を関数化したのは、Tm1をグローバル変数にしたくなかった。ただそれだけの理由です。

    つい数ヶ月前、より高精度な判定の必要性から、CMCNT利用版を作ったわけですが、他タスクも実行できて実用面で マル でした。 理論上max±0.16μs誤差なら十分でしょう!ってね。

    更に高精度版?  私らには無縁。ナイナイ。

  • 自分だと

    static volatile ulong millis;
    
    void Int_CMT1(void)
    {
        millis++;
    }
    
    ulong getCurrentTicks(void)
    {
        ulong olderMillis = millis;
        ulong ticks = CMT1.CMCNT;
        ulong newerMillis = millis;
        return 6250 * newerMillis + ((olderMillis == newerMillis) ? ticks : 0);
    }
    

    こんな感じの↑を用意して、

        ulong startTicks = getCurrentTicks();
    

    初期化はこんなん↑で、

        #define Millis2Ticks(m) (6250 * (m))
        if ((getCurrentTicks() - startTicks) > Millis2Ticks(1)) {
            /* 1m秒経った */
        }
    

    とか

        #define Micros2Ticks(u) (25 * (u) / 4)
        if ((getCurrentTicks() - startTicks) > Micros2Ticks(1)) {
            /* 1u秒経った */
        }
    

    とかでしょうか。検証してないので期待通り動くかわかりませんが。

  • ワッ! すばらしい! お見事!! さすが fujita nozomuさん

    ±0.16μs誤差で11分あまりの時間監視ができるものをサクッ!と仕上げてくるとは。

    ポイントもしっかり押さえてあり、完成度も高く申し分ないです。μs精度の時間監視にほぼ制限無く使えますね。

    私も機会があればと漠然と構想していただけで、必要な状況にならないと作ることは無かったでしょう。

    CPU設定、周期時間違いによる数値の相違は、6250、(25 * (u) / 4)、0.16μs、1ms 箇所ぐらいかな。

    ありがとうございました(感謝!)

Reply Children
No Data