当前位置: 首页 > news >正文

STM32 ADC底层原理与寄存器配置详解

一、ADC硬件架构分析

STM32的ADC是逐次逼近型模数转换器(SAR ADC),通过电容充放电和比较器逐位确定数字量。以STM32F103为例,内部集成3个12位ADC,每个ADC有18个输入通道,转换速度可达1MHz。

1.1 ADC物理结构

ADC核心由以下几个部分组成:

输入通道选择器: 通过模拟多路开关实现18个通道的选择,包括16个外部通道和2个内部通道(温度传感器和内部参考电压VREFINT)。

采样保持电路: 内部采样电容通过开关接入输入信号,采样时间由ADC_SMPR寄存器控制。采样电容典型值为8pF,输入阻抗在采样阶段约为1MΩ,保持阶段接近无穷大。

逐次逼近寄存器(SAR): 12位转换需要12个时钟周期,每个周期确定1位。从MSB开始,通过二分查找算法逐位逼近输入电压。

比较器: 将采样电压与DAC输出电压比较,输出结果送入SAR逻辑。

参考电压: ADC转换范围由VREF+和VREF-决定,VREF+通常连接到VDDA(模拟电源),VREF-连接VSSA(模拟地)。转换精度=(VREF+ - VREF-)/4096。

1.2 时钟系统

ADC时钟由APB2时钟经分频得到:

  • ADC时钟频率 = PCLK2 / 分频系数(2/4/6/8)
  • 最高14MHz,建议不超过14MHz以保证12位精度
  • 转换时间 = 采样时间 + 12.5个ADC时钟周期

例如:PCLK2=72MHz,6分频得到12MHz,采样时间1.5周期,则单次转换时间=(1.5+12.5)/12MHz=1.17μs。

二、关键寄存器详解

2.1 ADC控制寄存器(ADC_CR1)

Bit 23: AWDEN - 规则通道模拟看门狗使能
Bit 22: JAWDEN - 注入通道模拟看门狗使能
Bit 16: DUALMOD[3:0] - 双ADC模式选择
Bit 13: DISCEN - 规则通道间断模式
Bit 12: JDISCEN - 注入通道间断模式
Bit 11: DISCNUM[2:0] - 间断模式通道数
Bit 8: SCAN - 扫描模式使能
Bit 7: JEOCIE - 注入通道转换结束中断
Bit 6: AWDIE - 模拟看门狗中断
Bit 5: EOCIE - 规则通道转换结束中断

2.2 ADC控制寄存器2(ADC_CR2)

Bit 23: TSVREFE - 温度传感器和内部参考电压使能
Bit 20: EXTTRIG - 外部触发使能
Bit 17-19: EXTSEL[2:0] - 外部触发源选择
Bit 15: JEXTTRIG - 注入通道外部触发
Bit 12-14: JEXTSEL[2:0] - 注入通道触发源
Bit 11: ALIGN - 数据对齐方式(0右对齐/1左对齐)
Bit 8: DMA - DMA使能
Bit 3: RSTCAL - 复位校准
Bit 2: CAL - 启动校准
Bit 1: CONT - 连续转换模式
Bit 0: ADON - ADC使能

重点说明: ADON位需要写两次,第一次上电,第二次启动转换。两次写入之间需要延时稳定时间(典型值2-3μs)。

2.3 采样时间寄存器(ADC_SMPR1/SMPR2)

每个通道可独立设置采样时间(单位:ADC时钟周期):

  • 000: 1.5周期
  • 001: 7.5周期
  • 010: 13.5周期
  • 011: 28.5周期
  • 100: 41.5周期
  • 101: 55.5周期
  • 110: 71.5周期
  • 111: 239.5周期

选择原则:

  1. 源阻抗越大,采样时间越长
  2. 最小采样时间需满足: Ts ≥ (Radc + Rsource) × Cadc × ln(2^(n+2))
  3. 其中Radc=1kΩ,Cadc=8pF,n=12位

