STM32_ADC_寄存器操作

文章目录

  • 一、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 参数(扫描 / 连续 / 采样时间)→校准→启动转换→读数据;

相关推荐
全栈游侠2 小时前
06-任务管理与创建
stm32
LCG元2 小时前
STM32实战:基于STM32F103的智能门禁系统(RFID+指纹)
stm32·单片机·嵌入式硬件
可乐鸡翅好好吃3 小时前
NRF芯片下的ADC采集
单片机·嵌入式硬件
3壹4 小时前
LED模块控制与左移运算详解
单片机·嵌入式硬件
senijusene4 小时前
通信概念,51UART的使用,以及MODBUS的简单应用
c语言·开发语言·单片机·51单片机
Zevalin爱灰灰4 小时前
零基础入门学用物联网(ESP8266) 第一部分 基础知识篇(五)
单片机·物联网·嵌入式·esp8266
全栈游侠5 小时前
05-FreeRTOS的移植与适配
stm32
学嵌入式的小杨同学5 小时前
STM32 进阶封神之路(二十二):DMA 实战全攻略 ——ADC 采集 + 串口收发 + 内存复制(库函数 + 代码落地)
c++·stm32·单片机·嵌入式硬件·mcu·硬件架构·pcb
是翔仔呐6 小时前
C语言从黑框框到控硬件!51单片机零基础保姆式全系列教程 开篇前言+全书总览
c语言·开发语言·单片机·嵌入式硬件·gitee·51单片机