ADC简介

12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。
模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。
● ADC(Analog-Digital Converter)模拟-数字转换器
● ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
● 12位逐次逼近型ADC,1us转换时间
● 输入电压范围:0 ~ 3.3V,转换结果范围:0 ~ 4095
● 18个输入通道,可测量16个外部和2个内部信号源
● 规则组和注入组两个转换单元
● 模拟看门狗自动监测输入电压范围
● STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道

ADC主要特征

● 12位分辨率
● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断
● 单次和连续转换模式
● 从通道0到通道n的自动扫描模式
● 自校准
● 带内嵌数据一致性的数据对齐
● 采样间隔可以按通道分别编程
● 规则转换和注入转换均有外部触发选项
● 间断模式
● 双重模式(带2个或以上ADC的器件)
● ADC转换时间:
─ STM32F103xx增强型产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs)
─ STM32F101xx基本型产品:时钟为28MHz时为1μs(时钟为36MHz为1.55μs)
─ STM32F102xxUSB型产品:时钟为48MHz时为1.2μs
─ STM32F105xx和STM32F107xx产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs)
● ADC供电要求:2.4V到3.6V
● ADC输入范围:V REF- ≤ VIN ≤ VREF+
● 规则通道转换期间有DMA请求产生。

逐次逼近型ADC

在这里插入图片描述

ADC功能描述

在这里插入图片描述
在这里插入图片描述

ADC基本结构

在这里插入图片描述

输入通道

在这里插入图片描述

转换模式

在这里插入图片描述

连续转换,非扫描模式

在这里插入图片描述
单次转换,扫描模式
在这里插入图片描述
连续转换,扫描模式
在这里插入图片描述

触发控制

转换可以由外部事件触发(例如定时器捕获,EXTI线)。如果设置了EXTTRIG控制位,则外部事件就能够触发转换。EXTSEL[2:0]和JEXTSEL2:0]控制位允许应用程序选择8个可能的事件中的某一个,可以触发规则和注入组的采样。
注意: 当外部触发信号被选为 ADC 规则或注入转换时,只有它的上升沿可以启动转换
在这里插入图片描述

数据对齐

ADC_CR2寄存器中的ALIGN位选择转换后数据储存的对齐方式。数据可以左对齐或右对齐,如图29和图30所示。
注入组通道转换的数据值已经减去了在ADC_JOFRx寄存器中定义的偏移量,因此结果可以是一个负值。SEXT位是扩展的符号值。
对于规则组通道,不需减去偏移值,因此只有12个位有效。
在这里插入图片描述

转换时间

AD转换的步骤:采样,保持,量化,编码
ADC使用若干个ADC_CLK周期对输入电压采样,采样周期数目可以通过ADC_SMPR1和ADC_SMPR2寄存器中的SMP[2:0]位更改。每个通道可以分别用不同的时间采样。
总转换时间如下计算:
TCONV = 采样时间+ 12.5个周期
例如:
当ADCCLK=14MHz,采样时间为1.5周期
TCONV = 1.5 + 12.5 = 14周期 = 1μs

校准

ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。在校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。
通过设置ADC_CR2寄存器的CAL位启动校准。一旦校准结束,CAL位被硬件复位,可以开始正常转换。建议在上电时执行一次ADC校准。校准阶段结束后,校准码储存在ADC_DR中。
在这里插入图片描述
1 建议在每次上电后执行一次校准。
2 启动校准前, ADC 必须处于关电状态 (ADON=’0’) 超过至少两个 ADC 时钟周期。

在这里插入图片描述
在这里插入图片描述
应用:
一般用于测量电路电压,根据电压与PWM的关系,通过电位器控制PWM的占空比输出,从而达到可以手动调节的pwm输出。
报警功能:引脚电压超过上限或下限进行报警
ADC用于监测和控制各种过程参数(如温度、压力、流量等)

DAC

数字/模拟转换模块(DAC)是12位数字输入,电压输出的数字/模拟转换器。DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用。DAC工作在12位模式时,数据可以设置成左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压VREF+以获得更精确的转换结果。
  主要功能特点如下:
  ● 2个DAC转换器:每个转换器对应1个输出通道
  ● 8位或者12位单调输出
  ● 12位模式下数据左对齐或者右对齐
  ● 同步更新功能
  ● 噪声波形生成
  ● 三角波形生成
  ● 双DAC通道同时或者分别转换
  ● 每个通道都有DMA功能
  ● 外部触发转换
  ● 输入参考电压VREF+
 
STM32DAC结构框图如下:
在这里插入图片描述

STM32DAC工作原理

