简 介: 摘要:
本文分析了STC32F单片机使用DMA采集ADC数据时的时钟配置与转换速率计算方法。ADC转换过程包含12个时钟周期,通过ADCTIM寄存器设置分频系数和转换参数,最终转换速率不得超过800kHz。当结合DMA传输时,总时间包括ADC转换时间(16个系统周期)和DMA传输时间。实验基于56MHz系统时钟,理论采样频率为875kHz,实测150kHz信号通过FFT分析验证了采样频率的准确性(873.658kHz)。代码示例展示了ADC-DMA配置与数据处理流程,包括均值去除、补零FFT等操作。

关键词 STC32ADC

 

01 STC32单片机ADC采集频率


一、ADC时钟

  STC32F 单片机的 ADC转换过程,  包括有 12个转换时钟,   前面三部分, 分别是设置时间,准备时间,保持时间。  都是在ADCTIM 寄存器中设置。  分别在对应的 bit 位对应的数值基础上加1.  最后,可以通过这些设置获得 ADC的转换速率。  分母上包括有 两部分, 前面是ADC时钟分频系数对应的倍数,  后面是 ADC 转换过程所需要的时钟对应的参数。  最终, 使用系统时钟除以上述两个部分的乘积, 便可以得到ADC的实际转换速率。  根据 STC32F 单片机内部工艺, 最高的 12位 ADC 转换速率不能够超过 800kHz。

GG00010289464_1920_1080.MP4|_-10

二、DMA传输时间

  如果使用 DMA将 ADC 结果传输到内存, 整个转换时间包括有两部分。  第一部分就是前面分析的 ADC 转换时间。    这部分在前面已经分析了。  可以通过 ADCTIM 寄存器设置数值计算出来。  第二部分是 DMA数据传输时钟,通过实际测试, 等于16个系统时间周期。   由此, 可以得到 ADC转换与数据传输总共所需要的系统时钟。  在此基础上, 便可以得到ADC实际转换速率了。  下面根据这个计算公式, 对实际采集的数据进行分析, 看是否可以得到验证。

GG00011289813_1920_1080.MP4|_-11

三、数据采集

  这里有一颗基于 STC32F的信号采集板,  采集信号是 一个 工字电感接收到的 150kHz的导航信号。  DG1062 产生的 150kHz 的正弦信号通过另外一个工字电感耦合到电路中。  根据现在软件设置, 在 STC32F单片机 使用内部 56MHz的工作频率下, 理论上对应的采样频率为 875kHz。

GG00005290398_1920_1080.MP4|_-5

▲ 图1.3.1 补零之后的FFT数据

▲ 图1.3.1 补零之后的FFT数据

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST2.PY                     -- by Dr. ZhuoQing 2025-12-21
#
# Note:
#============================================================
from headm import *
ddim = tspload('sample1', 'ddim')
d = list(ddim[0]-mean(ddim[0]))
d0 = [0]* len(d)
for _ in range(128):
    d.extend(d0)
