9.stm32中ADC数模转换器原理与应用

上节学习了PWM的驱动与控制,并认识了直流电机和舵机,这节来学习ADC数模转换器的原理与应用,工业检测系统和日常生活中有许多物理量都是模拟量,比如温度、长度、压力、速度等等,这些物理模拟量可以通过传感器变成与之对应的电压、电流等电模拟量。由于单片机系统是一个典型的数字系统,为了实现数字系统对这些电模拟量的检测、运算和控制,就需要模数转换模块ADC。

1.ADC数模-模数转换器基本原理

ADC可以将引脚上连续变化的模拟电压转换为内存中储存的数字变量,建立模拟电路到数字电路的桥梁。(DAC数字模拟转换器)

输入电压范国0-3.3V,转换结果0-4095
18个输入通道,可测量16个外部(GPIO口)和2个内部信号源(内部温度传感器,内部参考电压)
规则组(常规使用)和注入组(实发事件)两个转换单元
模拟看门狗自动监测输入电压范围(高于阈值/低于时,看门狗会触发中断)

ADC转换步骤:采样,保持,量化,编码(后两个为ADC逐渐比较的过程)

stm32ADC总转换时间T=采样时间+12.5个ADC周期

2.ADC基本结构图

触发控制分为硬件触发(定时器),和软件触发

3.ADC通道与引脚的复用关系

由ADC的内部结构可知,STM32的ADC对应16个输入通道。这16个输入通道对应的GPIO端口如下表所示

4.规则组的四种转换模式

单次转换模式中:需要手动触发转换。 ADC就会对序列中的通道进行转换,转换完成后,将转换结果储存在数据寄存器中,同时将EOC标志位置1,转换过程结束。

在连续转换模式中:一次转换完成后不会停止,而是立刻开始下一轮的转换,并持续下去。这样就可以在最开始时触发一次,之后就可以一直转换了。这个模式的好处在于:开始转换后不需要等待,不需要手动开启转换,也不用判断转换是否结束。需要读取AD值时,直接在数据寄存器中读取即可。

5.硬件电路

6.ADC+单通道实例(代码已详细注释)

接线图如下

main.c

#include "stm32f10x.h"                  
#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, "Volatge: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);
	}
}

AD.c

配置过程

①开RCC时钟,包括ADC和GPIO的时钟,ADCCLK
②配置GPIO成模拟输入的模式
③配置多路开关,把左边通道接到右边的规则组

④配置ADC转换器,用结构体来配置

⑤调用ADC_cmd()函数

#include "stm32f10x.h"              

void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADCCLK
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//配置GPIO口
	
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//通道口+55.5个ADC采样周期
	
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发使用软件触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//单次
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1, &ADC_InitStructure);
	
	ADC_Cmd(ADC1, ENABLE);
	
	ADC_ResetCalibration(ADC1);//复位ADC校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);//获取ADC重置校准器状态
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);//开始指定ADC的校准程序
}//以上为检验过程

//启动转换,获取结果
uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//设置ADC转换开始的触发条件为软件触发
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//规则组是否转换完成
	return ADC_GetConversionValue(ADC1);//获取转换结果
}

AD.h

#ifndef __AD_H
#define __AD_H

void AD_Init(void);
uint16_t AD_GetValue(void);

#endif

OLED和Delay可从之前章节复制学习,内有详细配置过程及其代码解析

7.ADC+多通道实例

接线图如下

代码实例

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);
	}
}

AD.c  和.h同单通道

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的实例代码,用于在STM32上进行DAC和ADC的数据采集和转换: ``` #include "stm32f10x.h" #include "stm32f10x_adc.h" #include "stm32f10x_dac.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" int main(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; DAC_InitTypeDef DAC_InitStructure; // 开启GPIOA、ADC1、DAC通道1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 | RCC_APB1Periph_DAC, ENABLE); // 配置DAC通道1的GPIO引脚PA4 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置ADC1的GPIO引脚PA0 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置DAC通道1 DAC_InitStructure.DAC_Trigger = DAC_Trigger_None; DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; DAC_Init(DAC_Channel_1, &DAC_InitStructure); // 配置ADC1 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); // 配置ADC1的通道0 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); // 启动ADC1的转换 ADC_Cmd(ADC1, ENABLE); // 启动DAC通道1 DAC_Cmd(DAC_Channel_1, ENABLE); // 等待DAC通道1的输出稳定 while (DAC_GetFlagStatus(DAC_Channel_1, DAC_FLAG_DMAUDR) == RESET); // 开始ADC1的转换 ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 等待转换结束 while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); // 读取转换结果 uint16_t adc_value = ADC_GetConversionValue(ADC1); uint16_t dac_value = DAC_GetDataOutputValue(DAC_Channel_1); while (1); } ``` 此代码,我们首先启用了GPIOA、ADC1和DAC通道1的时钟。然后,我们配置了PA4作为DAC通道1的引脚,PA0作为ADC1的引脚。接下来,我们配置了DAC通道1和ADC1,并将ADC通道0与ADC1关联起来。然后,我们启动了ADC1的转换和DAC通道1,并等待DAC通道1的输出稳定。最后,我们开始ADC1的转换,并等待转换结束。我们读取了ADC和DAC的转换结果并停止程序的运行。 希望这个例子对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值