ADC模数转换器电路设计

模拟技术

2347人已加入

描述

在我们的控制系统中需要使用一个"模数转换"的功能。所谓“模数转换”,就是将模拟量转换为数字量,也就是我们经常说的ADC采集,其中ADC的全称为:“Analog to Digital Converter”。我们的电路设计中,采用的电源是3S锂电池,其电压最大值为12.6v,最小值为11.1v。所以我们通过了一路ADC采集到电压的模拟量将其转为数字量。我们先来看一下电路设计:

adc

adc

我们可以看到,电源输出VIN通过R6和R5两个分压电阻,将其线性的降压到ADC1_CH0电路中,并接到PA0引脚上。我们可以通过计算得到以下内容:

adc

也就是说,我们通过了这个两个电阻分压的线性降压电路将11.1v到12.6v的电压降低到2.868v到3.256v。为什么这个做呢?原因是STM32的AD转换引脚能够接受的模拟电压范围为的最大值是3v。当我们通过STM32的ADC采集将降压后的电源转为数字信号,再通过程序还原成原始的电压数值(线性降压之前的电压),我们就可以得到电源电压了。假设ADC采集到的电压为v,于是我们可以得到电源电压V的值的计算方法为:

adc

接下来,我们来编写STM32程序,通过ADC采集到我们的电源电压:

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //配置GPIOA时钟总线
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, DISABLE); //停用ADC1,以便下面进行ADC1的配置


GPIO_InitTypeDef GPIO_InitStructure;              //配置PA0作为ADC1的CH0的引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);


ADC_Cmd(ADC1, DISABLE);    //禁用ADC1
ADC_DeInit();        //ADC1重定义


ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles; //两个采样阶段之间的延迟
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //DMA使能
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8; //预分频8分频
ADC_CommonInit(&ADC_CommonInitStructure);


ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_NbrOfConversion = 16; //顺序进行转换通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //初始化外设ADC寄存器


ADC_Cmd(ADC1, ENABLE); //使能指定的ADC

这样我们就完成了ADC1的CH0的配置工作。之后我们还需要编写一个函数用于读取ADC1中CH0的数字信号值:

uint16_t adc_get(uint8_t ch)
{
  //设置指定ADC的规则组通道,一个序列,采样时间
  ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_144Cycles);
  ADC_SoftwareStartConv(ADC1);
  while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
  {
  }
  //返回最近一次ADC1规则组的转换结果
  return ADC_GetConversionValue(ADC1); 
}

最后,我们通过在main函数中对ADC1中的CH0进行配置,并在主循环中读取电源电压信息:

init_adc();
uart_init();


while (1)
{
  uint16_t v = adc_get(0);
  uart_write(v & 0xff);
  uart_write((v > > 8) & 0xff);
  uart_write(0x0);
  uart_write(0x0);
  // ...
}

当我们通过调用adc_get(0)函数得到采集电压v之后,再通过uart_write(v)函数将电压值发送到串口当中方便我们观察。注意,其中串口功能的配置与使用我们将下一讲中来学习,这里不再赘述。读者只需要了解得到电压v之后如何计算出电源电压即可。例如我们从串口中得到的数据如下:

adc

实际上,我们得到了很多组数据,它们之间会存在一些小的差别,但并不很大,我们以上图中的D0 FD这一组数据为例,16进制数据为0xFDD0,转为10进制为64976,除以16位ADC分辨率65535(16进制为0xFFFF)得到结果为0.991470207,再乘以3.256,得到3.228226993v,最后转换为电源电压3.228226993v * 387 / 100 = 12.493238463v,保留3位小数得到12.493v。于是我们就得到了当前电源电压为12.493v。

我们,可以在电源电压接近11v时再进行一次采集和计算,我们采集到的数据为50 DF:

adc

通过上述方法计算得到电源电压为:

  • 16进制转10进制: 0xDF50 = 57168
  • 除以16通道分辨率: 57168 / 65535 = 0.872327764
  • 乘以电路中分压值3.256v: 0.872327764 × 3.256 = 2.840299199
  • 转为实际电源电压: 2.840299199 × 387 ÷ 100 = 10.992

最后,我们就得到了电源电压为10.992v约等于11v

于是,我们通过程序来计算上面的内容:

adc

这样我们就完成了通过STM32采集电源的电压,方便我们以后实时查看小车的电源情况。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分