今日は引き続き学習ノートを作成します。
前の数日で ADC モジュールを学び終えたので、タイマーを書いた後に ADC モジュールも書きました。
(一)ADC とは#
インターネットで少し調べると、ADC の定義がわかります:アナログ - デジタル変換、つまり Analog-to-Digital Converter、一般に ADC と呼ばれ、連続変数のアナログ信号を離散的なデジタル信号に変換するデバイスを指します。例えば、ADC を使用して温度センサーが生成する電気信号をデジタル信号 0101 のようなものに変換します。
ADC モジュールに対応するのは DAC モジュールで、DAC モジュールはデジタル信号をアナログ信号に変換できます。STM32 には ADC モジュールがありますが、DAC モジュールはありません。
(二)STM32 上の ADC 機能#
私たちが使用しているチップ STM32F103C8T6 の ADC リソースには ADC1、ADC2 があり、10 の外部入力チャネルがあります。その中で注入グループには 4 つのチャネルがあり、残りは規則グループです。
注入グループの 4 つのチャネルは互いに干渉せず、同時に使用および変換できます。
規則グループはリストに従って順次変換する必要があり、ストレージ位置は 1 つだけで、後に変換されたデータは前のデータの位置を上書きします。複数チャネルの ADC 変換を完了するには DMA の協力が必要です。
さらに、規則グループの設定にはさまざまなモードの選択肢があり、主に連続変換とスキャンモードを有効にするかどうかです。
連続変換は、一度の変換が完了した後にすぐに次の変換を接続することを意味し、スキャンモードは規則グループで設定されたリストの他のチャネルをスキャンし続けます。無効にすると、1 つのチャネルのみが変換されます。
(三)AD 単チャネル設定#
まず、単一チャネル変換の設定方法を学びます。
まずは構造図を見てみましょう。
構造図に基づいて AD 単チャネルの設定方法がわかります。
第一歩として、ADC と GPIO のクロックを有効にする必要があります:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
第二歩として、ADC クロックを設定し、プリスケーラーを調整します。ADC の最大周波数は 14Hz なので、6 分周以上に設定する必要があります:
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
第三歩として、GPIO ポートを初期化し、ADC モードに設定します:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
第四歩として、規則グループチャネルを設定します(注入グループは使用しません):
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
第五歩として、ADC を初期化します。この際、ADC 独立モード、非連続スキャンモード、外部トリガソースなし(ソフトウェアトリガ)、1 つのチャネル、データ右揃えを設定します:
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
第六歩として、ADC を起動します:
ADC_Cmd(ADC1, ENABLE);
第七歩として、ADC のキャリブレーションを行います。これは固定のプロセスです:
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
これで ADC の設定が完了しました。次に変換を開始できます:
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
変換プロセスには時間がかかります。フラグをチェックして変換が完了したかどうかを判断できます:
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
最後に、データを取得します:
ADC_GetConversionValue(ADC1);
(四)AD 多チャネル#
AD 多チャネルは、複数のチャネルを同時に開いて変換することを意味し、DMA を使用する必要があります。
DMA の構造図は以下の通りです:
では、AD 多チャネルをどのように設定するのでしょうか?
最初の 3 ステップは AD 単チャネルと基本的に同じですが、DMA クロックを追加で有効にし、いくつかのピンを初期化する必要があります:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
次に、規則グループチャネルを設定します。いくつかを追加で設定し、順序を決めます:
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);
次に ADC を初期化します。今回は連続スキャンモードを有効にし、チャネル数を 4 に設定します:
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 4;
ADC_Init(ADC1, &ADC_InitStructure);
次に DMA を初期化します。ここでは、外部デバイスとストレージのサイズを半単語(つまり 16 ビット)に設定し、外部デバイスのアドレスはレジスタ DR を指すポインタに設定します(前述のストレージデータの位置はこれです)、外部デバイスのアドレスは自動増加しない(常に DR アドレスを読み取る)、ストレージはデータを保存する配列を指し、循環を有効にします:
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 4;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
次に DMA を有効にし、ADC1 が DMA1 の信号をトリガーするように設定し、最後に ADC を有効にします:
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC のキャリブレーションも欠かせません:
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
最後に変換を開始します。これでストレージデータの配列に必要なデータが得られます:
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
これで ADC の機能はこれだけです。886。