組み込みデバイスでTDDを実行するにはどうすればよいですか?


17

プログラミングは初めてではなく、AVRで低レベルのCおよびASMを使用したこともありますが、大規模な組み込みCプロジェクトを回避することはできません。

RubyのTDD / BDDの哲学に退化されているため、このようなコードを作成してテストする方法を理解することはできません。私はそれが悪いコードだと言っているのではなく、これがどのように機能するのか理解していません。

低レベルのプログラミングにもっと入りたいと思っていましたが、これにどのようにアプローチするのか全く分かりません。それは私が慣れ親しんだまったく異なる考え方のように見えるからです。ポインターの計算やメモリの割り当ての仕組みを理解するのに問題はありませんが、Rubyと比較して複雑なC / C ++コードがどのように見えるかを見ると、信じられないほど難しいようです。

すでにArduinoのボードを注文しているので、低レベルCをさらに理解して、物事を適切に行う方法を本当に理解したいと思いますが、高レベル言語のルールは適用されないようです。

組み込みデバイスでTDDを実行したり、ドライバーやカスタムブートローダーなどを開発したりすることも可能ですか?


3
こんにちは、ダース、Cの恐怖を乗り越えるのは本当に助けになりませんが、組み込みデバイスのTDDに関する質問はここで話題になります。

回答:


18

まず、あなたが書いていないコードを理解しようとすることは、自分で書くよりも5倍難しいことを知っておく必要があります。プロダクションコードを読むことでCを学ぶことができますが、実行することで学ぶよりもはるかに時間がかかります。

RubyのTDD / BDDの哲学に退化されているため、このようなコードを作成してテストする方法を理解することはできません。私はそれが悪いコードだと言っているのではなく、これがどのように機能するのか理解していません。

それはスキルです。あなたはそれで良くなります。ほとんどのCプログラマーは、人々がRubyをどのように使用しているかを理解していませんが、それができないというわけではありません。

組み込みデバイスでTDDを実行したり、ドライバーやカスタムブートローダーなどを開発したりすることも可能ですか?

さて、主題に関する本があります:

ここに画像の説明を入力してください マルハナバチができるなら、あなたもできる!

通常、他の言語のプラクティスを適用しても機能しないことに注意してください。TDDはかなり普遍的です。


2
私の組み込みシステムで見たすべてのTDDは、自分で簡単に見つけられるエラーを簡単に解決できるシステムのエラーのみを見つけました。彼らは私が助けを必要とするもの、他のチップとの時間依存のインタラクション、インタラクションインタラクションを見つけられなかったでしょう。
Kortuk

3
これは、使用しているシステムの種類によって異なります。TDDを使用してソフトウェアをテストすることと、優れたハードウェア抽象化を組み合わせることで、時間依存の相互作用を簡単にモックアップできることがわかりました。よく見られる他の利点は、自動化されたテストをいつでも実行でき、ソフトウェアが動作することを確認するために誰かがロジックアナライザーでデバイスに座る必要がないことです。TDDにより、現在のプロジェクトだけで何週間もデバッグする必要がなくなりました。多くの場合、予期しないエラーを引き起こすのは、見つけやすいエラーです。
ニックパスクッチ

さらに、オフターゲットでの開発とテストが可能です。
cp.engr

非埋め込みCのTDDを理解するためにこのを参照できますか?ユーザー空間のCプログラミングの場合
過剰交換

15

ここでの多種多様な答え...主にさまざまな方法で問題に対処しています。

私はさまざまな言語で組み込みの低レベルのソフトウェアとファームウェアを25年以上書いてきました-主にC(ただし、途中でAda、Occam2、PL / M、およびさまざまなアセンブラーに流用しています)。

長い間考え、試行錯誤を重ねた結果、かなり迅速に結果を取得し、テストラッパーとハーネスを簡単に作成できる方法に落ち着きました(値を追加します!)

