保持時間はタイマー割り込みの組み込みマイクロコントローラ
質問
この質問は、プログラムの小さなマイコンなします。特に、私の為のものが一般的である。
私も数倍以下のパターンを保持時間:
タイマー割り込みコード(タイマーの火災に毎秒):
...
if (sec_counter > 0)
sec_counter--;
...
主力コード(非割込み):
sec_counter = 500; // 500 seconds
while (sec_counter)
{
// .. do stuff
}
の主力コードを繰り返しの設定はカウンターで受け付けており、さまざまな価値観を持った(秒)。
ていて思ったのがレースの条件をする時もこの課題 sec_counter
の主力コードな原子.例えば、PIC18の結果、以下のように表示され4ASM諸表の読み込み中の各バイトは時間を選択し、適切なバイトをメモリから銀行の前ます。場合に割り込みコードの中の最終値が壊れる可能性があります。
不思議の場合、値を割り当てられた以上256の課題 は 原子なので問題はありません。
私はすぐにこの問題なのか。どのパターンの使い分けの実施などの挙動を確認してください。◆いくつかのオプション:
- 無効に割り込みを行う前に各課題へのsec_counterを後ではないか
- 使われない、割込みが別のタイマー開始した後にポーリング.この、ワンランク上のプレミアムが使用するアイテムとして、タイマー(前の場合、1秒焼きタイマーで他の目的に使用していくでしょう。
その他の考えになりますか。
解決
PICのアーキテクチャは、それを取得として原子です。これは、メモリファイルへのすべての読み出し - 変更 - 書き込み操作は「アトミック」であることを保証します。それは全体のリードモディファイライトを実行するために4クロックを要するが、すべての4クロックは、単一の命令で消費され、次の命令は、次の4クロックサイクルを使用します。これは、パイプラインが動作する方法です。 8クロックで、2つの命令がパイプラインである。
値は8ビットよりも大きい場合PICは、8ビット・マシンであり、より大きなオペランドは複数の命令で処理されるように、は、それが問題となります。それは、原子の問題を紹介します。
他のヒント
それは最も簡単な選択肢であることを思わ必要な値であることを確認した値を書き込みます。
do {
sec_counter = value;
} while (sec_counter != value);
ところで、あなたはCを使用している場合、変数揮発性にする必要があります。
あなたが値を読み取る必要がある場合は、あなたはそれを二度読むことができます。
do {
value = sec_counter;
} while (value != sec_counter);
あなたは間違いなく、カウンターを設定する前に割り込みを無効にする必要があります。醜いそれができるように、それが必要です。常にハードウェアレジスタまたはISR方法に影響を与えるソフトウェア変数を設定する前に割り込みを無効にすることをお勧めします。あなたがC言語で記述している場合は、非アトミックとして、すべての操作を考慮すべきです。あなたが生成されたアセンブリ何度も見ていることが判明した場合、アセンブリ内のCプログラムを放棄する方が良いかもしれません。私の経験では、これはほとんどの場合ではありません。
議論の問題に関しては、これは私が提案するものです。
ISR:
if (countDownFlag)
{
sec_counter--;
}
とカウンタを設定します:
// make sure the countdown isn't running
sec_counter = 500;
countDownFlag = true;
...
// Countdown finished
countDownFlag = false;
あなたは余分な変数が必要と機能のすべてをラップすることをお勧めします:
void startCountDown(int startValue)
{
sec_counter = 500;
countDownFlag = true;
}
起動方法(および必要に応じて醜さを隠す)あなたは抽象的なこの方法で。たとえば、あなたが簡単にメソッドの呼び出し元に影響を与えることなく、ハードウェアタイマを開始するためにそれを変更することができます。
sec_counter変数へのアクセスがアトミックではありませんので、あなたが確定的な動作をする場合は、あなたのメインライン・コードでこの変数にアクセスし、アクセスした後、割り込みの状態を復元する前に割り込みを無効にしないようにする方法は本当にありません。これはおそらく、このタスクのためのHWタイマーを(あなたはあなたにも1を使用する場合があり、その場合にはタイマーの黒字を、持っていない限り)専用よりも良い選択でしょう。
経過時間を追跡するために、タイマオーバフローを使用しそこでのルーチンがあります。具体的に "tick.c" と "tick.h"。ちょうどあなたのプロジェクトにこれらのファイルをコピーます。
これらのファイル内には、彼らはそれを行う方法を確認することができます。
これは、256の未満の移動は、原子であることについてとても好奇心はありません - 8ビットの値を移動することはあなたが得るようアトミックですので、1つのオペコードです。
PICなどのマイコンに最適なソリューションを使用すると、タイマー値を変更する前に割り込みを無効にすることです。あなたがしたい場合は、メインループで変数を変更し、それを処理するときにも、割り込みフラグの値を確認することができます。その変数の値を変更し、あなたも同様ISRからそれを呼び出すことができます機能確認します。
さて、何を比較・アセンブリ・コードは次のようなものですか?
は、それが下向きに数えることを考慮して考えると、それはその後、LSB、それは最初のMSBかどうかをチェックするだけで、ゼロは比較、それは安全であるべきだということ。そこ破損かもしれないが、それは0x100のと0xffのと比較し破損した値の中間に来る0x1ffであれば、それは本当に問題ではありません。
あなたが今、あなたのタイマーを使用している方法は、それが、とにかく全体秒をカウントされません。 だから、あなたはそれを気にしない場合。最良の方法は、私の意見では、値を読み取るためにも、そしてちょうど違いを比較します。これは、より多くののOPのカップルがかかりますが、何のマルチスレッドの問題を持っていません。(タイマーが優先権を持っているので)
あなたは、時間値の詳細厳しい場合は、、私は自動的に0までカウントダウン後にタイマーを無効にし、タイマーの内部カウンタをクリアし、あなたがそれを必要と一度アクティブになります。
適切な機能を(主になり、コード部分)を移動し、それは条件付きISRによって呼び出される必要があります。
また、ISRが高PRIO割り込みになるこのタイマーを選択し、ダニを遅らせるか、行方不明の任意の並べ替えを避けるために(PIC18は2つのレベルがあり)ます。
一つのアプローチは、バイトの変数を保持し、割り込み、および少なくとも一回256回カウンターをヒットされて呼び出される何かを持っていることです。以下のような何かをします:
// ub==unsigned char; ui==unsigned int; ul==unsigned long ub now_ctr; // This one is hit by the interrupt ub prev_ctr; ul big_ctr; void poll_counter(void) { ub delta_ctr; delta_ctr = (ub)(now_ctr-prev_ctr); big_ctr += delta_ctr; prev_ctr += delta_ctr; }
わずかな変化を、あなたはLSBあなたの大きなカウンターのと同期して滞在する割り込みのカウンタを強制的に気にしない場合:
ul big_ctr; void poll_counter(void) { big_ctr += (ub)(now_ctr - big_ctr); }
誰がののマルチバイトハードウェアレジスタ(例えばタイマー読みの問題に対処していません。 タイマーがロールオーバーし、あなたがそれを読んでいる間、その第二のバイトをインクリメントすることができます。
それは0x0001ffffだと言うと、あなたはそれを読んで。あなたは0x0010ffff、または0x00010000を得る可能性があります。
16ビットペリフェラルレジスタは、あなたのコードをのの揮発性である。
任意のの揮発性の "変数" については、私は二重の読み取り技術を使用しています。
do {
t = timer;
} while (t != timer);