一、ADC的介绍
ADC是英文Analog-to-digital converter 的缩写,意思是模拟数字转换器,简称模数转换器。在STM32F103 系列有 3 个 ADC,精度为 12 位,每个 ADC 最多有 16 个外部通道。其中 ADC1 和ADC2 都有 16 个外部通道,ADC3 根据 CPU 引脚的不同通道数也不同,一般都有 8 个外部通道。ADC 的模式非常多,功能非常强大。ADC是由PCLK2经分频产生,它的输入时钟不得超过14MHz。
二、ADC的特点
● 它的精度是12位
● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断
● 他有两种模式:单次和连续转换模式
● 从通道
0
到通道
n
的自动扫描模式
● 它能够自校准
● 带内嵌数据一致性的数据对齐
● 采样间隔可以按通道分别编程
● 规则转换和注入转换均有外部触发选项
● 间断模式
● 双重模式
(一起使用两个或者两个以上
ADC
的器件
)
●
ADC
转换时间: STM32F103xx增强型产品:时钟为
56MHz
时为
1
μ
s(
时钟为
72MHz
为
1.17
μ
s)
●
ADC
供电要求:
2.4V
到
3.6V
●
ADC
输入范围:
V
REF-
≤
V
IN
≤
V
REF+。
● 规则通道转换期间有
DMA
请求产生。
三、ADC的功能框图

1)左上:电压输入范围
ADC输入范围为:
V
REF-
≤V
IN
≤V
REF+
。由
V
REF-
、
V
REF+
、
V
DDA
、
V
SSA
、这四个外部引脚决定。我们在设计原理图的时候一般把V
SSA
和
V
REF-
接地,把
V
REF+
和
V
DDA
接
3V3
,得到
ADC
的输入电压范围为:0~3.3V
。
2)左中:输入通道
STM32的ADC多达18
个通道,其中外部的
16
个通道就是框图中的
ADCx_IN0
、
ADCx_IN1
…
ADCx_IN5
。这16
个通道对应着不同的
IO
口,具体是哪一个
IO
口可以从手册查询到。其
ADC1/2/3
还有内部通道:ADC1
的通道
16
连接到了芯片内部的温度传感器,
Vrefint
连接到了通
17
。
ADC2
的模拟通道16
和
17
连接到了内部的
VSS
。
ADC3
的模拟通道
9
、
14
、
15
、
16
和
17
连接到了内部VSS
。具体看图:

3)左下:外部触发转换
转换可以由外部事件触发(
例如定时器捕获,
EXTI
线
)
。如果设置了
EXTTRIG
控制位,则外部事 件就能够触发转换。EXTSEL[2:0]
和
JEXTSEL2:0]
控制位允许应用程序选择
8
个可能的事件中的
某一个,可以触发规则和注入组的采样。
ADC1
和
ADC2
用于规则通道的外部触发

ADC1
和
ADC2
用于注入通道的外部触发

ADC3
用于规则通道的外部触发

ADC3
用于注入通道的外部触发

4)中中:通道类型
规则通道:由16个转换组规则通道和它们的转换顺序在ADC_SQRx寄存器中选择。规则组中转换的总数应写入ADC_SQR1寄存器的L[3:0]位中。使用的比较多就是这个通道。
注入通道:由多达4个转换组成。注入通道和它们的转换顺序在ADC_JSQR寄存器中选择。注入组里的转换总数目应写入ADC_JSQR寄存器的L[1:0]位中。
5)数据寄存器

四、转换顺序
规则顺序:规则序列寄存器有3个,分别为SQR3、SQR2、SQR1。SQR3控制着规则序列中的第一个到第六个转换,对应的位为:SQ1[4:0]~SQ6[4:0],第一次转换的是位4:0SQ1[4:0],如果通道16想第一次转换,那么在SQ1[4:0]写16即可。SQR2控制着规则序列中的第7到第12个转换,对应的位为:SQ7[4:0]~SQ12[4:0],如果通道1想第8个转换,则SQ8[4:0]写1即可。SQR1控制着规则序列中的第13到第16个转换,对应位为:SQ13[4:0]~SQ16[4:0],如果通道6想第10个转换,则SQ10[4:0]写6即可。具体使用多少个通道,由SQR1的位L[3:0]决定,最多16个通道。

注入顺序:注入序列寄存器JSQR只有一个,最多支持4个通道,由JSQR的JL[1:0]决定。如果JL的值小于4的话,则JSQR跟SQR决定转换顺序的设置不一样,第一次转换的不是JSQR1[4:0],而是JCQRx[4:0],x=4-JL),跟SQR刚好相反。如果JL=00(1个转换),那么转换的顺序是从JSQR4[4:0]开始,而不是从JSQR1[4:0]开始,这个要注意,编程的时候不要搞错。当JL等于4时,跟SQR一样。

