打印
[STM32F2]

STM32 ADC转换速度与精度

[复制链接]
48|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
kqh11a|  楼主 | 2024-6-30 23:43 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
系统时间查看



ADC挂在APB2上,对应的时钟为PCLK2,由系统时钟SYSCLK 分频得到,一般不做分频,也就是说ADC模块的时钟等于系统时钟,F103也就是72MHz,如下代码也可获取并查看:

RCC_ClocksTypeDef get_rcc_clock;
RCC_GetClocksFreq(&get_rcc_clock);


使用特权

评论回复
沙发
kqh11a|  楼主 | 2024-6-30 23:43 | 只看该作者

使用特权

评论回复
板凳
kqh11a|  楼主 | 2024-6-30 23:44 | 只看该作者
ADC转换时间

使用特权

评论回复
地板
kqh11a|  楼主 | 2024-6-30 23:44 | 只看该作者
手册中也有提到ADC的频率不能超过14MHz,也就是说F103需要至少6分频(最低只有这个分频系数合适,再有就是4分频已经超过14MHz)。

使用特权

评论回复
5
kqh11a|  楼主 | 2024-6-30 23:44 | 只看该作者
转化时间计算公式为:
Tconv = Sampling time + 12.5 cycles

使用特权

评论回复
6
kqh11a|  楼主 | 2024-6-30 23:44 | 只看该作者
如果ADC配置如下:
        RCC_ADCCLKConfig(RCC_PCLK2_Div6);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);

使用特权

评论回复
7
kqh11a|  楼主 | 2024-6-30 23:44 | 只看该作者
ADC的工作频率为:72MHz/6 = 12MHz
采样周期数为:239.5+12.5=252
总时间为:1000000us/12000000Hz*252=21us
也就是ADC得到一次转换结果需要耗时21us。

使用特权

评论回复
8
kqh11a|  楼主 | 2024-6-30 23:44 | 只看该作者
采集精度
VREF参考电压值3.3V情况下,12位采集精度如果用于采集3.3V电压,那么3300mV/4095≈0.8mV,也就是说理想情况下最多达到0.8mV的识别精度。STM32单片机有一个内部的参照电压,相当于一个标准电压测量点,在芯片内部连接到ADC1的通道17:

使用特权

评论回复
9
kqh11a|  楼主 | 2024-6-30 23:45 | 只看该作者
也就是说可以通过这个通道对其余ADC通道采集的值进行偏移校准,通过内部参考电压校准的话需要多开一路内ADC1_IN17采样通道.(以12位采样精度为例):

使用特权

评论回复
10
kqh11a|  楼主 | 2024-6-30 23:45 | 只看该作者
Vchx:通过计算得出的实际电压值
A D r e f ADrefADref:ADC1_IN17通道采集的ADC值,参考通道实时采集
A D s t ADstADst:参考电压的典型AD值,如果是12位采样精度:ADst=4095*(1.2/3.3)≈1489,其中1.2是内部参考电压,这个电压基本不会随着外部供电电压的变化而变化
A D c h x ADchxADchx:需要测量的ADC通道采样值

使用特权

评论回复
11
kqh11a|  楼主 | 2024-6-30 23:45 | 只看该作者


公式中的3.3是单片机的Vref引脚接的电压,这个引脚接的电压也决定ADC的测量范围,如果接的是3.3V,12位采样精度:采样值为2048就表示1.65V,如果接的是2V,采样值为2048就表示1V:

使用特权

评论回复
12
kqh11a|  楼主 | 2024-6-30 23:45 | 只看该作者
提高稳定性
一般也就是硬件和软件两个方面,硬件上可在ADC引脚与GND之间跨接一个1uF的电容,或者选择适合自己采集信号的滤波器。软件方面也是只能选择自己合适的软件滤波器。

使用特权

评论回复
13
kqh11a|  楼主 | 2024-6-30 23:45 | 只看该作者
示例代码
如下是F103的ADC1通道1的DMA使用(PA1引脚),连续自动转换(校准通道不需要初始化引脚,其余和普通ADC通道初始化一样)。

#define ADC_BUF_SIZE        2       
uint16_t ADC_Buf[ADC_BUF_SIZE];                // ADC原始值
uint16_t ADC_Data[ADC_BUF_SIZE];        // 通过校准后的ADC值
float voltage[ADC_BUF_SIZE];                   // 转换后的实际电压

void ADC1_Init(void)
{
          GPIO_InitTypeDef GPIO_InitStructure;
        ADC_InitTypeDef ADC_InitStructure;
        DMA_InitTypeDef DMA_InitStructure;
       
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 , ENABLE);
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
       
        // ADC引脚初始化
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;                     
        GPIO_Init(GPIOA, &GPIO_InitStructure);


        // ADC1配置
        ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                 // 工作模式:独立模式
        ADC_InitStructure.ADC_ScanConvMode = ENABLE;                       // 浏览模式
        ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                 // ADC工作在单次转化模式
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 软件触发
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;             // ADC数据右对齐
        ADC_InitStructure.ADC_NbrOfChannel = ADC_BUF_SIZE;                 // 通道数量
        ADC_Init(ADC1, &ADC_InitStructure);
         
        //设置指定ADC的规则组通道,设置通道对应的转化顺序和采样时间
        ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_71Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_17, 2, ADC_SampleTime_71Cycles5);
        ADC_TempSensorVrefintCmd(ENABLE);
       
        ADC_Cmd(ADC1, ENABLE);                                                          // 使能ADC                       

        ADC_ResetCalibration(ADC1);                      // 校验复位
        while(ADC_GetResetCalibrationStatus(ADC1));      // 等待复位完成
       
        ADC_StartCalibration(ADC1);                      // 开始ADC1校准
        while(ADC_GetCalibrationStatus(ADC1));           // 等待校验完成
       
        ADC_DMACmd(ADC1, ENABLE);                        // 使能ADC的DMA功能
                                                       
        DMA_DeInit(DMA1_Channel1);                                         // 外设地址
        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));
        DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Buf;          // 内存地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                 // 传输方向:外设 -> 内存
        DMA_InitStructure.DMA_BufferSize = ADC_BUF_SIZE;                   // 传输长度
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   // 外设递增:关闭
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;            // 内存递增:打开
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;// 数据宽度:16位
        DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                    // 循环模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;            // 优先级:高
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                       // 内存-内存:否
        DMA_Init(DMA1_Channel1, &DMA_InitStructure);
       
        DMA_Cmd(DMA1_Channel1, ENABLE);                                    // 使能通道
       
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);      // 启动转换
}

// 将ADC转换成0~3.3V实际电压
void voltage_converter(void)
{
        for(uint8_t i=0;i<ADC_BUF_SIZE-1;i++)
        {
                ADC_Data[i] = ADC_Buf[i]*1489/ADC_Buf[ADC_BUF_SIZE-1];  // 使用校准通道校准
                voltage[i] = ADC_Data[i]*3.3f/4095.0f;                                        // 转换成实际电压
        }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

21

主题

339

帖子

0

粉丝