2.4 规则序列寄存器(ADC_SQR1/2/3)

ADC_SQR1[23:20]存储转换序列长度(L[3:0],实际长度=L+1)
ADC_SQR1/2/3存储最多16个转换序列,每个序列5位表示通道号(0-17)

SQR1: [SQ16][SQ15][SQ14][SQ13]
SQR2: [SQ12][SQ11][SQ10][SQ9][SQ8][SQ7]
SQR3: [SQ6][SQ5][SQ4][SQ3][SQ2][SQ1]

2.5 数据寄存器(ADC_DR)

12位转换结果存储:

  • 右对齐: DR[11:0]存储数据,DR[31:12]为0
  • 左对齐: DR[15:4]存储数据,DR[3:0]为0

左对齐适用于不需要完整12位精度的场景,可直接读取高8位。

三、ADC工作模式

3.1 单次转换模式

配置CONT=0,启动一次转换后自动停止。每次转换需重新触发。

硬件流程:

  1. 写ADON=1(第二次)或外部触发启动
  2. 模拟开关连接选中通道到采样电容
  3. 采样结束后断开开关,开始逐次逼近
  4. 12.5个时钟后转换完成,EOC置1
  5. 读取DR寄存器,EOC自动清零

3.2 连续转换模式

配置CONT=1,转换完成后自动启动下一次。

3.3 扫描模式

配置SCAN=1,按SQR寄存器设定的序列依次转换多个通道。通常与DMA配合,将多通道数据存储到内存。

扫描时序:

[CH0采样]-[CH0转换]-[CH1采样]-[CH1转换]-...-[CHn转换]-[EOC=1]

3.4 间断模式

规则组可分为多个子组,每次触发只转换一个子组。通过DISCNUM设置子组通道数(1-8)。

3.5 注入转换模式

注入通道可打断规则通道转换,转换完成后继续规则通道。适用于优先级高的信号采集。

四、DMA传输原理

ADC与DMA配合实现零CPU占用的连续采集:

  1. DMA配置: 外设地址=ADC_DR寄存器地址,内存地址=数据缓冲区,传输宽度=16位
  2. 触发机制: 每次ADC转换完成,硬件自动触发DMA请求
  3. 数据流向: DMA控制器自动将DR寄存器内容搬移到内存
  4. 循环模式: DMA循环模式+ADC连续转换实现无限采集

关键点:

  • ADC_CR2的DMA位必须置1
  • DMA传输完成后不会自动关闭ADC,需手动控制
  • 扫描模式下,DMA传输次数=通道数

五、ADC校准原理

STM32内置校准功能消除内部电容的寄生电容误差。

校准流程:

  1. 写CAL=1启动校准
  2. 硬件自动执行校准序列(约83个ADC时钟)
  3. CAL自动清零表示完成
  4. 校准结果存储在内部寄存器,上电或复位后需重新校准

校准必须在ADC使能(ADON=1)且未转换时进行。

六、完整代码实现

6.1 单通道单次转换(寄存器版)

