6

私は組み込みシステム プログラミングは初めてです。勉強中にコースを受講したことはありますが、実用的なプログラミングはまだ少し先のことです。

問題は次のとおりです。オペレーティング システムを使用せずに、NXP LPC2103 マイクロコントローラー (ARM 7 ベース) で小さなシステムをプログラムする必要があります。定期的に更新する必要があるウォッチドッグタイマーがあります。システムには、TCP/IP スタックが組み込まれた GPRS モデムがあり、これを初期化すると、ウォッチドッグがタイムアウトするのに必要な時間よりも長くかかります。初期化関数を呼び出すと、システムがリセットされます。

より経験豊富な同僚と話をしたところ、メイン関数から同じ初期化関数を終了して再入力する必要があると提案されました。この関数では、関数の実行が完了するまで、ウォッチドッグ タイマーを非常に長く噛んでいます。アイデアは良さそうですが、他の経験も聞きたいです。また、参考文献 (本やウェブサイト) も役に立つかもしれません。

初期化関数からウォッチドッグタイマーを呼び出したくありません。これは良いとは思いません。

4

5 に答える 5

7

初期化関数からウォッチドッグ タイマーを呼び出したくありません。これは良くないと思います。

この 1 つの状況ではやり過ぎかもしれませんが、他の作業を実行する可能性がある長時間実行操作に使用した一般的な手法は、長時間実行関数が定期的に呼び出されるコールバック関数ポインターを受け入れるようにすることです。私が通常使用するパターンは、次のようなコールバック プロトタイプを使用することです。

int (callback_t*)(void* progress, void* context);

長時間実行される関数は、コールバックを定期的に呼び出します。進行状況を示す情報 (進行状況がどのように表現されるかは、特定の関数の詳細に依存します) と、元の呼び出し元が渡したコンテキスト値を使用します。コールバック ポインター (繰り返しますが、そのパラメーターが何を意味し、どのように解釈されるかは、完全にコールバック次第です)。一般に、コールバック関数の戻り値は、「長時間実行されているもの」をキャンセルするか、動作を変更する必要があることを示すために使用される場合があります。

このようにして、初期化関数はコンテキスト値を持つコールバック ポインターを取得し、定期的に呼び出すことができます。明らかに、あなたの状況では、これらのコールバックは、ウォッチドッグを満足させるのに十分な頻度で発生する必要があります.

int watchdog_callback( void* progress, void* context)
{
    kick_the_watchdog();

    return 0;  // zero means 'keep going...'
}


void init_modem( callback_t pCallback, void* callback_context)
{
    // do some stuff

    pCallback( 0, callback_context);

    // do some other stuff

    pCallback( 1, callback_context);


    while (waiting_for_modem()) {
         // do work...

         pCallback( 2, callback_context);
    }    
}

このパターンの良い点の 1 つは、さまざまな状況で使用できることです。たとえば、大量のデータを読み書きする関数を使用する場合があります。コールバック パターンは、進行状況を表示するために使用される場合があります。

他の長時間実行機能があることがわかった場合は、同じwatchdog_callback()機能を使用して、ウォッチドッグのリセットを防ぐことができることに注意してください。ただし、特にウォッチドッグのためにこの種のものに頻繁に依存する必要がある場合は、タスクがどのように相互作用しているかを検討し、それらをさらに分解するか、ウォッチドッグが管理するより複雑なウォッチドッグ スキームを使用する必要があります。ウォッチドッグ タイマーを間接的に満足させるために、他のタスクが相互作用する独自のタスクによって。

于 2010-06-29T17:32:56.257 に答える
6

一般に、この状況に対して私が採用したアプローチは 2 つあります。

ステート マシンの初期化

1つ目は、同僚が示唆したとおりです。メインループの一部として呼び出されるステートマシンに初期化ルーチンを実装し、初期化ルーチンの呼び出しを停止して、メインルーチンの呼び出しを開始します。

