2

STM32 プログラミングに関する適切なドキュメントがインターネット上で見つかりませんでした。STM 自身のドキュメントには、レジスタ機能以外の説明はありません。誰かが私の次の質問を説明していただければ幸いです。

  1. STM が提供するすべてのサンプル プログラムで、main() のローカル変数は常に main() 関数の外で定義されていることに気付きました (ときどき static キーワードを使用します)。その理由はありますか?同様の慣行に従う必要がありますか?メイン内でローカル変数を使用しないようにする必要がありますか?

  2. クロック割り込みハンドル内で更新されるグローバル変数があります。別の関数内で同じ変数をループ条件として使用しています。何らかの形式のアトミック読み取り操作を使用して、この変数にアクセスする必要はありませんか? 関数の実行中にクロック割り込みの値が変化しないことをどのように確認できますか? 関数内でこの変数を使用する必要があるたびに、クロック割り込みをキャンセルする必要がありますか? (ただし、ループ条件として使用しているため、これは非常に効果がないように思えます。より良い方法があるはずです)。

  3. Keil は、アセンブリで記述されたスタートアップ コード (つまり、startup_stm32f4xx.s) を自動的に挿入します。このスタートアップ コードには、次のインポート ステートメントがあります。 IMPORT SystemInit IMPORT __main .「C」では、意味があります。ただし、C++ では、main と system_init の名前が異なります (例: _ int _main__void)。このスタートアップ コードは、「extern "C"」を使用しなくても C++ で機能することができます (私が試したところ、機能しました)。c++ リンカ (armcc --cpp) はどのようにしてこれらのステートメントを正しい関数に関連付けることができますか?

4

3 に答える 3

4

質問1:ローカル変数

STが提供するサンプルコードは、特に効率的でもエレガントでもありません。それは仕事を成し遂げます、しかし時々彼らがすることの正当な理由がありません。

一般に、変数のスコープを可能な限り小さくする必要があります。1つの関数で変数のみを使用する場合は、その関数内で変数を定義します。関数の実行後に値を保持する必要がある場合にのみ、ローカル変数に「static」キーワードを追加します。

C18コンパイラを使用したPIC18アーキテクチャなど、一部の組み込み環境では、ローカル変数はグローバル変数よりもはるかに高価です(プログラムスペースが多く、実行時間が遅くなります)。Cortex M3では、これは当てはまらないため、ローカル変数を自由に使用する必要があります。アセンブリリストを確認して、自分の目で確かめてください。

質問2:割り込みとメインループ間で変数を共有する

人々は、このグループの質問に対する答えを説明する章全体を書いています。メインループと割り込みの間で変数を共有するときはいつでも、必ずそのvolatile上でキーワードを使用する必要があります。32ビット以下の変数は、アトミックにアクセスできます(位置がずれていない場合)。

より大きな変数、またはメインループから同時に2つの変数にアクセスする必要がある場合は、変数にアクセスしている間、クロック割り込みを無効にする必要があります。割り込みが正確なタイミングを必要としない場合、これは問題にはなりません。割り込みを再度有効にすると、必要に応じて自動的に起動します。

質問3:C++の主な機能

わからない。arm-none-eabi-nmオブジェクトファイルで(またはツールチェーンで呼び出されるものを)使用nmして、C++コンパイラが割り当てるシンボル名を確認できますmain()。私は、C ++コンパイラがこの正確な理由でメイン関数を操作することを控えているに違いありませんが、私にはわかりません。

于 2011-11-13T19:16:58.210 に答える
4

ローカル変数またはグローバル変数を使用できます。組み込みシステムでローカルを使用すると、スタックがデータと衝突するリスクがあります。グローバルでは、その問題はありません。しかし、これは、組み込みマイクロコントローラー、デスクトップなど、どこにいても当てはまります。

それを使用するフォアグラウンド タスクでグローバルのコピーを作成します。

unsigned int myglobal;

