从晶元到人工智能

    返回首页    发表留言
本文作者:李德强
          第三节 模数转换
 
 

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

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

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

        接下来,我们来编写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); //ADC1,ADC通道,采样时间为239.5周期
	ADC_SoftwareStartConv(ADC1);
	while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
	{
	}
	return ADC_GetConversionValue(ADC1); //返回最近一次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之后如何计算出电源电压即可。例如我们从串口中得到的数据如下:

        实际上,我们得到了很多组数据,它们之间会存在一些小的差别,但并不很大,我们以上图中的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:

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

  • 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。

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

uint16_t v = adc_get(0);
float a = (float)v / 65535.0f;
float b = v * 3.256f;
float V = b * 387.0f / 100.0f;
// V的值就是实际的电源电压

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

    返回首页    返回顶部
  看不清?点击刷新

 

  Copyright © 2015-2023 问渠网 辽ICP备15013245号