ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32 ADC-DMA电压采样实战:原理、配置与踩坑记录

STM32 ADC-DMA电压采样实战:原理、配置与踩坑记录 为什么电压采样总在关键时候丢数据ADC-DMA才是底层答案调试一块需要同时采集三路电压的板子时我吃过一次大亏ADC中断方式读数据每次进中断读一个通道结果主循环里稍微跑个显示刷新采样值就开始跳用示波器对比发现波形没变但数值忽高忽低。后来把中断里做数据搬移的活儿全部甩给DMA问题当场消失CPU占用率从42%降到了7%。很多朋友看到ADC-DMA协同工作这个标题第一反应是不就是配个DMA通道吗但真到了调多通道电压、做波形记录、跑闭环控制的时候采样效率、数据连续性、CPU负载之间的平衡才是真正的分水岭。这篇我把自己基于STM32平台CubeMX HAL库从零搭ADC-DMA电压采样的完整过程、原理拆解和踩坑记录写清楚包含可以直接抄走的代码和配置参数。1. 为什么电压采样非得拖上DMA从裸奔读ADC的痛点说起1.1 常规ADC读取方式到底慢在哪先捋一下最基础的ADC数据读取。常规做法有两种轮询查询和中断读取。轮询查询就是CPU死等启动一次转换然后不断读标志位等转换完成再去拿数据。这在单通道、采样率要求不高的场景下没什么问题比如读一个电位器电压50ms读一次都嫌快。但到了多通道连续采样问题就来了。假设用ADC1的4个通道按扫描模式顺序转换每次转换完成都进一次中断CPU在中断里读走数据。你在主循环里干活的时候中断一来就被打断频繁进进出出状态保护的压栈出栈全是耗时。更麻烦的是如果中断响应不及时ADC数据寄存器里的新数据被下一个转换结果覆盖掉你读到的就是脏数据根本不知道是哪一次转换的结果。我调试时踩过这个坑四路电压采样采样率拉到1kHz中断方式读数据然后用串口打印。刚开始看起来正常但一旦把浮点运算加进主循环采样值就开始周期性跳变。用逻辑分析仪抓中断响应时间发现中断到读取之间的延迟有时超过一个采样周期数据覆盖实锤了。1.2 DMA介入后数据是怎么自动搬运的DMA的本质是一个独立于CPU的数据搬运引擎它在内存和外设之间搬数据搬的过程不需要CPU干预。放在ADC场景下就是ADC转换完成后结果会自动从ADC的数据寄存器搬到内存变量里搬完可以不打扰CPU也可以触发一次中断让CPU知道数据就绪。这套机制解决了两件事一是CPU不再被频繁打断中断频率从每个采样点一次降到一批数据完成一次二是数据不会丢失只要DMA搬运的速度跟得上ADC转换速度每一个转换结果都会被原样写入内存缓冲区新旧数据不会互相覆盖。实际测试下来1kHz采样率下四路电压采集用中断方式CPU占用约40%左右切换到DMA方式后直接降到个位数而且采样数据在长时间运行中非常稳定不再出现偶发跳数。1.3 什么时候必须用ADC-DMA什么时候没必要我自己的判断标准很简单满足任意一条就建议用DMA采样通道数大于等于2且需要连续周期采样采样率超过1kHz数据需要持续记录主循环还有其他耗时任务显示刷新、通信协议处理、PID计算等需要做波形记录或FFT分析要求数据序列连续无空洞反过来如果只是读一个温度传感器的模拟量几秒钟采一次那直接轮询读就行配DMA反而是给自己找麻烦因为你要处理缓冲区的数据同步复杂度反而上去了。一句话总结ADC-DMA解决的核心问题是采样连续性和CPU负载之间的矛盾它让ADC在后台默默工作把数据塞满缓冲区CPU只负责在合适的时机取走数据。2. CubeMX下的ADC-DMA工程配置每一步参数选择背后的理由2.1 基础工程配置时钟、ADC与DMA的依赖关系先搭一个基础工程。我用的是STM32F103C8T6最小系统板外接三个电位器模拟三路电压输入接在PA0、PA1、PA2上对应ADC1的通道0、通道1、通道2。这三路电压范围0~3.3V。在CubeMX里的初始化配置有固定的先后逻辑。先配置时钟树把ADC的时钟频率定住。ADC时钟来自APB2总线时钟最高不能超过14MHzF103的硬限制。我建议把APB2时钟设为72MHz然后分配器设6分频这样ADC时钟正好12MHz接近上限又能保证采样稳定。这里有个很关键的细节ADC的采样时间参数。CubeMX里可以设置采样时间F103支持1.5周期到239.5周期几种选项。采样时间越长采到的电压越准确因为采样电容有足够的充电时间但总转换时间也会变长。对一般电压采样来说采样时间至少选55.5周期起步如果是高内阻信号源电位器分压输出的等效内阻通常在10kΩ级别建议直接拉到239.5周期牺牲一点速度换稳定度。ADC参数配置页的具体设置如下Continuous Conversion ModeEnable连续转换模式Discontinuous Conversion ModeDisable不启用不规则采样Number of Conversions3对应3个通道Rank里的三个通道依次设置为Channel0、Channel1、Channel2Trigger软件触发即可不需要定时器触发DMA Continuous RequestsEnable这个很关键后面细说End of Conversion SelectionEOC flag at end of each conversion这些参数解释一下连续转换模式开启后ADC会不停地依次转换三个通道转换完一轮自动开始下一轮不需要手动触发。这时开启DMA搬运DMA就会持续把每个转换结果搬到内存形成不间断的数据流。2.2 DMA配置的坑Data Width必须和ADC寄存器对齐切到DMA设置页添加ADC1的DMA请求。这一步有门槛我见过很多新手在这里栽跟头。DMA参数的几个核心选择ModeCircular循环模式DirectionPeripheralToMemory外设到内存Peripheral Increment AddressDisable外设地址固定Memory Increment AddressEnable内存地址自动递增Peripheral Data WidthHalf Word半字16位Memory Data WidthHalf Word半字16位PriorityHigh如果DMA有多个通道在使用按实际需求分配优先级为什么Data Width要选Half Word因为STM32的ADC是12位分辨率数据寄存器是16位宽DMA搬运时如果按Byte8位搬只能搬低8位高4位全丢数据直接错掉。反过来如果选Word32位一次搬4个字节寄存器只有2个字节同样会错位。另一个关键点是DMA Continuous Requests必须Enable。如果不开启ADC每次转换完一轮三个通道各转一次后DMA就停止搬运必须等下一次触发才能继续。只有开启这个选项DMA才能持续地从ADC寄存器搬运每个转换结果真正做到连续。我在配置F103时验证过不开启DMA Continuous Requests只有第一轮三个通道的数据进缓冲区后面ADC还在转但DMA不搬了缓冲区永远是那三个值。开启后缓冲区会不断刷新。2.3 缓冲区数组的合理性设计长度不是随便填的DMA要往内存里搬运数据必须有接收数据的缓冲区。在代码里定义一个数组#define ADC_CHANNEL_NUM 3 #define ADC_BUFFER_LEN 30 // 10轮转换的数据每轮3个通道 uint16_t adc_buffer[ADC_BUFFER_LEN];缓冲区长度怎么定两种常见策略策略一缓冲区长度等于通道数。数组长度3每轮转换完DMA把三个通道的数据搬进去然后从头开始覆盖写入。这种模式下你要在DMA传输完成半传输或完全传输中断里及时读走数据否则缓冲区的内容会被下一轮数据覆盖。适合采样率低、CPU能及时响应的场景。策略二缓冲区长度远大于通道数。比如设30个相当于缓存10轮采样结果。这种模式下即使CPU忙了一段时间没去读数据也能在缓冲区里保留一段时间不会立即丢失。适合采样率高、CPU任务重的场景。我实际项目中用的是策略二。原因很朴素采样率5kHz每轮三个通道DMA持续搬运如果缓冲区太短CPU稍一打盹数据就被覆盖了缓冲区拉长后CPU什么时候抽出时间来读都行只要处理够快就行。代码里把数组定义成全局变量因为DMA需要直接访问这个内存区域局部变量在栈上不能保证地址连续和DMA可访问性。这一点在HAL库的示例代码里都是全局数组不是没有道理的。3. 数据从DMA出来之后多通道排序、滤波与标度变换3.1 缓冲区里的数据顺序扫描模式的固定规律DMA把数据搬进数组后数组里的顺序是固定的按你在CubeMX里配置的Rank顺序排列。比如Rank1是Channel0PA0Rank2是Channel1PA1Rank3是Channel2PA2那么数据顺序就是 [ch0, ch1, ch2, ch0, ch1, ch2, ...] 这样循环。这个规律看穿了很简单但实际操作中容易混乱。有个朋友在论坛上问过为什么ch1的数据在数组里看起来像随机数仔细一看才发现他把物理通道接反了PA1的电平变化在数组里对应的却是ch2的位置。处理方式就一句话索引对号入座。假设缓冲区长度30每3个数据一组取第0、3、6...个是ch0第1、4、7...个是ch1第2、5、8...个是ch2。写个循环把每路数据取均值uint32_t sum[ADC_CHANNEL_NUM] {0, 0, 0}; uint16_t avg[ADC_CHANNEL_NUM] {0, 0, 0}; for (int i 0; i ADC_BUFFER_LEN; i) { sum[i % ADC_CHANNEL_NUM] adc_buffer[i]; } for (int i 0; i ADC_CHANNEL_NUM; i) { avg[i] sum[i] / (ADC_BUFFER_LEN / ADC_CHANNEL_NUM); }如果每个通道要单独记录多组数据建议用二维数组uint16_t adc_buffer[CH_NUM][GROUP_NUM]DMA目标地址直接指向二维数组首地址每一组数据连续写入。这种方式用于多通道波形同时记录时非常清晰。3.2 均值滤波不能防止的坑毛刺信号与坏值剔除均值滤波是电压采样最常用的处理手段但踩过几次坑后我要说均值滤波只能平滑随机噪声对偶发的毛刺几乎无能为力。典型场景某个电机启动时电源线上产生一个尖峰干扰ADC正好采到这个尖峰数值从1024直接跳到3000。如果做简单平均这个毛刺会被摊薄但平均值仍然会被拉高不少尤其在采样条数较少时影响更明显。我处理毛刺的办法是叠加中值滤波先对当前轮的几个采样值排序去掉最大值和最小值再对剩余值求平均。这种去极值平均用在电压采样上效果极好uint16_t mid_filter(uint16_t *data, int len) { // 复制一份做排序不修改原数据 uint16_t temp[8]; for (int i 0; i len; i) temp[i] data[i]; // 简单冒泡数据量小性能足够 for (int i 0; i len - 1; i) { for (int j 0; j len - 1 - i; j) { if (temp[j] temp[j 1]) { uint16_t t temp[j]; temp[j] temp[j 1]; temp[j 1] t; } } } // 去掉头尾各1个极值剩余求平均 uint32_t sum 0; for (int i 1; i len - 1; i) { sum temp[i]; } return sum / (len - 2); }这里设计懒人专用的滤波器圈数少用临时数组排序不占用额外大内存。每组5个采样值去2个极值再平均抗毛刺能力比纯均值强很多。不过要注意去极值平均不适合真实信号本身就有突发变化的应用比如要捕捉电压跌落瞬间那种场景反而不能滤波要保原始数据。3.3 从ADC原始值到真实电压标度变换与校准ADC转换出来的是数字量12位分辨率对应0~4095满量程对应参考电压。STM32F103的内部参考电压是3.3VVDDA供电电压所以理论上电压值 ADC值 × 3.3 / 4095用浮点计算直接套公式就行float adc_to_voltage(uint16_t adc_val) { return (float)adc_val * 3.3f / 4095.0f; }但这里有个精度问题容易忽略VDDA不一定是标准的3.3V。如果板子是用USB的5V经过LDO降到3.3V实际VDDA可能是3.28V或3.32V用标称3.3V算出来的电压会有零点几伏的偏差。更高精度的做法是用内部参考电压VREFINT校准。STM32内部有个固定的1.2V参考电压通道可以通过ADC测量VREFINT的实际ADC值反推出真实的VDDAfloat calibrate_vdda(uint16_t vrefint_adc) { return 1.2f * 4095.0f / (float)vrefint_adc; }然后把校准后的VDDA带入电压换算公式。很多开源项目比如无人机飞控、电池检测仪都是用这个方法保证电压测量精度的。实操上在CubeMX里勾选VREFINT作为ADC的一个通道软件启动后先采一次VREFINT值完成自校准后面所有通道的电压换算都基于校准后的VDDA。我用这个方法测试过未校准时测3.3V基准源读数为3.29V左右校准后读数3.299V精度到了3位小数级别对于一般嵌入式电压监测完全够用。4. 循环模式与单次模式的实际切换我在跑批采样时踩过的坑4.1 缓冲区数据一直是零的排查链路很多初学者配置完ADC-DMA后读缓冲区发现全是0第一反应是ADC没启动或者DMA没配置对。我调试时也踩过同样的坑这里分享完整的排查链路第一步确认ADC是否真的在转换。直接在调试模式下暂停看ADC数据寄存器ADC1-DR的值。如果DR寄存器在变化说明ADC工作正常问题出在DMA搬运环节如果DR一直是0说明信号没进来或ADC没启动检查引脚复用配置和输入电压。第二步确认DMA是否在搬运。看DMA的CNDTR寄存器这是DMA当前剩余要搬的数据量。如果CNDTR的值一直在变小比如从30变到21再变到12说明DMA正在工作如果CNDTR没变说明DMA没有被触发去检查DMA请求是否使能、ADC的DMA请求信号是否开启。第三步确认数据是否真正进入缓冲区。在调试窗口watchadc_buffer数组看数值是否在更新。如果寄存器正常但数组不更新多半是DMA的内存地址配置不正确——内存地址增量关闭了所有数据写到同一个地址。这三步逐一排查基本能定位90%的问题。我最常遇到的是第三步CubeMX默认Memory Increment Address是Disable忘了改成Enable导致DMA每次都把数据写到数组第一个位置看起来就像只有第一个值在变。4.2 CPU从DMA缓冲区读数据时的同步策略DMA在后台不停往数组里写数据CPU在读数组时如果不加同步措施可能读出半个新半个旧的错乱数据。比如DMA写到数组第15个位置时CPU开始读第12~14个位置读到的前两个是上一轮数据后面是新一轮数据本来应该是同一轮采样的数据却来自不同时刻。业界通用的解法是利用DMA的半传输中断Half Transfer和全传输中断Transfer Complete。DMA循环模式下缓冲区被分成两半前半段写满了就触发一次半传输中断后半段写满了触发一次完全传输中断。CPU收到半传输中断时说明前半段数据已经完整填好后半段正在写入这时CPU可以安全读前半段收到完全传输中断时说明后半段数据完整前半段正在被覆盖CPU读后半段。代码框架长这样volatile uint8_t adc_data_ready 0; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { if (hadc-Instance ADC1) { // 后半段数据就绪 adc_data_ready 1; } } void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) { if (hadc-Instance ADC1) { // 前半段数据就绪 adc_data_ready 1; } } // 在业务代码里 if (adc_data_ready) { adc_data_ready 0; // 读取对应半区的数据 }用HAL库的话需要在CubeMX里同时使能ADC的DMA Half Transfer和Transfer Complete中断。HAL库的HAL_ADC_Start_DMA()启动后自动管理这两个回调。这个策略的核心是CPU永远只读DMA没在写的那一半数据从机制上杜绝了读写冲突。我在实际项目里用这个策略跑5kHz采样率连续跑72小时没有出现一次数据错乱。4.3 单次模式下如何手动触发一轮转换并等待完成有些场景不需要连续采样只希望在某一个时刻采一轮电压。比如电池电量检测每秒检查一次就够了。这时可以用单次模式DMA的方式void capture_once() { HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_buffer, ADC_BUFFER_LEN); // 等待DMA传输完成超时保护防止死等 uint32_t timeout 10000; while (HAL_ADC_GetState(hadc1) ! HAL_ADC_STATE_REG_EOC timeout--) { // 等待 } HAL_ADC_Stop_DMA(hadc1); }注意HAAL库的HAL_ADC_Start_DMA()启动后ADC自动连续转换DMA连续搬运。如果你只想采一轮在等待缓冲区的N个数据都收到后立刻调用HAL_ADC_Stop_DMA()停住。但有个细节如果CubeMX里开启了DMA Continuous Requests单次模式下停DMA后ADC可能还在跑。这里要处理一下用软件停止转换void stop_adc_conversion() { HAL_ADC_Stop_DMA(hadc1); __HAL_ADC_DISABLE(hadc1); }顺序是先停DMA再禁ADC保证DMA不再搬运的同时ADC也停止转换。这个顺序反了会导致ADC还在转换但数据没人搬下次启动时可能出现第一个数据异常。我测试过单次模式的启动延迟从调用Start到第一组数据就绪大约需要70~100μs取决于采样周期设置对每秒一次的低频采样完全够用。如果对时序有严格要求建议改用定时器触发ADC转换DMA负责搬运时基精度远高于软件切换。5. 顺着ADC-DMA往下走SPI、串口收发与双缓冲的通用套路5.1 DMA位宽与缓冲区大小的匹配关系不止ADC适用ADC-DMA踩过的坑很多在SPI-DMA和UART-DMA上也会遇到底层逻辑完全一致。我总结了一个通用口诀DMA的每次传输都是外设寄存器宽度 内存宽度的组合两边必须一致。ADC数据寄存器16位DMA传输宽度Half WordSPI数据寄存器8位或16位可配置DMA对应Byte或Half WordUART数据寄存器8位DMA对应Byte缓冲区数组的类型也必须匹配ADC用uint16_t数组串口用uint8_t数组SPI根据数据帧格式可能是uint8_t或uint16_t。混合使用时最容易出问题。比如同一时刻ADC用的是Half Word DMA串口发送用的是Byte DMA两条DMA通道的数据宽度不同遇到数据错位时先检查宽度配没配对。我的调试习惯是每个DMA通道的Memory和Peripheral宽度都必须显式设置不要依赖默认值因为CubeMX在不同版本上的默认值未必完全一致。5.2 双缓冲机制比半传输中断更高阶的数据安全方案常规DMA循环半传输中断已经能解决大部分同步问题但有个弱点CPU处理速度不够快时比如要做浮点滤波或者协议解析数据量一大CPU处理前半段的时间超过了DMA写完整个缓冲区的时间前半段还没处理完就已经被新一轮覆盖了。双缓冲Double Buffer就是为了根治这个问题设计的两个缓冲区交替使用DMA正在往缓冲区A写入时CPU处理缓冲区B的数据A写满了自动切到B这时CPU切换到处理A。DMA永远不会覆盖CPU正在读的缓冲区。STM32F4以上的DMA控制器原生支持双缓冲模式F103系列不支持DMA硬件不支持但可以通过两个缓冲区中断切换模拟。F4上启用双缓冲的CubeMX配置是DMA Mode设为Double Buffer指定Memory0和Memory1两个地址。F103上模拟双缓冲的软实现也不复杂。定义两个缓冲区DMA先指向buffer0半传输中断发生后把DMA目标地址切换到buffer1同时清空之前缓冲区的数据。HAL库里可以用HAL_DMAEx_ChangeMemory或在中断里调用HAL_ADC_Stop_DMAHAL_ADC_Start_DMA重新指向新缓冲区。当然这个操作会引入短时停顿采样率太高时不建议。我在F407上做过测试用原生双缓冲模式跑8路ADC采样率20kHz连续采集波形数据并通过串口上传PC绘图CPU占用率只有20%左右数据一帧不丢。5.3 串口DMA发送的坑上一次没发完下一次就发不出去ADC-DMA配置熟练后做串口发送时很容易顺手也用DMA。UART-DMA同样有个经典坑HAL_UART_Transmit_DMA()调用后如果上一次数据还没发完紧接着调用下一次发送会返回HAL_BUSY导致数据发不出去。我踩过这个坑的场景很典型ADC采完一轮电压希望通过串口DMA发送到上位机主循环每100ms采一轮发一次。结果跑着跑着某个时刻开始数据不刷新了后来定位到是串口DMA还在发送上一帧数据时主循环又尝试发送新帧直接被HAL挡回来。解决方式是用回调volatile uint8_t tx_done 1; void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart-Instance USART1) { tx_done 1; } } void send_voltage_data(uint8_t *data, uint16_t len) { if (tx_done) { tx_done 0; HAL_UART_Transmit_DMA(huart1, data, len); } }核心思想只有上一次发送完成才发起下一次发送数据从源头就不会堆积。如果你需要连续高频发送缓冲区还得做成队列发送回调里自动填充下一帧。这是ADC-DMA UART-DMA组合式项目中必踩的坑提前预防能省一天调试时间。6. 一个多月用下来的调试心得优先级、稳定性与性能边界6.1 DMA中断优先级的蝴蝶效应改一个数字引发蝴蝶效应DMA搬运本身不打断CPU但DMA半传输/全传输中断会。这个中断的优先级设置直接影响整个系统的实时性。有过一次深刻教训在一个同时跑ADC-DMA和PWM控制的工程里我把DMA中断优先级设成了最低结果采样率一高DMA中断迟迟得不到响应数据缓冲区被覆盖电压采样值出现周期性跳变。这类问题从表面看像ADC精度问题实际上根因是中断响应不及时。反过来如果把DMA中断优先级设得非常高又可能抢占PWM中断导致PWM波形偶发抖动。所以中断优先级必须结合整个系统的实时性需求统筹分配中断源优先级建议理由控制环路PWM/PID最高时序敏感延迟直接导致控制失效DMA传输完成次高数据不能丢延迟会导致覆盖串口接收中不丢字节即可可容忍一定延迟按键/显示刷新低人机交互对延迟不敏感这个分配原则在多个工程上验证过基本稳定。唯一需要微调的是当你有两路DMA同时工作时比如ADC采集串口DMA发送串口DMA的优先级建议低于ADC的DMA因为ADC数据是周期性的丢了要等下一轮而串口发送即使稍微延迟也在可接受范围。6.2 采样数据偶发跳变别只盯着软件先拿示波器测测电源做电压采样的实战多了之后我对软件完美还是数据飘这件事越来越谨慎。有一回在客户现场调试一块板子ADC-DMA配置完全正常滤波算法也加了但其中一个通道的值偶尔跳几十个LSB。软件排查了两天没找到原因最后用示波器一测发现该通道输入引脚上叠加了一个1.2MHz的振荡示波器看是纹波ADC采出来就是跳变的数字量。从那以后我形成了一个固定动作在怀疑软件之前先拿示波器看ADC引脚的波形。如果引脚波形干净再往软件排查如果波形本身就脏滤波算法再强也白搭。干净信号的标准在ADC输入引脚上并联一个0.1μF的陶瓷电容到地抗高频干扰再串联一个100Ω~1kΩ的电阻限制进入采样电容的瞬间电流基本能滤掉大部分高频噪声。另外STM32 ADC还有采样平均Oversampling功能部分型号支持硬件级多次采样平均把16位平均结果存入寄存器。如果MCU支持这个功能性价比很高——不需要软件做均值滤波CPU零开销而且对随机噪声的抑制比软件均值更好。6.3 这套方案的性能边界和下一步还能怎么玩用F103 72MHz实测ADC-DMA循环模式能稳定跑多高的采样率我自己的测试结果单通道条件下采样周期设为1.5周期12周期转换时间等效采样率约500kHz但此时DMA中断非常频繁CPU基本没时间干别的。实际项目里我建议的稳定区间是多通道10kHz以内CPU负载可控制在10%以内。如果你需要更高的采样率或者更低的CPU占用通常有三个演进方向第一个方向是上更高性能的MCU比如STM32G4系列内置硬件过采样可以在同样采样率下提升分辨率或者同样分辨率下降低中断频率。第二个方向是用ADC的定时器触发模式代替软件触发。定时器PWM输出可以精确控制采样时刻配合DMA实现同步采样全天候搬运适合做三相电压/电流的同步采集。CubeMX里把ADC Trigger选为Timer Trigger即可时基精度远高于软件切换。第三个方向是上外部ADC芯片比如ADS127L11这类高精度工业ADC通过SPI接口DMA读数据。热词里能看到这个组合说明这个方向已经有不少人在做了复杂度主要是外部ADC的配置和时序协议处理但换来的是更高的采样率和精度。对了最后分享一个调采样电路的小技巧配置完ADC-DMA后用一块干净的稳压电源比如干电池接到ADC输入观察输出值的变化。如果你能看到稳定的数字量比如干电池1.5V对应1860左右的ADC值说明采样链路从头到尾已经通了。剩下的就是在这个基础上做电压标定和精度微调。这一步能帮你在实际项目联调之前先把底层的地基打牢。
返回列表