stm32-ADC模数转换器

文章目录

  • 一、知识点
    • 1.DAC与PWM
    • 2.12位逐次逼近型ADC
    • 3.ADC通道资源
    • 4.输入通道
    • 5.规划组的4种转化模式(相当于点菜)
      • a.单次转换非扫描模式
      • b.连续转换非扫描模式
      • c.单次转换扫描模式
      • d.连续转换扫描模式
    • 6.触发控制(一般软件触发)
    • 7.数据对齐
    • 8.转换时间
    • 9.硬件电路
  • 二、实例
    • 1.基本思路
    • 2.代码
      • a.单通道
      • b.多通道

一、知识点

1.DAC与PWM

DAC 为数字/模拟转换模块,故名思议,它的作用就是把输入的数字编码,转换成对应的模拟电压输出,它的功能与ADC 相反。在常见的数字信号系统中,大部分传感器信号被化成电压信号,而ADC 把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由DAC 输出电压模拟信号,该电压模拟信号常常用来驱动某些执行器件,使人类易于感知。如音频信号的采集及还原就是这样一个过程。
PWM是 Pulse Width Modulation 的缩写,中文意思就是脉冲宽度调制,简称脉宽调制。
它是利用微处理器的数字输出来对模拟电路进行控 制的一种非常有效的技术,其控制简单、灵活和动态响应好等优点而成 为电力电子技术最广泛应用的控制方式,其应用领域包括测量,通信, 功率控制与变换,电动机控制、伺服控制、调光、开关电源,甚至某些 音频放大器,

PWM只有完全导通和完全断开两种状态,在这两种状态都没有功率损耗,故直流电机调速这种大功率的应用场景,使用PWM来等效模拟量,是比DAC更好的选择,PWM电路更简单,更常用

2.12位逐次逼近型ADC

  • 12位是分辨率,对于的量化范围就是0到4095,量化位数越大,对应的分辨率就越高。
  • 0到4095对应0到3.3V进行转化
    例子如下
    stm32-ADC模数转换器_第1张图片

3.ADC通道资源

  • ADC有18个输入通道,可测量16个外部(16个GPIO口)和2个内部信号源(内部温度传感器和内部参考电压)(芯片中ADC最多有18个,不是所有的芯片都有18个)

  • 温度传感器可测量CPU的温度,例如在电脑上显示CPU温度,可以读取温度传感器来测量

  • 内部参考电压是一个1.2伏左右的基准电压,是不随外部供电电压变化而变化的,所以当芯片的供电不是标准的3.3伏,会导致测量外部引脚的电压可能就出错,此时就可以读取这个基准电压进行校准,这样就能得到正确的电压值了

  • C8T6有ADC1、ADC2和10个外部输入通道

4.输入通道

stm32-ADC模数转换器_第2张图片

5.规划组的4种转化模式(相当于点菜)

单次转换非扫描模式、连续转换非扫描模式、单次转换扫描模式、连续转换扫描模式

a.单次转换非扫描模式

一道一道点,一道一道上
stm32-ADC模数转换器_第3张图片
非扫描的模式下,只有第一个序列1的位置有效
在序列1的位置,我们可以指定想转换的通道,比如通道2,写到序列1的位置

然后触发转换,ADC就会对这个通道2进行模数转换,转换完成后,转换结果放在数据计算器里,同时给EOC标志位置1,转换结束,通过判断这个EOC标志位是否转换完成,若完成,就可以在数据寄存器里读结果

若想再启动一次转换,需要再触发一次,转换结束至EOC标志位读结果
若想换一个通道转换,在转换之前,把第一个位置的通道2改成其他通道,然后再启动转换这样就行了

b.连续转换非扫描模式

一道一道点,一道一道上(中间近似不间断)
stm32-ADC模数转换器_第4张图片
连续模式:在完成一次转换后不会停止,而是开始下一轮转换(只需要触发一次,就可以一直转换),读取之后直接放到寄存器中(不会触发中断等)

非扫描模式:菜单列表就只使用第一个

为了防止数据被覆盖,用DMA及时将数据挪走,7个通道转换完成之后产生EOC信号,转换结束

c.单次转换扫描模式

一次性点,一次性上
stm32-ADC模数转换器_第5张图片
单次转换:每触发一次,转换结束后就会停下来,下次转换需要再触发
扫描模式:会用到这个菜单列表了,通道几(相当于菜)可以任意指定,且可以重复,初始化结构体有个参数表示通道数目(比如7个),说明只需要用多少序列

d.连续转换扫描模式

一次性点,一次性上,再一次性点,一次性上(中间近似不间断)
stm32-ADC模数转换器_第6张图片

6.触发控制(一般软件触发)

stm32-ADC模数转换器_第7张图片

7.数据对齐

stm32-ADC模数转换器_第8张图片

  • 一般都是右对齐
  • 左对齐应用:不想要这么高的分辨率,你觉得0-4095数太大了,左对齐后取高八位即可,就从12位的ADC退化为8位了

8.转换时间

