PIC32対dsPIC対ARM対AVR、とにかくC言語でプログラミングする場合、アーキテクチャは重要ですか?[閉まっている]


10

現在32ビットPIC32マイクロコントローラーを使用しています。それは私たちのニーズに対してはうまく機能していますが、私たちをよりよく適合させることができる他のマイクロコントローラーも模索しています+ MCUを選択している他のプロジェクトがあります。その目的のために、32ビットと同じであるがARMベースのPIC DAマイクロコントローラーであるARMベースを選択しました(PIC32よりも人気があり、業界的にも優れています)。

PIC32ではMPLABを使用しますが、ARM cortex-M0ではAtmel Studioを使用します。両方のプラットフォームでC言語を使用します。私の懸念事項は、2つの32ビットマイクロコントローラー(同じ会社から)を使用するが、アーキテクチャが異なることです。これには、2つの異なるデバイスを学習する必要があり、「学習曲線」+配信時間が増加します。しかし、その一方で、どちらの場合もC言語を使用するので、ARMの学習曲線はそれほど聞こえないはずであり、そのプロセッサを探索する価値もあります。

私の主な質問は、C-Languageでプログラミングしているときに、アーキテクチャーがマイクロコントローラーの内部の抽象化を提供するため、アーキテクチャーがどれほど大きな違いをもたらすかです。また、C言語プログラミングを考えると、MPLAPとAtmel Studioの主な違いは何ですか


2
PIC32で問題が解決しない場合、切り替えのポイントは何ですか?コードが完全に移植されても(移植されません)、慣れるための新しいツールチェーンとIDEがまだあります。ポイントは何ですか?宗教上の理由から、または「ARMベース」(またはその他のベース)に切り替えることはばかげています。正当な理由がある必要がありますが、まだ何も示していません。
Olin Lathrop 2017

切り替えについては尋ねませんでした。複数のプロジェクトに取り組んでいるため、他のプロジェクトに別のアーキテクチャを選択することについて話しました。既存の設計には改善の余地があります。主なポイントは、2つの異なるアーキテクチャを同時に使用する場合の学習曲線と課題についてでした。
エンジニア

Atmel StudioはMPLAB youtubeビデオ
エンジニア

回答:


20

これはかなり意見の分かれるトピックです。私は自分のために話すことができます(AVR、ARM、MSP430)。

違い1(最も重要)は周辺機器にあります。各MCUには、同様のUART、SPI、タイマーなどがあります。レジスタ名とビットが異なるだけです。ほとんどの場合、チップ間でコードを移動するときに対処しなければならない主要な問題でした。解決策:ドライバーを共通のAPIで記述して、アプリケーションを移植できるようにします。

違い2はメモリアーキテクチャです。AVRのフラッシュに定数を配置する場合は、特別な属性と特別な関数を使用してそれらを読み取る必要があります。ARMの世界では、アドレス空間が1つしかないため、ポインターを逆参照するだけです(小さいPICがそれを処理する方法はわかりませんが、AVRに近いと想定します)。

違い3は、割り込みの宣言と処理です。avr-gcc持っているISR()マクロを。ARMには関数名しかありません(CMUARTヘッダーとスタートアップコードを使用する場合、someUART_Handler()など)。ARM割り込みベクトルは(RAMを含む)どこにでも配置でき、実行時に変更できます(たとえば、切り替え可能な2つの異なるUARTプロトコルがある場合に非常に便利です)。AVRには、「メインフラッシュ」または「ブートローダーセクション」でベクターを使用するオプションしかありません(したがって、割り込みを別の方法で処理したい場合は、ifステートメントを使用する必要があります)。

違い4-スリープモードと電源制御。最も低い消費電力が必要な場合は、MCUのすべての機能を活用する必要があります。これはMCU間で大きく異なる可能性があります。より粗い省電力モードを備えているものや、個々の周辺機器を有効/無効にできるものもあります。一部のMCUは調整可能なレギュレーターを備えているため、より低い電圧でより低速で実行できます。3つのグローバル電源モードと7つの電源モードを備えたMCU(たとえば、)で同じ効率を達成する簡単な方法はありません。個別のペリフェラルクロック制御。

移植性を気にするときに最も重要なことは、コードをハードウェアに依存する(ドライバー)部分とハードウェアに依存しない(アプリケーション)部分に明確に分割することです。後者は、モックドライバーを備えた通常のPCで開発およびテストできます(UARTの代わりにコンソールなど)。プロトタイプのハードウェアがリフローオーブンから出てくる前に、アプリケーションコードの90%が完了しているので、これにより何度も節約できました。

