web-dev-qa-db-ja.com

STM32デバイスで予期しないリセットをデバッグするにはどうすればよいですか?

STM32F107チップを使用してCで開発を行っていますが、特定の関数を呼び出すと、ある時点でデバイスがリセットされ始めました。私はデバッガーを持っておらず、デバッグはシリアルポートを介したプレーンテキストです。

レジスタにアクセスしてリセットの原因を確認できた他のマイクロコントローラをいくつか使用しましたが、このデバイスに相当するものが見つからないようです。 Cortex-M3のハードウェア例外を認識していますが、これらのハンドラー内にいるとusart経由でテキストを送信できないように見えるため、それらの1つがトリガーされているかどうかはわかりません(おそらくTXが原因です)関数は割り込みを使用しますか?)。

そこで、このデバイスで私よりも経験のある人に尋ねることにしました。このような状況をデバッグするために通常何が行われるのでしょうか。

[〜#〜]編集[〜#〜]

開発者の1人がWWDGウォッチドッグをアクティブにし、フォールトハンドラーから情報を取得する前にハードウェアをリセットしていました。間違った場所を指しているポインタで関数を呼び出したため、ハードフォールトでした。ただし、誰かが、たとえばハードフォールト(@dwelchのアイデア)に保存されているレジスタからCコードを指すための詳細(またはそれに関する資料)を提供してくれることを期待して、この質問を続けます。

12
ivarec

Cortex M3は、あなたの生活を楽にする優れた障害処理機能を備えています。障害が発生すると、PCやLRなどの複数のレジスタが自動的にスタックされ、障害ステータスレジスタがバス障害のアドレスなどを通知します。

優れたフォールトハンドラーを実装する必要があります(たとえば、ここのハードフォールトハンドラー: http://blog.frankvh.com/2011/12/07/cortex-m3-m4-hard-fault-handler/ )スタックされたレジスタを出力し、障害ステータスレジスタをデバッグします。

印刷にはUARTを使用する必要があります。割り込みに依存しない、障害ハンドラーから使用するための独自の単純なカスタムバージョンのprintfを作成するだけです。バイトをuartTxデータレジスタに直接書き込むだけです。バイト完了をポーリングします。

11
TJD

デバッグ用の割り込みハンドラについて述べたこととは別に、一部のSTマイクロには、電源投入時(つまり、リセット後)に読み取ることができるリセットソースレジスタもあります。 cortex Mファミリ(m0/m3/m4)の場合、レジスタはRCC_CSRです。 http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf

残念ながら、ハードフォールトなどの詳細を知ることはできませんが、ウォッチドッグ(ウィンドウまたは独立)がトリップしたかどうかはわかります。

3
embeddedninja

リセットと言うときは、割り込みやハンドラーの1つではなく、リセットベクトルにヒットしたと思います。それは確かにチップをリセットしてソフトウェアを最初からやり直すと言っているのですか、それともどこかにぶら下がっていると言っているのですか?または、ベクトルテーブルがすべてリセットベクトルを指しているのですか?

続行する方法は、実際に見ているものによって異なります。より明確または具体的にする必要があります。あるいは、それを理解するための支援が必要な場合もあります。

通常、未使用のベクトルを、それ自体に分岐する単純なハングコード行にマップします。後でそれらのいくつかを実際のコードに再マップするかもしれません。

cortex-mは、Cコードを指すことができるという点で非常に優れています。例外が発生していると思われる場合は、現在のモードを把握するのに役立つ何かを取得するルーチンを指していると思われる場合は、リンクレジスタにその情報があるか、どこかにcsrがあり、それを出力して無限ループに入ります。 。ベクターテーブルの未使用部分に、この汎用デバッグ関数のアドレスを入力します。

そこから、なぜその例外が発生するのかを理解する必要があります。たとえば、整列されていないアクセスのようなものである可能性があります。知っているハンドラーを完全にセットアップする前にデバイスを初期化しようとしたときに、割り込みが生成された可能性があります。

これを実行しながら、より多くの回答や情報を使用して質問を編集します。

1
old_timer

デバッガーがないことを考えると、マイクロコントローラー上に役立つ周辺機器を見つけることをお勧めします。おそらく、トグルできるLEDか、オシロスコープに接続できる使用されていない単純なGPIOピンがあります。 GPIOピンを十分にゆっくりと切り替えると(1 Hzより速くなく、メーターによってはもっと遅くなる可能性があります)、スコープの代わりに電圧計を使用できます。追跡するまで、各例外ハンドラーのLEDまたはGPIOピンを一度に1つずつ切り替えるコードを配置します。複数のGPIOピンを使用できる場合は、プロセスを高速化できます。リセットの原因となっている特定の関数のラッパーを作成することもできます。ラッパー関数は、ブレーク関数が実行される直前に有効になっている割り込みのリストを送信します。このようにして、有効になっていないものをテストする時間を無駄にする必要はありません。

この場合のGPIOピンの利点の1つは、割り込みを必要としないことです。割り込みが必要なもの(この場合はUSARTなど)には近づかないことが最善です。優先度の高い例外が原因でリセットが発生している場合、デバッグコードは実行されません。

リセットが初期化されていないポインタによって引き起こされることも一般的です。関数ポインタをゼロに設定すると、実行はリセットのように見えます。この場合、USART初期化コードは、バイトがUSARTによって完全に送信される前に実行されている可能性があり、この場合、USARTはデバッグツールとして使用できなくなります。

1
semaj

残念ながら、「正しい」ことはSTM32では実用的ではありません。それは、ソースコードの知識を持ち、スタックを巻き戻し、障害の原因となる完全な呼び出しスタックと行番号を提供できる大きな例外ハンドラーを配置することです。これには、アプリケーションからSTM32のフラッシュメモリにすべてのデバッグ情報を追加する必要があり、それは実用的ではありません。

IDEをだまして、コールスタックを提供する方法があります。詳細を説明しますが、書き忘れたので忘れました。手動で変更する必要があると思いますあるシャドウレジスタから別のシャドウレジスタへのスタックポインタ。

私が通常行うことは、ハードフォールト例外ベクトルにブレークポイントを設定し、ブレークポイントがヒットしたときにすべてのレジスタを確認することです。それらをプラスチック爆薬で行われた殺人からの法医学的証拠と考えてください。レジスタの値はあなたにアイデアを与えるでしょう。 0x20000000で始まるレジスタ値はRAMアドレスです。0x08000000で始まるレジスタ値はフラッシュアドレスです。逆アセンブラを開いてそれらのアドレスを入力します。おそらく変数または関数に直接移動します。それらのメモリ位置。それでも問題が解決しない場合は、スタックポインタを確認します。スタックポインタのメモリ位置を確認し、同じトリックを実行します。例外が発生した関数を特定するのに十分な破片を常に見つけました。

0
Mark Lakata

以下のコードを使用してデバッグできます。

void HardFault_Handler(void)
{
    __asm volatile
       (
           " tst lr, #4                                                \n"
           " ite eq                                                    \n"
           " mrseq r0, msp                                             \n"
           " mrsne r0, psp                                             \n"
           " ldr r1, [r0, #24]                                         \n"
           " ldr r2, handler2_address_const                            \n"
           " bx r2                                                     \n"
           " handler2_address_const: .Word prvGetRegistersFromStack    \n"
       );

  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

これも追加します。

void prvGetRegistersFromStack( uint32_t *pulFaultStackAddress )
{
    /* These are volatile to try and prevent the compiler/linker optimising them
    away as the variables never actually get used.  If the debugger won't show the
    values of the variables, make them global my moving their declaration outside
    of this function. */
    volatile uint32_t r0;
    volatile uint32_t r1;
    volatile uint32_t r2;
    volatile uint32_t r3;
    volatile uint32_t r12;
    volatile uint32_t lr; /* Link register. */
    volatile uint32_t pc; /* Program counter. */
    volatile uint32_t psr;/* Program status register. */

    r0 = pulFaultStackAddress[ 0 ];
    r1 = pulFaultStackAddress[ 1 ];
    r2 = pulFaultStackAddress[ 2 ];
    r3 = pulFaultStackAddress[ 3 ];

    r12 = pulFaultStackAddress[ 4 ];
    lr = pulFaultStackAddress[ 5 ];
    pc = pulFaultStackAddress[ 6 ];
    psr = pulFaultStackAddress[ 7 ];

    /* When the following line is hit, the variables contain the register values. */
    for( ;; );
}

私はこれを使用して、ハードフォールトに入る前にレジスタの値を取得しています。必要に応じて、レジスタを追加することもできます。

0
Dheeraj Kumar