void fun ( void )
{
   unsigned int myg;

   myg=myglobal;

関数の残りの部分には myg のみを使用します。基本的に、スナップショットを取得し、スナップショットを使用しています。レジスタを読んでいる場合は同じことをしたいでしょう。何かのサンプルに基づいて複数のことをしたい場合は、そのサンプルを1つ取り、その1つのサンプルで決定を下します。そうしないと、アイテムがサンプル間で変わる可能性があります。1 つのグローバルを使用して割り込みハンドラーとやり取りする場合は、1 つのフォアグラウンドで割り込みを行い、もう 1 つの割り込みをフォアグラウンドで使用する 2 つの変数を使用します。はい、そのような共有リソースを慎重に管理する必要がある場合があります。通常は、複数のことを行う必要がある場合に関係しています。たとえば、ハンドラーがそれらの変更を確認する前に、グループとしてすべてを変更する必要があるいくつかの項目がある場合は、すべての項目が変更されるまで割り込みハンドラーを無効にする必要があります。ここでも組み込みマイクロコントローラについて特別なことは何もありません。これはすべて、本格的なオペレーティング システムを備えたデスクトップ システムで見られる基本的なものです。

Keil は、C++ をサポートしている場合に何をしているかを知っており、システム レベルからこれを解決しました。私は Keil を使用しません。このようなマイクロコントローラーには gcc と llvm を使用します。

編集:

これが私が話していることの例です

https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/blinker05

タイマー ベースの割り込みを使用する stm32 では、割り込みハンドラーがフォアグラウンド タスクと共有される変数を変更します。フォアグラウンド タスクは、(ループごとに) 共有変数の単一のスナップショットを取得し、必要に応じて、変更可能な共有変数ではなく、ループ内でスナップショットを複数回使用します。これは C++ ではなく C であることを理解しています。Keil ではなく gcc と llvm を使用しています。(llvm にはタイトな while ループの最適化に関する既知の問題、非常に古いバグがあることに注意してください。なぜ彼らがそれを修正することに関心がないのかわかりません。llvm はこの例で動作します)。

于 2011-11-13T01:42:29.067 に答える
2

STM のサンプル コードは、優れたコーディング プラクティスの見本ではありません。標準の周辺機器ライブラリの使用を例示することを目的としています (それらがあなたが話している例であると仮定して)。場合によっては、変数がmain()割り込みコンテキスト (共有メモリ) からアクセスされるため、外部で変数が宣言されることがあります。おそらく、デバッガーで変数を任意のコンテキストから監視できるようにするためだけに、そのように行われた可能性もあります。しかし、それはテクニックをコピーする理由にはなりません。STM のサンプル コードに対する私の意見は、ソフトウェア エンジニアリングの観点からは言うまでもなく、サンプル コードとしても概してかなり貧弱であるということです。

この場合、クロック割り込み変数は、複数のライターで読み取り-変更-書き込みセマンティクスを使用していない限り、32 ビット以下である限りアトミックです 。1 つのライターと複数のリーダーを安全に使用できます。これはこの特定のプラットフォームに当てはまりますが、必ずしも普遍的ではありません。答えは、たとえば 8 または 16 ビット システム、またはマルチコア システムでは異なる場合があります。いずれの場合も、変数を宣言volatileする必要があります。

Keil を使用して STM32 で C++ を使用していますが、問題はありません。C++ のエントリ ポイントが異なると思われる理由がわかりません。ここにはありません (Keil ARM-MDK v4.22a)。SystemInit()たとえば、スタートアップ コードは、PLL とメモリ タイミングを初期化する呼び出しを呼び出し、次に__main()グローバルな静的初期化を実行する呼び出しを呼び出し、次にグローバルな静的オブジェクトの C++ コンストラクターを呼び出してから、main(). 確信が持てない場合は、デバッガーでコードをステップ実行してください。__main()はアプリケーション用に作成する関数ではなく、main()C と C++ で動作が異なるラッパーですが、最終的に関数を呼び出すことに注意してくださいmain()

于 2011-11-15T12:15:14.593 に答える