1.电源、参考电压及输出通道
VDDA和VSSA为DAC模块模拟部分的供电,VREF+则是DAC模块的参考电压。DAC_OUTx就是DAC的输出通道了.
在这里插入图片描述
2.DAC转换及输出
数字至模拟转换器x是DAC核心部件,它以VREF+作为参考电源,以DAC 的数据寄存器“DORx”的数字信号作为输入,经过它转换得的模拟信号由“DAC_OUTx”通道输出。但是,用户不能直接对寄存器DAC_DORx写入数据,任何输出到DAC通道x的数据都必须写入DHRx寄存器。
在这里插入图片描述
◆ DHR相关寄存器
  ➢ 用户写入的数据保持寄存器:
    单DAC通道模式:总共有6个DAC_DHRyyyx寄存器
      ● DAC通道x的12位右对齐数据保持寄存器 DAC_DHR12Rx (x=1、2)
      ● DAC通道x的12位左对齐数据保持寄存器 DAC_DHR12Lx (x=1、2)
      ● DAC通道x的8位右对齐数据保持寄存器 DAC_DHR8Rx (x=1、2)
    双DAC通道模式:总共有3个DAC_DHRyyyD寄存器
      ● 双DAC的12位右对齐数据保持寄存器 DAC_DHR12RD
      ● 双DAC的12位左对齐数据保持寄存器 DAC_DHR12LD
      ● 双DAC的8位右对齐数据保持寄存器 DAC_DHR8RD
➢ 内部的数据保存寄存器x
  ● 内部的数据保存寄存器1:DHR1
  ● 内部的数据保存寄存器2:DHR2
◆ DAC数据格式
  根据选择的配置模式,数据按照下文所述写入指定的寄存器:
  ● 单DAC通道x,有3种情况:
    ─ 8位数据右对齐:用户须将数据写入寄存器DAC_DHR8Rx[7:0]位(实际是存入寄存器DHRx[11:4]位)
    ─ 12位数据左对齐:用户须将数据写入寄存器DAC_DHR12Lx[15:4]位(实际是存入寄存器DHRx[11:0]位)
    ─ 12位数据右对齐:用户须将数据写入寄存器DAC_DHR12Rx[11:0]位(实际是存入寄存器DHRx[11:0]位)
   
  根据对DAC_DHRyyyx寄存器的操作,经过相应的移位后,写入的数据被转存到DHRx寄存器中(DHRx是内部的数据保存寄存器x)。随后,DHRx寄存器的内容或被自动地传送到DORx寄存器,或通过软件触发或外部事件触发被传送到DORx寄存器。
在这里插入图片描述
◆ DAC通道x数据输出寄存器DAC_DORx寄存器
  DAC通道2数据输出寄存器(DAC_DOR1)
 在这里插入图片描述
DAC通道1数据输出寄存器(DAC_DOR2)
在这里插入图片描述
◆ 触发配置
 由DAC控制寄存器DAC_CR的第2位TEN1和第18位TEN2分别控制DAC的通道1和通道2是否使能触发。
 在这里插入图片描述
如果使能触发,即寄存器DAC_CR的TENx位置’1’,DAC转换可以配置为由某外部事件或软件触发。
 配置DAC_CR寄存器的控制位TSELx[2:0]可以选择8个触发事件之一触发DAC转换。
 如果配置为软件触发,则需要通过对DAC软件触发寄存器DAC_SWTRIGR相应的SWTRIGx位置’1’ 。
 触发发生后,3个APB1时钟周期后,最近存放在寄存器DAC_DHRx中的数据会被传送到寄存器DAC_DORx中,转换即开始。
 
◆ DAC转换及输出总结:
 针对单DAC模式,无触发
 ● 不能直接对寄存器DAC_DORx写入数据,任何输出到DAC通道x的数据都必须写入DAC_DHRyyyx数据保持寄存器。
 ● 写入DAC_DHRyyyx寄存器的数据经过相应的移位后,被转存到内部的数据保存寄存器x(DHRx寄存器)中。
 ● 如果没有使能触发(寄存器DAC_CR的TENx位置’0’),存入寄存器DAC_DHRx的数据会在一个APB1时钟周期后自动传至寄存器DAC_DORx。
 ● 一旦数据从DAC_DHRx寄存器装入DAC_DORx寄存器,在经过时间tSETTLING之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。

在这里插入图片描述
3.DAC输出电压
  数字输入经过DAC被线性地转换为模拟电压输出,其范围为0到VREF+。
  任一DAC通道引脚上的输出电压满足下面的关系:
    DAC输出 = VREF x (DOR / 4095)
4.使能DAC通道
  将DAC_CR寄存器的ENx位置’1’即可打开对DAC通道x的供电。经过一段启动时间tWAKEUP,DAC通道x即被使能。
  注意: ENx位只会使能DAC通道x的模拟部分,即便该位被置’0’,DAC通道x的数字部分仍然工作。
5.DAC输出缓存
  DAC集成了2个输出缓存,可以用来减少输出阻抗,无需外部运放即可直接驱动外部负载。每个DAC通道输出缓存可以通过设置DAC_CR寄存器的BOFFx位来使能或者关闭。
 