私の意見では、ARMの良い点は「モノカルチャー」です。多くのコンパイラ(gcc、Keil、IAR ...を例に挙げると)、無料で正式にサポートされているIDE(少なくともNXP、STM32、Silicon Labs)が利用可能です。 Nordic)、多くのデバッグツール(SEGGER-特にOzone、ULINK、OpenOCD ...)と多くのチップベンダー(私はそれらに名前を付けさえしません)。PIC32は主にマイクロチップに限定されています(ただし、それらのツールが気に入らない場合にのみ問題になります。

Cコードに関しては。それは99%同じです、ifステートメントは同じです、ループは同じように動作します。ただし、ネイティブワードサイズに注意する必要があります。たとえばfor、AVRのループuint8_tは、カウンターに使用する場合が最も高速ですuint32_tが、ARMの場合は最高速のタイプ(またはint32_t)です。より小さい型を使用すると、ARMは毎回8ビットオーバーフローをチェックする必要があります。

MCUやベンダーの一般的な選択は、主に政治とロジスティックスに関するものです(たとえば、高温-MSP430またはVoragoを使用するなど、技術的な制約が明確でない限り)。アプリケーションが何でも実行でき、コード(ドライバー)の5%のみが開発され、製品のライフタイム全体でサポートされる必要があるとしても、それは会社にとって追加のコストです。私が働いたすべての場所には、お気に入りのベンダーとMCUラインがありました(「何か別のものを選択する十分な理由がない限り、必要なKinetisを選択してください」など)。また、他の人に助けを求める場合にも役立ちます。マネージャーとして、5人の開発部門で全員がまったく異なるチップを使用することは避けます。


3
「カウンタにuint8_tを使用する場合、AVRが最も高速ですが、ARMではuint32_tが最も高速なタイプ(またはint32_t)です。より小さな型を使用すると、ARMは毎回8ビットオーバーフローをチェックする必要があります。」少なくとも8ビットが必要な場合は、uint_fast8_tを使用できます。
Michael

@Michael-_fast型を使用できることを確認してください。ただし、オーバーフロー動作を期待することはできません。私のgccのstdint.hには、「typedef unsigned int uint_fast8_t」があります。これは基本的にuint32_tです:)
filo

異なるプラットフォームには異なる機能があるため、効率的で普遍的で完全なAPIを作成することは困難です。CPUは、おそらくペリフェラルやそれらを使用して行われた設計決定よりも重要ではありません。たとえば、さまざまな周辺機器をいつでも最大で数マイクロ秒で再構成できるデバイスもあれば、数百マイクロ秒または数ミリ秒に及ぶ複数のステップを必要とするデバイスもあります。前者のパターンを対象としたAPI関数は、10,000Hzで実行される割り込みサービスルーチン内で使用できる可能性がありますが...
supercat

...数百マイクロ秒を超える操作の分散を必要とするプラットフォームでは、このような使用法をサポートできませんでした。ハードウェア設計者が「いつでも迅速な操作」のAPIセマンティクスをサポートしようとしない理由はわかりませんが、多くの場合、状態ではなく個々の操作を同期するモデルを使用しているため、たとえば、デバイスをオンにすると、コードはデバイスをオンにする必要がないことを認識します。コードは、デバイスがオフになる要求を発行する前に、デバイスがオンになるのを待つ必要があります。APIでそれをスムーズに処理すると、大きな複雑さが増します。
スーパーキャット2017

11

4つの異なるメーカーのMCUをいくつか使用しました。毎回の主な作業は、周辺機器に慣れることです。

たとえば、UART自体はそれほど複雑ではなく、ドライバーのポートは簡単に見つかります。しかし、前回、クロック、I / Oピンの割り込み、有効化などを整理するのにほぼ1日かかりました。

GPIOは非常に複雑になる可能性があります。ビットセット、ビットクリア、ビットトグル、特殊機能の有効化/無効化、トライステート。次に、割り込みを取得します:任意のエッジ、立ち上がり、立ち下がり、レベル低、レベル高、セルフクリアなど。

次に、I2C、SPI、PWM、タイマー、さらに20種類以上のペリフェラルがあり、それぞれに独自のクロックイネーブルがあり、レジスタが新しいビットで異なるたびに。それらすべての場合、どのような状況でどのビットを設定するかをデータシートを読むのに何時間もかかります。

最後の製造元には、使用できないコード例がたくさんありました。すべてが抽象化されました。しかし、それをたどると、コードは6つ通過しましたGPIOビットを設定する関数呼び出しのレベル。3GHzのプロセッサは搭載しているが、48MHzのMCUは搭載していない場合に最適です。最後の私のコードは単一行でした:

GPIO->set_output = bit.

より一般的なドライバーを使用しようとしましたが、あきらめました。MCUでは、常にスペースとクロックサイクルに苦労しています。10KHzで呼び出される割り込みルーチンで特定の波形を生成する場合、抽象化レイヤーが最初にウィンドウから出ることがわかりました。

ですから、今はすべてが機能しており、非常に正当な理由がない限り、再び切り替えないように計画しています。

上記のすべては、販売する製品の数と節約する製品について償却する必要があります。100万の販売:0.10を節約して別のタイプに切り替えると、ソフトウェアの工数に100.000を費やすことができます。1000を販売すると、100しか費やすことができません。


1
個人的には、これが私がアセンブラーを使う理由です。素敵なバイナリ、抽象化なし。
Ian Bland 2017

Cのプリプロセッサは、特に__builtin_constant組み込み関数と組み合わせると、かなりうまく機能します。(ポート番号* 32 +ビット番号)の形式の各I / Oビットの定数を定義OUTPUT_HI(n)するGPIOD->bssr |= 0x400;場合、if nが0x6Aのような定数であるのと同等のコードを生成するマクロを作成できますが、isの場合nは単純なサブルーチンを呼び出します一定ではありません。そうは言っても、私が目にしたほとんどのベンダーAPIは、平凡なものから恐ろしいものまでの範囲です。
スーパーキャット2017

8

これは答えというよりも意見/コメントです。