#include "stm32f10x.h"// ADC初始化 - PA0/ADC1_IN0
void ADC1_Init(void)
{// 1. 使能时钟RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;    // GPIOA时钟RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;    // ADC1时钟// 2. 配置GPIO为模拟输入GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);  // PA0模拟输入// 3. 配置ADC时钟分频 PCLK2/6=12MHzRCC->CFGR &= ~RCC_CFGR_ADCPRE;RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;// 4. 复位ADC配置RCC->APB2RSTR |= RCC_APB2RSTR_ADC1RST;RCC->APB2RSTR &= ~RCC_APB2RSTR_ADC1RST;// 5. ADC基本配置ADC1->CR1 = 0;  // 独立模式,单次转换,不扫描// 6. 右对齐,单次转换模式ADC1->CR2 = 0;ADC1->CR2 &= ~ADC_CR2_ALIGN;  // 右对齐ADC1->CR2 |= ADC_CR2_EXTTRIG;  // 使能外部触发(软件触发也需要)ADC1->CR2 |= ADC_CR2_EXTSEL;   // 软件触发(SWSTART)// 7. 配置采样时间 通道0采样239.5周期ADC1->SMPR2 &= ~ADC_SMPR2_SMP0;ADC1->SMPR2 |= ADC_SMPR2_SMP0_2 | ADC_SMPR2_SMP0_1 | ADC_SMPR2_SMP0_0;// 8. 配置规则序列ADC1->SQR1 = 0;  // 转换1个通道ADC1->SQR3 = 0;  // 第1个转换通道为CH0// 9. 使能ADCADC1->CR2 |= ADC_CR2_ADON;// 延时稳定for(uint32_t i = 0; i < 10000; i++);// 10. 校准ADCADC1->CR2 |= ADC_CR2_RSTCAL;  // 复位校准while(ADC1->CR2 & ADC_CR2_RSTCAL);  // 等待复位完成ADC1->CR2 |= ADC_CR2_CAL;  // 启动校准while(ADC1->CR2 & ADC_CR2_CAL);  // 等待校准完成
}// 读取ADC值
uint16_t ADC1_Read(void)
{// 启动转换(第二次写ADON或写SWSTART)ADC1->CR2 |= ADC_CR2_SWSTART;// 等待转换完成while(!(ADC1->SR & ADC_SR_EOC));// 读取数据(读DR会自动清除EOC标志)return (uint16_t)ADC1->DR;
}// 转换为电压(mV)
uint16_t ADC_ToVoltage(uint16_t adc_value)
{// Vref=3.3V, 12位ADCreturn (uint16_t)((adc_value * 3300) / 4096);
}

6.2 多通道扫描+DMA(寄存器版)

#include "stm32f10x.h"#define ADC_CH_NUM  4  // 采集4个通道
uint16_t ADC_Value[ADC_CH_NUM];  // DMA目标缓冲区// 多通道ADC+DMA初始化
// PA0-ADC1_IN0, PA1-ADC1_IN1, PA2-ADC1_IN2, PA3-ADC1_IN3
void ADC1_DMA_Init(void)
{// 1. 使能时钟RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN;RCC->AHBENR |= RCC_AHBENR_DMA1EN;// 2. 配置GPIOGPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0 |GPIO_CRL_MODE1 | GPIO_CRL_CNF1 |GPIO_CRL_MODE2 | GPIO_CRL_CNF2 |GPIO_CRL_MODE3 | GPIO_CRL_CNF3);// 3. ADC时钟12MHzRCC->CFGR &= ~RCC_CFGR_ADCPRE;RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;// 4. 配置DMADMA1_Channel1->CCR = 0;DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR);  // 外设地址DMA1_Channel1->CMAR = (uint32_t)ADC_Value;    // 内存地址DMA1_Channel1->CNDTR = ADC_CH_NUM;            // 传输数量// DMA配置: 内存递增,循环模式,半字传输,外设到内存,传输完成中断DMA1_Channel1->CCR |= DMA_CCR1_MINC |   // 内存地址递增DMA_CCR1_CIRC |   // 循环模式DMA_CCR1_PSIZE_0 | // 外设16位DMA_CCR1_MSIZE_0 | // 内存16位DMA_CCR1_TCIE;     // 传输完成中断DMA1_Channel1->CCR |= DMA_CCR1_EN;  // 使能DMA通道// 5. 配置ADCADC1->CR1 = 0;ADC1->CR1 |= ADC_CR1_SCAN;  // 扫描模式ADC1->CR2 = 0;ADC1->CR2 |= ADC_CR2_DMA;      // DMA使能ADC1->CR2 |= ADC_CR2_CONT;     // 连续转换ADC1->CR2 |= ADC_CR2_EXTTRIG;ADC1->CR2 |= ADC_CR2_EXTSEL;// 6. 采样时间配置ADC1->SMPR2 = 0;ADC1->SMPR2 |= (7 << 0) | (7 << 3) | (7 << 6) | (7 << 9); // 239.5周期// 7. 配置转换序列 CH0-CH3ADC1->SQR1 = (ADC_CH_NUM - 1) << 20;  // 转换4个通道ADC1->SQR3 = (0 << 0) | (1 << 5) | (2 << 10) | (3 << 15);// 8. 使能ADCADC1->CR2 |= ADC_CR2_ADON;for(volatile uint32_t i = 0; i < 10000; i++);// 9. 校准ADC1->CR2 |= ADC_CR2_RSTCAL;while(ADC1->CR2 & ADC_CR2_RSTCAL);ADC1->CR2 |= ADC_CR2_CAL;while(ADC1->CR2 & ADC_CR2_CAL);// 10. 使能DMA中断NVIC_EnableIRQ(DMA1_Channel1_IRQn);NVIC_SetPriority(DMA1_Channel1_IRQn, 0);
}// 启动连续转换
void ADC1_Start(void)
{ADC1->CR2 |= ADC_CR2_SWSTART;
}// DMA中断处理
void DMA1_Channel1_IRQHandler(void)
{if(DMA1->ISR & DMA_ISR_TCIF1)  // 传输完成{DMA1->IFCR |= DMA_IFCR_CTCIF1;  // 清除标志// 此时ADC_Value[]已更新,可以处理数据// 由于循环模式,DMA会自动继续传输}
}