1.ADC单通道示例
在这里插入图片描述

adc.c

#include "adc.h"

void adc_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStruct;
	
	//开启ADC时钟  PA0 --> ADC1_0
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
	//开启GPIOA的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//设置为6分频  72M / 6 = 12M 
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	
	//GPIO配置 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed =   GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//ADC配置
	ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
	ADC_InitStruct.ADC_ScanConvMode = DISABLE;
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1, &ADC_InitStruct);
	
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	//使能ADC
	ADC_Cmd(ADC1, ENABLE);
	
	//校准ADC
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	
}

uint16_t adc_getvalue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != SET);
	
	return ADC_GetConversionValue(ADC1);
}


main.c

#include "stm32f10x.h"

#include "delay.h"
#include "oled.h"
#include "adc.h"


 int main(void)
 {	

	 uint16_t value = 0;
	 float volate = 0;
	 
	 //初始化
	 OLED_Init();
	 adc_init();

	 //显示字符串
	 OLED_ShowString(1, 1, "ADCValue: ");
	 OLED_ShowString(2, 1, "Volate:0.00V");
	 
	 while(1)
	 {
		 value = adc_getvalue();
		 volate = (float)value / 4095 * 3.3;
		 
		 OLED_ShowNum(1, 10, value, 4);
		 OLED_ShowNum(2, 8, volate, 1);		 
		 OLED_ShowNum(2, 10, (uint16_t)(volate * 100) % 100, 2);	
		 
		 delay_ms(100);
	 }
	 
 }

连续转换,非扫描模式
adc.c

#include "adc.h"

void adc_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStruct;
	
	//开启ADC时钟  PA0 --> ADC1_0
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
	//开启GPIOA的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//设置为6分频  72M / 6 = 12M 
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	
	//GPIO配置 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed =   GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//ADC配置
	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; //连续转换模式
	ADC_InitStruct.ADC_ScanConvMode = DISABLE;
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1, &ADC_InitStruct);
	
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	//使能ADC
	ADC_Cmd(ADC1, ENABLE);
	
	//校准ADC
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	
	//启动一次转换即可
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	
}

uint16_t adc_getvalue(void)
{

	
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != SET);
	
	return ADC_GetConversionValue(ADC1);
}

main.c

#include "stm32f10x.h"

#include "delay.h"
#include "oled.h"
#include "adc.h"

 int main(void)
 {	
	 uint16_t value = 0;
	 float volate = 0;
	 
	 //初始化
	 OLED_Init();
	 adc_init();

	 //显示字符串
	 OLED_ShowString(1, 1, "ADCValue: ");
	 OLED_ShowString(2, 1, "Volate:0.00V");
	 
	 while(1)
	 {
		 value = adc_getvalue();
		 volate = (float)value / 4095 * 3.3;
		 
		 OLED_ShowNum(1, 10, value, 4);
		 OLED_ShowNum(2, 8, volate, 1);		 
		 OLED_ShowNum(2, 10, (uint16_t)(volate * 100) % 100, 2);	
		 
		 delay_ms(100);
	 } 
 }

ADC多通道
在这里插入图片描述
adc.c

#include "adc.h"

void adc_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStruct;
	
	//开启ADC时钟  PA0 --> ADC1_0
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
	//开启GPIOA的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//设置为6分频  72M / 6 = 12M 
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	
	//GPIO配置 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed =   GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//ADC配置
	ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; 
	ADC_InitStruct.ADC_ScanConvMode = DISABLE;
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1, &ADC_InitStruct);
	

	
	//使能ADC
	ADC_Cmd(ADC1, ENABLE);
	
	//校准ADC
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	
}

uint16_t adc_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) != SET);
	
	return ADC_GetConversionValue(ADC1);
}


main.c

#include "stm32f10x.h"

#include "delay.h"
#include "oled.h"
#include "adc.h"


 int main(void)
 {	

	 uint16_t ad0 = 0;
	 uint16_t ad1 = 0;
	 uint16_t ad2 = 0;
	 uint16_t ad3 = 0;
	 
	 float volate = 0;
	 
	 //初始化
	 OLED_Init();
	 adc_init();

	 //显示字符串
	 OLED_ShowString(1, 1, "AD0: ");
	 OLED_ShowString(2, 1, "AD1: ");
	 OLED_ShowString(3, 1, "AD2: ");
     OLED_ShowString(4, 1, "AD3: ");
	 
	 while(1)
	 {
		ad0 = adc_getvalue(ADC_Channel_0);
		ad1 = adc_getvalue(ADC_Channel_1);
		ad2 = adc_getvalue(ADC_Channel_2);
		ad3 = adc_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);
	 }
	 
 }

更多推荐