メソッドは次のようになります。

  1. 使用する主要な周辺機器ごとに、ドライバーまたはハードウェアアブストラクションコードユニットを作成します。また、プロセッサを初期化し、すべてをセットアップするための1つを作成します(これにより、フレンドリーな環境になります)。通常、小型の組み込みプロセッサ(AVRを例に)には、このようなユニットが10〜20個ありますが、すべて小型です。これらは、初期化、スケーリングされていないメモリバッファへのA / D変換、ビット単位の出力、プッシュボタン入力(サンプリングされたデバウンスなし)、パルス幅変調ドライバ、UART /シンプルシリアルドライバ、割り込みおよび小さなI / Oバッファの単位です。EEPROM、EPROM、またはその他のI2C / SPIデバイス用のI2CまたはSPIドライバーなど、さらにいくつかあります。

  2. 次に、ハードウェアアブストラクション(HAL)/ドライバーユニットごとに、テストプログラムを作成します。これは、シリアルポート(UART)とプロセッサの初期化に依存しています。したがって、最初のテストプログラムでは、これら2つのユニットのみを使用し、基本的な入出力を行うだけです。これにより、プロセッサを起動できること、およびシリアルI / Oが動作する基本的なデバッグサポートがあることをテストできます。それが機能するようになったら(そしてその後のみ)、他のHALテストプログラムを開発し、それらを既知の正常なUARTおよびINITユニットの上に構築します。したがって、ビット単位の入力を読み取り、シリアルデバッグ端末でこれらを適切な形式(16進数、10進数など)で表示するためのテストプログラムを用意することができます。その後、EEPROMやEPROMのテストプログラムなど、より大きく複雑なものに移行できます。これらのメニューのほとんどを駆動するので、実行するテストを選択して実行し、結果を確認できます。私はそれをスクリプティングできませんが、通常はしません

  3. すべてのHALを実行したら、通常のタイマーティックを取得する方法を見つけます。これは通常、4〜20ミリ秒のレートです。これは、割り込みで生成された定期的なものでなければなりません。カウンターのロールオーバー/オーバーフローは通常、これを行う方法です。割り込みハンドラは、バイトサイズ「セマフォ」をインクリメントします。この時点で、必要に応じて電源管理をいじることもできます。セマフォの考え方は、その値が> 0の場合、「メインループ」を実行する必要があるということです。

  4. EXECUTIVEはメインループを実行します。セマフォが0以外になるのを待つだけです(この詳細を抽象化します)。この時点で、これらのティック(ティックレートを知っているcos)をカウントするカウンターで遊んで、現在のエグゼクティブティックが1秒、1分、および他の一般的な間隔であるかどうかを示すフラグを設定できます使いたいかもしれません。幹部は、セマフォが> 0であることを認識すると、すべての「アプリケーション」プロセス「更新」機能を介して単一のパスを実行します。

  5. アプリケーションプロセスは互いに効果的に並んでおり、「更新」ティックによって定期的に実行されます。これは、エグゼクティブによって呼び出される単なる機能です。これは、非常にシンプルな自家製RTOSを備えた、貧弱なマルチタスクであり、すべてのアプリケーションが入って、少しの作業をして終了することに依存しています。アプリケーションは独自の状態変数を維持する必要があり、公平性を強制するプリエンプティブなオペレーティングシステムがないため、長時間の計算を実行できません。明らかに、アプリケーションの実行時間(累積)は、主要なティック期間よりも短くする必要があります。

上記のアプローチは簡単に拡張できるため、通信スタックを追加して非同期で実行し、通信メッセージをアプリケーションに配信できます(それぞれに「rx_message_handler」である新しい関数を追加し、メッセージディスパッチャーを記述します)どのアプリケーションにディスパッチするかを指定します)。

このアプローチは、名前を付けたいほとんどすべての通信システムで機能します。多くのプロプライエタリシステム、オープンスタンダードコミュニケーションシステムで機能します(実際に機能します)。TCP/ IPスタックでも機能します。

また、明確に定義されたインターフェースを備えたモジュール式の部品で構築されるという利点もあります。いつでもピースを出し入れでき、別のピースに置き換えることができます。途中の各ポイントで、既知の適切な下位層パーツ(以下のもの)に基づいてテストハーネスまたはハンドラーを追加できます。設計の約30%から50%が、通常かなり簡単に追加できる特別に作成された単体テストを追加することで利益を得られることがわかりました。

私はこれをさらに一歩進め(これを行った他の誰かからニックネームを付けました)、HALレイヤーをPCの同等のものに置き換えました。したがって、たとえば、PCでC / C ++およびwinformsなどを使用し、コードを慎重に記述することにより、各インターフェイスをエミュレートでき(たとえば、EEPROM = PCメモリに読み込まれたディスクファイル)、PCで組み込みアプリケーション全体を実行できます。使いやすいデバッグ環境を使用する機能は、膨大な時間と労力を節約できます。通常、このような労力を正当化できるのは、本当に大きなプロジェクトだけです。

上記の説明は、組み込みプラットフォームでの作業方法に固有のものではありません-同様のことを行う多くの商業組織に出くわしました。通常、実装方法は大きく異なりますが、原則はほとんど同じです。