6.3 注入通道+模拟看门狗

// 注入通道配置(高优先级采集)
void ADC1_Injected_Init(void)
{// GPIO和基本配置同上ADC1_Init();  // 复用单通道初始化// 配置注入通道ADC1->CR1 |= ADC_CR1_JAUTO;  // 规则通道结束后自动转换注入通道// 注入序列长度和通道ADC1->JSQR = 0;ADC1->JSQR |= (0 << 20);     // 转换1个注入通道(JL=0)ADC1->JSQR |= (1 << 15);     // 注入通道1: CH1// 使能注入通道转换结束中断ADC1->CR1 |= ADC_CR1_JEOCIE;NVIC_EnableIRQ(ADC1_2_IRQn);
}// 配置模拟看门狗(监测电压范围)
void ADC1_Watchdog_Init(uint16_t low, uint16_t high)
{// 设置阈值(12位数据)ADC1->LTR = low & 0x0FFF;ADC1->HTR = high & 0x0FFF;// 使能看门狗和中断ADC1->CR1 |= ADC_CR1_AWDEN;   // 规则通道看门狗ADC1->CR1 |= ADC_CR1_AWDIE;   // 看门狗中断ADC1->CR1 |= ADC_CR1_AWDSGL;  // 单通道模式ADC1->CR1 |= (0 << 0);        // 监测CH0NVIC_EnableIRQ(ADC1_2_IRQn);
}// ADC中断处理
void ADC1_2_IRQHandler(void)
{// 注入通道转换完成if(ADC1->SR & ADC_SR_JEOC){ADC1->SR &= ~ADC_SR_JEOC;uint16_t inj_value = ADC1->JDR1;  // 读取注入通道1数据// 处理高优先级数据}// 模拟看门狗if(ADC1->SR & ADC_SR_AWD){ADC1->SR &= ~ADC_SR_AWD;// 电压超出阈值,报警处理}
}

6.4 温度传感器读取