AD转换的步骤:采样,保持,量化,编码

  • 采样保持电路:量化编码之前,设置一个采量开关,先打开采样开关,收集外部的电压(比如可以用一个小容量的电容,存储一下这个电压,存储好了之后断开采样开关,再进行后面的AD转换,这样再量化编码的期间电压始终保持不变。
  • 采样保持的过程的需要闭合采样开关过一段时间再断开,就会产生一个采样时间
  • 总转换时间为:TCONV = 采样时间 + 12.5个ADC周期(多了0.5个周期,可能是做其他事情需要花的时间)
  • 采样时间越大,越能避免一些毛刺信号的干扰,不过转换时间也会相应延长
  • ADC周期就是从RCC分频过来的ADCCLK,这个ADCCLK最大是14兆赫值例如:
    当ADCCLK=14MHz,采样时间为1.5个ADC周期 TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs(故最快是1us)

9.硬件电路

stm32-ADC模数转换器_第9张图片
a.电位器
b.分压法
c.简单的电压转换电路

二、实例

1.基本思路

stm32-ADC模数转换器_第10张图片
一、开启时钟(包括ADC内部时钟)
二、配置GPIO
三、ADC通道选择
四、ADC通道配置
五、ADC_Cmd总开关
六、ADC校准(更稳定)

2.代码

a.单通道

ad.c

#include "stm32f10x.h"                  // Device header

void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//配置ADCCLK分频器,对APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//ADC规则组通道配置,给序列的每个位置填写指定的通道,就是填写点菜菜单的过程
	//第一个参数是ADCx,第二个是ADC指定的通道(通道0-17)
	//第三个是写在序列几的位置,然后第四个指定通道的采样时间
	//通道可以重复,序列不要重复,需要的话可以多写几个,这是填充菜单的方法
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//单次
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//右对齐
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;  不使用外部触发转换,即软件触发
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent; 工作模式,独立模式:ADC1和ADC2各转各的
	ADC_InitStructure.ADC_NbrOfChannel=1;//总共需要扫描多少个通道
	ADC_InitStructure.ADC_ScanConvMode=DISABLE; //非扫描
	ADC_Init(ADC1,&ADC_InitStructure);
	
	ADC_Cmd(ADC1,ENABLE);
	
	ADC_ResetCalibration(ADC1);  //复位
	while(ADC_GetResetCalibrationStatus(ADC1)==SET);
	ADC_StartCalibration(ADC1);   //开始校准
	while(ADC_GetCalibrationStatus(ADC1)==SET);

}

	
uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);  //中断软件触发,开始转换
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
	return ADC_GetConversionValue(ADC1);  //返回量化值
}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t ADValue;
float Voltage;   

int main(void)
{
	OLED_Init();
	AD_Init();
	
	OLED_ShowString(1,1,"ADValue:");
	OLED_ShowString(2,1,"Voltage:0.00V");
	
	
	while(1)
	{
		ADValue=AD_GetValue();
		Voltage = (float)ADValue / 4095 * 3.3;   //转化为浮点数,乘除之后才有小数
		OLED_ShowNum(1,9,ADValue,4);
		OLED_ShowNum(2,9,Voltage,1);
		OLED_ShowNum(2,11,(uint16_t)(Voltage*100)%100,2);   //浮点数不能进行取余操作
		
		Delay_ms(100);
	}
}

b.多通道

电位器:同第一个实验
光敏电阻:遮挡,光纤减小,AD值增大;移开,光线增大,AD值减小
热敏电阻:用手热一下,温度升高,AD减小,反之则反
反射红外传感器:手靠近,由反光,AD值减小,移开,没有反光,AD值增大

AD.c

#include "stm32f10x.h"                  // Device header

void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;
	ADC_InitStructure.ADC_NbrOfChannel=1;
	ADC_InitStructure.ADC_ScanConvMode=DISABLE;
	ADC_Init(ADC1,&ADC_InitStructure);
	
	ADC_Cmd(ADC1,ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)==SET);
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1)==SET);

}

	
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1,ADC_Channel,1, ADC_SampleTime_55Cycles5);  //手动更改一下列表第一个位置的通道
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
	return ADC_GetConversionValue(ADC1);
}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0,AD1,AD2,AD3;

int main(void)
{
	OLED_Init();
	AD_Init();
	
	OLED_ShowString(1,1,"AD0:");
	OLED_ShowString(2,1,"AD1:");
	OLED_ShowString(3,1,"AD2:");
	OLED_ShowString(4,1,"AD3:");
	
	while(1)
	{
		AD0=AD_GetValue(ADC_Channel_0);
		AD1=AD_GetValue(ADC_Channel_1);
		AD2=AD_GetValue(ADC_Channel_2);
		AD3=AD_GetValue(ADC_Channel_3);
		
		OLED_ShowNum(1,5,AD0,4);
		OLED_ShowNum(2,5,AD1,4);
		OLED_ShowNum(3,5,AD2,4);
		OLED_ShowNum(4,5,AD3,4);
		
		Delay_ms(100);
	}
}

你可能感兴趣的:(stm32,嵌入式硬件,单片机)