五、采样时间
根据介绍可知,ADC的时钟最大值位14MHz,它是由
输入时钟
ADC_CLK
由
PCLK2经过分频产生的。分频因子由RCC时钟配置寄存器RCC_CFGR的位15:14ADCPRE[1:0]设置,可以是2/4/6/8分频。
采样的周期数可通过ADC
采样时间寄存器ADC_SMPR1
和
ADC_SMPR2
中的
SMP[2:0]
位设置,
ADC_SMPR2
控制的是通道
0~9
,ADC_SMPR1控制的是通道
10~17。每个通道可以分别用不同的时间采样。
ADC的转换时间跟
ADC
的输入时钟和采样时间有关,公式为:
Tconv=
采样时间
+12.5
个周期。
当
ADCLK=14MHZ
(最高),采样时间设置为
1.5
周期(最快),那么总的转换时间(最短)
Tconv =1.5周期
+12.5
周期
=14
周期
=1us
。
六、中断
数据转换结束后,可以产生中断,中断分为三种:规则通道转换结束中断,注入转换通道转换结束中断,模拟看门狗中断。其中转换结束中断很好理解,跟我们平时接触的中断一样,有相应的中断标志位和中断使能位,我们还可以根据中断类型写相应配套的中断服务程序。
模拟看门狗中断
当被
ADC
转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断,前提是我们开启了模
拟看门狗中断,其中低阈值和高阈值由
ADC_LTR
和
ADC_HTR
设置。例如我们设置高阈值2.5V,那么模拟电压超过
2.5V
的时候,就会产生模拟看门狗中断,反之低阈值也一样。
七、DMA请求
规则和注入通道转换结束后,除了产生中断外,还可以产生
DMA请求,把转换好的数据直接存储在内存里面。要注意的是只有ADC1和ADC3可以产生DMA请求。有关DMA请求需要配合
《
STM32F10X-
中文参考手册》
DMA
控制器这一章节来学习。一般我们在使用
ADC
的时候都会
开启
DMA
传输。
八、电压转换
ADC的输入电压范围设定在:
0~3.3v
,因为
ADC
是
12
位的,那么12
位满量程对应的就
3.3V
,
12
位满量程对应的数字值是:
2^12
。数值
0
对应的就是
0V
。如果转换后的数值为X
,
X
对应的模拟电压为
Y
,那么会有这么一个等式成立:
2^12/3.3=X/Y,
=>Y=(3.3*X)/2^12
。
九、串口电压检测实验
ADC
宏定义
#ifndef __ADC_H
#define __ADC_H
#include "stm32f10x.h"
// ADC 编号选择
// 可以是 ADC1/2,如果使用ADC3,中断相关的要改成ADC3的
#define ADC_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADCx ADC2
#define ADC_CLK RCC_APB2Periph_ADC2
// ADC GPIO宏定义
// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
#define ADC_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADC_GPIO_CLK RCC_APB2Periph_GPIOC
#define ADC_PORT GPIOC
#define ADC_PIN GPIO_Pin_1
// ADC 通道宏定义
#define ADC_CHANNEL ADC_Channel_11
// ADC 中断相关宏定义
#define ADC_IRQ ADC1_2_IRQn
#define ADC_IRQHandler ADC1_2_IRQHandler
//#define ADC_IRQ ADC3_IRQn
//#define ADC_IRQHandler ADC3_IRQHandler
void ADCx_Init(void);
#endif /* __ADC_H */
ADC_GPIO初始化函数
static void ADCx_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 打开 ADC IO端口时钟
ADC_GPIO_APBxClock_FUN ( ADC_GPIO_CLK, ENABLE );
// 配置 ADC IO 引脚模式
// 必须为模拟输入
GPIO_InitStructure.GPIO_Pin = ADC_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
// 初始化 ADC IO
GPIO_Init(ADC_PORT, &GPIO_InitStructure);
}
ADC工作模式配置
static void ADCx_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
// 打开ADC时钟
ADC_APBxClock_FUN ( ADC_CLK, ENABLE );
// ADC 模式配置
// 只使用一个ADC,属于独立模式
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// 禁止扫描模式,多通道才要,单通道不需要
ADC_InitStructure.ADC_ScanConvMode = DISABLE ;
// 连续转换模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
// 不用外部触发转换,软件开启即可
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// 转换结果右对齐
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
// 转换通道1个
ADC_InitStructure.ADC_NbrOfChannel = 1;
// 初始化ADC
ADC_Init(ADCx, &ADC_InitStructure);
// 配置ADC时钟为PCLK2的8分频,即9MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
// 配置 ADC 通道转换顺序和采样时间
ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1,
ADC_SampleTime_55Cycles5);
// ADC 转换结束产生中断,在中断服务程序中读取转换值
ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE);
// 开启ADC ,并开始转换
ADC_Cmd(ADCx, ENABLE);
// 初始化ADC 校准寄存器
ADC_ResetCalibration(ADCx);
// 等待校准寄存器初始化完成
while(ADC_GetResetCalibrationStatus(ADCx));
// ADC开始校准
ADC_StartCalibration(ADCx);
// 等待校准完成
while(ADC_GetCalibrationStatus(ADCx));
// 由于没有采用外部触发,所以使用软件触发ADC转换
ADC_SoftwareStartConvCmd(ADCx, ENABLE);
}
static void ADC_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// 配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
main()函数
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "bsp_adc.h"
extern __IO uint16_t ADC_ConvertedValue;
// 局部变量,用于保存转换计算后的电压值
float ADC_ConvertedValueLocal;
// 软件延时
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
// 配置串口
USART_Config();
// ADC 初始化
ADCx_Init();
printf("\r\n ----这是一个ADC单通道中断读取实验----\r\n");
while (1)
{
ADC_ConvertedValueLocal =(float) ADC_ConvertedValue/4096*3.3;
//printf("\r\n AD value = 0x%04X \r\n",
//ADC_ConvertedValue);
printf("\r\n AD value = %f V \r\n",
ADC_ConvertedValueLocal);
printf("\r\n\r\n");
Delay(0xffffee);
}
}
十、总结
通过以上的学习,应该对ADC有一定的了解,在STM32的官方手册有更多的详细模式哦,代码仅是主要代码。