文章目录
- 一、ADC寄存器
-
- [ 1、ADC状态寄存器(ADC_SR)](# 1、ADC状态寄存器(ADC_SR))
- [ 2、ADC控制寄存器 1(ADC_CR1)](# 2、ADC控制寄存器 1(ADC_CR1))
- [ 3、ADC控制寄存器 2(ADC_CR2)](# 3、ADC控制寄存器 2(ADC_CR2))
- [ 4、ADC采样时间寄存器 1(ADC_SMPR1)](# 4、ADC采样时间寄存器 1(ADC_SMPR1))
- [ 5、ADC采样时间寄存器 2(ADC_SMPR2)](# 5、ADC采样时间寄存器 2(ADC_SMPR2))
- [ 6、ADC注入通道数据偏移寄存器x (ADC_JOFRx)(x=1..4)](# 6、ADC注入通道数据偏移寄存器x (ADC_JOFRx)(x=1..4))
- [ 7、ADC看门狗高阀值寄存器(ADC_HTR)](# 7、ADC看门狗高阀值寄存器(ADC_HTR))
- [ 8、ADC看门狗低阀值寄存器(ADC_LRT)](# 8、ADC看门狗低阀值寄存器(ADC_LRT))
- [ 9、ADC规则序列寄存器 1(ADC_SQR1)](# 9、ADC规则序列寄存器 1(ADC_SQR1))
- [ 10、ADC规则序列寄存器 2(ADC_SQR2)](# 10、ADC规则序列寄存器 2(ADC_SQR2))
- [ 11、ADC规则序列寄存器 3(ADC_SQR3)](# 11、ADC规则序列寄存器 3(ADC_SQR3))
- [ 12、ADC注入序列寄存器(ADC_JSQR)](# 12、ADC注入序列寄存器(ADC_JSQR))
- [ 13、ADC 注入数据寄存器x (ADC_JDRx) (x= 1..4)](# 13、ADC 注入数据寄存器x (ADC_JDRx) (x= 1..4))
- [ 14、ADC规则数据寄存器(ADC_DR)](# 14、ADC规则数据寄存器(ADC_DR))
- [ 15、ADC寄存器地址映像](# 15、ADC寄存器地址映像)
- 二、代码实例
-
- [ 1、ADC1 单次采样(PA0,查询模式)](# 1、ADC1 单次采样(PA0,查询模式))
- [ 2、ADC1 连续采样(PA0,中断模式)](# 2、ADC1 连续采样(PA0,中断模式))
- [ 3、ADC1 多通道扫描(PA0/CH0 + PA1/CH1,DMA 批量采集)](# 3、ADC1 多通道扫描(PA0/CH0 + PA1/CH1,DMA 批量采集))
- 三、总结
-
- [ 1、核心寄存器](# 1、核心寄存器)
- [ 2、注意事项](# 2、注意事项)
- [ 3、ADC 寄存器编程核心流程:](# 3、ADC 寄存器编程核心流程:)
一、ADC寄存器
1、ADC状态寄存器(ADC_SR)
地址偏移:0x00
复位值:0x0000 0000

2、ADC控制寄存器 1(ADC_CR1)
地址偏移:0x04
复位值:0x0000 0000


3、ADC控制寄存器 2(ADC_CR2)
地址偏移:0x08
复位值:0x0000 0000



4、ADC采样时间寄存器 1(ADC_SMPR1)
地址偏移:0x0C
复位值:0x0000 0000

5、ADC采样时间寄存器 2(ADC_SMPR2)
地址偏移:0x10
复位值:0x0000 0000

6、ADC注入通道数据偏移寄存器x (ADC_JOFRx)(x=1...4)
地址偏移:0x14-0x20
复位值:0x0000 0000

7、ADC看门狗高阀值寄存器(ADC_HTR)
地址偏移:0x24
复位值:0x0000 0000

8、ADC看门狗低阀值寄存器(ADC_LRT)
地址偏移:0x28
复位值:0x0000 0000

9、ADC规则序列寄存器 1(ADC_SQR1)
地址偏移:0x2C
复位值:0x0000 0000

10、ADC规则序列寄存器 2(ADC_SQR2)
地址偏移:0x30
复位值:0x0000 0000

11、ADC规则序列寄存器 3(ADC_SQR3)
地址偏移:0x34
复位值:0x0000 0000

12、ADC注入序列寄存器(ADC_JSQR)
地址偏移:0x38
复位值:0x0000 0000

13、ADC 注入数据寄存器x (ADC_JDRx) (x= 1...4)
地址偏移:0x3C -- 0x48
复位值:0x0000 0000

14、ADC规则数据寄存器(ADC_DR)
地址偏移:0x4C
复位值:0x0000 0000

15、ADC寄存器地址映像


二、代码实例
1、ADC1 单次采样(PA0,查询模式)
最基础的 ADC 用法,单次触发转换、查询结果,适合低频采样场景。
c
#include "stm32f10x.h"
/**
* @brief 系统时钟初始化(72MHz,HSE=8MHz)
*/
void SystemClock_Init(void)
{
RCC->CR |= RCC_CR_HSEON;
while(!(RCC->CR & RCC_CR_HSERDY));
FLASH->ACR |= FLASH_ACR_LATENCY_2;
RCC->CFGR &= ~RCC_CFGR_HPRE;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
RCC->CFGR &= ~RCC_CFGR_PPRE2;
RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
RCC->CFGR |= RCC_CFGR_PLLMULL9;
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
/**
* @brief ADC1初始化(单次转换,CH0/PA0,查询模式)
*/
void ADC1_Single_Init(void)
{
// 1. 开启时钟:GPIOA、ADC1
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN;
// ADC时钟分频:6分频(72/6=12MHz ≤14MHz)
RCC->CFGR &= ~RCC_CFGR_ADCPRE;
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;
// 2. 配置GPIO:PA0模拟输入(无上下拉)
GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0); // 模拟输入模式
// 3. 配置ADC1核心参数
ADC1->CR1 &= ~ADC_CR1_SCAN; // 关闭扫描模式(单次通道)
ADC1->CR2 &= ~ADC_CR2_CONT; // 关闭连续转换(单次转换)
ADC1->CR2 &= ~ADC_CR2_ALIGN; // 数据右对齐(低12位有效)
ADC1->SQR1 &= ~ADC_SQR1_L; // 转换序列长度:1(L[3:0]=0000)
ADC1->SQR3 &= ~ADC_SQR3_SQ1; // 转换序列第1位:CH0(0)
ADC1->SQR3 |= 0;
// 4. 配置采样时间:CH0采样时间239.5周期(提高精度)
ADC1->SMPR2 &= ~ADC_SMPR2_SMP0;
ADC1->SMPR2 |= ADC_SMPR2_SMP0_2; // SMP0[2:0]=110 → 239.5周期
// 5. 开启ADC1并校准
ADC1->CR2 |= ADC_CR2_ADON; // 开启ADC
ADC1->CR2 |= ADC_CR2_CAL; // 启动校准
while(ADC1->CR2 & ADC_CR2_CAL); // 等待校准完成
}
/**
* @brief ADC1单次采样(查询模式)
* @return 采样值(0~4095)
*/
uint16_t ADC1_Single_Read(void)
{
// 1. 启动ADC转换
ADC1->CR2 |= ADC_CR2_SWSTART;
// 2. 等待转换完成(EOC=1)
while(!(ADC1->SR & ADC_SR_EOC));
// 3. 读取采样值(DR寄存器,右对齐)
return ADC1->DR;
}
/**
* @brief 采样值转电压(mV)
* @param adc_val:ADC采样值(0~4095)
* @return 电压值(mV,如1650=1.65V)
*/
uint16_t ADC_To_Voltage(uint16_t adc_val)
{
// 公式:电压 = (adc_val / 4095) * 3300mV
return (uint32_t)adc_val * 3300 / 4095;
}
// 主函数
int main(void)
{
uint16_t adc_val, voltage;
SystemClock_Init();
ADC1_Single_Init();
while(1)
{
// 单次采样
adc_val = ADC1_Single_Read();
voltage = ADC_To_Voltage(adc_val);
// 可通过串口输出adc_val和voltage,或做其他处理
// 简单延时
for(uint32_t i=0; i<1000000; i++);
}
}
2、ADC1 连续采样(PA0,中断模式)
连续转换 + 中断通知,CPU 无需轮询,适合高频采样场景。
c
#include "stm32f10x.h"
// 全局变量:存储最新采样值
uint16_t ADC1_Cont_Value = 0;
/**
* @brief ADC1初始化(连续转换,CH0/PA0,中断模式)
*/
void ADC1_Cont_IRQ_Init(void)
{
// 1. 时钟/GPIO配置(同场景1)
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN;
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;
GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
// 2. 配置ADC1:连续转换+中断
ADC1->CR1 &= ~ADC_CR1_SCAN;
ADC1->CR2 |= ADC_CR2_CONT; // 开启连续转换
ADC1->CR2 &= ~ADC_CR2_ALIGN;
ADC1->SQR1 &= ~ADC_SQR1_L;
ADC1->SQR3 |= 0;
ADC1->SMPR2 |= ADC_SMPR2_SMP0_2;
// 3. 开启ADC转换完成中断
ADC1->CR1 |= ADC_CR1_EOCIE; // 开启EOC中断
// 4. 配置NVIC(ADC1_IRQn=18号中断)
SCB->AIRCR = 0x05FA0800; // 优先级分组2
NVIC->IP[18] &= ~0xFF;
NVIC->IP[18] |= 0x10; // 抢占优先级1
NVIC->ISER[0] |= (1 << 18); // 使能ADC1中断
// 5. 开启ADC并校准,启动转换
ADC1->CR2 |= ADC_CR2_ADON;
ADC1->CR2 |= ADC_CR2_CAL;
while(ADC1->CR2 & ADC_CR2_CAL);
ADC1->CR2 |= ADC_CR2_SWSTART; // 启动连续转换
}
/**
* @brief ADC1中断服务函数
*/
void ADC1_IRQHandler(void)
{
// 检查转换完成标志
if(ADC1->SR & ADC_SR_EOC)
{
ADC1_Cont_Value = ADC1->DR; // 读取采样值
ADC1->SR &= ~ADC_SR_EOC; // 清除EOC标志
}
}
// 主函数
int main(void)
{
uint16_t voltage;
SystemClock_Init();
ADC1_Cont_IRQ_Init();
while(1)
{
// 实时读取连续采样值并转电压
voltage = ADC_To_Voltage(ADC1_Cont_Value);
// 业务处理逻辑
}
}
3、ADC1 多通道扫描(PA0/CH0 + PA1/CH1,DMA 批量采集)
扫描多个通道 + DMA 批量传输,适合多模拟量同步采集(如温度、电压、电流)。
c
#include "stm32f10x.h"
// DMA采集缓冲区:存储CH0和CH1的采样值
uint16_t ADC1_Scan_Buf[2] = {0};
#define ADC_SCAN_CH_NUM 2
/**
* @brief ADC1+DMA初始化(多通道扫描,CH0+CH1,DMA1_Channel1)
*/
void ADC1_Scan_DMA_Init(void)
{
// 1. 开启时钟:GPIOA、ADC1、DMA1
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN;
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;
// 2. 配置GPIO:PA0/PA1模拟输入
GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0 | GPIO_CRL_MODE1 | GPIO_CRL_CNF1);
// 3. 配置ADC1:扫描模式+连续转换
ADC1->CR1 |= ADC_CR1_SCAN; // 开启扫描模式
ADC1->CR2 |= ADC_CR2_CONT; // 连续转换
ADC1->CR2 |= ADC_CR2_DMA; // 开启ADC DMA模式
ADC1->CR2 &= ~ADC_CR2_ALIGN;
// 转换序列长度:2(L[3:0]=0001 → 2个通道)
ADC1->SQR1 &= ~ADC_SQR1_L;
ADC1->SQR1 |= (ADC_SCAN_CH_NUM - 1) << 20;
// 转换序列:第1位=CH0,第2位=CH1
ADC1->SQR3 &= ~(ADC_SQR3_SQ1 | ADC_SQR3_SQ2);
ADC1->SQR3 |= 0 << 0; // SQ1=CH0
ADC1->SQR3 |= 1 << 5; // SQ2=CH1
// 采样时间:CH0/CH1均为239.5周期
ADC1->SMPR2 |= ADC_SMPR2_SMP0_2 | ADC_SMPR2_SMP1_2;
// 4. 配置DMA1_Channel1(ADC1映射通道)
DMA1_Channel1->CCR &= ~DMA_CCR_EN; // 关闭DMA通道
DMA1_Channel1->CCR = 0;
DMA1_Channel1->CCR &= ~DMA_CCR_DIR; // 方向:外设→内存
DMA1_Channel1->CCR |= DMA_CCR_MINC; // 内存地址自增
DMA1_Channel1->CCR &= ~DMA_CCR_PINC; // 外设地址不自增
DMA1_Channel1->CCR |= DMA_CCR_PSIZE_0; // 外设数据宽度:16位
DMA1_Channel1->CCR |= DMA_CCR_MSIZE_0; // 内存数据宽度:16位
DMA1_Channel1->CCR |= DMA_CCR_CIRC; // 循环模式(连续采集)
DMA1_Channel1->CCR |= DMA_CCR_PL_1; // 优先级:高
// 5. 配置DMA地址和长度
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR; // 外设地址(ADC_DR)
DMA1_Channel1->CMAR = (uint32_t)ADC1_Scan_Buf; // 内存缓冲区
DMA1_Channel1->CNDTR = ADC_SCAN_CH_NUM; // 传输长度
// 6. 启动DMA和ADC
DMA1_Channel1->CCR |= DMA_CCR_EN; // 开启DMA
ADC1->CR2 |= ADC_CR2_ADON; // 开启ADC
ADC1->CR2 |= ADC_CR2_CAL; // 校准
while(ADC1->CR2 & ADC_CR2_CAL);
ADC1->CR2 |= ADC_CR2_SWSTART; // 启动连续转换
}
// 主函数
int main(void)
{
uint16_t voltage0, voltage1;
SystemClock_Init();
ADC1_Scan_DMA_Init();
while(1)
{
// ADC1_Scan_Buf[0] = CH0采样值,ADC1_Scan_Buf[1] = CH1采样值
voltage0 = ADC_To_Voltage(ADC1_Scan_Buf[0]);
voltage1 = ADC_To_Voltage(ADC1_Scan_Buf[1]);
// 多通道数据处理逻辑
}
}
三、总结
1、核心寄存器

2、注意事项
DC 时钟限制 :
STM32F103 ADC 时钟最大 14MHz,72MHz 系统时钟下必须 6 分频(72/6=12MHz),否则 ADC 采样精度下降。
采样时间选择:
采样时间越长,抗干扰能力越强,常用 239.5 周期(高精度)、13.5 周期(高速);
公式:采样时间 = (SMPx + 1) × ADC 时钟周期(如 239.5 周期 @12MHz = ~20μs)。
校准操作:
每次开启 ADC 后必须执行校准(ADC_CR2_CAL),校准完成后才能启动转换,否则采样值偏差大。
DMA 循环模式:
多通道连续采集时,DMA 需开启CIRC循环模式,否则采集完一次后停止。
电压转换精度:
公式(adc_val * 3300) / 4095中,需用uint32_t强制转换,避免 16 位整数溢出。
3、ADC 寄存器编程核心流程:
开时钟→配 GPIO→配 ADC 参数(扫描 / 连续 / 采样时间)→校准→启动转换→读数据;