Table of Contents
IoTデバイス用のポータブルCコードを書くことは、多様なハードウェアプラットフォーム間でアプリケーションをデプロイする必要がある組み込み開発者にとって基本的なスキルです。 IoTエコシステムには、ARM Cortex-M、RISC-V、AVR、および独自のアーキテクチャが搭載されています。それぞれに固有のメモリマップ、周辺登録、コンパイラのリクエストが搭載されています。 移植性の設計が非審然とせず、ターゲットを1つに分けるコードは、コスト面で書き直し、メンテナンスのナイト記事を導きます。 このガイドは、実際のガイドとガイドの両方を組み合わせることが可能になります。
IoT開発におけるポータビリティの理解
ポータビリティとは、ソースコードをコンパイルし、変更をすることなく異なるハードウェアアーキテクチャで実行することができることを意味します。 IoT の世界では、ポータビリティは単なる利便性ではありません。それはビジネス要件です。 製品のライフサイクルは長く、サプライチェーンシフト、新しいシリコンが常に現れます。 ポータブルコードベースを使用すると、既存のファームウェアを製品生成全体に再利用し、短時間で代替コンポーネントに迅速にピボットを素早くピボットし、デリバティブ製品向けのタイムツーマーケットを削減できます。
ポータビリティはスペクトル上に存在します。一方、完全にプラットフォームに依存しないコード(例えば、一般的なソートアルゴリズム)はどこでもコンパイルされます。一方、ハードウェアレジスタを直接操作するコードは、現在非移植性です。ポータブルC for IoTの目標は、コアビジネスロジックとアルゴリズムコードが再利用可能なため、抽象レイヤーの背後にある非ポータブルな詳細を分離することです。
ポータビリティのコードに対する共通の課題
いくつかの低レベルの差は、疫学埋め込まれたCの移植性を差します。
- [Endianness.] ARM Cortex-M と AVR は、少しエンドアンです。いくつかの古いアーキテクチャ(例えば、Freescale HC12)は、大エンドアンです。直接バイトオーダーのポインタまたはユニオンを鋳造すると、サイレントなデータ破損を引き起こします。
- []Wordサイズとタイプ定義。An []は8ビットAVR、Cortex-M0の32ビット、RISC-V 64ビットプロセッサー上の64ビットで16ビットである可能性があります。 [を仮定するコードは、正確に32ビットが壊れます。
- [レジスタマップの差。[]]])同じベンダーから2つのMCUでさえ、しばしば異なる周辺ベースアドレス、ビットフィールド、および構成シーケンスを持っています。
- [コンパイラの拡張とフラグマ。[]] GCC、IAR、ARM Compiler 6、Keilそれぞれ独自の[構文とインラインアセンブリのダイアレクトを持っています。
- [ メモリーレイアウトとアライメント。[]]] 一部のプラットフォームでは、32ビットアクセスの厳密なアライメントが必要です。 他の人は、障害ハンドラとの不正なアクセスを処理します。
- [ 処理とスタックの使い方を中断します。[ ベクトル、優先モデル、およびネスティングの動作が広く変化します。
ポータブルCコードを書くための重要な戦略
ハードウェア抽象化層(HAL)
ポータブル ‐ 符号 arsenal の最も強力なツールは、 ハードウェア抽象化レイヤー] です。 よく設計された HAL は、一般的な周辺機器 (GPIO、UART、I2C、SPI、タイマー) の Uniform API を expose に 明示し、 アンダーリー レジスタ を 隠すときに します。 は、 それぞれの フィルタを 直接 設定する機能 (HLT: ) を します。 [FLTF] は、 は、 は、 それぞれ それぞれ [FLTF] を します。 [FLTF] は、 [F] [F] は、 は、 は、 は、 は、 は、 は、 は、 は、 は、 は、 [FLTFLTFLTF] の の は、 は、 は、 は、 を を を を を を を を します。 [FLTF
典型的なHAL実装パターンは、このように見えます。
// hal_gpio.h (common)
typedef uint8_t gpio_pin_t;
typedef uint8_t gpio_port_t;
void hal_gpio_set_output(gpio_port_t port, gpio_pin_t pin);
void hal_gpio_set_high(gpio_port_t port, gpio_pin_t pin);
void hal_gpio_set_low(gpio_port_t port, gpio_pin_t pin);
プラットフォーム固有のファイル(例、)は、実際のレジスタ書き込みを含みます。新しいMCUに移動するときは、低レベルのHALソースのみが書き換えを必要とするが、すべての高層は未処理のままである。
標準的な図書館を採用します
C標準ライブラリは、多くの一般的な操作にポータブル基盤を提供します。 [、 []、文字列ユーティリティ、および数学関数は、Cコンパイラのコンパイラに合致するすべての機能で利用できます。 ライブラリの内部の仮定を回避することは、コンパイラの実装が不十分であることを検証していない限り、パフォーマンスのためにをリライトするたびに重要です。
限られたメモリを持つIoTシステムでは、独自の文字列ルーチンを転がすのではなく、GCCエコシステム内の「」のような標準ライブラリ(])のサブセットを使用して検討してください。同様に、[]]マクロと[は、ユニバーサルで利用可能です。リンク:]]GNU Cライブラリのドキュメントは、ポータブル理解のために優れた参考文献です。
固定式・幅データ型の使用
明示的な幅を持つ整数変数を宣言するために、常に [ と [] からタイプを使用して、 []、、]、[]]]] 、等。 平凡な]、]、、[既知のサイズを持つもののために、または、[[FLT:]]]、[[FLT:[FLT:[FLT:]]]、[[[FLT:[FLT:[FLT:] - [[FLT:] - [[[[[[FLT:] - [[[FLT:] - [[[[[[FLT:] - [[[[FLT:] - [[[[FLT:] - [[[[[[[[[[[[[FLT:] - [[[FLT:] - [[[[[FLT:] - [[FLT:] -
バイト指向の輸送間でデータをシリアライズする必要がある場合は、固定幅の型を明示的なバイト順変換関数()、、またはポータブルの等価関数と組み合わせてください。 ]]を[]にキャストし、ネットワークを介して送信するだけです。
条件付きコンパイル
Preprocessor ディレクティブは、プラットフォーム固有のコードの正当なツールですが、それらはジューシャスに使用しなければなりません。 1 つの中央ヘッダー (例えば、]) の構成マクロの小集合を定義します。 それぞれのファイルを通して ] をスキャタリングするのではなく、。 例:
// platform_config.h
#if defined(STM32L4)
#define PLATFORM_STM32L4
#elif defined(EFM32GG)
#define PLATFORM_EFM32GG
#else
#error "Unsupported platform"
#endif
それからコードでは、必要なときにのみ、ジェネリック を使用します。 過剰な ] がコードを読みやすく維持しにくいことを覚えておいてください。 可能な条件付きコンパイル上のHALの抽象化を優先します。
外部の依存関係の最小化
これらは、潜在的な移植性危険性である。依存性を追加する前に、すべてのターゲットアーキテクチャをサポートし、非ポータブルな仮定で引き離さないことを確認してください。ポータブルC(例えば、]])に完全に書かれたライブラリは、FatFS]または[]])は、インラインアセンブリやコンパイラに依存するよりも安全です。さらに、アプリケーションをラップすることなく、それを自分で検討することができます。
リンク: ]Embedded.com は、実際のポータブルCコードに関する記事で、依存関係の管理に関する追加の視点を提供しています。
ポータビリティを高めるための実用的なヒント
モジュラーコードを書く
ファームウェアを独立したモジュールによく定義されたインターフェイスで破棄します。各モジュールは、ヘッダーファイルを介してその機能を公開し、内部の詳細を非表示にする必要があります。この問題の分離は、新しいプラットフォームに移植するときにポータブルバージョンでモジュールを簡単に交換できます。例えば、モーター制御モジュールは、PWM出力用のHALに話すべきで、タイマー周辺レジスタに直接は使用できません。
文書ハードウェアの依存関係
特定のハードウェアの動作を想定するコードを明確に注釈付けます。特定の非ポータブルなアプローチが選ばれる理由、どのプラットフォームが動作するか、異なるターゲットに変更する必要があるのかを解説するためにコメントを使用します。このドキュメントは、元の開発者が利用できなくなった場合、新しいエンジニアがコードを移植しなければならない場合に有利です。
クロスプラットフォームビルドツールを使用する
[CMake や Meson などのシステムを構築することで、単一のプロジェクト構造から複数のターゲット構成を管理できます。例えば、CMake は、各プラットフォーム用のツールチェーンファイルを指定し、対象に基づいてコンパイラ定義を設定することができます。これにより、IAR、Keil、GCC 用の別々のプロジェクトファイルを手動で維持するための必要性がなくなります。リンク: :4:4] 広範囲なドキュメントを変換] 設定:[FLT] 設定] は、 [FLT] 設定] の5: [FLT] 設定] 設定] 設定のオプションのオプションが提供されます。
ポータブルビット操作技術を使用する
レジスタでビットを設定またはクリアするときは、ビットフィールドの場所を想定する絶対マスクを書くことを避けます。代わりに、HALで定義されたシンボリック定数を使用し、マクロやインライン関数を安全なビット操作に使用します。
#define BIT_SET(reg, bit) ((reg) |= (1u << (bit)))
#define BIT_CLEAR(reg, bit) ((reg) &= ~(1u << (bit)))
を、リテラル整数ではなく抽象的なパラメーターとして定義します。 このようにして、ビット位置が異なるMCUに変化した場合、定数の定義のみが変更されなければなりません。
プラットフォーム間でのテストと検証
ポータビリティの要求は検証されなければなりません。サポートされるすべてのプラットフォームでプロジェクトをビルドする、継続的統合(CI)を使用します。CIでは、静的解析ツール(])を実行します。PC-lintまたは]]]Coverityは、非移植性のコンストラスの構造の誤用を検出します。機能テストでは、エミュレータ(例えば、ARMまたはRenode for ARMまたはRenodeは、通常は、Vをシミュレーションすることができない、物理的なファームをシミュレーションするために、物理的な機器を監視します。
回帰テストは、各プラットフォームですべてのHAL APIを練習して、初期の互換性をキャッチする必要があります。 「バイトをUARTに書き込む」ようなテストでは、ループバックで読み戻す」と、UARTの実装間のタイミングや構成の違いを説明します。
コンテンツ
IoTデバイス用のポータブルCコードを書くことは、それが一日からアーキテクチャに焼くべき規準であるべきではありません。ハードウェア抽象的なレイヤーに投資することで、条件付きコンパイルをスパースリーに使用し、ターゲット全体で厳格にテストすることで、ハードウェアの景観の避けられない変化を生き生き残ることができるファームウェアを作成します。 先行する努力は、メンテナンスの減少、新しいシリコンへの移植、および将来の戦略を適用するためにより大きな反乱を払う。 これらは、C-C-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F-F