ADC 模拟量转数字量
1.ADC概述
在STM32 MCU中,利用ADC端口完成【电压值转换为数字值】方式。大多数传感器会利用ADC方式已进行模拟量转化数字量方式进行传感器数据提供。例如:光敏电阻,压敏电阻,水位传感器,电池电路传感器。
ADC可以将数据转换后,交给后续MCU 进行阈值判断,完成需求后进行其他操作。
1.1ADC技术点分析
1.1.1ADC主要特征
1.1.2ADC框图分析
1.1.3ADC转换器工作原理
ADC转换器根据【参考电压】和【采样精度】,采用【逐次逼近法】进行电压转数值处理
- 当前ADC采样精度是4096(0X00~0X0FFF);
- 参考电压是Vref += 3.3V Vref -=0V
- ADC每一个数据对应的电压可以趋近于认为是3.3V/4096 ≈0.0008
- 假设当前ADC输入通道对应的电压是2.2V
1.2ADC编程实现光敏模块
1.2.1ADC电路相关模块分析
- 对应MCU引脚为PF8,要求PF8 GPIO 工作模块为【模拟输入】模式
- 对应ADC模块为ADC3 , 同时数据端口为IN6,操作的目标ADC==》ADC3_IN6
- 需要开放的时钟有
- ADC时钟使能
- GPIOF始终使能
- ADC要求的时钟最大14MHz,同时提供了规定的降频倍数/预分频倍数。需要根据当前MCU设计的要求提供时钟,72MHz的MCU主频,最快周期情况下,有且只能选择预分配倍数/降频倍数 为8 提供给ADC时钟9MHz
- 需要对APB2预分频倍数进行处理。
1.2.2 ADC时钟配置
- RCC APB2ENR 时钟使能
- RCC CFGR 预分频倍数配置
1.2.3ADC配置相关寄存器
ADC_CR1 寄存器配置
ADC_CR2 寄存器配置
我们需要再CONT位置进行连续转换,置为1 ,图中标记错误,已经划掉。
ADC采样周期控制寄存器
对应寄存器为 ADC_SMPR1 ADC_SMPR2 分别对应的 ADC 通道为
ADC_SMPR1 = 10 ~ 17 ADC 通道
ADC_SMPR2 = 0 ~ 9 ADC 通道
其中 16 和 17 ADC 通道对应 MCU 的温度传感器和 MCU 电压传感器。
ADC_SMPR1 => ADCx_IN10 ~ ADCx_IN15
ADC_SMPR2 => ADCx_IN0 ~ ADCx_IN9
当前光敏电阻对应的 ADC 通道是 ADC3_IN6 ,选择 ADC_SMPR2 进行配置。
ADC 采用周期越长,对应的 ADC 精度越高。通常情况下采用 111 ==> 239.5 周期进行采样,真实周期 239.5 周期 + 12.5 周期。
ADC 规则通道配置
- ADC 规则通道支持 16 路 ADC 输入,每次转换一路 ADC 数据,需要配置当前 16 路 ADC 输入通道的对应关系。例如
- ADC3 规则通道 0 ===> ADC3_IN5
- ADC3 规则通道 1 ===> ADC3_IN10
- ADC3 规则通道 2 ===> ADC3_IN6
- 当前有且只有一路 ADC 数据输入,规则通道只需要开放通道 0 配置 ADC3_IN6 即可。
ADC规则通道数据转换结果寄存器
ADC 开启转换之后,DR 数据会根据用户要求,定时更新。

