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
(「相対ジャンプ」)の場合、値は明らかに再計算する必要があります)