笔记整理自B站UP主江科大自化协教程《STM32入门教程-2023持续更新中》,所用单片机也为教程推荐单片机。
STM32的ADC是12位的,所以AD结果最大值是4095,也就是2^12-1
DAC的应用主要是在波形生成这些领域,比如信号发生器、音频解码芯片等
本型号STM32也没有DAC的外设
ADC简介
ADC(Analog-Digital Converter)模拟-数字转换器,ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁,12位逐次逼近型ADC(ADC工作模式),1us转换时间,输入电压范围:0~3.3V,转换结果范围:0~4095,18个输入通道,可测量16个外部(16个GPIO)和2个内部信号源(内部温度传感器和内部参考电压),规则组和注入组(突发事件)两个转换单元,模拟看门狗自动监测输入电压范围。STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道
逐次逼近型ADC
逐次逼近的含义:二分法,0~255取中间128,与128比较大小,如果大则在128~255之间选择...

STM32 ADC框图

ADC基本结构

输入通道

转换模式
单次转换,非扫描模式
在非扫描的模式下,只有第一个序列1的位置有效,同时选中一组的方式就退化为简单地选中一个的方式。转换完成结果存放在数据寄存器中,EOC标志位置1。

连续转换,非扫描模式
非扫描模式,菜单列表还是只用第一个,与单次转换不同的是,它在一次转换结束之后不会停止,而是立刻开始下一轮的转换,然后一直持续下去。

单次转换,扫描模式
单次转换,每触发一次,转换结束后就会停下来,下次转换就得再触发才能开始。
扫描模式:用到“菜单”模式,每个位置是通道几可以任意指定,并且也是可以重复的。转换的结果都放在数据寄存器中,为了防止数据被覆盖,需要用DMA及时将数据挪走,通道全部转换完成后,产生EOC信号,转换结束。

连续转换,扫描模式
扫描模式:一次转换完成后,立刻开始下一次的转换

触发控制

数据对齐
一般使用的都是右对齐

转换时间
AD转换的步骤:采样,保持,量化,编码
采样时间短,速度快;采样时间长,避免毛刺干扰
STM32 ADC的总转换时间为:TCONV = 采样时间 + 12.5个ADC周期
例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期
TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs
校准
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差
建议在每次上电后执行一次校准
启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期
硬件电路

PA0~PA7、PB0、PB1这10个引脚是ADC的10个通道
AD单通道代码讲解
第一步:开启RCC时钟,包括ADC和GPIO时钟,ADCCLK的分频器
第二步:配置GPIO,把需要用的GPIO配置成模拟输入的模式
第三步:配置多路开关
第四步:配置ADC转换器(单次转换or多次转换、扫描or非扫描、有几个通道、...)
RCC_ADCCLKConfig——配置ADCCLK分频器,可以对APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK,stm32f10x_rcc.h
再介绍一个ADC的相关函数,stm32f10x_adc.h
ADC_DeInit——恢复缺省配置
ADC_Init——初始化
ADC_StructInit——结构体初始化
ADC_Cmd——给ADC上电(开关控制)
ADC_DMACmd——开启DMA输出信号
ADC_ITConfig——中断输出控制
ADC_ResetCalibration——复位校准
ADC_GetResetCalibrationStatus——获取复位校准状态
ADC_StartCalibration——开始校准
ADC_GetCalibrationStatus——获取开始校准状态
ADC_SoftwareStartConvCmd——ADC软件开始转换控制,给SWSTRAT位置1
ADC_GetSoftwareStartConvStatus——ADC获取软件开始转换状态,返回SWSTART的状态,ADC_GetFlagStatus——获取标志位状态
ADC_DiscModeChannelCountConfig——每隔几个通道间断一次
ADC_DiscModeCmd——是否启用间断模式
ADC_RegularChannelConfig——ADC规则组通道配置
ADC_ExternalTrigConvCmd——ADC外部触发转化控制
ADC_GetConversionValue——ADC获取转换值
ADC_GetDualModeConversionValue——ADC获取双模式转换值
第一步——开启时钟
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
第二步——配置ADCCLK
//配置ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72MHz / 6 = 12MHz
第三步——配置GPIO
IO口模拟输入
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
第四步——选择规则组的输入通道
在规则组菜单列表的第一个位置,写入通道0
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
第五步——结构体初始化ADC
//结构体初始化ADC
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工作模式
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
第六步——开启ADC电源
//开启ADC电源
ADC_Cmd(ADC1, ENABLE);
第七步——ADC校准
//ADC校准
ADC_ResetCalibration(ADC1); //复位校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);
源代码
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72MHz / 6 = 12MHz
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//选择规则组的输入通道
/*
在规则组菜单列表的第一个位置,写入通道0
*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
//结构体初始化ADC
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工作模式
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
//开启ADC电源
ADC_Cmd(ADC1, ENABLE);
//ADC校准
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);
}
经验总结

AD多通道代码讲解
区别于单通道

源代码
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72MHz / 6 = 12MHz
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; //PA0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//结构体初始化ADC
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工作模式
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
//开启ADC电源
ADC_Cmd(ADC1, ENABLE);
//ADC校准
ADC_ResetCalibration(ADC1); //复位校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);
}
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
//选择规则组的输入通道
/*
在规则组菜单列表的第一个位置,写入通道0
*/
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);
}
感谢抽出宝贵时间阅读的各位小读者们,创作不易,如果感觉有帮助的话,帮忙点个赞再走吧!你们的支持是我创作的动力,希望能带给大家更多优质的文章。