这些都是进行配置ADC所需要的。
以下是实现代码
adc.h
#ifndef _ADC_H
#define _ADC_H
#include "stm32f10x.h"
#include "delay.h"
#define GPIOF_RCC_APB2_CLOCK_ENABLE (0x01 << 7)
#define ADC3_RCC_APB2_CLOCK_ENABLE (0x01 << 15)
#define Analog_Input_Mode (0x00)
void LSEN_Init(void);
u16 LSEN_GetValue(void);
#endif
adc.c
#include "adc.h"
void LSEN_Init(void)
{
/*
1.RCC 时钟使能,需要使能有 GPIOF 和 ADC3
因为当前光敏电阻对应 GPIO PF8 ,ADC 通道是 ADC3_IN6
*/
RCC->APB2ENR |= GPIOF_RCC_APB2_CLOCK_ENABLE | ADC3_RCC_APB2_CLOCK_ENABLE;
/*
2. GPIOF 对应 PF8 配置为【模拟输入模式】
对应 GPIOF->CRH [CNF8:MODE8]
*/
GPIOF->CRH &= ~(0x0F);
/*
3. ADC 配置
*/
/*
3.1 ADCCLK 对应 APB2 预分频倍数配置,对应寄存器是
RCC->CFGR, 设置预分配倍数位 6. ADCCLK 对应 12MHz
*/
RCC->CFGR &= ~(0x03 << 14);
RCC->CFGR |= (0x02 << 14);
/*
3.2 ADC 采用周期时间控制
Tconv = 采样时间 + 12.5个周期 官方 ADC 规定周期。
一般情况下采样周期越长,对应的 ADC 数据精度越高。
239.5 + 12.5 周期。
*/
ADC3->SMPR2 |= (0x07 << 18);
/*
3.3 ADC 工作通道配置
选择规则通道进行 ADC 转换,同时开放一个转换通道。
对应 ADC3_IN6
*/
ADC3->SQR1 &= ~(0x0F << 20); // ADC 规则通道开始一个转换通道
ADC3->SQR3 &= ~(0x1F); // 擦除 ADC3 规则转换通道第一个转换通道原本数据
ADC3->SQR3 |= (0x06); // 赋值 ADC3 规则转换通道第一个转换通道对应 ADC3_IN6
/*
3.4 ADC CR 配置
CR1
DUALMOD[3:0] [位19:16] 配置 ADC 独立模式 ==> 0000
SCAN [位8] 关闭 ADC 扫描模式 ==> 0
CR2
SWSTART [位22] 开启规则转换通道 ==> 1
EXTTRIG [位20] 规则转换通道外部触发方式开始 ==> 1
EXTSEL[2:0] [位19:17] 选择启动规则通道组转换的外部事件 ==> 111
明确为软件触发当前 ADC 转换。
ALIGN [位11] 转换结果数据对齐方式 ==> 0 右对齐
CONT [位1] 连续转换 ==> 1 连续转换模式
*/
ADC3->CR1 &= ~(0x0F << 16); // DUALMOD[3:0] [位19:16]
ADC3->CR1 &= ~(0x01 << 8); // SCAN [位8]
ADC3->CR2 &= ~(0xFFFFFFFF);
ADC3->CR2 |= (0x01 << 22); // SWSTART [位22]
ADC3->CR2 |= (0x01 << 20); // EXTTRIG [位20]
ADC3->CR2 |= (0x07 << 17); // EXTSEL[2:0] [位19:17]
ADC3->CR2 |= (0x01 << 1);
/*
因为整个 ADC3->CR2 已经进行整体擦除操作,对应 ALIGN 无需再次赋值。
*/
/*
3.5 ADC 校准和开启
需要在 ADC 时钟,通道和其他相关配置完成之后,才可以开启校准。
ADC 自校准 + 重启过程
需要利用 ADC CR2 配置寄存器完成RSTCAL [位3] 复位校准
CAL [位2] AD 校准
ADON [位0] AD 转换模块开启
*/
// 3.5.1 ADC 关闭掉电,重启
ADC3->CR2 &= ~(0x01); // ADC 关闭断电
Delay_ms(10); // 延时 10 ms
ADC3->CR2 |= 0x01; // ADC 打开
/*
3.5.2 ADC 开启复位校准
需要对 CR2 寄存器 RSTCAL 位赋值为 1,如果 复位校准完毕
硬件会将对应 RSTCAL 位置为 0.
1 ==> 开启复位校准
0 ==> 硬件复位校准结束
*/
ADC3->CR2 |= (0x01 << 3); // 开启复位校准
while (ADC3->CR2 & (0x01 << 3)); // 等待硬件将对应寄存器位置为 0,校准结束
Delay_ms(10); // 延时 10 ms
/*
3.5.3 ADC A/D 校准
需要对 CR2 寄存器 CAL 位赋值为 1,如果 复位校准完毕
硬件会将对应 CAL 位置为 0.
1 ==> 开启 A/D 校准开启
0 ==> 硬件 A/D 校准结束
*/
ADC3->CR2 |= (0x01 << 2); // 开启 A/D 校准
while (ADC3->CR2 & (0x01 << 2)); // 等待硬件将对应寄存器位置为 0,校准结束
ADC3->CR2 |= 0x01; // ADC 打开
}
u16 LSEN_GetValue(void)
{
/*
ADC->SR 状态寄存器中,位[1] EOC 标志位,在规则通道或者注入通道转换数据
结束之后,被硬件置为 1,表示有 ADC 转换数据在对应的 DR 存在。
该位清除方式是读取 ADCx->DR 寄存器数据。
*/
while (0 == (ADC3->SR & (0x01 << 1)));
return ADC3->DR;
}
main.c
#include "stm32f10x.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "led.h"
#include "beep.h"
#include "delay.h"
#include "key.h"
#include "usart1.h"
#include "adc.h"
int main(void)
{
USART1_Init(9600);
LSEN_Init();
char buffer[32];
while (1)
{
u16 lsen_adc_value = LSEN_GetValue();
sprintf(buffer, "ADC Value : %d\r\n", lsen_adc_value);
USART1_SendString(buffer);
memset(buffer, 0, 32);
Delay_ms(2000);
}
}