用STM32搭建低成本功耗分析采集系统 📅 2026/8/26 6:04:16 拿着MCU做功耗分析采集Power Analysis Capture这件事我一开始是拒绝的。功耗轨迹采集这种活儿听起来就该是示波器或者几万块的专用设备来干。直到真有项目需要低成本、可自动化地抓取几千条功耗轨迹时我才认真操起一颗STM32把整个采样链路从零打通才发现这条路不仅走得通踩完坑之后甚至比想象中顺。这篇博文想和你聊的就是怎么用一颗普通MCU搭出一套能实际干活的功耗分析采集系统。它解决的核心问题是在没有昂贵示波器和商用侧信道采集设备的情况下依然能拿到干净、同步、可重复的功耗轨迹支撑后续的SPA/DPA分析或者低功耗调试。这套东西特别适合三类人做嵌入式安全入门、想验证功耗分析整套流程的研究者做低功耗产品开发、需要观察设备电流波形的硬件工程师以及纯粹想搞明白功耗到底怎么被数字化的爱好者。整个链路成本大概几十块钱核心材料就是一块常见的MCU开发板、一颗采样电阻、一个仪表放大器和一根USB线。1. 项目整体设计与思路拆解1.1 功耗分析到底在采什么信号先说清楚一个容易被忽略的事实功耗分析采集的目标不是目标板平均电流多大而是电流随时间变化的微小波动。芯片内部CMOS电路翻转时会从电源吸取瞬态电流这个瞬态电流的幅度、持续时间、发生时刻都和芯片内部正在处理的数据强相关。比如AES加密里的S-box查表操作输出字节的不同会导致内部总线翻转数量不同宏观上就表现为功耗波形上幅度差几个百分点甚至千分之几的差异。所以一套功耗采集系统本质上解决三件事第一把电流变化线性地转换成电压信号第二用足够高的采样率和分辨率把这个模拟电压数字化第三在数据流中精确标记触发时刻让每条轨迹能从同一起点切开。我做的这套方案就是围绕这三个环节逐步展开的。任何一个环节做不好后续的DPA统计都会直接废掉这也是我在这篇文章里会把每个环节单独拆开讲的原因。1.2 为什么选MCU而不是示波器示波器当然是最直觉的方案探头往采样电阻两端一夹设置好触发电平一条完整的功耗波形就出来了。但示波器做功耗采集有几个特别难受的痛点。第一是贵尤其是采样率上GHz的型号价格普通人很难接受第二是自动化能力弱DPA分析往往需要采集几百上千条轨迹用示波器手动操作简直要命即使带远程控制接口脚本化抓取的效率也远不如专用设备第三是体积和功耗示波器注定只能放在实验台上没法变成一个可以塞进产品的测试模块。MCU方案的优势恰好对冲着这些痛点。成本上一颗带高速ADC的MCU只要几十块部署上采样前端加MCU加USB串口整个采集系统能缩到拇指大小自动化上USB连上电脑Python脚本一跑就能自动抓几千条轨迹并落盘。当然它也有自己的短板采样率上不去常见MCU的ADC最高也就是几Msps到十几Msps带宽超过几十MHz的信号指望不上。但如果你分析的对象是8MHz甚至16MHz以下的芯片MCU方案的采样率绰绰有余。1.3 系统架构与指标需求拆解我的系统分为三个模块目标板、采集板、上位机。目标板就是被测对象运行一段固定算法比如软件AES-128并在加密开始前输出一个GPIO触发信号采集板是一颗STM32F407负责把目标板的电流波形连续采样下来同时监测触发信号的到来上位机是一个Python脚本从串口接收采样数据流按触发时刻切分轨迹保存并绘制波形。在设计之初我给自己定了几个硬性指标采样率不低于1.6MspsADC分辨率12bit起步单条轨迹最大能存4096个采样点支持连续自动采集1000条轨迹不丢数据。这些指标不是拍脑袋定的而是结合AES-128在8MHz目标芯片上的实际运行周期估算出来的一次完整加密大约需要几百微秒我要求单条轨迹覆盖整个加密过程所以单条长度至少几千个点按1.6Msps算就是几百微秒的窗口足够覆盖了。2. 硬件设计从采样电阻到运放选型2.1 采样电阻取值怎么算采样电阻是整个采集系统的信号源头它的作用是像电流表一样串在目标板的供电回路上利用欧姆定律把电流变成电压。阻值选多大是个典型的权衡问题电阻太小信号幅度不够ADC量化后只剩几十个LSB看不出来什么细节电阻太大目标板的供电等效内阻升高瞬态电流一大供电电压就跌落轻则波形失真重则目标板直接复位。我踩过最典型的坑是第一次直接用10欧姆电阻去采一块峰值电流100mA的板子结果加密跑到一半电压被拉低了0.5V以上目标板当场复位。后来换成了0.1欧姆峰值压降只有10mV对3.3V供电完全没影响。但0.1欧姆在50mA电流下只有5mV输出信号这就要靠后面的运放把信号放大到ADC能充分量化的区间。如果你测的是微安级的低功耗设备0.1欧姆产生的信号就太小了那时可能得换成TIA跨阻放大架构这个话题可以另写一篇。还要注意采样电阻本身的寄生参数。高频场景下有引脚电阻的寄生电感会和电路中的寄生电容形成谐振波形上出现振铃。所以尽量选贴片电阻并且让采样电阻紧贴目标板的电源引脚环路面积越小越好。我最终用的是一颗2010封装的0.1欧姆贴片电阻温漂系数也很低长时间采集时波形基线不会漂移。2.2 运放选型与ADC接口设计采样电阻出来的信号只有几毫伏到几十毫伏ADC的满量程一般是3.3V或2.5V中间必须加一级放大。运放选型时我重点关注四个参数增益带宽积、压摆率、输入失调电压和噪声密度。增益带宽积决定了你在这个增益下能处理的信号最高频率。比如我设定增益20倍信号带宽1MHz那么运放的GBW至少要20MHz以上用INA240这类电流检测放大器就非常合适它是专门为低端电流检测设计的失调电压小、增益精度高。有一点要提醒仪表放大器虽然方便但它的带宽普遍不如普通高速运放。如果目标是更高采样率或者需要捕捉非常窄的功耗尖峰可以改用OPA836这类高速运放搭配精密电阻搭建差分放大电路代价是电路复杂度和噪声都会上升。运放输出到ADC输入之间我加了一个RC低通滤波器通常取22欧姆串联电阻加100pF对地电容。这个滤波器有两个作用一是抗混叠滤掉高于奈奎斯特频率的高频噪声二是隔离防止ADC输入端采样电容的电荷注入效应影响到运放的稳定性。很多人在这一步偷懒不加结果波形上出现奇怪的开关噪声就是这个原因。2.3 MCU选型与ADC关键参数我选的STM32F407主要看中三点ADC是12bit单ADC理论最高采样率约2.4Msps有完整的DMAUSB和串口都够用。如果你要选其他MCU判断标准可以简化成三条ADC分辨率至少10bit、最好12bit以上ADC采样率至少1MspsDMA是必须的没有DMA就意味着CPU每采一个点都要去读一次数据寄存器根本扛不住连续采样。这里有个容易被忽视的参数ADC的采样保持时间。STM32的ADC可以配置采样时间从3个周期到480个周期采样时间越短理论上转换速度越快但内部采样电容充电时间不够会导致测量值偏低而且噪声增大。我实测下来在ADC时钟30MHz的情况下3个周期采样时间能做1.6Msps稳定采集但有效位数只能到10bit左右如果肯牺牲采样率把采样时间拉到15个周期噪声会明显下降。做功耗分析时别一味追求最高采样率稳定性和有效位数更关键。2.4 触发信号设计与同步机制触发是整个采集系统中设计不好最容易翻车的地方。示波器是连续扫描、等到触发条件满足再保存数据MCU方案也类似但需要把触发信号的引入方式规划好。我的做法是在目标固件里AES加密开始时把D10拉高加密结束后拉低这个信号直接连到采集板的一个带输入捕获功能的GPIO。采集板的固件用定时器输入捕获监听这个信号检测到上升沿时记录下当前ADC的采样计数值上位机以这个计数值为基准切分轨迹。这种做法的触发抖动非常小一般不超过1到2个采样周期对齐质量远比比较器触发的方案好。如果目标板上没法改固件、拿不到触发信号也有替代方案电流触发。把采集到的放大信号经过一颗比较器和阈值电压比较电流一旦超过设定阈值就产生触发脉冲。但阈值穿越点的噪声会让触发时刻产生几十纳秒到几百纳秒的随机抖动直接影响轨迹对齐精度。所以能用GPIO触发就尽量用GPIO触发这是我在项目里反复验证过的经验。3. 固件实现核心代码与调参过程3.1 ADC时钟、采样率与定时器触发配置STM32F407的ADC时钟挂在APB2总线上我通过ADC预分频器把ADC时钟配置为30MHz这个值在数据手册规定的上限36MHz以内。然后配置定时器2作为ADC的触发源产生周期性的触发事件。系统主频80MHz定时器预分频设为24得到3.2MHz的计数时钟自动重装值设为1这样更新事件频率就是3.2MHz/21.6MHz也就是每秒触发1.6M次ADC采样。在1.6MHz触发频率下ADC每次转换必须在625ns内完成。我配置采样时间为3个ADC周期加12.5个周期的逐次逼近转换总耗时15.5个周期按30MHz算约517ns刚好小于625ns留有一定余量。核心配置大概是这样// 定时器2触发事件配置PSC24, ARR1, 更新事件频率1.6MHz TIM2-PSC 24; TIM2-ARR 1; TIM2-CR2 | TIM_CR2_MMS_1; // 更新事件作为TRGO输出 TIM2-CR1 | TIM_CR1_CEN; // ADC1配置外部触发上升沿通道0 ADC1-CR1 0; // 独立模式单通道 ADC1-CR2 ADC_CR2_EXTEN_RISING | ADC_CR2_EXTSEL_2; // 选择TIM2 TRGO ADC1-SQR3 0; // 采样通道0 ADC1-SMPR2 0; // 通道0采样时间3周期初学的时候很容易忽略的一点是ADC的外部触发极性必须设置正确而且EXTEN位和EXTSEL位是配合使用的搞错一个采样就完全不动或者频繁误触发。我自己调试时也花了不少时间看参考手册的Figure和寄存器位定义才搞清楚。3.2 DMA双缓冲与中断处理ADC连续采样会产生海量数据必须靠DMA自动搬进内存。我的设计是使用DMA2把ADC1的数据寄存器映射到内存里两个缓冲区BufferA和BufferB各4096字节可以存2048个16bit采样点。DMA配置为循环模式自动在BufferA和BufferB之间交替填充。当DMA填满一个缓冲区并切换到另一个缓冲区时会触发传输完成中断。我在中断回调里拿到填满的那个buffer的指针和长度然后通过串口或者USB把这段数据打包发送给上位机。这样做的好处是采样和传输完全并行DMA继续往另一个buffer灌数据CPU只需要在中断里搬运已经稳定下来的数据不会阻塞采样过程。这个机制里最容易出问题的点是缓冲区大小和传输速度的匹配。在1.6Msps采样率下2048个采样点约1.28ms填满也就是说每1.28ms就要把一段数据发出去。USB虚拟串口的实际吞吐约1MB/s发4096字节大概4ms看起来有余量但操作系统和上位机的调度延迟会导致发送请求排队所以我在上位机里用了较大的接收缓冲区和独立线程读取避免背压丢数据。3.3 轨迹切分与时间戳记录上位机收到的是一条源源不断的采样流怎么在流里准确切出每一条轨迹我的方案是采集板在检测到触发信号上升沿时额外发送一个触发包里面包含当前DMA缓冲区的序号和当前采样点的偏移地址。上位机收到触发包后以触发包携带的位置信息为起点向后截取固定数量的采样点作为一条轨迹。这种方式的切分精度主要取决于触发信号和ADC采样时钟的相位关系。如果目标板的加密操作和采集板的触发信号完全同步那么切分误差能控制在1个采样点内。但如果存在异步时钟漂移长期采集时轨迹起点会缓慢偏移这时候就需要上位机做后处理对齐。我常用的做法是对每一条轨迹做一次峰值检测把S-box的第一个大功耗脉冲对齐到固定位置效果比纯靠触发包好很多。3.4 上位机数据接收与存储上位机我用的Pythonpyserial读串口按帧协议解析。协议定义很简单帧头0xAA552字节长度一串采样数据最后2字节CRC16校验。加CRC是因为USB串口偶发丢字节时光靠帧头无法保证数据完整性坏轨迹一旦混进DPA数据集里统计结果就废了。实测下来CRC校验能拦住几乎所有传输错误。采样数据保存时我直接存成二进制npy格式每份文件存一个数组形状是(轨迹数, 采样点数)。耗时操作是绘制波形matplotlib画一条轨迹还好画几百条叠加就吃力了所以我通常先做叠加平均再画图这样既能反映数据形态又省时间。如果你要做DPA建议直接把原始轨迹保存好分析模型放在另一个脚本里做。4. 实测过程、结果分析与问题排查4.1 目标板环境与实验流程测试目标我用的是Arduino UnoATmega328P跑在8MHz固件里实现了一个软件AES-128。每次加密开始前D10引脚拉高加密结束D10拉低。目标板的VCC引脚接入0.1欧姆采样电阻采样电阻两端的差分信号经过INA240放大20倍后进入采集板ADC。实验流程很简单PC通过串口向采集板发一个开始采集命令采集板随后通过另一根IO线向目标板发送开始加密的脉冲同时开始监听触发信号。目标板收到命令后执行一次AES加密加密期间采集板持续采样检测到触发沿后继续采集4096个采样点然后打包上传。整个过程由上位机脚本循环控制一次跑1000次采集大概需要十几分钟全部自动完成。4.2 波形观察与SPA特征我先把单条轨迹画出来能看到明显的功耗轮廓加密开始后前几十微秒是一串幅度较小但很密集的脉冲对应密钥扩展和轮密钥加操作接着会出现几个幅度突出的大脉冲这是S-box查表操作的典型特征因为查表访问了不同地址总线和存储器翻转活动集中爆发。如果改变明文的一个字节再叠加上去能勉强看到某些采样点幅度有变化但单个点差异实在太小肉眼基本分辨不出来。对同一条明文重复加密100次并做平均噪声显著降低这时可以看到更多波形细节。把不同明文的平均值叠加对比S-box区段的波形明显错开这就是SPA里最直观的数据依赖现象。实际操作中这个现象常常用来验证采集系统是否正常工作如果改变明文后波形完全不动说明信号没采到或者增益太低如果波形变化过于剧烈那可能是运放饱和了需要降低增益。4.3 SNR评估与采集质量判断评估采集系统质量我常用一个简单指标固定明文加密1000次算出每个采样点在1000次采集中的标准差取平均作为噪声水平再换1000条不同明文的轨迹算出每个采样点的均值变化范围作为信号水平。两者相除就是信噪比。我的系统在1.6Msps下测出来大约15dB到20dB之间足够支撑基础SPA和简单DPA。如果SNR低于10dB做DPA的时候分组统计就会很不稳定得先排查噪声来源。降低噪声有几个立竿见影的手段减小采样电阻到MCU之间的飞线长度、给运放和ADC分别加去耦电容、检查电源是否干净。我在实验中发现USB供电的5V噪声会直接串到采样结果里后来改成用锂电池供电之后波形干净了一大截。4.4 常见问题与排查技巧实录调试这套系统过程中我遇到了不少典型的坑整理成一个速查表希望能帮你省点时间现象可能原因解决办法波形毛刺特别大采样电阻环路面积过大引入电磁干扰缩短采样电阻与目标板电源间的连线尽量靠近引脚轨迹起点明显错位触发信号与ADC采样时钟异步改用GPIO触发并记录采样计数上位机再做峰值对齐USB传输偶发丢数据串口背压或帧校验失败加大上位机缓冲区使用独立读取线程必须加CRC校验实际采样率达不到配置值ADC转换时间超过触发间隔缩短采样周期或降低采样率确保转换时间小于触发周期波形看着在但幅度特别小运放增益不够或采样电阻太小增大采样电阻注意压降或提高运放增益相同明文多次采集波形却差异大目标板供电不稳或采样电阻发热检查电源纹波改用低温度系数电阻排查时我习惯先看原始ADC码流确认采样率是否正常然后看固定明文多次采集的叠加波形确认噪声水平最后才看不同明文的差异确认信号有效性。这个顺序能快速定位问题出在采集链路还是目标板本身。注意在做任何对比分析之前先确认两条轨迹是不是从同一个触发点切开的。轨迹没对齐后面的统计全是白算。我常常在脚本里先做一个简单的相关性对齐再进入正式分析流程。这个项目整体做下来我的体会是功耗采集的硬件门槛真不高但工程细节决定成败。采样电阻的位置、运放的去耦、触发信号的同步每一步看着不起眼积累起来就是波形质量的巨大差异。如果你也准备做一套建议从我的配置出发先跑通全链路再根据你的目标芯片调整采样率和增益。最后分享一个小技巧刚开始调试时不要急着做DPA先用固定明文和随机明文各采几十条对比平均波形和SNR只有这一步稳定了后面的分析才是可靠的。