fftd = abs(fft.fft(d))[:len(d)//2]
id = where(fftd==max(fftd))[0][0]
printf(id)
plt.plot(fftd, lw=3)
plt.xlabel("N", color="steelblue", fontsize=24)
plt.ylabel("FFT", color="steelblue", fontsize=24)
plt.grid(True, which='both', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.show()
#------------------------------------------------------------
#        END OF FILE : TEST2.PY
#============================================================

  采集一组数据,  减去均值之后, 通过补零扩展128倍。  得到信号峰值 在5626的数据位。 由此可以计算出信号的频率。 根据外部信号为 150kHz,   ADC采样频率是 873.658kHz, 这与前面分析的频率是很接近的。

GG00005291286_1920_1080.MP4|_-5

/*
**==============================================================================
** CONTROL.C:             -- by Dr. ZhuoQing, 2023-02-18
**
**==============================================================================
*/
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void ControlInit(void) {
    int i;
    float fOmiga;
    g_fSampleOmiga = SampleOmiga();
    for(i = 0; i < DATA_LENGTH; i ++) {
        fOmiga = i * DATA_OMIGA;
        g_fSine[i] = sin(fOmiga);
        g_fCosine[i] = cos(fOmiga);
        if(i <= DATA_LENGTH/2) {
            fOmiga = (float)i / (float)(DATA_LENGTH/2);
        } else {
            fOmiga = 1.0 - (float)(i - DATA_LENGTH/2) / (float)(DATA_LENGTH/2);
        }        
        g_fWindows[i] = fOmiga;
    }
}
//------------------------------------------------------------------------------
void ADCDMASample(unsigned char ucChannel) {
    DMA_ADC_CHSW0 = (1 << ucChannel);
    DMA_ADC_CHSW1 = ADC_DMA_CHANNEL_1;
    ADC_DMA_START;
    for(;;) {
        if(ADC_DMA_OVER) break;
    }    
}
//------------------------------------------------------------------------------
float ADCMeasure(unsigned char ucChannel) {
    unsigned int i, nNumNum;
    float fAverage;
    float fSigmaA, fSigmaB, fValue;
    unsigned int * pNumber;
    DMA_ADC_CHSW0 = (1 << ucChannel);
    DMA_ADC_CHSW1 = ADC_DMA_CHANNEL_1;
    ON(LED);
    ADC_DMA_START;
    for(;;) {
        if(ADC_DMA_OVER) break;
    }
    OFF(LED);
    nNumNum = DATA_LENGTH;
    fAverage = g_ucADCDMABuffer[513];
    fAverage /= 256.0;
    fAverage += *((unsigned int *)&g_ucADCDMABuffer[514]);
    pNumber = (unsigned int *)g_ucADCDMABuffer;
    fSigmaA = fSigmaB = 0;    
    for(i = 0; i < nNumNum; i ++) {        
        fValue = *(pNumber ++);
        fValue -= fAverage;
        fValue *= g_fWindows[i];
        fSigmaA += fValue * g_fSine[i];
        fSigmaB += fValue * g_fCosine[i];        
    }
    return sqrt(fSigmaA * fSigmaA + fSigmaB * fSigmaB ) / nNumNum;    
}
float ADCMeasureMA(unsigned char ucChannel) {
    float fValue;
    int i;
    fValue = 0;
    for(i = 0; i < ADC_MA_NUMBER; i ++)
        fValue += ADCMeasure(ucChannel);
    return fValue / ADC_MA_NUMBER;
}
//------------------------------------------------------------------------------
float SampleOmiga(void) {
    unsigned char ucReg = ADCTIM;
    unsigned char ucTsetup, ucThold, ucTduty;
    unsigned char ucSpeed;
    unsigned int nNclock;
    float fSample, fSignal;
    ucTsetup = ucReg >> 7;
    ucThold = (ucReg & 0x60)>>5;
    ucTduty = ucReg & 0x1f;
    ucSpeed = ADCCFG & 0xf;
    nNclock = 2*(ucSpeed+1);
    nNclock = nNclock * (ucTsetup + ucThold + ucTduty + 15);
    nNclock += 16;                          // Add DMA transfer system clock
    fSample = OSC_FREQUENCY;
    fSample /= nNclock;
    fSignal = 150000.0;
    return 2*3.1415926*fSignal/fSample;
}
//==============================================================================
//                END OF FILE : CONTROL.C
//------------------------------------------------------------------------------

 

  结 ※


  里给出了通过 STC32F 内部寄存器的设置计算数据采集归一化频率的程序。  对于 150kHz的信号来说, 对应的归一化采样频率为 1.077。  利用这个采样归一化频率,  对 采集到的 251 个数据进行计算, 可以获得非常稳定的谐波幅度数值。

GG00004293058_1920_1080.MP4|_-4


■ 相关文献链接:

● 相关图表链接:

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