ADC(Analog-to-Digital Converter)
STM32F10x ADC特点
- 12位(位数决定精度)逐次逼近型的模拟数字转换器。
- 最多带3个ADC控制器(每个控制器有多个通道)
- 最多支持18个通道,可最多测量16个外部和2个内部信号源。
- 支持单次和连续转换模式
- 转换结束,注入转换结束,和发生模拟看门狗事件时产生中断。
- 通道0到通道n的自动扫描模式
- 自动校准
- 采样间隔可以按通道编程
- 规则通道和注入通道均有外部触发选项
- 转换结果支持左对齐或右对齐方式存储在16位数据寄存器
- ADC转换时间:最大转换速率 1us。(最大转换速度为1MHz,在ADCCLK=14M,采样周期为1.5个ADC时钟下得到。)
- ADC供电要求:2.4V-3.6V(通常0-3.3)
- ADC输入范围: V R E F ≤ V I N ≤ V 0 R E F + V_{REF}≤ V_{IN} ≤ V_{0REF+} VREF≤VIN≤V0REF+
STM32F10x系列芯片ADC通道和引脚对应关系
ADC引脚
模拟参考负极: 一般接地GND
模拟参考正极: 接VDDA
模拟电源: 接数字电源
模拟电源地: 接数字电源地
模拟输入信号: 不可超过模拟参考正极
STM32通道组
- 规则通道组: 相当正常运行的程序。最多16个通道。
规则通道和它的转换顺序在ADC_SQRx寄存器中选择,规则组转换的总数应写入ADC_SQR1寄存器的L[3:0]中 - 注入通道组: 相当于中断。 最多4个通道。
注入组和它的转换顺序在ADC_JSQR寄存器中选择。注入组里转化的总数应写入ADC_JSQR寄存器的L[1:0]中
单次转化&连续转换
ADC时钟配置
不要让ADC时钟超过14MHz,否则可能不准。
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
扫描模式在最后一个通道转换结束才会产生中断
ADC的采样时间
最小转换时间1us(ADC时钟=14MHz,采样周期为1.5周期下得到)
常用库函数
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);//初始化函数
void ADC_DeInit(ADC_TypeDef* ADCx)
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);//使能函数
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);//中断配置函数
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);//软件其中转换
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);//配置规则通道
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
//下面两个是校准代码,直接复制即可
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_StartCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
ADC初始化函数ADC_Init
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
typedef struct
{
uint32_t ADC_Mode;//ADC模式:配置ADC_CR1寄存器的位[19:16] :DUALMODE[3:0]位
FunctionalState ADC_ScanConvMode; //是否使用扫描模式。ADC_CR1位8:SCAN位
FunctionalState ADC_ContinuousConvMode; //单次转换OR连续转换:ADC_CR2的位1:CONT
uint32_t ADC_ExternalTrigConv; //触发方式:ADC_CR2的位[19:17] :EXTSEL[2:0]
uint32_t ADC_DataAlign; //对齐方式:左对齐还是右对齐:ADC_CR2的位11:ALIGN
uint8_t ADC_NbrOfChannel;//规则通道序列长度:ADC_SQR1的位[23:20]: L[3:0]
}ADC_InitTypeDef;
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数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure);
ADC使能函数 ADC_Cmd();
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC使能软件转换函数 ADC_SoftwareStartConvCmd
不使用外部触发可以使用软件转换
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState)
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能ADC1的软件转换启动
ADC 规则通道配置函数ADC_RegularChannelConfig
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx,uint8_t ADC_Channel,uint8_t Rank, uint8_t ADC_SampleTime);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1,ADC_SampleTime_239Cycles5 );
//ADC1的通道1在规则序列里的第一个转换,单通道单词转换
ADC 获取转换结果函数ADC_GetConversionValue
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
ADC_GetConversionValue(ADC1);//获取ADC1转换结果
实验
实验目的: ADC1的通道1(PA1)进行单次转化
- 开启PA口时钟和ADC1时钟,设置PA1为模拟输入。
GPIO_Init();
APB2PeriphClockCmd();
- 复位ADC1,同时设置ADC1分频因子。
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_DeInit(ADC1);
- 初始化ADC1参数,设置ADC1的工作模式以及规则序列的相关信息。
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
- 使能ADC并校准。
ADC_Cmd(ADC1, ENABLE);
- 配置规则通道参数:
ADC_RegularChannelConfig();
- 开启软件转换:
ADC_SoftwareStartConvCmd(ADC1);
- 等待转换完成,读取ADC值。
ADC_GetConversionValue(ADC1);
代码
adc.c
#include "adc.h"
#include "delay.h"
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_InitStructure.GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能GPIO1,ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC时钟,设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
//初始化ADC参数
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
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数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
//校准,直接复制就行
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //配置规则通道参数ADC1,ADC通道为ch,在规则序列中第一个转换,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //开启(使能)指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束,EOC转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
adc.h
#ifndef __ADC_H
#define __ADC_H
#include "sys.h"
void Adc_Init(void);//初始化,步骤1-4
u16 Get_Adc(u8 ch); //获取某个通道转换结果,步骤5-7
u16 Get_Adc_Average(u8 ch,u8 times); //多次调用Get_Adc取平均值
#endif
DAC数模转换
原理
- 2个DAC转换器:每个转换器对应1个输出通道
- 8位或者12位单调输出
- DAC工作在12位模式时,数据可以设置成左对齐或右对齐。
- 双DAC通道同时或者分别转换
- 每个通道都有DMA功能
- 同步更新功能
- 噪声波形生成
- 三角波形生成
- 12位数字输入
- 电压输出型的DAC
- DAC可以通过引脚输入参考电压VREF+以获得更精确的转换结果。
原理图
VDDA和VSSA为DAC模块模拟部分的供电。
Vref+则是DAC模块的参考电压。
DAC_OUTx就是DAC的输出通道了(对应DAC_0UT1->PA4或者DAC_OUT2->PA5引脚)。
输出电压计算
12位 0-4095
步骤
- 开启 PA 口时钟,设置 PA4 为模拟输入。
RCC_APB2PeriphClockCmd(RCC_APB2Per iph_GPIOA, ENABLE ); 使能 PORTA 时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; 模拟输入
- 使能 DAC1 时钟。
RCC_APB1PeriphClockCmd(RCC_APB1Peri ph_DAC, ENABLE ); 使能 DAC 通道时钟
- 初始化 DAC, 设置 DAC 的工作模式。
void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct)
typedef struct
{
uint32_t DAC_Trigger; //是否外部触发
uint32_t DAC_WaveGeneration; //三角波形生成
uint32_t DAC_LFSRUnmask_TriangleAmplitude;//噪声波三角波生成
uint32_t DAC_OutputBuffer;//设置输出缓存控制位,不使用输出缓存,
}DAC_InitTypeDef
- 使能 DAC 转换通道
DAC_Cmd(DAC_Channel_1, ENABLE); // 使能 DAC1
- 设置 DAC 的输出值。
DAC_SetChannel1Data(DAC_Align_12b_R, 0);//输出0
DAC_ GetDataOutputValue(DAC_Channel_1);//获取输出值
程序
{
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitType;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高
DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0
DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1
DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1
DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1
DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值
}
//设置通道1输出电压
//vol:0~3300,代表0~3.3V
void Dac1_Set_Vol(u16 vol)
{
float temp=vol;
temp/=1000;
temp=temp*4096/3.3;
DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值
}