Cでのプログラミングは望ましくなく、またすべきではありません。C++ は、正しい方法使用するとはるかに優れています。(OK、間違った使い方をすると、Cよりもはるかに悪いことを認めざるを得ません。)これにより、(最新の)C ++コンパイラを搭載したチップに制限されます。これは、AVRを含むGCCでサポートされているほぼすべてのものです(いくつかの制限、filoは不均一なアドレス空間の問題について言及していますが、ほとんどすべてのPICを除外しています(PIC32はサポートされている可能性がありますが、適切なポートはまだ見ていません)。

C / C ++でアルゴリズムをプログラミングしている場合、言及する選択肢の違いはわずかです(ただし、16ビット、32ビット、またはそれ以上のビット演算を大量に実行する場合、8ビットまたは16ビットのチップは深刻な欠点になります)。最後の1オンスのパフォーマンスが必要な場合は、おそらくアセンブラ(独自のコード、またはベンダーまたはサードパーティが提供するコード)を使用する必要があります。その場合は、選択したチップを再検討する必要があります。

ハードウェアにコーディングするときは、抽象化レイヤーを使用するか(多くの場合、製造元から提供されます)、独自に作成することもできます(データシートやコード例に基づいて)。IMEの既存のCの抽象化(mbed、cmsisなど)は、機能的に(ほぼ)正確であることがよくありますが、パフォーマンス(ピンセット操作の間接参照の約6層についてオールドファートが認めている)、使いやすさ、移植性がひどく失敗します。彼らは特定のチップのすべての機能を公​​開たいと考えています。ほとんどすべての場合、それは不要であり、気にせず、コードをその特定のベンダー(そしておそらくその特定のチップ)にロックします。

これは、C ++の方がはるかに優れているためです。適切に実行すると、ピンセットは6つ以上の抽象化レイヤーを通過できます(これにより、より優れた(ポータブル!)インターフェイスと短いコードが可能になるため)、ターゲットに依存しないインターフェイスを提供できます。単純なケースは、アセンブラーで書くのと同じマシンコードになります

私が使用しているコーディングスタイルのスニペット。

// GPIO part of a HAL for atsam3xa
enum class _port { a = 0x400E0E00U, . . . };

template< _port P, uint32_t pin >
struct _pin_in_out_base : _pin_in_out_root {

   static void direction_set_direct( pin_direction d ){
      ( ( d == pin_direction::input )
         ? ((Pio*)P)->PIO_ODR : ((Pio*)P)->PIO_OER )  = ( 0x1U << pin );
   }

   static void set_direct( bool v ){
      ( v ? ((Pio*)P)->PIO_SODR : ((Pio*)P)->PIO_CODR )  = ( 0x1U << pin );    
   }
};

// a general GPIO needs some boilerplate functionality
template< _port P, uint32_t pin >
using _pin_in_out = _box_creator< _pin_in_out_base< P, pin > >;

// an Arduino Due has an on-board led, and (suppose) it is active low
using _led = _pin_in_out< _port::b, 27 >;
using led  = invert< pin_out< _led > >;

実際には、抽象化の層がさらにいくつかあります。しかし、LEDを最後に使用すると、たとえばオンにしても、ターゲットの複雑さや詳細は表示されません(Arduin unoまたはST32青い錠剤の場合、コードは同じです)。

target::led::init();
target::led::set( 1 );

コンパイラーはこれらのすべてのレイヤーに威圧されていません。また、オプティマイザがすべてを見通す仮想関数がないため、周辺機器のクロックを有効にするなど、一部の詳細は省略されています。

 mov.w  r2, #134217728  ; 0x8000000
 ldr    r3, [pc, #24]   
 str    r2, [r3, #16]
 str    r2, [r3, #48]   

これが、アセンブラーでそれを書いた方法です。共通ベースからのオフセットでPIOレジスタを使用できることに気付いた場合。この場合、私はおそらくそうするでしょうが、コンパイラーは私よりもそのようなものを最適化することにはるかに優れています。

だから私が答える限り、それはあなたのハードウェアのための抽象化層を書くが、それはあなたのパフォーマンスに害を及ぼさないように最新のC ++(概念、テンプレート)でそれを書くことです。それを配置すると、別のチップに簡単に切り替えることができます。配置しているランダムチップで開発を開始したり、使い慣れたり、適切なデバッグツールを使用したりして、最終的な選択を後で延期することもできます(必要なメモリ、CPU速度などの詳細情報がある場合)。

IMO組み込み開発のファラシーの1つは、最初にチップを選択することです(これは、このフォーラムでよく尋ねられる質問です。どのチップを選択すればよいですか。一般に、最良の答えは、問題ではありません)。

(編集-「パフォーマンスに関して、CまたはC ++は同じレベルになるでしょうか?」への応答)

同じ構成の場合、CとC ++は同じです。C ++には、他の抽象化用の構成要素(クラス、テンプレート、constexprがいくつかあります)があり、他のツールと同様に、良いものにも悪いものにも使用できます。ディスカッションをより面白くするために、誰もが良い点と悪い点に同意するわけではありません...


では、パフォーマンスに関しては、CまたはC ++は同じレベルでしょうか。C ++はもっと過負荷になると思います。間違いなくあなたは私を正しい方向に向けました、C ++はCではなく行く方法です
エンジニア、

C ++テンプレートは、特定のユースケースごとにコードがコンパイルされるため、パフォーマンスの点でコストがゼロ(またはマイナス)になる可能性があるコンパイル時のポリモーフィズムを強制します。ただし、これは目標速度(GCCの場合はO3)に最適です。実行時の多態性は、仮想関数と同様に、はるかに大きなペナルティを被る可能性がありますが、維持が容易であり、場合によっては十分に優れていることもあります。
Hans

1
あなたはC ++の方が優れていると主張しますが、Cスタイルのキャストを使用します。恥ずかしいです。
JAB 2017

@JAB新しいスタイルのキャストにはあまり感動しなかったが、試してみる。しかし、私の現在の優先事項は、このライブラリの他の部分です。実際の問題は、もちろん、ポインタをテンプレートパラメータとして渡すことができなかったことです。
Wouter van Ooijen

@Hans my cto(コンパイル時間オブジェクト)スタイルの使用例はかなり狭く(ハードウェアに近く、コンパイル時の既知の状況)、仮想ベースのOOの従来の使用法の代わりよりもCに優れています。有用な混獲は、間接指定がないためにスタックサイズを計算できることです。
Wouter van Ooijen

4

私が正しく理解している場合は、C言語環境でプラットフォームの「ポップアップ」アーキテクチャ固有の機能を知りたいため、両方のプラットフォームで保守可能で移植可能なコードを作成することがより困難になります。

Cは「ポータブルアセンブラ」であるという点で、すでに非常に柔軟です。選択したすべてのプラットフォームで、C89およびC99言語標準をサポートするGCC /商用コンパイラを利用できます。つまり、すべてのプラットフォームで同様のコードを実行できます。

いくつかの考慮事項があります。

  • 一部のアーキテクチャはVon Neumann(ARM、MIPS)であり、その他はHarvardです。主な制限は、CプログラムがデータをROMから読み取る必要がある場合、たとえば文字列を印刷する場合に、「const」または同様のデータが定義されている場合に発生します。

一部のプラットフォーム/コンパイラは、この「制限」を他のプラットフォームよりもうまく隠すことができます。たとえば、AVRでは、ROMデータを読み取るために特定のマクロを使用する必要があります。PIC24 / dsPICでは、専用のtblrdインストラクションも利用できます。ただし、一部のパーツには、フラッシュのページをRAMにマッピングできる「プログラムスペースの可視性」(PSVPAG)機能もあり、tblrdなしで即時データアドレッシングを利用できます。コンパイラはこれを非常に効果的に行うことができます。

ARMとMIPSはフォンノイマンであるため、ROM、RAM、およびペリフェラルのメモリ領域が1つのバスにパックされています。RAMまたは "ROM"からのデータの読み取りに違いはありません。

  • Cの下に潜り、特定の操作に対して生成された命令を見ると、I / Oに関していくつかの大きな違いがあることがわかります。ARMとMIPSはRISC ロードストアレジスタアーキテクチャです。つまり、メモリバス上のデータアクセスはMOV命令を経由する必要があります。これは、ペリフェラル値を変更すると、読み取り-変更-書き込み(RMW)操作が発生することも意味します。Bit-BandingをサポートするいくつかのARMパーツがあり、I / Oペリフェラル空間でset / clr-bitレジスタをマップします。ただし、このアクセスを自分でコーディングする必要があります。

一方、PIC24を使用すると、ALU操作で間接的なアドレッシングを介してデータを直接読み書きできます(ポインターを変更した場合でも)。これは、CISCのようなアーキテクチャーにいくつかの特徴があるため、1つの命令でより多くの作業を実行できます。この設計は、より複雑なCPUコア、より低いクロック、より高い電力消費などにつながる可能性があります。幸い、この部品はすでに設計されています。;-)

これらの違いは、PIC24が同様にクロックされるARMまたはMIPSチップよりもI / O処理に「パンチの効いた」可能性があることを意味します。ただし、同じ価格/パッケージ/デザイン制約で、はるかに高いクロッカーARM / MIPSパーツが得られる場合があります。実用的な用語だと思いますが、多くの「プラットフォームの学習」は、アーキテクチャで実行できることと実行できないこと、いくつかの操作セットの速度などを把握することになると思います。

  • 周辺機器、クロック管理などは、パーツファミリごとに異なります。厳密に言えば、NVICやSysTickなどのいくつかのCortex mバインド周辺機器を除き、これはベンダー間のARMエコシステム内でも変更されます。

これらの違いは、デバイスドライバーによって多少カプセル化することができますが、最終的には、組み込みファームウェアはハードウェアとの結合レベルが高いため、カスタム作業を回避できない場合があります。

また、マイクロチップ/元のAtmelのエコシステムを離れる場合、ARMパーツを機能させるためにさらに設定が必要になることがあります。という意味で; 周辺機器へのクロックを有効にしてから、周辺機器を構成して「有効化」し、NVICを個別にセットアップするなど。これは、学習曲線の一部にすぎません。これらのことをすべて正しい順序で行うことを覚えておくと、これらのすべてのマイクロコントローラー用のデバイスドライバーを作成することは、ある時点で非常によく似た感じになります。

  • また、まだ使用していない場合は、stdint.h、stdbool.hなどのライブラリを使用してみてください。これらの整数型は幅を明示的にするため、プラットフォーム間でコードの動作が最も予測可能になります。これは、8ビットAVRで32ビット整数を使用することを意味する場合があります。しかし、あなたのコードがそれを必要とするなら、そうしてください。

3

はいといいえ。プログラマーの観点からは、命令セットの詳細を隠すことが理想的です。しかし、それはすでにある程度関連していないため、プログラムを作成する全体のポイントである周辺機器は、命令セットの一部ではありません。同時に、これらの命令セット全体で4096Byteのフラッシュパーツを比較することはできません。特にCを使用している場合、フラッシュ/メモリの消費量は命令セットとコンパイラーによって大幅に決定されます。コンパイラーが表示されない場合もあります(PIC咳)これらのリソースの無駄がコンパイルによってどれだけ消費されるかによる。その他のフラッシュ消費は、オーバーヘッドが小さいです。高性能は高水準言語を使用する場合の問題でもあり、MCUアプリケーションのパフォーマンスが重要であるため、MCUにボードあたり3ドルを費やすか、1ドルを費やすかによって違いが生じる可能性があります。

(製品の全体的なコストで)プログラミングを簡単にすることである場合は、MCUの開発者向けパッケージをダウンロードして、命令セットアーキテクチャが決して見られないものになるようにする必要があります。心配する必要はありません。これらのライブラリを使用するには、製品のコストまで費用がかかりますが、市場に出すまでの時間は短くなる可能性があります。ライブラリを使用するには、周辺機器に直接話しかけるよりも、時間と作業に時間がかかることがわかります。

結論として、命令セットはあなたの心配の中で最も少ないです、本当の問題に移ります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.