前言#
著者が以前書いた「第一歩の設定」と「MSPM0 チュートリアルの SYSCONFIG の呼び出しおよび GPIO の出力と入力の設定」をまだ見ていない場合は、設定ミスを避けるために、これらの 2 つを先に読むことを強くお勧めします。すでに SYSCONFIG の設定を完了している場合は、そのまま読み進めてください。
創易栈 MSPM0 開発ボードの参考電圧選択#
創易栈のユーザーマニュアルによると、ボード上の VREF は 2.5V であり、ボードのスイッチを ON にするだけです。
SYSCONFIG 設定#
Keil で empty.syscfg を開き、次に SYSCONFIG ツールを呼び出します。
SYSCONFIG に入ったら、ADC を追加し、まずその紹介を見てみましょう。
基本設定では、ユーザーは以下のことができます:
- サンプリングクロックとサンプリングモードの設定:これらのパラメータは ADC のサンプリングレートに影響を与え、すなわち、1 秒あたりの入力信号のサンプリング回数です。
- ADC 変換メモリの設定:アナログ信号から変換されたデジタルデータを保存します。
- 以下の機能の有効化 / 無効化:
- ハードウェア平均モード:複数回のサンプリング結果を平均化することで、ランダムノイズの影響を減少させます。
- 消費電流源の使用:ADC 変換中に安定した基準電圧を提供するために使用される可能性があります。
- ウィンドウコンパレータモード:入力信号が指定された範囲内にある場合のみ、ADC 変換を行います。
- トリガーモード:特定のトリガ信号を受信したときのみ ADC 変換を開始するように設定できます。
高度な設定では、ユーザーは以下のことができます:
- 変換周波数 / 解像度の設定:ADC 変換の精度と速度に影響を与えます。
- FIFO モードの有効化:このモードでは、ADC は連続して複数回の変換を行い、結果を先入れ先出し(FIFO)キューに保存し、後続の処理を容易にします。
- 電源降下モードの設定:ADC 変換が必要ない場合、ADC モジュールの電源を低下させてエネルギーを節約できます。
- サンプリング時間の指定:急速に変化するアナログ信号の場合、より短いサンプリング時間が必要になることがあります。
ADC パラメータ#
参考マニュアルに示されているように、この ADC の異なる解像度での変換周期です。
ULPCLK を使用する場合、このサンプリング遅延を回避できます。
再サンプリングモードを選択します。
SYSCONFIG でトリガソースを設定し、イベントトリガに設定します。このイベントはタイマーによって発生します。
Conversion Data Format のオプションで無符号右揃えを選択します。
高度な設定では、収集時間を設定することでサンプリングレートを決定できます(12bit の上限は 1.45M)。サンプリングレートは 32(クロック周波数)/(1 / サンプリング時間 + 変換時間(12bit 解像度は 14 クロック周期)) で計算できます。計算により、サンプリング時間を 250ns(すなわち 8 つの (32M) クロック周期)に設定すると、サンプリングレートは 32/(8+14)=1.45M になります。
次に、タイマーを設定します。タイマーの設定は図の通りで、対応するトリガイベントが正しく設定されていることを確認します。
次に ADC 設定ページに戻り、サブスクリプションイベントを設定します。
次に DMA 転送を有効にし、DMA のパラメータを設定します。これにはトリガソース、データ長、転送アドレスモードの設定、および転送モードが含まれます。
転送モードは、データがソースアドレスからターゲットアドレスにどのように転送されるかを定義します。4 つのモードがあります:
-
Single Transfer:このモードでは、各転送にはトリガ信号が必要です。「Transfer Size」で設定された回数の転送が完了すると、DMA は無効になります。
-
Block Transfer:このモードでは、1 つのトリガ信号で完全なデータブロックを転送できます。データブロックのサイズは「Transfer Size」で決まります。各転送が終了すると、DMA は無効になります。
-
Repeated Single Transfer:このモードでは、各転送にはトリガ信号が必要ですが、すべての転送が完了した後も DMA は有効のままです。
-
Repeated Block Transfer:このモードでは、1 つのトリガ信号で完全なデータブロックを転送できます。データブロックのサイズは「Transfer Size」で決まります。「Repeated Single Transfer」と同様に、すべての転送が完了した後も DMA は有効のままです。
どの転送モードを選択するかは、具体的なニーズによります。たとえば、大量のデータを連続して転送する必要がある場合は、「Block Transfer」または「Repeated Block Transfer」モードを選択できます。各データ転送を細かく制御する必要がある場合は、「Single Transfer」または「Repeated Single Transfer」モードを選択できます。
FILE-save をクリックし、次に Keil でコンパイルし、設定をインポートします。
main 関連コード#
#define ADCSize 128
typedef enum ADCFlag
{
ADCFlagDmaStart = 0,
ADCFlagDmaDone,
ADCFlagConversionDone,
ADCFlagConversionStart
}ADCFlag;
unsigned short ADCResult[ADCSize];//ADC受信バッファを定義
ADCFlag adcflag;//列挙変数adcflagを定義
void UART1SendString(char *str)
{
while(*str != '\0')
{
DL_UART_transmitDataBlocking(UART1_INST, *str);
str++;
}
}
void UART1SendChar(char c)
{
DL_UART_transmitDataBlocking(UART1_INST, c);
}
void strreverse(char* begin, char* end) {
char aux;
while(end>begin)
aux=*end, *end--=*begin, *begin++=aux;
}
/**
* @brief 整数値を指定基数の空終止文字列に変換し、strパラメータで指定された配列に結果を格納します。
*
* @param value: 文字列に変換する値。
* @param str: 結果を格納する空終止文字列の配列。この配列は結果文字列(空文字を含む)を格納するのに十分な長さである必要があります。
* @param base: 変換する数値の基数。これは[2, 36]の範囲内でなければなりません。10を超える基数の場合、この関数は小文字を使用します。
*
* @note 基数が10でvalueが負の場合、結果文字列の前に負号(-)が付加されます。他の基数の場合、valueは常に符号なしと見なされます。
*
* @note 基数が[2, 36]の範囲外の場合、関数は何も実行せず、strは空の文字列になります。
*
* @note この関数は「unsigned long」数字を処理しません - それらは「long」として扱われます。したがって、2147483647を超える値に対して、この関数は10を超える基数には適用されません。
*
* @return なし。
*/
void itoa(int value, char* str, int base) {
static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz";
char* wstr = str;
int sign;
// 基数の検証
if (base < 2 || base > 35) { *wstr = '\0'; return; }
// 符号の処理
if ((sign=value) < 0) value = -value;
// 変換。数値は逆順になります。
do *wstr++ = num[value%base]; while(value/=base);
if(sign<0) *wstr++='-';
*wstr='\0';
// 文字列を逆にする
strreverse(str,wstr-1);
}
/**
* @brief ADC結果を処理する関数
*
* @param adcflag: ADCフラグを指すポインタ。ADCがデータ変換を完了すると、このフラグはADCFlagDmaDoneに設定されます。
* @param ADCResult: ADC結果を格納する配列を指すポインタ。
* @param resultSize: ADC結果配列のサイズ
*
* @note adcflagが指す値がADCFlagDmaDoneの場合、関数はタイマーとADCのデータ変換を停止し、ADCResult配列を走査します。
* そして、itoa関数を使用して各結果を文字列に変換し、UARTを介して送信します。すべての結果を送信した後、関数はadcflagが指す値をADCFlagConversionStartに設定し、
* ADCが新しいデータ変換を開始できることを示します。文字列を送信する際、各結果の後に改行文字が送信され、受信側が各結果の境界を正しく認識できるようにします。
*
* @return なし。
*/
void processADCResults(ADCFlag* adcflag, unsigned short* ADCResult, int resultSize)
{
if (*adcflag == ADCFlagDmaDone)
{
DL_Timer_stopCounter(TIMER_0_INST);
DL_ADC12_stopConversion(ADC12_0_INST);
volatile int i;
for ( i = 0; i < resultSize; i++)
{
char buffer[12]; // 整数を文字列に変換するためのバッファ、int32の最大サイズは11文字 + null終端
itoa(ADCResult[i], buffer, 10);
UART1SendString(buffer);
UART1SendString("\r\n");
}
*adcflag = ADCFlagConversionStart;
i = 0;
}
}
void setupDMA(DMA_Regs *dma, uint8_t channelNum, unsigned int srcAddr, unsigned int destAddr, unsigned int transferSize)
{
DL_DMA_setSrcAddr(dma, channelNum, (unsigned int) srcAddr);
DL_DMA_setDestAddr(dma, channelNum, (unsigned int) destAddr);
DL_DMA_setTransferSize(dma, channelNum, transferSize);
DL_DMA_enableChannel(dma, channelNum);
}
int main(void)
{
SYSCFG_DL_init();
NVIC_EnableIRQ(UART1_INT_IRQn);
NVIC_EnableIRQ(ADC0_INT_IRQn);
adcflag = ADCFlagConversionStart;
setupDMA(DMA,DMA_CH0_CHAN_ID,(unsigned int) & (ADC0->ULLMEM.MEMRES[0]),(unsigned int) &ADCResult,ADCSize);
if(adcflag == ADCFlagConversionStart)
{
DL_Timer_startCounter(TIMER_0_INST);
DL_ADC12_startConversion(ADC12_0_INST);
}
while (1)
{
processADCResults(&adcflag, ADCResult, ADCSize);
}
}
実際のデモ#
私は毎回 128 個の点をサンプリングし、連続サンプリングではないため、毎回 RST をリセットすると、サンプリング信号は前回と連続しないことが確実で、以下のような尖った点が現れます。一度の連続信号の観点から見ると、効果は悪くありません。
転載の際は出典を明記してください。
by <a href=https://www.cnblogs.com/jiongsheng > QDU_jiongsheng