【初心者向け】STM32 ADC の使い方を徹底解説
はじめに
電子工作や組み込みシステムの開発において、現実世界の「アナログ」な情報をコンピュータが理解できる「デジタル」な情報に変換することは非常に重要です。温度、光の明るさ、圧力、音声など、私たちの周りにある多くの情報はアナログ信号として存在します。これらのアナログ信号をマイコンで扱うためには、ADC(Analog-to-Digital Converter、アナログ-デジタルコンバーター)と呼ばれる機能が必要です。
STM32シリーズは、高性能で多機能なARM Cortex-Mマイクロコントローラーであり、多くのモデルに高品質なADCが搭載されています。この記事では、電子工作や組み込み開発を始めたばかりの初心者の方々に向けて、STM32のADCを理解し、実際に使いこなせるようになるための徹底的な解説を行います。
この記事を読むことで、ADCの基本的な仕組みから、STM32CubeIDEを使った具体的な設定方法、ポーリング、割り込み、DMAといった様々な変換方法、さらに実践的な応用例まで、STM32のADCに関する知識を体系的に習得できます。約5000語の詳細な説明を通して、あなたのSTM32開発におけるADC活用スキルを一段階引き上げることを目指します。
さあ、STM32のADCの世界へ飛び込みましょう!
1. ADCの基本を理解しよう
STM32のADCの使い方を学ぶ前に、まずはADCが一体何をするものなのか、その基本的な原理を理解することが大切です。
1.1. アナログ信号とデジタル信号
- アナログ信号: 時間と共に連続的に変化する信号です。例えば、音の波形や、電球の明るさはアナログ信号と言えます。電圧や電流の値が無限の階調を取りうるのが特徴です。
- デジタル信号: 離散的な値、つまり飛び飛びの値で表現される信号です。コンピュータが扱うのは基本的にデジタル信号です。例えば、オン/オフのスイッチ、CDの音楽データなどはデジタル信号です。
マイコンはデジタル回路で構成されているため、アナログ信号を直接扱うことができません。そこで登場するのがADCです。ADCは、入力されたアナログ信号をデジタル信号に変換する役割を担います。
1.2. ADCの基本的な仕組み
ADCは主に以下の3つのステップでアナログ信号をデジタル信号に変換します。
- サンプリング (Sampling): アナログ信号は時間と共に連続的に変化しますが、デジタルで扱うためには、ある一定の時間間隔でその時点での信号の値を「切り取る」必要があります。この切り取る作業をサンプリングと呼びます。サンプリングする時間間隔(またはその逆数であるサンプリング周波数)が細かいほど、元の信号を正確に再現できます。
- 量子化 (Quantization): サンプリングによって取得したアナログ信号の値は、まだ無限の階調を持ちえます。これをデジタルで扱えるように、あらかじめ定められたいくつかの段階(レベル)に丸める作業が量子化です。アナログ値を最も近いデジタル値に対応付けます。
- 符号化 (Encoding): 量子化された値を、コンピュータが理解できる0と1の組み合わせ(バイナリコード)に変換する作業です。
1.3. ADCの性能を表す指標
ADCの性能はいくつかの指標で評価されます。特に重要なのが「解像度」と「変換速度」です。
- 解像度 (Resolution): アナログ値をどれだけ細かい段階に分解できるかを示します。通常ビット数で表されます。例えば、12bitのADCは、$2^{12} = 4096$段階にアナログ値を分解できます。解像度が高いほど、より微細なアナログ値の変化を捉えることができ、より正確なデジタル値が得られます。
- 例: 0Vから3.3Vの電圧範囲を測定する場合、12bit ADCなら1段階あたり $3.3V / 4096 \approx 0.8mV$ の分解能を持ちます。
- 変換速度 (Conversion Speed): アナログ信号をサンプリングし、デジタル値に変換するまでにかかる時間を示します。通常、サンプリング時間と量子化・符号化にかかる時間の合計です。変換速度が速いほど、変化の速い信号に対応できます。単位はHz(samples per second, サンプル/秒)やμs(マイクロ秒)で表されます。
1.4. リファレンス電圧 (Reference Voltage, Vref)
ADCは、入力されたアナログ電圧を、ある基準となる電圧(リファレンス電圧)と比較してデジタル値に変換します。このリファレンス電圧の範囲内に入力電圧がある必要があります。通常、Vref+ (正のリファレンス電圧) と Vref- (負のリファレンス電圧) があり、入力電圧は Vref- から Vref+ の間に収まるように設計します。
STM32では、電源電圧 (VDDA) をリファレンス電圧として使用することが多いですが、外部から精密なリファレンス電圧を入力できるピンを備えているモデルもあります。Vrefが安定していることは、正確なADC変換のために非常に重要です。
1.5. チャンネル (Channel)
多くのADCモジュールは、複数の異なるアナログ入力信号を切り替えて変換することができます。それぞれの入力端子を「チャンネル」と呼びます。STM32のADCは、外部ピンからのアナログ入力だけでなく、マイコン内部の信号(温度センサー、内部リファレンス電圧など)を入力として選択できる内部チャンネルも持っています。
2. STM32のADCについて
STM32に搭載されているADCモジュールは、非常に多機能で高性能です。ここでは、STM32のADCの主な特徴と種類について説明します。
2.1. STM32 ADCの一般的な特徴
- 高解像度: 多くのSTM32シリーズは12bitのSAR (Successive Approximation Register) 型ADCを搭載しています。より高精度な16bit ADCを持つシリーズや、低消費電力向けの10bit/8bit ADCを持つシリーズもあります。解像度は設定で下げて変換速度を上げることも可能です(例: 12bit, 10bit, 8bit, 6bit)。
- 高速変換: 数Msps (Mega samples per second) の変換速度を持つモデルが多く、高速な信号の取り込みも可能です。
- 複数のチャンネル: 1つのADCモジュールが多数の外部ピン(最大16チャンネル以上)からのアナログ入力に対応しています。
- 内部チャンネル: マイコン内部の便利な信号を測定できます。
- 温度センサー (Temperature Sensor): チップ自体の温度を測定できます。
- VrefINT: 内部で生成される基準電圧です。外部リファレンス電圧が不要な場合に使えます。電源電圧の変動を監視するのに役立ちます。
- Vbat: バッテリー電源の電圧を測定できます(一部シリーズ)。
- 柔軟な設定: 変換時間、サンプリング時間、トリガーソース(ソフトウェア、タイマー、外部イベントなど)、変換モード(シングル、スキャン、連続、非連続)など、様々な設定が可能です。
- DMA対応: ADCの変換結果をCPUを介さずにメモリに転送するDMA (Direct Memory Access) 機能をサポートしています。これにより、CPUの負荷を大幅に減らすことができます。
- 校正機能: ADCの精度を向上させるために、内部的に校正(キャリブレーション)を行う機能を持っています。
2.2. STM32 ADCの種類と注意点
STM32シリーズには多くの製品ラインナップがあり、搭載されているADCモジュールも少しずつ異なります。
- ADCモジュールの数: 1つのチップに複数のADCモジュール(ADC1, ADC2, ADC3など)が搭載されている場合があります。これらのモジュールは独立して動作させることも、同期させて同時に変換を開始することも可能です。
- ピンアサイン: どのADCモジュールのどのチャンネルがどの外部ピンに接続されているかは、使用するSTM32チップのデータシートやリファレンスマニュアルで確認する必要があります。STM32CubeIDEを使えば、ピンアウト設定で視覚的に確認できます。
- 共有ピン: 複数のADCモジュールで同じピンをアナログ入力として使用できる場合があります(ただし同時に使うことはできません)。
- 電源: ADCモジュールには専用のアナログ電源ピン (VDDA, VSSA) があります。これらのピンにノイズの少ない安定した電源を供給することが、ADCの精度を確保するために非常に重要です。デジタル電源 (VDD, VSS) とは分離して配線し、適切にパスコンを配置することを推奨します。
3. ADCの基本的な使い方(ポーリング方式)
まずは最もシンプルで理解しやすい「ポーリング方式」を使ってADCを動かしてみましょう。ポーリング方式では、変換を開始した後、マイコンが変換完了を待ち続け、完了したら結果を読み取ります。
3.1. ハードウェアの準備
- STM32開発ボード: NUCLEOやDiscoveryなど、お手持ちのSTM32開発ボードを用意します。
- 可変抵抗 (ポテンショメータ): アナログ入力として電圧を変化させるために使用します。(例: 10kΩ〜100kΩ程度)
- ジャンパー線: 開発ボードと可変抵抗を接続します。
- その他: 必要に応じてブレッドボードなど。
配線:
可変抵抗を開発ボードに接続します。一般的な3端子の可変抵抗の場合:
* 片方の外側端子を開発ボードの3.3Vピン (VDDまたはVDDA) に接続します。
* もう片方の外側端子を開発ボードのGNDピン (VSSまたはVSSA) に接続します。
* 中央のワイパー端子を、ADCアナログ入力に対応したピン(例: PA0, PC0など。使用するボードや設定によります)に接続します。
これにより、可変抵抗を回すと、中央の端子から出力される電圧が0Vから3.3Vの間で変化するようになります。
3.2. 開発環境の準備
STM32開発には、公式の統合開発環境である「STM32CubeIDE」を使用するのが一般的です。STM32CubeIDEは、プロジェクトの作成、ハードウェア設定(GUIによるピン配置や周辺機能設定)、コード生成、コンパイル、デバッグまでをこれ一つで行えます。
STM32CubeIDEのインストールと基本的な使い方はここでは割愛しますが、未導入の方は公式サイトからダウンロードしてセットアップしてください。
3.3. プロジェクト作成手順(STM32CubeIDE)
- 新しいプロジェクトの作成: STM32CubeIDEを起動し、「File」→「New」→「STM32 Project」を選択します。
- ターゲットの選択: 使用する開発ボードまたはマイコン型番を選択します。「Board Selector」タブで開発ボードを選ぶのが簡単です。(例: NUCLEO-F401REを選択)
- プロジェクト名と設定: プロジェクト名(例:
STM32_ADC_Basic)を入力し、「Targeted Project Type」は「Empty」または「STM32Cube」を選択します。ここでは「STM32Cube」を選択し、デフォルト設定で「Next」→「Finish」と進みます。 - CubeMX設定画面: プロジェクト作成後、
.iocファイルが開き、STM32CubeMXの設定画面が表示されます。ここで、マイコンのピン配置や周辺機能の設定を行います。 - System Core設定: 「System Core」→「SYS」を選択し、「Timebase Source」を「SysTick」以外(例: TIM6やTIM7)に設定することを推奨します。これは、HALライブラリの遅延関数
HAL_Delay()がデフォルトでSysTickタイマーを使用しており、ADCの変換完了待ちなどでSysTickが使われると競合する場合があるためです。 -
ADC設定:
- Pinout & Configurationタブで、ADCに使用するピン(例: PA0)をクリックし、「ADCx_INy」として設定します。(例: PA0をADC1_IN0として設定)。
- 左側の「Analog」項目から「ADC1」を選択します(使用するマイコンによってはADC1, ADC2など複数あります)。
- 「Parameter Settings」タブを開き、ADCモジュールの全体設定を行います。
- Clock Prescaler: ADCクロックの分周比を設定します。ADCクロックはAPBクロックから供給され、最大周波数はマイコンによって異なります(例: F4シリーズは36MHz)。適切な周波数(データシート参照)になるように設定します。
- Resolution: 変換の分解能を選択します。12bit, 10bit, 8bit, 6bitから選べます。今回は「12-bit resolution」を選択します。
- Data Alignment: 変換結果のデータをレジスタのどの位置に配置するか(右詰め/左詰め)を設定します。「Right alignment」が一般的です。
- Scan Conversion Mode: 複数のチャンネルを連続して変換するかどうか。「Disabled」を選択します(最初は1チャンネルのみ使用するため)。
- Continuous Conversion Mode: 変換を連続して行うかどうか。「Disabled」を選択します(ポーリングでは手動で開始するため)。
- DMA Conversion Mode: DMAを使用するかどうか。「Disabled」を選択します。
- EOC Selection: 変換完了フラグをいつ立てるか。シングルチャンネルの場合は「End of single conversion」でOKです。
- External Trigger Conversion Source: ハードウェアトリガーを使用するかどうか。「Regular conversion is software triggered」を選択します(ポーリングではソフトウェアトリガー)。
- 「Rank Settings」タブを開き、変換するチャンネルの設定を行います。
- 「Rank 1」の横にある「Add Channels」をクリックし、設定したピン(例: IN0)を選択します。
- 選択したチャンネル(例: IN0)の「Sampling Time」を設定します。サンプリング時間は、ADCが入力電圧を取り込む(コンデンサに充電する)時間です。入力信号のインピーダンスが高い場合や、精度を高めたい場合は、長めに設定する必要があります。データシートやリファレンスマニュアルに推奨値の計算方法が記載されていますが、最初は「3 Cycles」や「15 Cycles」など短い時間から試してみても構いません。ここでは例えば「3 Cycles」を選んでみます。
-
コード生成: 設定が完了したら、画面上部の「Generate Code」ボタン(歯車アイコン)をクリックします。これにより、設定に基づいた初期化コードが生成されます。
3.4. コードの実装(HALライブラリ)
生成されたコードを基に、ADCの変換と値の取得を行うコードを記述します。生成された main.c ファイルを開いて編集します。
“`c
/ USER CODE BEGIN Includes /
include // printfを使う場合
/ USER CODE END Includes /
/ USER CODE BEGIN PV /
ADC_HandleTypeDef hadc1; // ADC1のハンドル構造体
uint32_t adc_value = 0; // ADC変換結果を格納する変数
/ USER CODE END PV /
/ USER CODE BEGIN 0 /
// printfを有効にするための設定(必要に応じて)
// syscalls.cなどに実装が必要です。
// 例: ITM Debuggingを使う場合
/
int _write(int file, char ptr, int len)
{
HAL_StatusTypeDef status = HAL_UART_Transmit(&huartx, (uint8_t )ptr, len, 100); // UARTを使う場合
// HAL_UART_Transmit(&hlpuart1, (uint8_t )ptr, len, 100); // LPUARTを使う場合など
// ITM_SendChar(ptr); // ITMを使う場合
return len;
}
/
/ USER CODE END 0 /
int main(void)
{
/ USER CODE BEGIN 1 /
/ USER CODE END 1 /
/ MCU Configuration——————————————————–/
/ Reset of all peripherals, Initializes the Flash interface and the Systick. /
HAL_Init();
/ USER CODE BEGIN Init /
/ USER CODE END Init /
/ Configure the system clock /
SystemClock_Config(); // 生成されたシステムクロック設定関数
/ USER CODE BEGIN SysInit /
/ USER CODE END SysInit /
/ Initialize all configured peripherals /
MX_GPIO_Init(); // 生成されたGPIO初期化関数
MX_ADC1_Init(); // 生成されたADC1初期化関数
// MX_USART2_UART_Init(); // printfを使う場合はUARTなども初期化
/ USER CODE BEGIN 2 /
// ADCの校正(キャリブレーション)を実行します。
// 精度向上のために、変換開始前に一度実行することを強く推奨します。
if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK)
{
// Calibration Error
// エラー処理(例: LED点滅、無限ループなど)
while(1);
}
/ USER CODE END 2 /
/ Infinite loop /
while (1)
{
/ USER CODE BEGIN WHILE /
// 1. ADC変換を開始します。
if (HAL_ADC_Start(&hadc1) != HAL_OK)
{
// Start Conversion Error
// エラー処理
while(1);
}
// 2. ADC変換の完了を待ちます(ポーリング)。
// タイムアウト値を指定できます。HAL_MAX_DELAYは無限待ち。
if (HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) != HAL_OK)
{
// Poll For Conversion Error
// エラー処理
while(1);
}
// 3. ADC変換結果を取得します。
if ((HAL_ADC_GetState(&hadc1) & HAL_ADC_STATE_EOC_REG) == HAL_ADC_STATE_EOC_REG)
{
// EOC (End Of Conversion) フラグが立っていることを確認
adc_value = HAL_ADC_GetValue(&hadc1);
// 4. 変換結果を処理します。
// 例: printfで値を表示する
// printf("ADC Value: %lu\r\n", adc_value); // %luはunsigned long
// 例: UARTなどで送信する
// 例: 取得した値に応じてLEDを制御するなど
// デバッグ用に値をLCDやUARTで表示する
// printf("ADC Value: %lu\n", adc_value);
// 取得したアナログ値を電圧値に変換する例
// 12bit分解能、リファレンス電圧3.3Vの場合
float voltage = (float)adc_value * (3.3f / 4095.0f); // 12bitの最大値は4095 (0-4095)
// printf("Voltage: %.2f V\n", voltage);
// 次の変換まで少し待つ(任意)
HAL_Delay(100); // 100ミリ秒待つ
} else {
// 変換完了していない場合(HAL_ADC_PollForConversionで待つので通常ここには来ないはず)
// エラー処理やデバッグ
}
// 5. ADC変換を停止します(次の変換開始前に停止が必要な場合。連続変換モードでない場合は不要なことも)
// HAL_ADC_Stop(&hadc1); // シングル変換の場合は通常不要
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/ USER CODE END 3 /
}
// 生成された他の関数(SystemClock_Config, MX_GPIO_Init, MX_ADC1_Initなど)は省略
“`
コード解説:
ADC_HandleTypeDef hadc1;: ADC1モジュールを制御するための構造体です。CubeMXが生成するMX_ADC1_Init()関数内でこの構造体が初期化されます。HAL_ADCEx_Calibration_Start(&hadc1): ADCモジュール内部のキャリブレーションを実行します。変換の精度を高めるために、ADCを使う前に一度呼び出す必要があります。HAL_ADC_Start(&hadc1): ADC変換を開始します。設定によってはこの関数呼び出し後にハードウェアトリガーを待つ場合もありますが、ソフトウェアトリガーの場合は即座に変換が開始されます。HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY): 変換完了を待ちます。第2引数はタイムアウト時間(ms)です。HAL_MAX_DELAYを指定すると、変換が完了するまで無限に待ちます。変換が完了すると、この関数はHAL_OKを返します。(HAL_ADC_GetState(&hadc1) & HAL_ADC_STATE_EOC_REG) == HAL_ADC_STATE_EOC_REG: 念のため、ADCの変換完了 (End Of Conversion, EOC) フラグが立っているかを確認しています。HAL_ADC_PollForConversionが成功すれば通常フラグは立っています。HAL_ADC_GetValue(&hadc1): 変換結果を取得します。変換結果はデジタル値(unsigned int型など)として返されます。uint32_t adc_value = HAL_ADC_GetValue(&hadc1);: 取得した変換結果をadc_value変数に格納します。float voltage = (float)adc_value * (3.3f / 4095.0f);: 取得したデジタル値(例: 0-4095)を、実際のアナログ電圧値(例: 0-3.3V)に変換する計算です。この例では、12bit分解能 (0-4095) でリファレンス電圧が3.3Vの場合を想定しています。使用するリファレンス電圧と分解能に合わせて計算式を変更してください。HAL_Delay(100);: 次の変換を行うまで100ミリ秒待つことで、ポーリング頻度を調整しています。これは必須ではありませんが、高速でポーリングしすぎるとCPUリソースを消費するため、適切な間隔を置くのが一般的です。
このコードをコンパイルして開発ボードに書き込み、可変抵抗を回しながらデバッガーで adc_value や voltage 変数の値を監視するか、UARTデバッグ出力(printf など)で値を確認してみてください。可変抵抗の位置に応じて値が変化するのが確認できるはずです。
3.5. デバッグ方法
- 変数監視: STM32CubeIDEのデバッグ機能を使って、
adc_value変数の値をリアルタイムで監視するのが最も簡単な方法です。 - printfデバッグ: UARTなどの通信ポート経由でPCに変換結果の値を文字列として送信し、ターミナルソフトで表示する方法です。
printfを使うためには、syscalls.cファイルを適切に設定し、UARTなどの通信ペリフェラルを初期化する必要があります。
ポーリング方式は実装がシンプルで分かりやすいですが、変換が完了するまでCPUが待ち状態になるため、その間は他の処理ができません。リアルタイム性が求められるアプリケーションや、複数のタスクを並行して実行したい場合には、割り込みやDMAを利用する方法が適しています。
4. ADCの応用的な使い方
ポーリング方式だけでは対応できない状況や、より効率的なADCの使い方を学びましょう。
4.1. 割り込み方式
ADC変換が完了したときにマイコンに割り込みを発生させる方式です。これにより、CPUは変換完了を待つ必要がなく、他の処理を実行できます。変換が完了したら、割り込みハンドラ内で結果を取得します。
割り込みが必要な理由: CPUがポーリングに時間を取られず、より効率的にタスクを実行できるためです。例えば、ADC変換と並行して通信処理や他のセンサーからのデータ取得などを行いたい場合に有効です。
設定方法 (STM32CubeIDE):
- ADC設定: 基本的なADC設定はポーリング方式と同様に行います。
- NVIC設定: 「System Core」→「NVIC」を選択し、使用するADCモジュール(例: ADC1 global interrupt)の割り込みを有効 (Enabled) にします。割り込みの優先度も設定できますが、最初はデフォルトで構いません。
- ADC設定の変更: 「Analog」→「ADC1」の設定で、変換モードを適切に設定します。例えば、1回の変換で割り込みたい場合は、シングルチャンネル設定のままにします。複数のチャンネルを連続してスキャンし、一連の変換が完了したときに割り込みたい場合は、「Scan Conversion Mode」を「Enabled」にします。
コードの実装(HALライブラリ):
生成されたコードに加えて、以下の変更を行います。
- ADC変換の開始:
HAL_ADC_Start(&hadc1);の代わりに、割り込みモードで変換を開始する関数を使用します。シングルチャンネルまたはスキャンモードで一連の変換が完了した後に割り込みを発生させる場合:
c
if (HAL_ADC_Start_IT(&hadc1) != HAL_OK)
{
// Start Conversion Error
while(1);
} -
変換完了コールバック関数: 変換が完了すると、HALライブラリが提供する変換完了コールバック関数が呼び出されます。この関数をユーザーコードで実装します。
“`c
/ USER CODE BEGIN 4 /
// ADC変換完了時に呼ばれるコールバック関数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
// 変換結果を取得
// シングルチャンネルの場合
adc_value = HAL_ADC_GetValue(hadc);// ここで取得した値を使った処理を行う
// 例: フラグを立ててmainループで処理する、直接何か制御するなど// 注意: 割り込みハンドラ内では、時間のかかる処理やprintfなどの低速な処理は避けるべきです。
// フラグを立ててmainループで処理を継続するのが一般的です。// 次の変換を開始する場合は、ここで再びHAL_ADC_Start_IT()を呼び出す
// HAL_ADC_Start_IT(hadc); // 連続して変換したい場合
}
/ USER CODE END 4 /
``HAL_ADC_ConvCpltCallback
**注意**:関数は、HALライブラリによってweak属性で定義されています。ユーザーはmain.cなどで同じ名前の関数を定義することで、デフォルトの実装をオーバーライドできます。CubeIDEで生成されたコードのUSER CODE BEGIN 4/USER CODE END 4` ブロック内に記述するのが良いでしょう。 -
mainループの変更:
main関数のwhile(1)ループ内では、ポーリングのように変換完了を待つ必要がありません。他の処理を行ったり、変換完了フラグをチェックして処理を進めたりします。
“`c
int main(void)
{
// … 省略 …
/ USER CODE BEGIN 2 /
if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK) while(1);// 最初の変換を開始
if (HAL_ADC_Start_IT(&hadc1) != HAL_OK) while(1);/ USER CODE END 2 /
while (1)
{
/ USER CODE BEGIN WHILE /
// ADC変換完了を待たずに、他の処理を行う
// 例: 別のタスクを実行する
// 例: ADC変換完了フラグが立っているかチェックして、フラグが立っていたら処理を行う
// if (adc_conversion_complete_flag) {
// // ADC値を使った処理
// adc_conversion_complete_flag = 0; // フラグクリア
// HAL_ADC_Start_IT(&hadc1); // 次の変換を開始(非連続モードの場合)
// }// 例: シンプルにLEDを点滅させるなど、ADCとは関係ない処理 HAL_Delay(50); // HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); // NUCLEOボードのLEDなど /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */}
/ USER CODE END 3 /
}
“`
割り込み方式を使うことで、CPUはADC変換中に別の有益なタスクを実行できます。しかし、変換結果の取得と処理はCPUが行う必要があります。複数のチャンネルを高速かつ連続的にスキャンしたい場合や、大量のデータを取得したい場合には、DMA方式がさらに効率的です。
4.2. DMA方式
DMA (Direct Memory Access) は、CPUを介さずにペリフェラル(この場合はADC)とメモリ間でデータを直接転送する機能です。ADCの変換結果をDMAを使って直接メモリ上の配列などに転送することで、CPUの負担を大幅に軽減できます。
DMAが必要な理由: ADC変換が完了するたびにCPUが変換結果をレジスタから読み出してメモリに書き込む作業は、特に高速かつ連続的な変換を行う場合にCPUリソースを大量に消費します。DMAを使えば、このデータ転送をCPUが介入せずに行えるため、CPUは他の重要なタスクに専念できます。複数のチャンネルをスキャンする場合に非常に有効です。
設定方法 (STM32CubeIDE):
- ADC設定: 基本的なADC設定はポーリング/割り込み方式と同様に行います。複数のチャンネルをスキャンする場合は、「Scan Conversion Mode」を「Enabled」にし、「Rank Settings」タブで変換したいチャンネルを順番に設定します。
- DMA設定:
- 「System Core」→「DMA」を選択します。
- 「Add」ボタンをクリックし、DMAチャネルを追加します。通常、ADCに使用できるDMAチャネルは決まっています(データシートやリファレンスマニュアル参照。CubeMXが自動で候補を表示します)。(例: DMA2 Stream0 for ADC1)
- 追加したDMAチャネルの設定を行います。
- Mode: 「Normal」または「Circular」を選択します。
- Normal: 指定したデータ数だけ転送したらDMA転送を停止します。
- Circular: 指定したデータ数だけ転送したら、アドレスを先頭に戻して繰り返し転送を続けます。連続的なデータ取得に便利です。
- Peripheral to Memory: ADCからメモリへの転送なので、これが有効になっていることを確認します。
- Data Width: ADCの分解能に合わせて設定します。12bitや10bitの場合はHalf Word (16bit)、8bitや6bitの場合はByte (8bit) を選択します。
- Increment Address: 「Memory」側は「Increment」を有効にします(メモリ上の連続した領域にデータを書き込むため)。「Peripheral」側は「Fixed」のままです(ADCのデータレジスタは1つなので)。
- Mode: 「Normal」または「Circular」を選択します。
- 「ADC1」の設定に戻り、「Parameter Settings」タブの「DMA Conversion Mode」を「Enabled」にします。
- NVIC設定: 必要に応じて、DMA転送完了や半分完了の割り込みを有効にします(DMA設定画面またはNVIC設定画面)。ADC変換完了の割り込み(ADC global interrupt)も必要に応じて有効にしますが、DMAを使用する場合は通常、DMA転送完了割り込みで処理を行うことが多いです。
コードの実装(HALライブラリ):
DMA転送用のバッファ(配列)と、DMA転送を開始する関数を使用します。
“`c
/ USER CODE BEGIN PV /
ADC_HandleTypeDef hadc1;
// DMAで取得したADC変換結果を格納するバッファ
// 1チャンネルの場合は要素数1の配列でも良いですが、複数チャンネルの場合はチャンネル数と同じ要素数の配列にします。
// 例えば、3チャンネルをスキャンする場合:
define NUM_ADC_CHANNELS 3
uint16_t adc_dma_buffer[NUM_ADC_CHANNELS]; // 12bit分解能なのでuint16_tが適切
/ USER CODE END PV /
// … 省略 …
int main(void)
{
// … 省略 …
/ USER CODE BEGIN 2 /
if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK) while(1);
// DMAモードでADC変換を開始
// 第2引数はデータ転送先のバッファアドレス
// 第3引数は転送するデータの数(バッファの要素数)
if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_dma_buffer, NUM_ADC_CHANNELS) != HAL_OK)
{
// Start Conversion Error
while(1);
}
/ USER CODE END 2 /
while (1)
{
/ USER CODE BEGIN WHILE /
// DMAがバックグラウンドでデータを転送している間、他の処理を実行
// 必要に応じて、DMA転送完了や半分完了の割り込みハンドラ内で処理を行うか、
// mainループ内でバッファのデータを定期的に処理する。
// DMAをCircularモードにした場合、バッファは常に最新の変換結果で更新される。
// 1チャンネル連続変換をDMA Circularで取得する場合:
// adc_dma_buffer[0] に常に最新の値が入っている
// 複数チャンネルスキャンをDMA Circularで取得する場合:
// adc_dma_buffer[0] にチャンネル1の値
// adc_dma_buffer[1] にチャンネル2の値
// ... が常に最新に更新される
// 例: 取得した値を表示(Circularモードを想定)
// printf("ADC Ch0: %u, Ch1: %u, Ch2: %u\r\n", adc_dma_buffer[0], adc_dma_buffer[1], adc_dma_buffer[2]);
// HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/ USER CODE END 3 /
}
/ USER CODE BEGIN 4 /
// DMA転送完了時に呼ばれるコールバック関数(DMA設定で割り込み有効時)
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef hadc)
{
// DMA Circularモードの場合、このコールバックは不要なことが多い。
// Normalモードで、転送完了後に次の変換を開始する場合などに使用。
// 例:
// printf(“DMA Transfer Complete!\r\n”);
// DMA Normalモードの場合、ここで次の変換を開始する必要がある。
// HAL_ADC_Start_DMA(hadc, (uint32_t)adc_dma_buffer, NUM_ADC_CHANNELS);
}
// DMA半分転送完了時に呼ばれるコールバック関数(DMA設定で割り込み有効時)
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
// DMA Circularモードで、バッファの半分が埋まった時点で処理を行いたい場合などに使用。
// いわゆるダブルバッファリングのような処理が可能になる。
// 例:
// printf(“DMA Transfer Half Complete!\r\n”);
}
/ USER CODE END 4 /
“`
コード解説:
uint16_t adc_dma_buffer[NUM_ADC_CHANNELS];: DMAによってADC変換結果が直接書き込まれるメモリ上の配列です。要素数は変換するチャンネル数または取得したいデータ数に合わせてください。12bit分解能なので、各要素は16bit (uint16_t) で十分です。HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_dma_buffer, NUM_ADC_CHANNELS): DMAモードでADC変換を開始します。第2引数にはバッファの先頭アドレスをuint32_t*にキャストして渡します。第3引数にはDMAで転送するデータ数(バッファの要素数)を指定します。HAL_ADC_ConvCpltCallback/HAL_ADC_ConvHalfCpltCallback: DMA設定で割り込みを有効にした場合に呼び出されるコールバック関数です。DMA NormalモードではHAL_ADC_ConvCpltCallbackで次の変換を開始したり、取得したデータを処理したりします。DMA Circularモードでは、バッファが常に更新されるため、これらのコールバックは必須ではありませんが、データの周期的な処理やダブルバッファリング実装に利用できます。
DMA方式は最も効率的にADCデータを取得できますが、設定項目がやや複雑になります。特に複数チャンネルのスキャンやDMAのCircularモードは、連続的なデータストリーム処理に非常に強力です。
4.3. トリガーソース
ADC変換を開始するきっかけ(トリガー)を何にするかを選択できます。
- ソフトウェアトリガー: プログラムコード中で
HAL_ADC_Start()やHAL_ADC_Start_IT(),HAL_ADC_Start_DMA()のような関数を呼び出すことで変換を開始します。これまでの例で使用してきた方法です。最もシンプルですが、変換開始タイミングはCPUの実行速度に依存します。 - ハードウェアトリガー: 外部からの信号(GPIOピンの変化、タイマーのイベント、他のペリフェラルの出力など)をきっかけに自動的に変換を開始します。
ハードウェアトリガーを使うメリット:
- 正確なサンプリング周期: タイマーをトリガーソースとして使用すれば、一定間隔で正確にADC変換を行うことができます。これは信号処理において非常に重要です。
- 外部イベントとの同期: 外部センサーの出力変化や、他のシステムの信号に同期して変換を開始できます。
- CPU負荷の軽減: CPUがポーリングやトリガーとなる信号の変化監視を行う必要がなくなります。
設定方法 (STM32CubeIDE):
- ADC設定: 「Analog」→「ADC1」の設定で、「External Trigger Conversion Source」の項目を変更します。デフォルトの「Regular conversion is software triggered」から、使用したいトリガーソースを選択します。(例: Timer 1 TRGO event, Timer 2 TRGO event, External interrupt line など)。
- トリガーソースの設定: 選択したトリガーソースとなるペリフェラル(タイマー、EXTIなど)を別途設定する必要があります。例えば、タイマーの更新イベント (Update Event) をトリガーにする場合は、該当のタイマー(例: TIM2)を有効にし、そのタイマーが一定周期で更新イベントを発生させるように設定します。そして、タイマーの設定項目で「TRGO Parameters」を選び、トリガー出力として「Update Event」などを選択します。
コードの実装(タイマーによる周期的な変換):
タイマーを使って1ms周期でADC変換を自動的に開始する例を考えます。
- CubeMX設定:
- TIM2を有効にする(Clock Sourceなどを設定)。
- TIM2の「Parameter Settings」で、周期(Period)を設定します。システムクロックとプリスケーラーを考慮して、1ms周期になるように設定します。
- TIM2の「TRGO Parameters」で、「Trigger Event Selection」を「Update Event」に設定します。
- ADC1の設定で、「External Trigger Conversion Source」を「Timer 2 TRGO event」に設定します。
- ADC1の変換モードは、シングルチャンネルを周期的に変換したい場合は「Continuous Conversion Mode」を「Disabled」、「Scan Conversion Mode」も「Disabled」(1チャンネルの場合)、「DMA Conversion Mode」は必要に応じて有効にします(DMAと組み合わせるのが一般的)。
-
コード:
main.cに以下のコードを追加します。“`c
int main(void)
{
// … 省略 …
/ USER CODE BEGIN 2 /
if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK) while(1);// タイマーの初期化(MX_TIM2_Init()が自動生成される)
// タイマーと連動してADC変換を開始
// ここではDMAと組み合わせる例(最も効率的)
if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_dma_buffer, NUM_ADC_CHANNELS) != HAL_OK)
{
while(1);
}// タイマーを起動することで、タイマーイベントをトリガーとしたADC変換が自動的に開始される
if (HAL_TIM_Base_Start(&htim2) != HAL_OK) // TIM2の基本タイマーを起動
{
while(1);
}
// または HAL_TIM_Base_Start_IT(&htim2); // タイマー割り込みも使う場合/ USER CODE END 2 /
while (1)
{
/ USER CODE BEGIN WHILE /
// タイマーとDMAがバックグラウンドで動作しているので、mainループは他の処理に専念できる。
// ADCの変換結果はadc_dma_bufferに自動的に格納される。
// 必要に応じてadc_dma_bufferのデータを処理する。
/ USER CODE END WHILE //* USER CODE BEGIN 3 */}
/ USER CODE END 3 /
}
“`
ハードウェアトリガー、特にタイマーとの組み合わせは、リアルタイムなデータサンプリングにおいて非常に強力な手法です。オーディオ信号の取得や、制御系のフィードバック信号のサンプリングなど、厳密なサンプリング周期が必要な場合に欠かせません。
4.4. 連続変換モードと非連続変換モード
ADCには、変換を開始した後の動作を決定するモードがあります。
- シングル変換モード (Single Conversion Mode): ソフトウェアトリガーまたはハードウェアトリガーで変換を開始すると、指定された1つまたは複数のチャンネルの変換を一度だけ実行し、自動的に停止します。次の変換を行うためには、再びトリガーを発生させる必要があります。これまでのポーリング、割り込み、DMAの例は、トリガーによって一度の変換シーケンスを実行する例でした。
- 連続変換モード (Continuous Conversion Mode): 変換を開始すると、指定されたチャンネル(またはチャンネルシーケンス)の変換をトリガーなしで繰り返し続けます。前回の変換が完了すると、自動的に次の変換を開始します。常に最新のアナログ値を監視したい場合に便利です。CubeMXのADC設定で「Continuous Conversion Mode」を「Enabled」にします。
- 非連続モード (Discontinuous Mode): 複数のチャンネルをスキャンする場合に設定できます。スキャンシーケンスをいくつかのグループに分け、トリガーが発生するたびに1つのグループの変換を実行します。次のトリガーで次のグループの変換を行います。例えば、4つのチャンネルをスキャンする際に、トリガー1でチャンネル1,2を変換し、トリガー2でチャンネル3,4を変換する、といった使い方ができます。CubeMXのADC設定で「Discontinuous Mode」を「Enabled」にし、グループのサイズ(Number of Conversions)を設定します。
5. ADC使用上の注意点とトラブルシューティング
ADCを正確に使うためには、いくつかの注意点があります。
5.1. ノイズ対策
アナログ信号はノイズに非常に弱いです。特にデジタル回路が多いマイコンボード上では、デジタル信号のスイッチングノイズがアナログ信号に影響を与えやすいです。
- 電源の分離: ADCのアナログ電源ピン (VDDA, VSSA) には、デジタル電源 (VDD, VSS) とは分けて、可能な限りクリーンな電源を供給します。専用のLDOレギュレーターを使用したり、フェライトビーズを通してノイズを除去したりといった対策が有効です。
- パスコン (デカップリングコンデンサ): VDDAとVSSAの間に、数種類の容量のパスコン(例えば、10μFの電解コンデンサや積層セラミックコンデンサと、0.1μFの積層セラミックコンデンサ)をできるだけピンの近くに配置します。これにより、電源ラインのノイズを吸収します。
- アナログGNDとデジタルGND: アナログGND (VSSA) とデジタルGND (VSS) は、一点で接続するのが理想的です(スターグラウンド)。これにより、デジタル回路のグラウンドノイズがアナログ回路に流れ込むのを防ぎます。開発ボードによっては既に設計されていますが、自作基板の場合は特に注意が必要です。
- アナログ入力ピンの配線: アナログ入力信号線は、ノイズ源(高速なデジタル信号線、クロック線、スイッチング電源など)から離して配線します。シールド線を使用するのも有効です。
- 未使用アナログ入力ピン: 未使用のアナログ入力ピンは、フローティングさせずにVDDAやVSSAに接続しておくことを推奨します(データシートやリファレンスマニュアルで推奨される接続方法を確認してください)。
5.2. 入力インピーダンスとサンプリング時間
ADCのアナログ入力は、内部的にはサンプリングキャパシタに接続されています。サンプリング時には、このキャパシタに入力電圧まで充電する必要があります。
- 入力インピーダンス: ADC入力ピンを見かけ上の抵抗と考えると、そのインピーダンスは非常に高いですが、実際にはサンプリングキャパシタへの充電電流が必要です。信号源の出力インピーダンスが高い場合(例えば、抵抗分圧回路で高い抵抗値を使用している場合)、サンプリング時間が短いとキャパシタが十分充電されず、正確な値が得られません。
- サンプリング時間: ADC設定で指定するサンプリング時間は、この充電にかける時間です。信号源の出力インピーダンスが高いほど、またはADCクロックが速いほど、より長いサンプリング時間が必要になります。データシートには、信号源インピーダンスとサンプリング時間の関係を示すグラフや計算式が記載されています。これを参考に、適切なサンプリング時間を設定してください。特に複数のチャンネルをスキャンする場合、各チャンネルの入力インピーダンスに応じて適切なサンプリング時間を選択することが重要です(通常、シーケンス中の各チャンネルごとにサンプリング時間を設定できます)。
5.3. リファレンス電圧の安定性
ADC変換結果はリファレンス電圧を基準としています。リファレンス電圧が不安定だと、正確な測定ができません。
- VDDAをリファレンス電圧として使用する場合、VDDAが安定していることが重要です。前述のノイズ対策やパスコン配置をしっかり行います。
- より高い精度が必要な場合は、マイコンの外部リファレンス入力ピンに、高精度な外部リファレンス電圧ICからの電圧を供給することを検討します。
5.4. ADC校正の重要性
ADCには製造上のばらつきや、温度、電圧による特性の変化があります。ADC校正(キャリブレーション)は、これらの影響を補正して精度を向上させるために行われます。
- HALライブラリの
HAL_ADCEx_Calibration_Start()関数は、ADCのデジタル校正を実行します。 - 変換を開始する前に、ADCモジュールの有効化後、一度だけ校正を実行することを推奨します。
- マイコンによっては、アナログ校正が必要な場合もあります。データシートやリファレンスマニュアル、HALドキュメントを確認してください。
5.5. 変換結果がおかしい場合のチェックポイント
- ハードウェア:
- 配線は正しいか?(電源、GND、アナログ入力ピン)
- 可変抵抗やセンサーは正しく動作しているか?
- VDDA/VSSAに正しく電源が供給されているか?パスコンは適切に配置されているか?
- リファレンス電圧は安定しているか?
- ソフトウェア (CubeMX設定):
- ADCモジュールは有効になっているか?
- 使用するピンはADCアナログ入力に設定されているか?
- Clock Prescalerの設定は適切か?(データシートの最大クロック周波数制限を守っているか)
- Resolution、Data Alignmentは意図通りか?
- 使用するチャンネルはRank設定に追加されているか?
- Sampling Timeは適切か?(特に信号源インピーダンスが高い場合)
- Conversion Mode(Scan, Continuous, Discontinuous)は意図通りか?
- DMAを使用する場合、DMAは有効か?チャネル、モード、データ幅、インクリメント設定は正しいか?
- 割り込みを使用する場合、NVICで割り込みが有効になっているか?
- ハードウェアトリガーを使用する場合、トリガーソースは正しく設定されているか?(タイマーなどの設定も確認)
- ソフトウェア (コード):
HAL_Init(),SystemClock_Config(), 周辺機能のInit関数は呼び出されているか?HAL_ADCEx_Calibration_Start()は呼び出されているか?HAL_ADC_Start_...()関数は呼び出されているか?- ポーリングの場合、
HAL_ADC_PollForConversion()で正常に完了を待っているか? - 割り込みの場合、
HAL_ADC_ConvCpltCallback()が呼び出されているか?(ブレークポイントなどで確認) - DMAの場合、
HAL_ADC_Start_DMA()は呼び出されているか?バッファサイズは正しいか?バッファにデータが書き込まれているか?(デバッガーでバッファの中身を確認) - 取得したデジタル値から物理量への変換計算は正しいか?(リファレンス電圧、分解能を考慮)
これらのチェックポイントを順に確認していくことで、問題の原因を特定できることが多いです。
6. 実践例
ここでは、簡単なセンサーを接続してADC値を読み取る例を挙げます。
例: 温度センサー LM35 を接続して温度を測定する
LM35は、温度に比例したアナログ電圧を出力する温度センサーICです。出力電圧は、例えば室温25℃で250mV、1℃あたり10mV変化します。0℃で0V、100℃で1Vを出力します(電源電圧範囲内)。
- ハードウェア:
- LM35(または互換品)を用意します。
- LM35の電源ピンをSTM32の3.3Vに、GNDピンをGNDに接続します。
- 出力ピン (Vout) をSTM32のADCアナログ入力ピン(例: PA1)に接続します。
- CubeMX設定:
- PA1ピンをADC1_IN1として設定します。
- ADC1の設定は、これまでの例と同様(分解能12bit、リファレンス電圧3.3VVDDA、ポーリングまたは割り込み/DMA)。サンプリング時間はLM35の出力インピーダンスを考慮して適切に設定します(通常、LM35の出力インピーダンスは低いので短い時間でも大丈夫なことが多いです)。
- コード:
- ADC変換結果を取得します(
adc_value)。 - 取得したデジタル値を電圧に変換します。
c
float voltage = (float)adc_value * (3.3f / 4095.0f); // リファレンス3.3V, 12bitの場合 - 電圧値から温度に変換します。LM35は1℃あたり10mVなので、電圧を0.01Vで割るか、100倍すれば温度(℃)になります。
c
float temperature_celsius = voltage * 100.0f; // 電圧をmV単位にして10で割るのと同じ - 変換した温度をprintfなどで表示します。
- ADC変換結果を取得します(
この例では、ADCを使って現実世界のアナログ情報(温度)をデジタル値として取得し、それを物理量(℃)に変換する一連の流れを体験できます。
例: 可変抵抗で取得したADC値を使ってLEDの明るさをPWMで制御する (ADC to PWM)
これは、アナログ入力(可変抵抗)を使ってデジタル出力(PWM制御されたLED)を制御する基本的な例です。
- ハードウェア:
- 可変抵抗をADC入力ピンに接続します(上記3.1参照)。
- LEDを抵抗と直列に接続し、STM32のPWM出力に対応したピン(例: PA8, TIM1_CH1など。タイマーとチャネルは使用するマイコンとピンによります)に接続します。
- CubeMX設定:
- 可変抵抗の接続ピンをADCアナログ入力として設定します。
- LEDの接続ピンをタイマーのPWM出力として設定します。(例: TIM1のChannel 1をPWM Generation Channelとして設定)。
- タイマーのクロック、周期(Period)、プリスケーラーを設定します。周期はPWMの分解能(デューティ比の設定範囲)を決定します。例えば、周期を999に設定すると、デューティ比は0から999の1000段階で設定できます。
- ADC設定は、ポーリング、割り込み、またはDMAで行います。
- コード:
- タイマーのPWM機能を初期化します(MX_TIMx_Init() が自動生成される)。
HAL_TIM_PWM_Start()関数でPWM出力を開始します。- ADC変換結果を取得します(
adc_value)。 - 取得したADC値(例: 12bitなら0-4095)を、PWMの周期(例: 0-999)に合わせてスケーリングします。
c
// ADC値(0-4095)をPWMデューティ値(0-999)に変換
uint32_t pwm_duty = (adc_value * 1000) / 4096; // 4095で割るのが厳密ですが、簡略化して4096で割ることもあります
if (pwm_duty > 999) pwm_duty = 999; // 範囲内に収める - 変換したPWMデューティ値を、PWM出力設定レジスタに書き込みます。HALライブラリでは、
HAL_TIM_SetCompare()関数を使用します。
c
// TIM1のChannel 1のデューティ比を設定
HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, pwm_duty); - ADC変換とPWMデューティ比更新の処理を、ポーリング、割り込み、またはDMAを使って周期的に実行します。
この例は、アナログ入力とデジタル出力(PWM)を組み合わせた基本的な制御システムの構築方法を示しています。センサーからの情報を基にモーターの速度や光の明るさを制御するなど、様々な応用が考えられます。
7. まとめ
この記事では、STM32のADCの使い方について、初心者向けに徹底的に解説しました。
- ADCの基本的な仕組み(サンプリング、量子化、符号化)、性能指標(解像度、変換速度)、リファレンス電圧、チャンネルといった基礎知識を学びました。
- STM32のADCの特徴、内部チャンネル、複数モジュールの注意点について理解しました。
- 最も基本的な「ポーリング方式」での具体的な使い方を、STM32CubeIDEの設定手順とHALライブラリのコード例を交えて解説しました。
- より高度な「割り込み方式」と「DMA方式」について、それぞれのメリット、設定方法、コード実装のポイントを説明しました。これらの方式は、CPU負荷を軽減し、効率的なデータ取得やリアルタイム処理を実現するために重要です。
- ADC変換の開始タイミングを制御する「トリガーソース」(ソフトウェア/ハードウェア)と、変換の繰り返し動作を決定する「変換モード」(シングル/連続/非連続)について学びました。
- ADCを正確に使うための注意点として、ノイズ対策、入力インピーダンスとサンプリング時間、リファレンス電圧の安定性、校正の重要性を挙げ、トラブルシューティングのチェックポイントを示しました。
- 簡単な実践例として、温度センサーからのデータ取得と、可変抵抗によるPWM制御のアイデアを紹介しました。
STM32のADCは非常に多機能であり、使いこなすには様々な設定やモードを理解する必要があります。しかし、この記事で解説した基本的な使い方や応用例を参考に、実際に手を動かして試してみることで、着実にスキルを習得できるはずです。
この記事が、あなたのSTM32開発におけるADC活用の強力な一助となれば幸いです。アナログ世界の情報をデジタル世界に取り込み、あなたのアイデアを形にするために、ぜひSTM32のADCを積極的に活用してください。
ADCマスターへの道は開かれました!
8. 参考資料
さらに深く学びたい場合は、以下の公式ドキュメントが非常に役立ちます。
- 使用するSTM32チップのデータシート: 電気的特性、ピン配置、ADCの最大クロック周波数、推奨サンプリング時間など、チップ固有の情報が記載されています。
- 使用するSTM32シリーズのリファレンスマニュアル: ADCモジュールのレジスタ、詳細な動作モード、ブロック図、各種設定項目の意味などが詳細に解説されています。HALライブラリはこのマニュアルに記載されたハードウェアレジスタを操作しています。
- STM32Cube HAL API ドキュメント: HALライブラリの関数仕様、引数、戻り値、使用方法などが記載されています。STM32CubeIDEのヘルプメニューから参照できます。
- STM32の各種アプリケーションノート: ADCの応用例や、より高度な機能(オーバーサンプリング、デュアルADCモードなど)に関する情報が記載されていることがあります。
これらの公式資料は内容が非常に専門的ですが、この記事で得た基礎知識があれば、必要な情報を探しやすくなるでしょう。
頑張ってください!