2

C51 コアのマイクロコントローラを使用しています。500 ミリ秒ごとに呼び出す必要がある、かなり時間のかかる大規模なサブルーチンがあります。RTOS は使用されていません。

私が今やっている方法は、10ミリ秒の既存のタイマー割り込みがあることです。メイン プログラム ループで真であることがチェックされる 50 回の割り込みごとにフラグを設定します。Flag が true の場合、サブルーチンが呼び出されます。問題は、プログラム ループがフラグを処理するまでに、すでに 500 ミリ秒を超えており、特定のコード パスの場合は 515 ミリ秒を超えることさえあります。所要時間は正確に予測できません。

明らかに、実行に時間がかかるため、タイマー割り込み内からサブルーチンを呼び出すことはできません。サブルーチンは、さまざまな条件に応じて 50ms から 89ms かかります。

サブルーチンが毎回正確に 500ms で呼び出されるようにする方法はありますか?

4

9 に答える 9

1

「アクション前」フラグと「トリガー」フラグの 2 つのフラグを使用することもできます (Mike F を出発点として使用)。

#define PREACTION_HOLD_TICKS (2)
#define TOTAL_WAIT_TICKS (10)

volatile unsigned char pre_action_flag;
volatile unsigned char trigger_flag;

static isr_ticks;
interrupt void timer0_isr (void) {
   isr_ticks--;
   if (!isr_ticks) {
      isr_ticks=TOTAL_WAIT_TICKS;
      trigger_flag=1;
   } else {
      if (isr_ticks==PREACTION_HOLD_TICKS)
          preaction_flag=1;
   }
}

// ...

int main(...) {


isr_ticks = TOTAL_WAIT_TICKS;
preaction_flag = 0;
tigger_flag = 0;
// ...

   while (1) {
      if (preaction_flag) {
          preaction_flag=0;
          while(!trigger_flag)
             ;
          trigger_flag=0;
          service_routine();
      } else {
          main_processing_routines();
      }
   }
 }
于 2008-10-24T17:04:18.240 に答える
1

それを保証する方法はないと思いますが、この解決策は許容できる代替手段を提供する可能性があります。

フラグを設定するのではなく、値を変更することをお勧めしますか?

これがどのように機能するかです。

1/ 値をゼロから開始します。

2/ 10 ミリ秒の割り込みごとに、ISR (割り込みサービス ルーチン) でこの値を 10 ずつ増やします。

3/ メイン ループで、値が >= 500 の場合、値から 500 を引き、500 ミリ秒のアクティビティを実行します。

値を変更する際には、タイマーとメイン プログラムの間の競合状態に注意する必要があります。

これには、待ち時間や期間に関係なく、関数が 500 ミリ秒の境界にできるだけ近く実行されるという利点があります。

何らかの理由で関数が 1 回の反復で 20 ミリ秒遅れて開始した場合、値は既に 520 であるため、関数はそれを 20 に設定します。つまり、次の反復まで 480 ミリ秒しか待機しません。

それがあなたが望むものを達成するための最良の方法であるように私には思えます。

私は何年もの間8051に触れていませんでした(それがC51がターゲットとしていると仮定すると、あなたの説明を考えると安全な賭けと思われます)が、割り込みができずに50を引く命令があるかもしれません。ただし、アーキテクチャが非常に単純だったことを覚えているようです。そのため、ロード/変更/ストア操作を実行している間、割り込みを無効にするか遅延させる必要があるかもしれません。

volatile int xtime = 0;
void isr_10ms(void)  {
    xtime += 10;
}
void loop(void) {
    while (1) {
        /* Do all your regular main stuff here. */
        if (xtime >= 500) {
            xtime -= 500;
            /* Do your 500ms activity here */
        }
    }
}
于 2008-10-17T12:09:46.480 に答える
1

ここには、相反する/熟考されていない要件があると思います。実行に時間がかかりすぎるため、タイマー ISR からこのコードを呼び出すことはできないと言いますが (遅延する他のものよりも優先度が低いことを意味します)、何かが発生しているという事実に見舞われています。それ以外の場合は、優先度が低いはずですが、フォアグラウンド パス (「プログラム ループ」) から実行すると遅延します。

この作業が正確に 500 ミリ秒で発生する必要がある場合は、タイマー ルーチンから実行し、それによる影響に対処します。これは、プリエンプティブな RTOS が実際に行っていることです。

「プログラムループ」から実行したい場合は、そのループから実行する他の何ものも、許容できる最大遅延を超えないようにする必要があります。これは、多くの場合、他の長時間実行作業を中断することを意味しますループを通過するたびに少しの作業を行うことができるステートマシン。

于 2008-10-17T10:12:33.143 に答える
1

RTOS を使用するか、独自の単純な RTOS を作成することをお勧めします。

ニーズを満たす RTOS は、次のことを行うだけで済みます。

  • 定期的なタスクをスケジュールする
  • ラウンド ロビン タスクをスケジュールする
  • コンテキスト切り替えを実行する

要件は次のとおりです。

  • 500ms ごとに周期的なタスクを実行する
  • ラウンド ロビン タスクを実行する間の余分な時間 (タイム クリティカルでない操作を実行する)

このような RTOS では、コードが時間どおりに実行される可能性が 99.9% 保証されます。ISR で行う操作が RTOS に干渉する可能性があるため、100% とは言えません。これは、一度に 1 つの命令しか実行できない 8 ビット マイクロコントローラの問題です。

RTOS を書くのは難しいですが、実行可能です。以下は、ATMEL の 8 ビット AVR プラットフォームを対象とした小さな (900 ライン) RTOS の例です。