上記が少し風味を与えることを願っています...このアプローチは、数kBで実行される小型の組み込みシステムで、積極的なバッテリー管理を行い、100K以上のソースラインの永久電源で動作するモンスターまで機能します。Windows CEなどの大きなOSで「埋め込み」を実行する場合、上記のすべては完全に重要ではありません。しかし、それはとにかく実際の組み込みプログラミングではありません。


2
ほとんどの場合、タイミング特性に主に関心があるため、ほとんどのハードウェア周辺機器はUARTでテストできません。ADCサンプルレート、PWMデューティサイクル、他のシリアルペリフェラル(SPI、CANなど)の動作、または単にプログラムの一部の実行時間を確認する場合、これを行うことはできません。 UART。深刻な組み込みファームウェアのテストにはオシロスコープが含まれます。これがないと組み込みシステムをプログラムできません。

1
そうそう、絶対に。言及するのを忘れました。しかし、UARTを立ち上げて実行すると、テストまたはテストケースのセットアップが非常に簡単になり(これが問題でした)、物事を刺激し、ユーザー入力を許可し、結果を取得し、フレンドリーな方法で表示します。これとあなたのCROは人生をとても楽にします。
すぐに

2

あなたが選んだ例のように、複数のプラットフォームのインクリメンタル開発と最適化の長い歴史を持つコードは、通常読みにくいです。

Cについてのことは、実際には、広範なAPIの豊富さとハードウェアパフォーマンス(およびその欠如)にわたってプラットフォームにまたがることができるということです。MacVimは、今日の一般的なスマートフォンよりもメモリとプロセッサのパフォーマンスが1000倍以上少ないマシンで応答しました。あなたのRubyコードはできますか?それが、あなたが選んだ成熟したCの例よりも簡単に見える理由の1つです。


2

私は過去9年間のほとんどをCプログラマーとして過ごし、最近いくつかのRuby on Railsフロントエンドに取り組んだという逆の立場にいます。

私がCで作業しているものは、ほとんどが自動倉庫を制御する中規模のカスタムシステムです(通常、数十万ポンドから数百万ポンドのコスト)。例の機能は、いくつかの短い応答時間要件とウェアハウスワークフローのより高いレベルの管理を備えた機械とインターフェイスするカスタムメモリ内データベースです。

まず第一に、TDDは行っていません。私は何度も単体テストを導入しようとしましたが、Cでは、少なくともカスタムソフトウェアを開発する場合、それが価値があるよりも厄介です。しかし、TDDはRubyに比べてCでの必要性ははるかに低いと思います。主に、Cがコンパイルされているからです。警告なしでコンパイルされる場合は、Railsでrspec自動生成された足場テストとほぼ同じ量のテストを既に実行しています。単体テストなしのRubyは実行不可能です。

しかし、私が言うことは、Cが一部の人が作るほど難しくする必要がないということです。C標準ライブラリの多くは理解できない関数名の混乱であり、多くのCプログラムはこの規則に従っています。私はそうではないと言ってうれしく、実際には標準ライブラリ機能のラッパーがたくさんあります(strncpyの代わりにST_Copy、regcomp / regexecの代わりにST_PatternMatch、iconv_open / iconv / iconv_closeの代わりにCHARSET_Convertなど)。私たちの社内Cコードは、私が読んだ他のほとんどのものよりも読みやすいです。

しかし、他の高レベル言語からのルールが適用されないようだと言うとき、私は同意しません。多くの優れたCコードは、オブジェクト指向を「感じ」ます。リソースへのハンドルを初期化し、ハンドルを引数として渡すいくつかの関数を呼び出し、最終的にリソースを解放するパターンがよく見られます。実際、オブジェクト指向プログラミングの設計原則は、主に人々が手続き型言語で行っていた良いことから来ました。

Cが本当に複雑になるのは、基本的に非常に低レベルのデバイスドライバーやOSカーネルのようなことをするときです。より高いレベルのシステムを作成する場合、Cのより高いレベルの機能を使用して、低レベルの複雑さを回避することもできます。

あなたが見たいと思うかもしれない非常に興味深いのは、Ruby用のCソースコードです。Ruby APIドキュメント(http://www.ruby-doc.org/core-1.9.3/)で、さまざまなメソッドのソースコードをクリックして表示できます。おもしろいのは、このコードが非常に美しくエレガントに見えることです-あなたが想像するほど複雑に見えません。


... Cの高レベル機能を使用することもできます...」;-)
alk

デバイスドライバーのタイプコードでよく見られるビット操作とポインターウィザードへのポインターよりも高いレベルを意味します。また、いくつかの関数呼び出しのオーバーヘッドが気にならない場合は、かなり高レベルに見えるCコードを作成できます。
asc99c

