別スレッドで時間待ちについて盛り上がったので、私はこうしてます。のサンプルソースです。
コンペアマッチタイマを常時動作中を前提にしています。(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周期タイマ
あれ~! 改行が消えてるぅ....
読める気がしません
ご迷惑おかけしました。改行の消えは修正しました。
max±分解能の誤差は、許容範囲と割り切りる必要があるかも。
ご意見聞かせてください。
LEON さん
私も前には1mS割り込みやってた時があります、1mS割り込みやるとどうしてもCPU時間が気になっています1/10は消費しないとは思うが、1/100ぐらいにはなりはしないかと、気兼ねします、計算するとごくわずかとは思われますが、私の場合10mSであれば許せるが、「1mSはちょっと」、と思います、割り込みは全ての動作に影響しますので、ご注意を、その結果割り込みではなくフリーランのカウンタ値を任意に確認する方法で行えばCPU時間の消費は0ですので動作に影響を与えません。
IKUZOさん
ありがとうございます。たしかに、使用するCPU、システムの用途によっては、1ms周期割込みの処理が重荷に感じるものもあるでしょうね。
で、割込み軽減策としてカウンタの無限インクリメント(0~0xFFFF,0~ 繰り返し)のみとし、変数の管理、計算、判定等は、割込みの外でやらせています。
IKUZOさんと同様、要求機能、特性とのバランスを考慮して、周期時間(~数十ms)を設定しています。たまたま早い周期タイマを使用することが多く、それを利用しているだけです。
割込み処理は最小限に。フリーランカウンタの利用。は、まさにその通りで同意です。
昔、割込み内処理に依存傾向のお客様に対し、割込み処理最小限による動作比較の検証結果を提示し、苦労してようやく納得してもらえたっけ。
私も、ほぼ毎回同様の処理を組み込んでいますね。
どこかのタイマを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 箇所ぐらいかな。
ありがとうございました(感謝!)
恐れ入ります。