これはシンプルでクリーンな機能ですが、低周波発振器の起動などの特定の長いプロセスに関しては、少し扱いに​​くい場合があります。

時間制限のある ISR ウォッチドッグ処理

「sysstick」または同等の割り込みがある場合、別の代替手段があります。たとえば、1 ミリ秒ごとに発生する割り込みです。この状況では、(たとえば) 50 回の割り込み呼び出しごとにウォッチドッグを供給することを検討できますが、ウォッチドッグが供給される回数を制限して、初期化ルーチンが完了するまでの最大許容時間と等しくなるようにします。次に、最小ウィンドウ時間に達する前にウォッチドッグが供給されないようにするために、初期化の最後に短い同期ループを設定することが一般的に必要です (私の意見では、ウィンドウ付きのウォッチドッグがある場合)。しかし、これを実装するのは簡単です。

これは非常にクリーンな解決策であり (初期化ルーチンを不要なステート マシンにしないため)、初期化ルーチンがハングする問題に対処します。ただし、ISR でのウォッチドッグ呼び出しの制限を適用することが非常に重要です。

討論

どちらにも長所と短所がありますが、要件ごとに異なるアプローチを使用すると便利です。私は後者のソリューションを好む傾向があり、低周波オシレーター (起動に時間がかかる場合があります) のようなものを使用すると、初期化ルーチンが過度に複雑になるのを避けることができます。

他の人も他の代替アイデアを提供すると確信しています...

于 2010-06-29T10:09:33.620 に答える
4

LPC2103 のウォッチドッグは高度にカスタマイズ可能です。それを制御するための多くのオプションがあります。

初期化シーケンスが終了するまで有効にできません。

フィード間の期間を非常に長くすることができます。

問題は、ウォッチドッグを何に使用しているのかということです。

ソフトウェアが正常に動作し、フリーズしていないかどうかを確認するために使用されていた場合、AI からの ISR オプションがどのように役立つかわかりません (プログラムが動かなくなっても ISR は動作し続けることができます)。

ウォッチドッグ オプションの詳細については、MCU のユーザー マニュアルのウォッチドッグ タイマー (WDT) の章 (17) を参照してください。 http://www.nxp.com/documents/user_manual/UM10161.pdf

于 2010-06-29T12:25:19.813 に答える
3

ウォッチドッグは優れていますが、プログラムやシステムが簡単に適合しない場合は、面倒です。(一般的に)次のようなコードがある場合に最適に機能します。

Watchdog_init();

hardware_init();
subsystem1_init();
subsystem2_init();
subsystem3_init();
...
subsystemN_init();

forever {
   Watchdog_tickle();

   subsystem1_work();
   subsystem2_work();
   subsystem3_work();
   ...
   subsystemN_work();
}

非常に多くの場合、これが機能するようにプログラムを設計できます。一般に、それは非常に簡単です (ただし、完全ではありません)。

しかし、あなたのような場合、これはうまくいきません。ウォッチドッグが愛撫されるかどうかを制御するために満たす必要があるさまざまな条件を持つフレームワークを設計および作成する (またはおそらくライブラリを使用する) 必要があります。ただし、これは非常に難しい場合があります。このコードの複雑さ自体がエラーを引き起こす可能性があります。ウォッチドッグ フレームワークを除いて完璧なアプリケーションを作成しても、プロジェクトが何度もリセットされる可能性があります。または、すべてのコードが正しくなく、ウォッチドッグを継続的にかわいがり、リセットされない可能性があります。

より複雑な状況を処理するために上記のコードを変更する 1 つの良い方法は、subsystemX_work 関数を状態に合わせて変更することです。これは、関数内の静的変数を使用するか、関数ではなく関数ポインターを使用して行うことができ、実行される実際の関数を変更して、そのサブシステムの現在の状態を反映させることができます。各サブシステムはステート マシンになります。

意図的な長い待機を回避するもう 1 つの方法は、実行時間の長い関数を短い部分に分割することです。それよりも:

slow_device_init();
Watchdog_tickle();