// 读取芯片内部温度
float ADC_ReadTemperature(void)
{// 使能温度传感器(CH16)ADC1->CR2 |= ADC_CR2_TSVREFE;// 配置转换CH16ADC1->SQR3 = 16;  // 通道16ADC1->SMPR1 |= ADC_SMPR1_SMP16;  // 最长采样时间// 启动转换ADC1->CR2 |= ADC_CR2_SWSTART;while(!(ADC1->SR & ADC_SR_EOC));uint16_t adc_val = ADC1->DR;// 温度计算公式(参考手册典型值)// V25 = 1.43V(25°C时电压)// Avg_Slope = 4.3mV/°Cfloat voltage = (adc_val * 3.3f) / 4096.0f;float temperature = (1.43f - voltage) / 0.0043f + 25.0f;return temperature;
}

6.5 HAL库版本对比

// HAL库简化版本
void HAL_ADC_Init_Example(void)
{ADC_HandleTypeDef hadc1;hadc1.Instance = ADC1;hadc1.Init.ScanConvMode = DISABLE;hadc1.Init.ContinuousConvMode = ENABLE;hadc1.Init.DiscontinuousConvMode = DISABLE;hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion = 1;HAL_ADC_Init(&hadc1);ADC_ChannelConfTypeDef sConfig;sConfig.Channel = ADC_CHANNEL_0;sConfig.Rank = 1;sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;HAL_ADC_ConfigChannel(&hadc1, &sConfig);HAL_ADC_Start(&hadc1);
}

七、实战应用案例

7.1 电池电压监测

#define VBAT_CHANNEL  0  // PA0连接分压电阻
#define VBAT_R1       10000  // 上拉10K
#define VBAT_R2       10000  // 下拉10Ktypedef struct {uint16_t voltage_mv;uint8_t  percent;uint8_t  status;  // 0正常 1低电压 2过压
} Battery_t;Battery_t Battery_Monitor(void)
{Battery_t bat;uint16_t adc = ADC1_Read();// 计算实际电池电压(考虑分压)uint16_t vdiv = (adc * 3300) / 4096;bat.voltage_mv = vdiv * (VBAT_R1 + VBAT_R2) / VBAT_R2;// 锂电池电量估算(3.0V-4.2V)if(bat.voltage_mv < 3000) {bat.percent = 0;bat.status = 1;} else if(bat.voltage_mv > 4200) {bat.percent = 100;bat.status = 2;} else {bat.percent = ((bat.voltage_mv - 3000) * 100) / 1200;bat.status = 0;}return bat;
}

7.2 快速数据采集系统