以下は、クラス CSC 460: リアルタイム オペレーティング システム (ビクトリア大学) 用に作成されたレポートとコードです。

于 2008-10-18T03:20:40.540 に答える
0

ああ、もう1つ検討すべき選択肢があります。x51アーキテクチャでは、2つのレベルの割り込み優先度が許可されています。ハードウェアにある程度の柔軟性がある場合は、外部割り込みピンの1つをタイマーISRによって500ミリ秒間隔で発生させてから、500ミリ秒ごとのコードの下位レベルの割り込み処理を実行させることができます。

特定のx51によっては、デバイスの内部で完全に優先度の低い割り込みを生成できる場合もあります。

Webで見つけたこのドキュメントのパート11.2を参照してください:http ://www.esacademy.com/automation/docs/c51primer/c11.htm

于 2008-10-21T23:38:33.293 に答える
0

私があなたの質問を正しく解釈しているなら、あなたは以下を持っています:

  • メインループ
  • 最大89msの期間、500msごとに実行する必要があるいくつかの優先度の高い操作
  • 少数の操作も実行する10msのタイマー。

私が見ているように、3つのオプションがあります。1つ目は、500ミリ秒の操作に優先度の低い2番目のタイマーを使用することです。10msの割り込みを処理することはできますが、完了したら、500msのタイマー割り込みの処理を続行します。

2番目のオプション-実際には、10msごとに10msの割り込みを処理する必要がありますか?計時以外のことをしていますか?そうでない場合、およびハードウェアで500ms操作の処理中に通過した10msティックの数を判別できる場合(つまり、割り込み自体を使用しないことにより)、10ms割り込み内で500ms操作を開始して処理できますか?あなたが終わったときにあなたが逃した10msのダニ。

3番目のオプション:Justin Tannerの回答に続くように、あまり問題なく要件を満たすために、独自のプリエンプティブマルチタスクカーネルを作成できるようです。必要なのは2つのタスクだけのようです。1つはメインスーパーループ用で、もう1つは500msタスク用です。

2つのコンテキスト間でスワップするコード(つまり、異なるスタックポインターを使用して、すべてのレジスターの2つのコピー)は非常に単純で、通常、一連のレジスタープッシュ(現在のコンテキストを保存するため)、一連のレジスターポップ(新しいコンテキストを復元するため)および割り込み命令からの復帰。500ミリ秒の操作が完了したら、元のコンテキストを復元します。

(厳密には、これはプリエンプティブマルチタスクと協調マルチタスクのハイブリッドだと思いますが、現時点では重要ではありません)


編集:簡単な4番目のオプションがあります。長時間の操作の前後の両方で、500msが経過したかどうかをチェックして、メインのスーパーループをたっぷりとペッパーします。正確には500ミリ秒ではありませんが、遅延を許容レベルまで減らすことができる場合があります。

于 2008-10-24T06:21:23.933 に答える
0

実行に非常に時間がかかるタイム クリティカルなルーチンがあるのはなぜですか?

ここにアーキテクチャ上の問題がある可能性があるという他のいくつかの意見に同意します。

正確な 500 ミリ秒 (またはその他) の間隔を設定する目的が、特定の時間間隔で信号の変化を発生させることである場合、前の計算に基づいて新しい信号を出力する高速 ISR を使用した方がよい場合があります。新しい計算が ISR の外で実行されるようにします。

この長期実行ルーチンが何をしているのか、特定の間隔が何のために必要なのかをよりよく説明できますか?


コメントに基づく追加:

サービスルーチンの時間が予測可能な期間であることを保証できれば、タイマー割り込みの通知を見逃す可能性があります...

例を挙げると、タイマー割り込みが 10 ミリ秒に設定されていて、サービス ルーチンに 89 ミリ秒かかることがわかっている場合は、先に進んで 41 タイマー割り込みをカウントアップしてから、89 ミリ秒のアクティビティを実行して 8 つのタイマー割り込み (42 ~ 42 ミリ秒) を逃します。 49日)。

次に、ISR が終了する (および保留中の割り込みをクリアする) と、次の 500 ミリ秒のラウンドの「最初の」割り込みが約 1 ミリ秒後に発生します。

あなたが「リソースを最大限に活用している」ということは、他のタイマーと割り込みソースも使用していることを示唆しています。間違った瞬間に。

于 2008-10-21T23:27:54.563 に答える
0

簡単な解決策の 1 つは、500 ミリ秒で発生するタイマー割り込みを使用することです...
ハードウェア設計にある程度の柔軟性がある場合は、1 つのタイマーの出力を第 2 段階のカウンターにカスケードして、長いタイム ベースを得ることができます。忘れましたが、x51 でタイマーをカスケードできることを漠然と思い出しました。

于 2008-10-21T23:21:37.680 に答える
0

これはあなたが必要とすることをしますか?

#define FUDGE_MARGIN 2    //In 10ms increments

volatile unsigned int ticks = 0;

void timer_10ms_interrupt( void )  { ticks++; }

void mainloop( void )
{
    unsigned int next_time = ticks+50;

    while( 1 )
    {
        do_mainloopy_stuff();

        if( ticks >= next_time-FUDGE_MARGIN )
        {
            while( ticks < next_time );
            do_500ms_thingy();
            next_time += 50;
        }
    }
}

注: 500 ミリ秒ごとのタスクの処理が遅れると、タスクがキューに入れられてしまい、これは望ましくない可能性があります。

于 2008-10-17T12:09:15.153 に答える