あなたがすることができます:

slow_device_init_begin();
Watchdog_tickle();
slow_device_init_finish();
Watchdog_tickle();

そして、これを拡張して、次のようにしてウォッチドッグ タイマーを延長します。

slow_device_init_begin();
for ( i = SLOW_DEV_TRIES; i ; i--) {
   Watchdog_tickle();
   if (slow_device_init_done()) {
       break;
   }
}
Watchdog_tickle();

それでも、ますます複雑になる可能性があります。多くの場合、満たされる条件をチェックし、これらの条件に基づいてウォッチドッグをかわいがるかどうかをチェックするウォッチドッグ デリゲートを作成する必要があります。これは非常に複雑になり始めます。サブシステムの正常性をテストするために呼び出すメソッド/関数を持つサブシステムごとにオブジェクトを作成することで実装できます。ヘルス メソッドは非常に複雑になる可能性があり、そのサブシステムの状態が変化すると変更されることもありますが、コードが正しいことをできるだけ簡単に検証できるように、また、サブシステムが機能するためには、健康の測定方法を変更する必要があります。

一部のコードが定期的に実行されることを確認できる場合は、サブシステムのローカル ウォッチドッグとして機能する各サブシステムの整数を使用できます。一部のコード (タイマー割り込みハンドラー内にある可能性がありますが、必ずしもそうとは限りません) は、各サブシステムの変数をデクリメントしてテストします。いずれかのサブシステムで 0 に達した場合、ウォッチドッグはティックリングされません。

Watchdog_periodic() {
   for_each subsustem in subsystem_list { // not C, but you get the idea
      if ( 0 > --(subsystem->count_down) ) {
           // Do something that causes a reset. This could be returning and not petting
           // the hardware watchdog, doing a while(1);, or something else
      }
   }
   Watchdog_tickle();
}

次に、各サブシステムは、その count_down を正の値に設定することにより、さまざまな時間、独自の count_down をくすぐることができます。

また、実際のリセットを行うためにハードウェア ウォッチドッグを使用している場合でも、これは実際には単なるソフトウェア ウォッチドッグであることに注意してください。

また、ウォッチドッグ フレームワークが複雑になるほど、そのフレームワークでエラーが発生する可能性が高くなり、他のコードでエラーが発生して不適切に動作する可能性が高くなることにも注意してください。たとえば、次のようなポインター エラーです。

int x;
fscanf(input, "%i", x); // Passed uninitialized x rather than address of x

一部のサブシステムの count_down 値が設定される可能性があり、その結果、ウォッチドッグが必要なときに噛むことができなくなる可能性があります。

于 2010-06-29T19:16:18.533 に答える
1

WD タイマーがサービスされるコードの場所を再考することもできます。

通常、WD タイマーは、アイドル時間 (アイドル ループまたはアイドル タスク) および最下位レベルのドライバー (たとえば、GPRS モデムまたは TCP/IP 接続の MAC との間で読み取り/書き込みを行っている場合など) にサービスを提供する必要があります。 )。

これが十分でない場合、ファームウェアは遅延ルーチンで CPU サイクルを消費するだけである可能性があります。ここに WD タイマー サービスを追加しても問題ありませんが、WD サービス時間を考慮して遅延タイマーを調整する必要がある場合があります。

アプリケーションに、WD タイマー期間で許容されるよりも実行に時間がかかる、CPU を集中的に使用する長時間のタスクがあるだけの場合は、WD タイマー間隔を少し長くすることを検討してください。これは常に可能であるとは限りませんが、私は WD タイマーの参照をファームウェアの上位層から除外して、アプリケーション層の移植性を可能な限り維持したいと考えています。通常、WD タイマーはハードウェアに依存するため、コード内の WD タイマー参照はほとんど移植できません。とにかく、低レベルのドライバーはめったに移植できないため、これは通常、WD タイマーを提供するのに適した場所です。

于 2010-06-29T15:56:28.723 に答える