#define SAMPLE_RATE  10000  // 10KHz采样率
#define BUFFER_SIZE  1024uint16_t ADC_Buffer[BUFFER_SIZE];
volatile uint8_t buffer_full = 0;void ADC_HighSpeed_Init(void)
{// 使用定时器触发实现精确采样率// TIM2配置为10KHzRCC->APB1ENR |= RCC_APB1ENR_TIM2EN;TIM2->PSC = 7199;  // 72MHz/7200 = 10KHzTIM2->ARR = 0;     // 不分频TIM2->CR2 |= TIM_CR2_MMS_1;  // 更新事件作为TRGOTIM2->CR1 |= TIM_CR1_CEN;// ADC配置ADC1->CR1 = 0;ADC1->CR2 = 0;ADC1->CR2 |= ADC_CR2_EXTTRIG;ADC1->CR2 |= (6 << 17);  // 外部触发源: TIM2_TRGOADC1->CR2 |= ADC_CR2_DMA;// DMA配置DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR);DMA1_Channel1->CMAR = (uint32_t)ADC_Buffer;DMA1_Channel1->CNDTR = BUFFER_SIZE;DMA1_Channel1->CCR = DMA_CCR1_MINC | DMA_CCR1_PSIZE_0 | DMA_CCR1_MSIZE_0 | DMA_CCR1_TCIE | DMA_CCR1_EN;ADC1->CR2 |= ADC_CR2_ADON;// 校准...
}void DMA1_Channel1_IRQHandler(void)
{if(DMA1->ISR & DMA_ISR_TCIF1){DMA1->IFCR = DMA_IFCR_CTCIF1;buffer_full = 1;// 数据处理(FFT/滤波等)Process_ADC_Data(ADC_Buffer, BUFFER_SIZE);// 重启DMADMA1_Channel1->CCR &= ~DMA_CCR1_EN;DMA1_Channel1->CNDTR = BUFFER_SIZE;DMA1_Channel1->CCR |= DMA_CCR1_EN;}
}

7.3 多路传感器采集

typedef struct {uint16_t temp_adc;       // 温度传感器uint16_t light_adc;      // 光敏电阻uint16_t pressure_adc;   // 压力传感器uint16_t humidity_adc;   // 湿度传感器
} Sensor_Data_t;Sensor_Data_t sensors;void Multi_Sensor_Init(void)
{// 4通道扫描+DMAADC1_DMA_Init();  // 复用前面的DMA配置ADC1_Start();
}void Process_Sensor_Data(void)
{sensors.temp_adc = ADC_Value[0];sensors.light_adc = ADC_Value[1];sensors.pressure_adc = ADC_Value[2];sensors.humidity_adc = ADC_Value[3];// 转换为实际物理量float temp = (sensors.temp_adc * 3.3f / 4096.0f - 0.5f) / 0.01f; // TMP36传感器uint8_t light_percent = sensors.light_adc * 100 / 4096;float pressure_kpa = sensors.pressure_adc * 500.0f / 4096.0f;    // 0-500kPauint8_t humidity_rh = sensors.humidity_adc * 100 / 4096;
}

八、优化技巧与注意事项

8.1 精度优化

  1. 硬件电路:

    • 参考电压使用低温漂LDO(如REF3030)
    • 输入信号增加RC低通滤波(截止频率 < ADC时钟/2)
    • PCB布线远离数字信号,独立模拟地平面
    • VDDA单独供电,串联磁珠滤波
  2. 软件校准:

// 多次采样取平均
uint16_t ADC_ReadAverage(uint8_t times)
{uint32_t sum = 0;for(uint8_t i = 0; i < times; i++){sum += ADC1_Read();}return (uint16_t)(sum / times);
}// 中位值滤波
uint16_t ADC_Median_Filter(void)
{uint16_t buf[5];for(uint8_t i = 0; i < 5; i++)buf[i] = ADC1_Read();// 冒泡排序for(uint8_t i = 0; i < 4; i++)for(uint8_t j = 0; j < 4-i; j++)if(buf[j] > buf[j+1]) {uint16_t temp = buf[j];buf[j] = buf[j+1];buf[j+1] = temp;}return buf[2];  // 返回中位值
}

8.2 速度优化

  1. 缩短采样时间(源阻抗允许的情况下)
  2. 使用DMA减少CPU开销
  3. 多ADC交替使用,提高吞吐量
  4. 降低ADC分辨率(10位模式更快)

8.3 功耗优化

// ADC省电模式
void ADC_PowerSave_Mode(void)
{// 采样完成后关闭ADCADC1->CR2 &= ~ADC_CR2_ADON;// 关闭ADC时钟RCC->APB2ENR &= ~RCC_APB2ENR_ADC1EN;
}// 需要时重新初始化
void ADC_Wakeup(void)
{RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;ADC1->CR2 |= ADC_CR2_ADON;for(volatile int i = 0; i < 1000; i++);  // 稳定时间
}

8.4 常见问题

问题1: 读数不稳定跳动

  • 原因: 采样时间不足,输入阻抗过大
  • 解决: 增加采样周期或降低源阻抗

问题2: DMA只传输一次

  • 原因: 未配置循环模式
  • 解决: DMA_CCR |= DMA_CCR1_CIRC

问题3: 转换结果始终为0或4095

  • 原因: GPIO未配置为模拟输入或VREF异常
  • 解决: 检查CRL/CRH寄存器,测量VDDA电压

问题4: 多通道数据错位

  • 原因: DMA传输速度 < ADC转换速度
  • 解决: 降低ADC时钟或使用双缓冲

九、调试方法

// ADC状态诊断函数
void ADC_Diagnose(void)
{printf("ADC1 Status:\n");printf("ADON: %d\n", (ADC1->CR2 & ADC_CR2_ADON) ? 1 : 0);printf("EOC: %d\n", (ADC1->SR & ADC_SR_EOC) ? 1 : 0);printf("STRT: %d\n", (ADC1->SR & ADC_SR_STRT) ? 1 : 0);printf("Current Channel: %d\n", ADC1->SQR3 & 0x1F);printf("DR Value: %d\n", ADC1->DR);// 检查时钟配置uint32_t pclk2 = SystemCoreClock / 2;  // 假设PCLK2为HCLK/2uint32_t adcpre = (RCC->CFGR & RCC_CFGR_ADCPRE) >> 14;uint32_t adc_clk = pclk2 / (2 * (adcpre + 1));printf("ADC Clock: %d Hz\n", adc_clk);
}

十、总结

STM32的ADC从硬件层面理解需要掌握:

  1. SAR转换原理和时序关系
  2. 寄存器位域的具体作用
  3. 时钟树对ADC性能的影响
  4. DMA与ADC的握手机制

实际应用中,根据需求选择合适的工作模式,通过硬件优化和软件滤波提升精度。掌握寄存器级编程能更灵活地控制ADC行为,理解底层机制对调试和优化至关重要。

以上代码均基于STM32F103系列,其他系列需参考对应的参考手册调整寄存器定义。建议结合示波器观察ADC_IN引脚波形和DR寄存器更新时序,加深对ADC工作流程的理解。

http://www.dtcms.com/a/593476.html

相关文章:

  • 互联科技行业网站wordpress+纯净主题
  • 短剧广告联盟APP盈利模式分析:B 端合作商如何通过系统实现收益增长
  • 「腾讯云NoSQL」技术之向量数据库篇:自研分布式向量数据库,实现毫秒级时序一致备份的挑战和实践
  • seo站长助手网站设计师加油站
  • 基于Springboot的游戏后台管理系统a803t(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
  • springboot在线课堂教学辅助系统07741
  • 成都网站制作中国互联国家企业信用信息公示系统山东
  • C++ 哈希
  • Rust编程问题修复:如何解决“no method named partition_at_mut found”错误
  • OrangePi(运行 Ubuntu 22.04)安装 ROS 2 Humble
  • Vue3:详解toRef
  • Nvm 实现vue版本切换
  • Jenkins配置vue前端项目(最简单的操作)
  • 网站中如何做图片轮播flashfxp链接网站
  • Bootstrap 模态框
  • 做网站 怎么样找客户个人与公司网站备案
  • YMatrix 通过“可信数据库”测评!超融合架构能否成为未来趋势?
  • 分布式系统概要
  • 【C#】System.Text.Encoding.Default 属性在framework和.netcore中的区别
  • 天山路街道网站建设热搜关键词查询
  • Node.js:让JavaScript走出浏览器
  • AEO终极指南:步步为营,提升内容的AI可见性
  • vue甘特图
  • 家具网站开发设计论文电商商城开发
  • STM32时钟系统对于STM32F1系列(详解)
  • C++set学习笔记
  • 做 个收废品网站建设教育网站
  • 做中英文游戏门户网站关键词怎么弄棠下手机网站建设电话
  • 2025/11/10 IO流(转换流、序列化流/反序列化流、打印流、压缩流/解压缩流)Commons-io Hutool工具包 练习-制造假数据
  • 底层视觉及图像增强-项目实践(十六-0-(11):针对LED低灰细节丢失的AI超分技术:从原理到产品化实战):从奥运大屏,到手机小屏,快来挖一挖里面都有什么