他のスレッドで「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()が実行されるという仕組みですが
どうやらたまに実行されない場合があるということで、なにが原因なのか思案しています
アドバイスお願いできませんでしょうか?
Higetakaさん ご丁寧に説明してくださり、一時的に「割り込み禁止」ということだったんですね、 「タイマを停止しても、直前に割り込み要因が成立していれば、 割り込みは発生します。cmi1_関数が実行されるとタイマは再び 動作し始めます。そしてこの後の処理でも割り込みががんがん 発生する可能性があります。タイマ登録した関数の処理時間が 合計で1msを超えれば、数回の割り込みがこの後で発生する かもしれません。」 なるほど、そうですね、この現象が発生するのは、複数登録している場合に限らず発生します、例えば極端にTIME_PROC tproc[]を廃止してシングルにしても不都合が発生するのを確認しています。
わわいです >遅延関数で実行されるのはLEDを消すためにポートをLOWにするとか、実行フラグをFALSEにするとかの後処理です で、あればいいんでしょうけどねえ。 そういう自分のコードの中の約束事をずっと覚えておけばいいんです。そこの関数を登録するときに、ここは割り込み内だから、変数を使うときはメインルーチンとの輻輳を気にしなければならない、スタックを使いすぎないように、時間がかかる処理はだめ、かんたんな処理しか登録できない、と。 まあ、それで今組んでるやつに関しては大丈夫かもしれないです。きちんと動いているならそれでOKOK。 しかし、半年たって修正の必要が出たときにそれをキチンと覚えておけるか、とか、会社に新人が入ってきて、そのコードを引き継いだときにどーなるか、 あるいは別の仕事で、ああ、タイマルーチンは前に組んだこれあるからそのままもってきたらいいぢゃん、ああ、ちゃんと動くし、わざわざ組まなくてラッキやねー とかなんとか考えていくと、なんか暗いストーリーしか思い浮かばないですねー
現状のコードでは 「合計で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 } ★ タイマ再開は行わない }
LEONです。 私も以下例のように、超々シンプルなルーチンを使っています。 分周やインタバルを変えたり、uint型にしたりで、実用用途に合わせます。 typedef unsigned short int ushort; // 符号無し2Byte #define PCLOCK 50000000 // PCLOCK=50MHz #define TM10 10 // 10ms インタバルタイマ周期時間 #define TM_CMCOR (((PCLOCK/8)/1000)*TM10)-1 // =62499 CMT0.CMCORの設定値 #define TM_1000MS (1000/TM10) // 1000ms #define TMC_100US (((TM_CMCOR+1)*100)/10000) // 100us volatile static ushort Tm1; // TMカウンタ 10ms精度. ushort型 //-- 10ms精度のタイマ監視用 --// void Int_CMT0(void) // CMT0 インタバルタイマ割込み { Tm1++; // TMカウンタ更新(ushort型) // 0x0000~0xFFFFを永遠に繰り返し } void Tm1_Start(ushort *ptm1) // 時間計測開始 { *ptm1 = Tm1; // 開始時のTm1値 } ushort Tm1_Check(ushort tm1) // 時間経過チェック { return((ushort)(Tm1 - tm1)); // 経過時間演算 } <使用例> static ushort TmChk; Tm1_Start(&TmChk); // 時間計測開始 : if(Tm1_Check(TmChk) < TM_1000MS){ // 1000ms未経過? return 等 // 未経過時の処理.他タスク実施等 } 経過の処理 ・CMT0は10msインタバルの設定で初期化時に開始。以降、動作しっぱなし。 ・メインループ中で、時間計測開始と経過時間チェックを行う。 ・ushort型なので最大655350ms(約655秒) ・開始時のTm1値が 0xFFFE、チェック時のTm1値が 0x0001 の場合、 0x0001 - 0xFFFE = 0x0003 => 30ms経過 ・±10ms誤差。1msインタバルなら±1ms誤差。 実用上、用途によっては上記で十分ではないでしょうか。 更に精度を上げたい時、以下を追加。10ms未満限定でμsレベルの精度 が得られます。(メインループが追いつかないけどね) //-- 10ms未満の計測用 --// void TmC_Start(ushort *ptmC) { *ptmC = CMT0.CMCNT; // タイマ開始時の CMT0.CMCNT値 } ushort TmC_Check(ushort tmC) { ushort tmCnt = CMT0.CMCNT; if(tmCnt < tmC) tmCnt += TM_CMCOR+1; return((ushort)(tmCnt - tmC)); // 経過時間演算 } <使用例> static ushort TmC_Chk; TmC_Start(&TmC_Chk); // 時間計測開始 : if(TmC_Check(TmC_Chk) < TMC_100US) // 100μs未経過?