STM32 ADCとは?機能と基本の使い方:詳細解説
はじめに
現代のエレクトロニクス製品において、センサーからの情報や外部環境の変化を正確に捉えることは非常に重要です。これらの情報は、多くの場合、連続的に変化するアナログ信号として存在します。しかし、マイコンは基本的にデジタル信号しか扱うことができません。ここで必要となるのが、アナログ信号をデジタル信号に変換する機能、すなわちADC (Analog-to-Digital Converter) です。
特に高性能で多機能なマイクロコントローラーとして知られるSTMicroelectronics社のSTM32シリーズは、幅広いアプリケーションで利用されており、その強力なペリフェラルのひとつとして高機能なADCを内蔵しています。STM32のADCを使いこなすことは、様々なアナログ入力を正確に測定し、より高度なシステムを構築するための鍵となります。
本記事では、STM32に搭載されているADCについて、その「とは何か」という基本的な概念から、内部アーキテクチャ、多彩な機能、そして実際にプログラムで利用するための「基本の使い方」まで、約5000語のボリュームで詳細に解説します。STM32のADCの理解を深め、皆様のプロジェクトに役立てていただければ幸いです。
1. ADCとは?(基礎知識)
1.1 ADC (Analog-to-Digital Converter) の定義
ADCは、連続的なアナログ電圧信号を、不連続なデジタル数値に変換する電子回路です。例えば、センサーが出力するアナログ電圧(0Vから3.3Vの間で連続的に変化)を、マイコンが処理できる「0」や「1」のビット列(例えば0から4095までの整数値)に変換します。
1.2 なぜADCが必要なのか?
私たちが扱う物理世界(温度、光量、圧力、音声など)の多くはアナログ的な性質を持っています。つまり、それらを計測した信号は、時間とともに連続的に変化する電圧や電流として現れます。一方、デジタルコンピュータやマイコンは、離散的な「0」と「1」の信号(デジタル信号)しか直接処理できません。したがって、アナログの世界とデジタルの世界を結びつけ、アナログ信号をマイコンで処理するためには、ADCによる変換が不可欠となります。
例えば、温度センサーは温度に応じたアナログ電圧を出力します。このアナログ電圧をADCに入力することで、特定の温度に対応するデジタル値が得られます。マイコンはこのデジタル値を読み取り、プログラムで計算や判断を行い、例えばディスプレイに温度を表示したり、ヒーターやクーラーを制御したりします。
1.3 ADCの基本的な原理:標本化、量子化、符号化
アナログ信号をデジタル信号に変換するプロセスは、主に以下の3つのステップからなります。
-
標本化 (Sampling)
- 連続的に変化するアナログ信号から、一定の時間間隔(サンプリング周期)で信号の値を「点」として取り出すプロセスです。
- この時間間隔の逆数を「サンプリング周波数」または「サンプリングレート」と呼びます。サンプリング周波数が高いほど、元の信号の情報をより多く取り込むことができます。
- ナイキスト・シャノン標本化定理によれば、元の信号に含まれる最も高い周波数成分の2倍以上のサンプリング周波数で標本化すれば、理論上、元の信号を完全に復元できます。
-
量子化 (Quantization)
- 標本化によって得られたアナログの点の値を、あらかじめ決められた有限個の離散的なレベル(量子化レベル)のいずれかに割り当てるプロセスです。
- 例えば、0Vから3.3Vの間の電圧を測定するADCが、0Vを0、3.3Vを4095とする4096個のレベル(12ビット分解能の場合)に量子化する場合、測定されたアナログ電圧は最も近い量子化レベルに対応する値に丸められます。
- この量子化によって必ず誤差が生じます。これを「量子化誤差」と呼びます。量子化レベルが多いほど(つまり分解能が高いほど)、量子化誤差は小さくなります。
-
符号化 (Encoding)
- 量子化された各レベルに、対応するデジタルコード(バイナリ値)を割り当てるプロセスです。
- 例えば、0から4095までの4096個のレベルがあれば、それぞれに000000000000から111111111111までの12ビットのバイナリコードが割り当てられます。
- このバイナリコードが、マイコンが読み取るデジタル値となります。
1.4 ADCの重要なパラメータ
ADCの性能を評価する上で重要なパラメータがいくつかあります。
-
分解能 (Resolution)
- アナログ入力をいくつの離散的なデジタル値に分割できるかを示します。通常はビット数で表されます(例: 12ビット、16ビット)。
- Nビットの分解能を持つADCは、$2^N$個の異なるデジタル値を表現できます。分解能が高いほど、より微細な電圧の変化を捉えることができ、量子化誤差が小さくなります。
- 例えば、3.3Vの基準電圧を持つ12ビットADCは、$2^{12} = 4096$個のレベルを持ちます。この場合、検出可能な最小の電圧変化(1LSB: Least Significant Bitに相当する電圧値)は、$3.3V / 4096 \approx 0.8mV$となります。
-
サンプリングレート (Sampling Rate / Sampling Frequency)
- 1秒間に何回、アナログ信号を標本化できるかを示します。単位はHz (サンプル/秒) です。
- サンプリングレートが高いほど、高速に変化する信号を正確に捉えることができます。
- STM32のADCでは、ADCクロック周波数とチャネルごとのサンプリング時間の設定によって実効的なサンプリングレートが決まります。
-
精度 (Accuracy)
- 測定されたデジタル値が、実際のアナログ入力値に対してどれだけ近いかを示します。
- 精度は、内部の誤差(オフセット誤差、ゲイン誤差、リニアリティ誤差など)によって影響されます。通常はLSB単位やパーセンテージで仕様に記載されます。
-
リニアリティ (Linearity)
- 微分非直線性 (Differential Non-Linearity, DNL): 隣接する量子化レベル間のステップ幅が、理想的なステップ幅からどれだけずれているかを示します。DNLが-1LSB以下になると、特定のデジタル値が出力されない「ミッシングコード」が発生する可能性があります。
- 積分非直線性 (Integral Non-Linearity, INL): 入力電圧に対する出力デジタル値の関係が、理想的な直線からどれだけずれているかを示します。INLが大きいと、全体的な変換特性が歪みます。
1.5 主要なADCタイプとSTM32で主流のタイプ
様々なADC変換方式がありますが、マイコンに内蔵されるADCとして最も一般的で、STM32シリーズの多くの製品で採用されているのは逐次比較型 (SAR: Successive Approximation Register) ADCです。
- 逐次比較型 (SAR) ADC:
- コンパレーターとSAR、そしてD/Aコンバーター (DAC) を組み合わせて、入力電圧に最も近いデジタル値を探します。
- 変換は、最も上位のビット(MSB)から順に、仮のデジタル値をDACでアナログ電圧に戻し、それと入力電圧をコンパレーターで比較することで行われます。ビット数分だけこの比較を繰り返すことで、最終的なデジタル値が得られます。
- 比較的高いサンプリングレートと分解能を実現でき、面積も小さく消費電力も抑えられるため、マイコンへの内蔵に適しています。
その他のADCタイプには、デルタシグマ型(高分解能・低速)、フラッシュ型(低分解能・高速)、パイプライン型(高速・中分解能)などがありますが、STM32の汎用シリーズでは主にSAR型が使われます。一部の高性能シリーズでは、シグマデルタ型ADCが搭載されている場合もあります。本記事では、最も一般的なSAR型ADCを前提に解説を進めます。
2. STM32 ADCのアーキテクチャと機能
STM32に内蔵されているADCは、多くの便利な機能と高い柔軟性を持っています。シリーズや製品ラインによって細かな仕様は異なりますが、ここでは多くのSTM32シリーズに共通する、または典型的なADCのアーキテクチャと主要機能について説明します。
2.1 主要なブロック構成
STM32のADCは、主に以下のブロックで構成されています。
-
入力マルチプレクサ (Input Multiplexer):
- 複数のアナログ入力チャネル(外部ピンや内部チャネル)の中から、現在変換対象とするチャネルを選択するための回路です。
- このマルチプレクサを切り替えることで、順番に複数のアナログ信号を測定することができます(スキャンモード)。
-
サンプル&ホールド回路 (Sample and Hold, S&H):
- 入力マルチプレクサから選択されたアナログ電圧を、変換期間中に一定の値に「保持」するための回路です。
- 標本化のプロセスを担い、SAR変換中にアナログ入力電圧が変動しないようにします。これにより、正確な変換が可能になります。
- チャネルごとにサンプリング時間(ホールドする時間)を設定できます。
-
SAR (Successive Approximation Register) コア:
- ADCの変換そのものを行う中心部分です。逐次比較アルゴリズムを実行し、入力電圧をデジタル値に変換します。
- 分解能(例: 12ビット、10ビット、8ビット、6ビット)はこのコアの設計によって決まります。STM32では、多くのシリーズで12ビット分解能がサポートされていますが、設定で低分解能モード(高速化)を選択できる場合もあります。
-
リファレンス電圧 (Reference Voltage, Vref):
- ADCがアナログ電圧をデジタル値に変換する際の基準となる電圧です。
- 通常、マイコンの電源電圧(VDDA)を基準とするか、専用のリファレンス電圧ピン(VREF+)から供給される外部リファレンス電圧を使用します。
- 変換結果のデジタル値は、$デジタル値 = 基準電圧 \times \frac{アナログ入力電圧}{基準電圧}$のような比例関係になります(実際には分解能とデータアライメントによりますが)。
- 基準電圧の安定性は、ADCの精度に直接影響します。外部リファレンスを使用する場合や、ノイズの多い環境では、基準電圧ピン周りにフィルタ回路などを設けることが推奨されます。
-
デジタル変換ロジックとデータレジスタ:
- SARコアによって得られたデジタル変換結果を保持するレジスタ (ADC_DR: Data Register) です。
- 変換完了を示すフラグ (EOC: End Of Conversion) や、変換結果を読み取ったことを示すフラグなども管理します。
- データの整列方法(右寄せ/左寄せ)を設定できます。
-
制御レジスタ:
- ADCの様々な動作モード、チャネル設定、トリガー設定、割り込み設定などを制御するためのレジスタ群です。
- これらのレジスタを適切に設定することで、ADCの動作を詳細に制御できます。
2.2 チャネルの種類
STM32のADCは、様々なソースからのアナログ入力を変換できます。
-
外部ピン (External Pins):
- 多くのアナログ入力チャネルは、マイコンのGPIOピンに割り当てられています。これらのピンは、ADC機能を使用するために「アナログモード」に設定する必要があります。
- 通常、特定のピン(例: PA0, PA1, PC5など)がADCの特定のチャネル番号(例: ADC_IN0, ADC_IN1, ADC_IN15など)に対応しています。
- データシートやリファレンスマニュアルで、どのピンがどのADCチャネルに割り当てられているかを確認できます。
-
内部チャネル (Internal Channels):
- 外部からの配線なしに、マイコン内部の特定のアナログ信号を測定するためのチャネルです。これらは特定のチャネル番号に固定されています。
- Vrefint (Internal Reference Voltage): マイコン内部で生成される安定したリファレンス電圧です。外部リファレンスが利用できない場合や、基準電圧自身の変動を測定したい場合に利用できます。この値を測定することで、ADCの電源電圧(VDDA)を相対的に推定することも可能です。
- Temperature Sensor: マイコンチップ内部の温度を測定するためのセンサーです。このセンサーのアナログ出力をADCで変換することで、チップ温度を知ることができます。この値は、チップ温度とADCのデジタル値の間に特定の変換式(データシートに記載)を使って温度に換算されます。
- Vbat (Battery Voltage): マイコンのバックアップ領域に供給されるVBAT電源の電圧を測定します。通常は低電力モード時のリアルタイムクロックやバックアップレジスタの保持に使用される電源ですが、その電圧レベルを監視できます。
- 外部からの配線なしに、マイコン内部の特定のアナログ信号を測定するためのチャネルです。これらは特定のチャネル番号に固定されています。
2.3 入力モード
STM32のADCは、シングルエンド入力と差動入力の両方に対応している場合があります(シリーズによる)。
-
シングルエンド入力:
- 一つのアナログ入力信号を、共通のグランドに対して測定する最も一般的な方法です。
- 多くの外部ピンや内部チャネルはこのモードで利用されます。
-
差動入力 (Differential Input):
- 二つの入力ピン間の電圧差を測定する方法です。
- ノイズ除去に強く、微小な信号を測定する場合に有効です。特定のチャネルペアでのみ利用可能です。
2.4 変換モード
-
シングル変換モード (Single Conversion Mode):
- トリガーイベントが発生するたびに、設定されたチャネルリスト(シーケンス)のうち、現在のチャネルリストの先頭にあるチャネルを一度だけ変換します。
- 変換完了後、ADCはアイドル状態に戻ります。
- 一度だけ特定の値を測定したい場合に適しています。
-
連続変換モード (Continuous Conversion Mode):
- 一度トリガーイベントが発生して変換が開始されると、設定されたチャネルリスト(シーケンス)を変換し終えた後、再びリストの先頭に戻って変換を繰り返します。
- 外部からのトリガーを待つことなく、連続的に変換が行われます。
- 常に最新の測定値が必要な場合に適しています。
2.5 スキャンモード (Scan Mode)
複数のチャネルを順番に変換したい場合に利用するモードです。
-
非スキャンモード:
- 一度の変換サイクルで、設定されたチャネルリストのうち先頭のチャネルのみを変換します。シングル変換モードや、連続変換モードでチャネルリスト長が1の場合に使用されます。
-
定期スキャンモード (Regular Scan Mode):
- トリガーが発生するたびに、設定された「レギュラースキャンシーケンス」に含まれる全てのチャネルを順番に変換します。シーケンス内のチャネルをすべて変換し終えた後、次のトリガーを待ちます(シングル変換モードの場合)。
- 連続変換モードと組み合わせると、トリガーを待つことなく、レギュラースキャンシーケンスを連続的に繰り返します。
- 複数の異なるセンサー値を周期的に取得したい場合などに利用されます。
-
インジェクションスキャンモード (Injected Scan Mode):
- これはレギュラースキャンとは独立した、別の変換シーケンスです。「インジェクショントリガー」が発生すると、現在実行中のレギュラースキャンを中断して、設定された「インジェクションシーケンス」に含まれるチャネルを優先的に変換します。
- インジェクションシーケンスの変換が完了すると、中断されていたレギュラースキャン(またはアイドル状態)に戻ります。
- 例えば、レギュラースキャンで複数の低速なセンサーを監視しつつ、特定のイベント(例: 外部割り込み)をトリガーとして、応答性の必要な高速なセンサー値を即座に取得したい場合などに有効です。
2.6 トリガーソース
ADCの変換を開始するトリガーは、ソフトウェアまたはハードウェアのどちらかを選択できます。
-
ソフトウェアトリガー:
- プログラムから特定のレジスタ(またはHAL関数)を操作することで、ADCの変換を開始します。
- 最も単純なトリガー方法で、シングル変換や簡単な連続変換で使用されます。
-
外部トリガー (Hardware Trigger):
- 特定のハードウェアイベントによって、自動的にADC変換を開始します。これにより、マイコンのCPUが常に変換開始をポーリングする必要がなくなり、より正確なタイミングでのサンプリングが可能になります。
- 主な外部トリガーソースには以下があります。
- タイマーイベント: 各種タイマー(TIMx)の更新イベント、比較一致イベント、PWM出力イベントなどをトリガーとして利用できます。これにより、一定時間間隔で正確にサンプリングを行うことができます。
- 外部割り込み (EXTIライン): 特定のGPIOピンの状態変化(立ち上がり/立ち下がりエッジ)をトリガーとして利用できます。外部イベントに同期してサンプリングしたい場合に便利です。
- 他のペリフェラル: DACの出力イベントなど、他のペリフェラルのイベントをトリガーとして利用できる場合もあります。
2.7 データアライメント (Data Alignment)
変換されたデジタル値がデータレジスタ(ADC_DR)にどのように格納されるかを設定できます。
-
右寄せ (Right Alignment):
- 変換結果の下位ビットがレジスタの最下位ビット側に格納されます。例えば12ビット分解能の場合、結果はADC_DRレジスタのビット0からビット11に格納されます。これは最も一般的な形式です。
-
左寄せ (Left Alignment):
- 変換結果の上位ビットがレジスタの最上位ビット側に格納されます。例えば12ビット分解能の場合、結果はADC_DRレジスタのビット15からビット4に格納されます。この形式は、小数点以下を考慮した固定小数点演算などを行う場合に便利なことがあります。
2.8 オーバーサンプリング機能 (Over Sampling)
一部のSTM32シリーズのADCは、オーバーサンプリング機能を搭載しています。これは、設定された回数だけ連続してサンプリングを行い、その平均値や中央値などを計算して最終的なデジタル値とする機能です。
- サンプリング回数を増やすことで、ランダムノイズの影響を低減し、実効的な分解能を向上させることができます。
- 例えば、4回オーバーサンプリングして平均を取る場合、ノイズによる誤差は$\sqrt{4}=2$分の1に低減される効果が期待できます。
- この機能を使用すると、変換速度は低下しますが、より高精度な測定が可能になります。
2.9 アナログウォッチドッグ機能 (Analog Watchdog, AWD)
ADCのアナログ入力電圧が、あらかじめ設定された上限値と下限値の範囲を超えたかどうかを監視する機能です。
- 監視対象のチャネル(単一または複数)と、上限/下限のアナログ電圧に対応するデジタル値を設定します。
- 変換結果が設定範囲外に出ると、アナログウォッチドッグ割り込みやフラグを発生させることができます。
- これにより、センサー値の異常や電圧の異常をリアルタイムで検知し、迅速に対応することができます。
2.10 DMA(Direct Memory Access)連携
STM32のADCは、DMAコントローラーと連携して動作させることができます。
- DMA連携を使用すると、ADC変換が完了するたびに、マイコンのCPUを介さずに変換結果を直接メモリ上のバッファに転送できます。
- これにより、連続変換やスキャンモードでの複数チャネル変換において、CPUにほとんど負荷をかけることなく、効率的に大量の変換データを取得できます。
- 特に高速なサンプリングや、多くのチャネルを同時に監視する場合に非常に有効です。
2.11 マルチADCモード
複数のADCペリフェラルを搭載しているSTM32シリーズでは、これらのADCを協調動作させるためのマルチADCモードが提供されています。
- インターリーブモード (Interleaved Mode): 複数のADCが順番に、高速にサンプリングを行います。これにより、単一のADCの最大サンプリングレートよりも高い全体的なサンプリングレートを実現できます。
- 同期モード (Synchronous Mode): 複数のADCが同時にサンプリングを開始・終了します。同じタイミングで複数のチャネルを測定したい場合に適しています。
- 非同期モード (Asynchronous Mode): 各ADCが独立して動作します。
3. STM32 ADCの基本の使い方
STM32のADCを実際にプログラムで使用する際の基本的な手順と、HAL (Hardware Abstraction Layer) ライブラリを使った具体的な方法について解説します。HALライブラリは、STMicroelectronicsが提供する標準的なドライバライブラリで、レジスタの直接操作よりも簡単にペリフェラルを設定・制御できます。
3.1 開発環境とツール
- STM32CubeIDE: STMicroelectronics公式の統合開発環境です。プロジェクトの新規作成、ペリフェラルの設定(STM32CubeMX機能内蔵)、コード生成、ビルド、デバッグまでを一つの環境で行えます。ADC設定もグラフィカルに行えるため、非常に便利です。
- HALライブラリ: STM32CubeIDEでコード生成する際に標準で利用されるライブラリです。ペリフェラルの初期化、設定、データ転送などのAPIが提供されています。
- LLライブラリ (Low-Layer Library): HALよりも低レベルな、レジスタに近い操作を提供するライブラリです。より細かい制御や高速化が必要な場合に利用できます。
- レジスタ直接操作: HALやLLライブラリを使わず、レジスタを直接操作する方法です。最も柔軟で効率的なコードを書けますが、ハードウェアの詳細な理解が必要です。
本記事では、最も一般的なHALライブラリを中心に説明します。
3.2 ADCを使用するための一般的な手順(HALの場合)
STM32でADCを使用するには、通常以下の手順を踏みます。
-
システムクロックとADCクロックの設定:
- STM32全体のシステムクロックを設定します。
- ADCに供給されるクロック(PCLKの分周など)を設定します。ADCの最大動作周波数や、サンプリングレートに影響します。
-
GPIO設定(アナログモード):
- アナログ入力として使用するGPIOピンを、「アナログモード (Analog Mode)」に設定します。これにより、デジタル入出力バッファが無効化され、ADCへのクリーンなアナログ信号供給が可能になります。プルアップ/プルダウン抵抗は通常無効化されます。
-
ADCユニットの有効化と基本設定:
- ADCペリフェラルにクロックを供給し、有効化します。
- ADC共通設定(複数のADCがある場合)や、個別のADCユニットの基本設定を行います。
- 分解能 (Resolution): 12ビット、10ビット、8ビット、6ビットなどから選択します。高い分解能ほど精度は上がりますが、変換時間は長くなります。
- データアライメント (Data Alignment): 右寄せ (Right) または左寄せ (Left) を選択します。
- スキャンモード (Scan Mode): 有効 (Enable) または無効 (Disable) を選択します。複数のチャネルを変換する場合は有効にします。
- 連続変換モード (Continuous Conversion Mode): 有効 (Enable) または無効 (Disable) を選択します。連続的に変換する場合は有効にします。
- 外部トリガー (External Trigger): ソフトウェアトリガーか、特定のハードウェアトリガーソース(タイマー、EXTIなど)を選択します。
- DMA設定: DMAを使用するかどうかを設定します。
-
チャネル設定 (Channel Configuration):
- 変換対象とするアナログ入力チャネル(外部ピンまたは内部チャネル)を設定します。
- レギュラースキャンシーケンスに含めるチャネルとその順番、および各チャネルのサンプリング時間 (Sampling Time)を設定します。
- サンプリング時間は、S&H回路が入力電圧を保持する時間です。入力信号源のインピーダンスが高い場合や、入力に容量負荷がある場合は、十分に長いサンプリング時間を設定しないと、S&H回路のコンデンサが完全に充電されず、変換結果に誤差が生じます。データシートに推奨される最小サンプリング時間が記載されています。
- インジェクションシーケンスを使用する場合は、同様にインジェクションチャネルとそのサンプリング時間を設定します。
-
キャリブレーション (Calibration):
- 多くのSTM32シリーズでは、ADCの変換精度を向上させるためのキャリブレーション機能があります。
- ADCを使用する前に、このキャリブレーションを実行することが推奨されます。通常はADCを有効化した後に行います。
-
変換の開始:
- 設定が完了したら、ADCの変換を開始します。これはソフトウェアトリガーの場合は特定の関数呼び出しで行い、ハードウェアトリガーの場合はトリガーイベントが発生するのを待ちます。
- DMAを使用する場合は、変換開始と同時にDMA転送も開始します。
-
変換結果の取得:
- 変換が完了したら、変換結果を取得します。取得方法は、設定したモードによって異なります。
- ポーリング (Polling): 変換完了フラグ (EOC) がセットされるまで待ち、フラグがセットされたらデータレジスタから結果を読み取ります。最も単純ですが、変換完了を待っている間CPUがブロックされます。
- 割り込み (Interrupt): 変換完了時に割り込みを発生させ、割り込みハンドラ内でデータレジスタから結果を読み取ります。CPUをブロックせず、変換完了時にのみ処理を行うため効率的です。スキャンモードの場合は、シーケンス全体が完了した際に割り込みを発生させる設定もあります。
- DMA: 変換完了ごとにDMAが自動的に結果をメモリに転送します。バッファがいっぱいになった際などにDMA転送完了割り込みを発生させ、まとめてデータを処理できます。CPUへの負荷が最も少ない方法です。
- 変換が完了したら、変換結果を取得します。取得方法は、設定したモードによって異なります。
-
結果の利用:
- 取得したデジタル値を、必要に応じて電圧値や物理量(温度など)に換算して利用します。
- デジタル値から電圧への換算は、通常以下の式で行います(右寄せの場合、Vrefは基準電圧)。
$電圧値 = デジタル値 \times \frac{基準電圧}{2^{分解能ビット数}}$ - 内部温度センサーなどの内部チャネルの場合、データシートに記載された専用の換算式を使用します。
3.3 HALライブラリを使った具体的なサンプルコード解説 (擬似コード)
ここでは、HALライブラリを使った基本的なADC設定と変換方法の擬似コードを示します。具体的な関数名やパラメータはSTM32シリーズやHALバージョンによって若干異なる場合がありますので、使用するマイコンのリファレンスマニュアルやSTM32CubeIDEのコード生成結果を参照してください。
3.3.1 ADCユニットとGPIOの初期化
STM32CubeIDEでADCと対応するGPIOピン、そして必要に応じてタイマーやDMAを設定すると、自動的に初期化コードが生成されます。手動で行う場合の主要な手順は以下の通りです。
“`c
// 1. クロックの有効化
__HAL_RCC_ADCx_CLK_ENABLE(); // ADCxのクロックを有効化 (xはADC番号)
__HAL_RCC_GPIOy_CLK_ENABLE(); // アナログ入力ピンを含むGPIOポートyのクロックを有効化
// 2. GPIO設定 (アナログモード)
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_z; // アナログ入力ピン番号
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; // アナログモードに設定
GPIO_InitStruct.Pull = GPIO_NOPULL; // プルアップ/プルダウン無効
HAL_GPIO_Init(GPIOy, &GPIO_InitStruct); // GPIOポートyの初期化
// 3. ADCユニットの初期化
ADC_HandleTypeDef hadc1; // ADCハンドル構造体を宣言
hadc1.Instance = ADC1; // 使用するADCペリフェラル (ADC1など)
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIVx; // クロック分周設定
hadc1.Init.Resolution = ADC_RESOLUTION_12B; // 分解能 (例: 12ビット)
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // データアライメント (右寄せ)
hadc1.Init.ScanConvMode = ENABLE; // スキャンモード有効 (複数チャネルの場合)
hadc1.Init.ContinuousConvMode = DISABLE; // 連続変換モード無効
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_Tx_TRGO; // 外部トリガーソース (例: TIMx TRGO)
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGEDGE_RISING; // 外部トリガーエッジ
hadc1.Init.DMAContinuousRequests = DISABLE; // DMA連続リクエスト無効
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; // EOCフラグ発生タイミング (シングル変換毎)
// その他、アナログウォッチドッグ、オーバーサンプリングなどの設定
// hadc1.Init.NbrOfConversion = 2; // レギュラースキャンシーケンスのチャネル数 (スキャンモード有効時)
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler(); // 初期化失敗時の処理
}
// 4. ADCキャリブレーション
if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK) // キャリブレーション開始
{
Error_Handler(); // キャリブレーション失敗時の処理
}
“`
3.3.2 チャネル設定
初期化後、変換対象となるチャネルを設定します。レギュラースキャンシーケンスに追加する場合の例です。
“`c
ADC_ChannelConfTypeDef sConfig = {0};
// 1つ目のチャネル設定 (例: 外部ピン ADC_IN0)
sConfig.Channel = ADC_CHANNEL_0; // チャネル番号 (データシート参照)
sConfig.Rank = 1; // レギュラースキャンシーケンスでの順番 (1から始まる)
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES; // サンプリング時間 (ADCクロックサイクル数で指定)
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
// 2つ目のチャネル設定 (例: 内部温度センサー)
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; // 内部温度センサーチャネル
sConfig.Rank = 2; // シーケンス2番目
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES; // 温度センサーには推奨されるサンプリング時間あり (データシート参照)
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
// NbrOfConversion は、シーケンスに設定したチャネルの総数と一致させる必要があります。
// hadc1.Init.NbrOfConversion = 2; (初期化時に設定するか、ここで設定を更新)
“`
3.3.3 変換結果の取得方法
a) シングル変換 (ポーリング)
一度だけ変換し、結果を取得する最もシンプルな方法です。
“`c
uint32_t adc_value;
// 変換開始
if (HAL_ADC_Start(&hadc1) != HAL_OK)
{
Error_Handler();
}
// 変換完了まで待機 (ポーリング)
if (HAL_ADC_PollForConversion(&hadc1, 100) != HAL_OK) // 100msのタイムアウト設定
{
Error_Handler(); // タイムアウトまたはエラー
}
// 結果取得
if ((HAL_ADC_GetState(&hadc1) & HAL_ADC_STATE_EOC_REG) == HAL_ADC_STATE_EOC_REG) // EOCフラグを確認 (スキャンモード時はEOC_SEQ_REG)
{
adc_value = HAL_ADC_GetValue(&hadc1); // 変換結果を取得
}
// ADC停止 (必要に応じて)
// HAL_ADC_Stop(&hadc1);
``
HAL_ADC_PollForConversion(&hadc1, 100)
※スキャンモード有効時は、はシーケンス全体の完了を待ちます。EOCフラグは
HAL_ADC_GetStateで確認できますが、スキャンモードの場合はシーケンス完了フラグを確認する方が一般的です。
HAL_ADC_GetValueは、レギュラースキャンモードではシーケンスの最後のチャネルの結果を返す場合と、シーケンスの各チャネルの結果を順に返す場合があります。スキャンモードでの各チャネルの結果取得方法は、シリーズのリファレンスマニュアルを確認してください。多くの場合、
ADC_DRレジスタを読み出すたびにポインタが進むか、専用のレジスタ(ADC_DRに格納されていることが多い)から取得します。HALでは
HAL_ADC_GetValue`がシーケンス内の次のチャネル結果を返すように実装されていることが多いです。
b) 連続変換 (割り込み)
連続的に変換を行い、各変換完了時またはシーケンス完了時に割り込みを発生させて結果を処理します。
“`c
// 割り込みを有効にして変換開始
// ADCの割り込み設定(NVIC設定)は別途行う必要があります。
if (HAL_ADC_Start_IT(&hadc1) != HAL_OK)
{
Error_Handler();
}
// 変換完了割り込みハンドラ (stm32xx_it.cなどに実装)
void ADC_IRQHandler(void)
{
HAL_ADC_IRQHandler(&hadc1); // HALの割り込みハンドラを呼び出す
}
// 変換完了コールバック関数 (ユーザーが実装)
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if (hadc->Instance == ADC1)
{
uint32_t adc_value;
// レギュラースキャンモードでない場合(シングルチャネル)
// adc_value = HAL_ADC_GetValue(hadc);
// レギュラースキャンモードの場合(複数チャネル)
// シーケンスの各チャネルの結果は、通常、変換完了割り込みごとに順番に取得します。
// または、シーケンス全体完了割り込みでまとめて取得します。
// ここでは例として、スキャンモードで、チャネル0の結果を取得する場合
// (スキャンモードでは、EOC_SINGLE_CONV設定の場合、チャネル毎にこのコールバックが呼ばれます)
// adc_value = HAL_ADC_GetValue(hadc); // この場合、シーケンスの現在のチャネルの結果が得られるはずです
// あるいは、シーケンス全体完了(EOC_SEQUENCE_CONV)でコールバックを呼び出す設定にし、
// 複数のHAL_ADC_GetValue()を呼び出すか、DMAで取得したデータを処理します。
// 例:スキャンモードで、シーケンスのチャネルが複数あり、EOCSelectionを適切に設定した場合
// 各チャネルの変換結果は、このコールバック内で順次処理するか、
// シーケンス完了時にまとめて処理します。具体的な実装はシリーズに依存します。
// 一般的には、スキャンモード+連続変換+割り込みは、DMAと組み合わせて使われることが多いです。
// DMAを使わない場合、シーケンス内の各チャネルの変換完了ごとにこのコールバックが呼ばれるように設定し、
// その都度HAL_ADC_GetValueを呼び出して、配列などに格納していくことになります。
// 取得した値を処理...
// 例えば、温度センサー値の換算など
}
}
``
HAL_ADC_GetValue`で取得できる値が変わります。複数チャネルを高頻度で取得する場合は、次に説明するDMA連携が推奨されます。
※注意:スキャンモードと割り込みの組み合わせは、EOCSelectionの設定(シングル変換毎またはシーケンス完了毎)によってコールバックの呼ばれるタイミングや
c) 連続変換 (DMA)
連続変換モードまたはスキャンモードで、CPU負荷をかけずに変換結果をメモリに転送します。
“`c
uint32_t adc_values[NUM_CHANNELS]; // 変換結果を格納するバッファ配列
// 1. DMA設定 (STM32CubeIDEでADCにDMAを設定すると自動生成されます)
// ADC_Request を適切なDMAチャネル/ストリームにマッピングし、
// データサイズ、メモリ/ペリフェラルインクリメントモードなどを設定します。
// 2. DMAと連携して変換開始
// DMA_CIRCULAR モード(バッファがいっぱいになったら先頭に戻って転送を繰り返す)が一般的です。
if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_values, NUM_CHANNELS) != HAL_OK) // NUM_CHANNELSはスキャンシーケンスのチャネル数
{
Error_Handler();
}
// 3. DMA転送完了コールバック関数 (ユーザーが実装)
// DMAバッファの半分または全体への転送が完了した際に呼び出されます。
// 必要に応じて、DMAの نصف転送完了 (Half-Transfer) 割り込みも設定できます。
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if (hadc->Instance == ADC1)
{
// adc_values 配列に、設定したスキャンシーケンスの順番で変換結果が格納されています。
// ここで取得したデータを処理します。
// DMA_CIRCULARモードの場合、このコールバックが繰り返し呼ばれます。
}
}
// 4. DMA 半転送完了コールバック関数 (必要であれば実装)
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
if (hadc->Instance == ADC1)
{
// adc_values 配列の半分までデータが転送された際に呼び出されます。
// バッファの半分を処理している間に、DMAは残りの半分への転送を続けます。
}
}
“`
DMAを使用する場合、HAL_ADC_Start_DMA
を呼び出すだけで変換が開始され、DMAが自動的にメモリにデータを転送します。CPUは他のタスクを実行できます。データが必要になったら、adc_values
配列から読み出すか、DMA転送完了コールバックでデータを処理します。
3.4 内部チャネルの利用方法
内部チャネル(Vrefint, Temperature Sensor, Vbat)も、外部ピンと同様にチャネルとして設定することで使用できます。チャネル番号はシリーズによって固定されています。
- Vrefint: ADC_CHANNEL_VREFINT をチャネルとして設定します。キャリブレーションデータ(VREFINT_CAL_ADDR)と組み合わせて、VDDA電圧の推定などに利用できます。
- Temperature Sensor: ADC_CHANNEL_TEMPSENSOR をチャネルとして設定します。取得したデジタル値と、キャリブレーションデータ(TEMP_CAL_ADDR)やデータシートの換算式を使ってチップ温度を計算します。
- Vbat: ADC_CHANNEL_VBAT をチャネルとして設定します。VBAT電源を測定します。多くの場合、VBATはVDDAより低いため、VBATチャネルはVDDA/2などに内部的に分圧されてからADCに入力されます。変換結果を電圧に換算する際は、この分圧比を考慮する必要があります(データシート参照)。
これらの内部チャネルも、外部チャネルと一緒にスキャンシーケンスに含めることができます。ただし、温度センサーやVrefintは安定するまでに特定のサンプリング時間を必要とする場合が多いので、データシートで推奨されるサンプリング時間を設定してください。
4. STM32 ADC使用上の注意点とテクニック
STM32のADCを最大限に活用し、正確な測定を行うためには、いくつかの注意点やテクニックがあります。
4.1 アナログ入力の設計
- フィルタリング: センサーからの信号にはノイズが混入していることがよくあります。不要な高周波ノイズを除去するために、アナログ入力ピンの近くにRCフィルタなどのローパスフィルタを挿入することが推奨されます。コンデンサはマイコンのVDDA/VSSAグランドに接続します。
- インピーダンス整合: アナログ入力信号源の出力インピーダンスが高い場合、ADCのS&H回路のコンデンサを十分に充電するのに時間がかかります。これにより、変換結果に誤差が生じたり、設定可能な最大サンプリングレートが制限されたりします。信号源のインピーダンスが高い場合は、アナログ入力ピンの前に低出力インピーダンスのバッファ回路(オペアンプなど)を入れるか、サンプリング時間を十分に長く設定する必要があります。データシートに推奨される信号源インピーダンスが記載されています。
- 入力電圧範囲: ADCの入力電圧範囲は通常、VSSA (アナロググランド) から VDDA (アナログ電源) の間です。これを超える電圧を入力すると、マイコンが破損する可能性があります。入力電圧がこの範囲外になる可能性がある場合は、保護回路やレベルシフト回路が必要です。
4.2 リファレンス電圧の安定性
ADCの変換結果は、基準電圧(Vref)に直接比例します。基準電圧が変動すると、変換結果も変動し、精度が低下します。
- VDDAを基準電圧とする場合: VDDAはマイコンの電源電圧であり、他のデジタル回路のスイッチングノイズなどの影響を受けやすいです。VDDAをADCの基準電圧として使用する場合は、VDDAピンの近くにバイパスコンデンサ(数μFと0.1μFなど)を配置して、電源ノイズを低減することが重要です。
- 外部リファレンス電圧を使用する場合: 専用のリファレンス電圧ICなどから安定した基準電圧を供給できる場合、より高い精度が得られます。VREF+ピンに供給される電圧も、ノイズの影響を受けないようにフィルタリングなどの対策が必要です。
4.3 サンプリング時間とサンプリング周波数の関係
- 実効的なサンプリング周波数(1秒間に実行できる変換回数)は、ADCのクロック周波数と、チャネルごとの設定されたサンプリング時間、そして変換時間によって決まります。
- 変換時間 = サンプリング時間 + 変換コアの固定時間(分解能によって決まる)
- サンプリング時間を長く設定すると、入力信号源のインピーダンスが高い場合でも正確なサンプリングが可能になりますが、実効的なサンプリング周波数は低下します。
- 必要な信号帯域幅(どれくらい速く変化する信号を捉えたいか)に応じて、ナイキスト・シャノン定理を考慮した上で、適切なサンプリング時間を設定する必要があります。
4.4 ノイズ対策
- 電源ノイズ: VDDA/VSSAへのノイズはADCの変換精度に大きな影響を与えます。適切なデカップリングコンデンサの配置や、アナログ電源とデジタル電源の分離(またはビアによる接続点の最適化)が重要です。
- クロックノイズ: ADCクロックやシステムクロックのジッターも精度に影響します。ADCクロックにはPLLなどから派生した安定したクロックソースを選択することが望ましいです。
- 基板レイアウト: アナログ信号ラインとデジタル信号ラインを分離し、平行に長く配線しない、グランドループを避けるなど、適切な基板レイアウトがノイズ対策には不可欠です。アナロググランドとデジタルグランドは、一点で接続する(スターグラウンド)などの手法が用いられます。
- 未使用ピン: 使用しないアナログ入力ピンは、ノイズを拾わないようにグランド(VSSA)に接続するか、入力モードにしておきます。
4.5 分解能と精度
分解能はデジタル値のステップ数を示しますが、必ずしも精度と一致するわけではありません。精度は、分解能に加えて、内部誤差(オフセット、ゲイン、リニアリティ)やノイズによって制限されます。12ビット分解能でも、実効的なノイズフリー分解能(ENOB: Effective Number of Bits)は10ビットや11ビットになることもあります。
4.6 キャリブレーション
STM32のADCは、出荷時に内部でキャリブレーションが行われたデータ(ファクトリキャリブレーションデータ)を内部フラッシュメモリに持っています。また、多くのシリーズではユーザーが実行できるキャリブレーションルーチンが用意されています。これを実行することで、製造ばらつきによるオフセット誤差などを補正し、変換精度を向上させることができます。特に正確な測定が必要な場合は、ADCを使用する前にキャリブレーションを実行することが強く推奨されます。
4.7 複数チャネルのスキャン順序とタイミング
スキャンモードを使用する場合、設定した順番にチャネルが変換されます。各チャネルのサンプリング時間と変換時間を考慮して、シーケンス全体の変換時間が決まります。リアルタイム性が求められるシステムでは、シーケンス全体の変換時間が許容できる範囲内であることを確認する必要があります。必要であれば、サンプリング時間を短くするか、チャネル数を減らす、または複数のADCを利用するといった対策が必要になります。
4.8 電源管理とADC性能
マイコンが低電力モードに移行すると、ADCの機能が制限されたり、クロックが停止したりすることがあります。低電力モード中でもADCを使用する必要がある場合は、データシートでサポートされている低電力モードとADC動作を確認する必要があります。また、一部の低電力モードでは、ADCの性能(特に最大サンプリングレート)が低下する場合があります。
5. 高度な使い方(簡単に触れる)
- インジェクションモード: 前述のように、レギュラースキャンとは独立して、高優先度で変換を実行したい場合に利用します。特定の緊急度の高いセンサー値を、現在の処理を中断して取得するのに適しています。
- アナログウォッチドッグの詳細: 単一チャネルだけでなく、複数のチャネルをグループとして監視したり、各チャネルの上限/下限を個別に設定したりできます。異常状態を検知して割り込みやフラグを発生させることで、ソフトウェアによる常時監視の負荷を軽減できます。
- オーバーサンプリングによる分解能向上: オーバーサンプリング機能を持つADCでは、複数のサンプルを平均化またはフィルター処理することで、ノイズを低減し、理論的な分解能を超える精度(実効的な分解能)を実現できます。特にゆっくりと変化する信号や、微小な信号を測定する場合に有効です。
- マルチADCモードの利用: 複数のADCペリフェラルを持つSTM32では、インターリーブモードで高速サンプリングを実現したり、同期モードで複数のチャネルを同時にサンプリングしたりすることで、アプリケーションの要求に応じた柔軟なデータ取得が可能です。
6. 実践的な例(簡単なアプリケーション例)
ここでは、ADCを使った簡単なアプリケーション例をいくつか紹介します。
- 温度センサーの読み取り: LM35などのアナログ温度センサー(温度に応じた電圧を出力)や、STM32内蔵の温度センサーを利用して、現在の温度を測定します。取得したデジタル値を温度に換算して、LCDに表示したり、閾値を超えたらファンを回したりといった制御に応用できます。
- ポテンショメータ(ボリューム)の読み取り: ポテンショメータの可動端子からの電圧をADCで読み取ることで、ボリュームのつまみの位置(0~100%など)を知ることができます。これを使って、明るさ調整や音量調整、メニュー操作など、ユーザーからのアナログ入力として利用できます。
- 光センサー(フォトダイオードなど)の読み取り: フォトダイオードやフォトレジスタ(CDSセル)など、光の強さに応じて抵抗値や電流が変化するセンサーを適切に回路に組み込み、発生する電圧をADCで読み取ることで、周囲の光量を測定できます。これにより、自動調光システムや、光によるトリガーイベントなどを実現できます。
これらの例は、単一チャネルの簡単な読み取りから始めることができます。慣れてきたら、複数のセンサーをスキャンモードで読み取ったり、タイマーと連携して周期的にサンプリングしたり、DMAを使って効率的にデータを取得したりと、ステップアップしていくと良いでしょう。
7. まとめ
本記事では、STM32に内蔵されているADCについて、その基本的な概念、アーキテクチャ、多彩な機能、そしてHALライブラリを使った基本的な使い方から、使用上の注意点や応用的な機能まで、詳細に解説しました。
STM32のADCは、高分解能、高速変換、複数チャネル対応、フレキシブルなトリガーオプション、DMA連携など、非常に強力な機能を備えています。これらの機能を理解し、適切に設定・利用することで、様々な種類のアナログ信号を正確に測定し、複雑な制御やデータ処理を行うことが可能になります。
ADCは、物理世界とデジタル世界を結びつける重要なインターフェースです。センサーからの情報を正確に捉えることは、システムの性能や信頼性に直結します。本記事で学んだ知識を基に、ぜひ実際にSTM32のADCを活用したプロジェクトに挑戦してみてください。
8. 参考文献/補足情報
STM32のADCについてさらに深く学ぶためには、以下の資料が不可欠です。
- STM32シリーズのデータシート: 使用する特定のSTM32マイコンの電気的特性やピン配置、ADCの主要な仕様(チャネル数、分解能、最大サンプリングレート、内部チャネル番号、キャリブレーションデータのアドレスなど)が記載されています。
- STM32シリーズのリファレンスマニュアル: STM32マイコンの全てのペリフェラルの詳細な機能説明、レジスタマップ、設定方法などが記載されています。ADCに関する章(多くの場合、ADCxxxという章)には、ADCの動作原理、各モードの詳細、レジスタの説明、変換時間の計算方法などが網羅されています。HALライブラリやLLライブラリを使用する場合でも、リファレンスマニュアルを参照することで、内部動作の理解が深まります。
- STM32CubeIDE/STM32CubeMX: ペリフェラルの設定をグラフィカルに行い、初期化コードを生成するのに非常に役立ちます。ADCの設定オプションを確認したり、生成されたコードを参考にしたりできます。
- STM32 HAL/LLドライバのソースコード: HAL/LL関数の内部実装を確認することで、より詳細な動作やレジスタ操作を理解できます。
これらの資料を活用し、実際のハードウェアとソフトウェア開発を通して、STM32 ADCの習得を進めてください。