...かなり高レベルに見えるCコードを作成できます。」、絶対に、私はそれに完全に同意します。しかし、「...上位レベルの機能...」はCのものではありませんが、あなたの頭の中ではそうではありませんか?
アルク

2

私がやったことは、デバイスに依存しないコードをデバイスに依存しないコードから分離し、デバイスに依存しないコードをテストすることです。優れたモジュール性と規律があれば、ほとんどが十分にテストされたコードベースになります。


2

できない理由はありません。問題は、他の種類の開発にあるような「既製の」単体テストフレームワークがない場合があることです。それで大丈夫です。それは、あなたがテストするために「自分でロール」アプローチを取る必要があることを意味します。

たとえば、A / Dコンバーターの「偽の入力」を生成するために計装をプログラムする必要がある場合や、組み込みデバイスが応答するための「偽のデータ」のストリームを生成する必要がある場合があります。

「TDD」という言葉を使用することに抵抗が生じた場合は、「DVT」(設計検証テスト)と呼びます。これにより、EEはアイデアに慣れやすくなります。


0

組み込みデバイスでTDDを実行したり、ドライバーやカスタムブートローダーなどを開発したりすることも可能ですか?

しばらく前に、ARM CPU用の第1レベルのブートローダーを作成する必要がありました。実は、このCPUを販売している人たちがいます。そして、ブートローダーがブートローダーをブートするスキームを使用しました。ただし、2つのファイルを1つではなくNORフラッシュにフラッシュする必要があったため、これは低速でした。ブートローダーのサイズを最初のブートローダーに組み込み、ブートローダーを変更するたびに再構築する必要がありました。

そこで、ブートローダーの機能を私たちのものに統合することにしました。商用コードであったため、すべてが期待どおりに機能することを確認する必要がありました。そこで、QEMUを変更してそのCPUのIPブロック(すべてではなく、ブートローダーに触れるブロックのみ)をエミュレートし、QEMUにコードを追加して、PLL、UART、SRAMコントローラーなどを制御するレジスタにすべての読み取り/書き込みを「printf」します。など。次に、このCPUをサポートするためにブートローダーをアップグレードし、その後、ブートローダーとそれらのオンエミュレーターを提供する出力を比較して、いくつかのバグを見つけるのに役立ちます。一部はARMアセンブラー、一部はCで作成されました。また、変更後、QEMUを変更すると、JTAGと実際のARM CPUを使用してキャッチできなかった1つのバグをキャッチできました。

したがって、Cとアセンブラーを使用しても、テストを使用できます。


-2

はい、組み込みソフトウェアでTDDを実行できます。それが可能ではない、関係がない、または適用できないと言っている人々は正しくありません。他のソフトウェアと同様に、TDDから組み込みで得られる重大な価値があります。

しかし、それらを実行する最善の方法は、ターゲットでテストを実行するのではなく、ハードウェアの依存関係を抽象化し、ホストPCでコンパイルして実行することです。

TDDを行うときは、多くのテストを作成して実行します。これを行うにはソフトウェアが必要です。自動テスト検出とモック生成により、これを迅速かつ簡単に行えるテストフレームワークが必要です。

現在、Cに最適なオプションはCeedlingです。ここに私がそれについて書いた投稿があります:

http://www.electronvector.com/blog/try-embedded-test-driven-development-right-now-with-ceedling

そして、それはRubyで構築されています!あなたが知っている必要はありませんいかなるのにそれを使用するルビーを。


答えは独力で立つことが期待されています。Stack Exchangeで物質を見つけるために読者に外部リソースへのアクセスを強制する(「記事を読む、またはCeedlingをチェックする」)。サイトの品質基準に合うように編集することを検討してください
-gnat

Ceedlingには、非同期イベントをサポートするメカニズムがありますか?リアルタイムの組み込みアプリケーションのより困難な側面の一つは、彼らが自分たちのモデルに困難な非常に複雑なシステム...からの入力を受け取るに対処することである
ジェイ・エルストン

@Jayそれをサポートするものは特にありません。しかし、私はそのようなことをモックでテストし、それをサポートするアーキテクチャを設定することで成功しました。たとえば、最近、割り込み駆動型イベントがキューに入れられ、「イベントハンドラ」ステートマシンで消費されるプロジェクトに取り組みました。これは基本的に、イベントが発生するたびに呼び出される単なる関数です。その関数をテストするとき、キューからイベントをプルする関数呼び出しをモックできたので、システムで発生するイベントをシミュレートできました。ここでもテスト走行が役立ちます。
チェルノ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.