30

誰かがArduinoブートローダーがどのように機能するか説明できますか?私はここで高レベルの答えを探していません。コードを読んで、その要点を理解しました。

Arduino IDEとブートローダーコードの間で発生する一連のプロトコルの相互作用があり、最終的には、シリアルインターフェイスを介して送信されるプログラムでフラッシュを自己プログラムする多数のインラインアセンブリ命令が生成されます。

私がはっきりしていないのは270行目です。

void (*app_start)(void) = 0x0000; 

...これは、関数ポインタの宣言およびNULLへの初期化として認識しています。ブートローダーがユーザーがロードしたコードの実行に委任することを目的とした場所で、app_startへの後続の呼び出しがあります。

確かに、app_startこれをすべてまとめるには、ある時点でNULL以外の値を取得する必要があります。ブートローダーのコードにそれが表示されていません...ブートローダーによってロードされるプログラムによって魔法のようにリンクされていますか?ブートローダーのメインは、チップのリセット後のソフトウェアへのエントリポイントであると思います。

70ほどのアセンブリ行にまとめられているのは、メインプログラムにapp_startが実際にどこにあるかを伝えるシークレットデコーダーリングである必要がありますか?それとも、Arduino IDEによって利用されている暗黙の知識ですか?私が知っているのは、誰かがapp_startを0以外の場所を指すように変更しない場合、ブートローダーコードはそれ自体で永久にスピンするだけだということです...それで、トリックは何ですか?

編集

ブートローダーコード用の個別のメモリスペースを持たないTinyAVRにブートローダーを移植することに興味があります。ブートローダーのコードが特定のヒューズ設定とチップサポートに依存していることが明らかになったので、私が本当に知りたいのは、それらのヒューズとハードウェアがないチップにブートローダーを移植するのに何が必要かということだと思います。サポート(ただし、セルフプログラミング機能はまだあります)?

4

2 に答える 2

43

NULLの場合

アドレス0はnullポインタを作成しません。「nullポインタ」はもっと抽象的なものです。適用可能な関数が無効であると認識する必要がある特別な値です。Cは、特別な値が0であると言い、言語はそれを「未定義の動作」と言いますが、マイクロコントローラーの単純な世界では、通常、非常に明確な効果があります。

ATmegaブートローダー

通常、リセット時に、AVRのプログラムカウンター(PC)は0に初期化されるため、マイクロコントローラーはアドレス0でコードの実行を開始します。

ただし、ブートリセットヒューズ( "BOOTRST")が設定されている場合、プログラムカウンターは代わりに、メモリの上端にあるブロックのアドレスに初期化されます(ヒューズの設定方法によって異なります。データシートを参照してください( PDF、7 MB)詳細)。そこから始まるコードは何でもできます。本当に必要な場合は、ICSPを使用すれば独自のプログラムをそこに置くことができます(ブートローダーは通常、自分自身を上書きできません)。

ただし、多くの場合、これは特別なプログラム(ブートローダー)であり、外部ソース(UART、I 2 C、CANなどを介して)からデータを読み取って、プログラムコード(内部または外部メモリに格納されている)を書き換えることができます。マイクロ)。ブートローダーは通常、文字通り何でもよい「特別なイベント」を探しますが、開発の場合、最も便利なのは、新しいコードをプルするデータバス上の何かです。(本番環境では、ほぼ瞬時にチェックできるため、ピンの特別なロジックレベルになる可能性があります。)ブートローダーが特別なイベントを検出すると、ブートローディングモードに入り、プログラムメモリを再フラッシュします。それ以外の場合は、制御を渡します。ユーザーコードに移ります。

余談ですが、ブートローダーのヒューズとアッパーメモリブロックのポイントは、元のソフトウェアを変更せにブートローダーを使用できるようにすることです(ブートローダーのアドレスまで拡張されない限り)。元のHEXと目的のヒューズだけでフラッシュする代わりに、元のHEX、ブートローダー、変更されたヒューズ、およびpresto、ブートローダーを追加してフラッシュすることができます。

とにかく、 STK500のプロトコルを使用していると私が信じているArduinoの場合、UARTを介して通信を試み、割り当てられた時間内に応答がない場合は次のようになります。

uint32_t count = 0;
while(!(UCSRA & _BV(RXC))) { // loops until a byte received
    count++;
    if (count > MAX_TIME_COUNT) // 4 seconds or whatever
        app_start();
}

または、予期しない応答が発生してエラーが多すぎる場合:

if (++error_count == MAX_ERROR_COUNT)
    app_start();

制御を0にあるメインプログラムに戻します。上記のArduinoソースでは、これはapp_start();、として定義されているを呼び出すことによって行われvoid (*app_start)(void) = 0x0000;ます。

これはC関数呼び出しとして実行されるため、PCが0にホップオーバーする前に、現在のPC値をスタックにプッシュします。スタックには、ブートローダーで使用される他の変数(上記countなどerror_count)も含まれます。これはあなたのプログラムからRAMを盗みますか?PCが0に設定された後、実行される操作は、適切なC関数(最終的には返される)が実行する必要があることを露骨に「違反」します。他の初期化手順の中でも、スタックポインタをリセットし(呼び出しスタックとすべてのローカル変数を効果的に消去し)、RAMを再利用します。グローバル/静的変数は0に初期化されます。ブートローダーとユーザープログラムは独立してコンパイルされているため、そのアドレスはブートローダーが使用していたものと自由にオーバーラップできます。

ブートローダーによる唯一の持続的な影響は、ハードウェア(周辺機器)レジスターの変更です。これは、優れたブートローダーが有害な状態(スリープしようとしたときに電力を浪費する可能性のある周辺機器をオンにする)のままになることはありません。一般に、使用する周辺機器も完全に初期化することをお勧めします。そのため、ブートローダーが何か奇妙なことをした場合でも、希望どおりに設定します。

ATtinyブートローダー

ATtinysでは、前述のように、ブートローダーのヒューズやメモリに余裕がないため、コードは常にアドレス0から始まります。ブートローダーをメモリのより高いページに配置し、RESETベクトルをそのページに向けることができる場合があります。次に、フラッシュする新しいhexファイルを受け取ったら、アドレス0:1にあるコマンドを実行し、それをブートローダーアドレスに置き換えてから、置き換えられたアドレスを別の場所に保存して、通常の実行を呼び出します。(RJMP(「相対ジャンプ」)の場合、値は明らかに再計算する必要があります)

于 2010-09-07T03:21:49.303 に答える
1

編集

ブートローダーコード用の個別のメモリスペースを持たないTinyAVRにブートローダーを移植することに興味があります。ブートローダーのコードが特定のヒューズ設定とチップサポートに依存していることが明らかになったので、私が本当に知りたいのは、それらのヒューズとハードウェアがないチップにブートローダーを移植するのに何が必要かということだと思います。サポート(ただし、セルフプログラミング機能はまだあります)?

最終的な目標によっては、ブートローダーを移植するよりも、独自のブートローダーを作成する方が簡単な場合があります。あなたは本当にその部分のためにいくつかの項目を学ぶ必要があるだけです。

1)uart tx

2)UART rx

3)セルフフラッシュプログラミング

これは個別に学習してから、ブートローダーに組み合わせることができます。ブートローダーが機能しない場合や付属のパーツが台無しになった場合でも開発を続行できるように、spiなどを使用してフラッシュを作成できるパーツが必要になります。

自分で移植するかロールするかにかかわらず、その部分に関してこれら3つの基本的なことを理解する必要があります。

于 2013-10-23T21:06:34.990 に答える