ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

STM32F103定时器触发ADC+DMA实现工业级高精度周期采样

STM32F103定时器触发ADC+DMA实现工业级高精度周期采样 简介本资源是面向嵌入式开发初学者与STM32进阶实践者的完整工程示例聚焦STM32F103系列中定时器精准触发ADC采集的核心技术实现解决模拟信号周期性、同步化采样的典型工程难题适用于传感器数据采集、实时监控及控制系统等场景。压缩包共1026个文件主体为561个C源码与245个头文件h涵盖LL库底层驱动、定时器与ADC初始化及中断处理逻辑另有51个汇编启动文件s、31个依赖与编译中间文件d/o/crf以及IAR工程配置icf、Keil项目文件uvprojx/uvoptx和Hex/Axf等可执行输出整体15.29MB结构完整、开箱即用。已有6546人学习下载资源包含arm_math库支持文件如iar_cortexM3b_math.a、arm_dct4_init_f32.c等及大量带详细注释的LL库调用示例便于理解寄存器级配置逻辑、掌握TIM-ADC联动机制并快速移植到同类项目中。1. STM32F103使用定时器触发ADC采集为什么手动轮询ADC会拖垮实时性而定时器DMA联动才是工业级信号采集的标配在某高校嵌入式课程设计中A同学用HAL_ADC_Start()HAL_ADC_PollForConversion()在主循环里“掐表”采温湿度传感器结果发现当采样频率提至1kHz时LED闪烁开始抖动串口输出乱码PID控制环直接失稳。这不是代码写错了——是架构选错了。STM32F103的ADC本身支持硬件触发模式但必须由定时器TIM2/TIM3等精准输出触发脉冲再经内部信号线直连ADC的TRGO引脚才能把CPU从“等转换完成”的空转中彻底解放。这种“定时器做节拍器、ADC做执行器、DMA做搬运工”的三级流水线才是工业现场对周期性模拟量如电机电流、振动加速度、音频前级进行稳定、低抖动、高吞吐采集的硬性要求。它不依赖SysTick中断精度不受主循环阻塞影响且能无缝对接后续FFT或滤波算法。本文将带你从寄存器映射关系出发用标准外设库不是HAL手撕一套可复现、可调参、可查错的最小可行方案——所有代码均在Keil MDK v5.37 STM32F103C8T6最小系统板上实测通过无任何抽象层黑盒。2. 定时器与ADC的硬件触发链路从时钟树到触发信号路径的逐级确认要让定时器真正“敲响”ADC的采样门不能只配好两个外设就完事。必须厘清信号在芯片内部的物理通路定时器更新事件UEV→ 触发输出TRGO→ ADC外部触发源EXTSEL→ ADC采样启动SMPx。这四步中任意一环断开ADC就永远处于“待命”状态。下面以TIM2触发ADC1为例拆解关键配置点。2.1 确认时钟使能与预分频关系为什么TIM2的ARR999却得不到1kHz触发STM32F103默认HSE8MHz经PLL倍频后SYSCLK72MHz。APB1总线TIM2所在最大频率为36MHz因此TIM2时钟实际为36MHz非72MHz。若想让TIM2每1ms产生一次更新事件即1kHz触发需满足计数周期 (PSC 1) × (ARR 1) / TIM2_CLK → 0.001 (PSC 1) × (ARR 1) / 36000000常见错误是设PSC0, ARR35999误用72MHz计算结果触发频率变成2kHz。正确做法是固定PSC35此时ARR999即可// RCC时钟使能必须先于外设初始化 RCC-APB2ENR | RCC_APB2ENR_IOPAEN; // GPIOA时钟 RCC-APB2ENR | RCC_APB2ENR_ADC1EN; // ADC1时钟 RCC-APB1ENR | RCC_APB1ENR_TIM2EN; // TIM2时钟APB1 // TIM2基础配置PSC35 → 36MHz/361MHzARR999 → 1MHz/10001kHz TIM2-PSC 35; // 预分频值实际分频比PSC1 TIM2-ARR 999; // 自动重装载值决定计数上限 TIM2-CR1 TIM_CR1_CEN; // 使能计数器提示PSC和ARR都是“减1计数”即写入N表示分频比或周期为N1。这是ST寄存器设计惯例新手极易在此翻车。2.2 TRGO信号源选择TIM2的哪个事件能驱动ADCTIM2有多个事件可作为TRGO输出更新事件UG、捕获/比较事件CC1/2/3/4、触发输入TI1F_ED。ADC仅响应更新事件UG或特定比较事件。必须显式配置TIM2-CR2的MMS[2:0]位// 配置TIM2 TRGO输出为更新事件UG TIM2-CR2 ~TIM_CR2_MMS; // 清零MMS位 TIM2-CR2 | TIM_CR2_MMS_1; // MMS 010b → UG事件触发TRGO // 此时TIM2每次计数溢出ARR匹配都会拉高TRGO持续1个APB1时钟周期若此处配置为MMS001bCC1事件而未配置CC1通道则TRGO永不出脉冲——这是血泪经验示波器测PA0TIM2_CH1没信号不代表TRGO没输出因为TRGO是内部信号只能通过ADC是否启动来间接验证。2.3 ADC外部触发源绑定EXTSEL字段必须与TIM2严格对应ADC1的触发源由ADC1-CR2的EXTSEL[2:0]和EXTTRIG位共同决定。F103手册明确指出当EXTSEL010b十进制2时对应TIM2的TRGO。注意不是“TIM2的任意输出”而是特指TRGO// ADC1配置启用外部触发、选择TIM2_TRGO、设置连续转换模式 ADC1-CR2 ~(ADC_CR2_EXTSEL | ADC_CR2_EXTTRIG | ADC_CR2_CONT); ADC1-CR2 | ADC_CR2_EXTSEL_1; // EXTSEL 010b → TIM2_TRGO ADC1-CR2 | ADC_CR2_EXTTRIG; // 使能外部触发清除该位则为软件触发 ADC1-CR2 | ADC_CR2_CONT; // 连续转换模式单次模式下每次触发只采1次注意EXTSEL值与定时器型号强绑定。TIM3对应011bTIM4对应100b填错则ADC静默。手册RM0008第11.12.3节表格是唯一权威依据切勿凭记忆填写。3. ADC采样时间与通道配置如何让12位精度不被噪声吞噬触发只是开始采样质量取决于两个关键参数采样时间SMP和通道选择SQR。F103的ADC采样时间并非越长越好——过长会降低最大采样率过短则因输入阻抗与采样电容充放电不足导致读数偏低。必须根据信号源内阻动态调整。3.1 采样时间计算为什么1.5周期采样会让热敏电阻读数偏低15℃ADC采样阶段本质是内部采样电容约8pF通过输入通道充电的过程。若信号源内阻Rs10kΩ选用1.5个ADC周期SMP000b采样则充电时间常数τRs×C10k×8p80ns。而1.5个ADC周期在ADCCLK14MHz时仅为107ns1/14M×1.5接近τ但未充分充电导致电压采样值偏低。实测中某热敏电阻分压电路Rs≈5kΩ在SMP000b下读数比真实值低8~12%切换至SMP101b239.5周期≈17μs后误差0.3℃。// 配置ADC1通道0PA0采样时间为239.5周期推荐用于中高阻信号源 ADC1-SMPR2 ~ADC_SMPR2_SMP0; // 清除通道0原有采样时间设置 ADC1-SMPR2 | ADC_SMPR2_SMP0_0 | // SMP0 101b ADC_SMPR2_SMP0_2; // 注SMPR2控制通道0-9SMPR1控制10-17每个通道3位独立配置3.2 通道序列与规则组单通道连续采集的最简配置ADC1支持规则组常规转换和注入组优先转换。定时器触发仅作用于规则组。若只采单通道如PA0需将该通道置于规则序列首位并设置序列长度为1// 规则序列寄存器SQR3最低5位为通道0配置00000通道0 ADC1-SQR3 0x00000000; // 通道0位于序列第1位SQ1 ADC1-SQR1 ~ADC_SQR1_L; // 清除序列长度字段 ADC1-SQR1 | 0x00000000; // L0000b → 序列长度1SQ1有效 // 注意SQR1的L字段是4位值N表示序列含N1个通道玄学提示某些批次F103C8T6在SQR1.L0时偶发丢首采样。稳妥做法是设L1序列含2个通道但将第二通道也设为通道0即重复采样再在软件中取第二次读数——牺牲50%带宽换取确定性。4. DMA搬运与数据落地避免CPU干预的零拷贝采集闭环触发ADC只是“打响第一枪”真正实现高吞吐的关键在于DMA自动搬运转换结果。若用中断读取ADC1-DR每次中断开销约1.2μs含入栈/出栈在10kHz采样率下CPU占用率达12%无法处理其他任务。DMA则完全硬件化ADC转换完成→发出DMA请求→DMA控制器将ADC1-DR值搬入内存→搬完触发DMA中断。全程CPU零参与。4.1 DMA通道与请求映射为什么DMA1_Channel1无法接收ADC1数据F103的DMA请求源有严格绑定ADC1的转换完成请求ADC1_EOC只能由DMA1_Channel1响应。若错误配置为Channel2则DMA永不启动。必须检查DMA1_CSELR寄存器F103无此寄存器故无需配置并确认通道使能// DMA1初始化仅Channel1用于ADC1 RCC-AHBENR | RCC_AHBENR_DMA1EN; // 使能DMA1时钟 // 配置Channel1内存地址递增、外设地址固定、数据宽度16位、传输数量256 DMA1_Channel1-CPAR (uint32_t)ADC1-DR; // 外设地址ADC1数据寄存器 DMA1_Channel1-CMAR (uint32_t)adc_buffer; // 内存地址用户定义的uint16_t数组 DMA1_Channel1-CNDTR 256; // 传输数据量256次转换 DMA1_Channel1-CCR DMA_CCR_MINC | // 内存地址递增 DMA_CCR_PSIZE_1 | // 外设数据宽度16位ADC为12位右对齐高位补0 DMA_CCR_MSIZE_1 | // 内存数据宽度16位 DMA_CCR_DIR | // 从外设读取DIR1 DMA_CCR_TEIE | // 传输错误中断使能 DMA_CCR_TCIE; // 传输完成中断使能4.2 启动顺序与使能时机DMA必须在ADC使能前就绪硬件依赖关系决定启动顺序DMA通道必须先配置完毕并使能再启动ADC最后启动定时器。若顺序颠倒如先启TIM2则ADC可能在DMA未就绪时完成首次转换导致DR寄存器被新值覆盖而丢失首采样// 正确启动流程三步不可逆 DMA1_Channel1-CCR ~DMA_CCR_EN; // 确保DMA初始关闭 DMA1_Channel1-CCR | DMA_CCR_EN; // 1. 先使能DMA通道 ADC1-CR2 | ADC_CR2_ADON; // 2. 再使能ADCADON1 delay_us(5); // 等待ADC稳定手册要求tSTAB3us TIM2-CR1 | TIM_CR1_CEN; // 3. 最后启动定时器开始触发避坑ADC_CR2_ADON置1后需等待至少3μs才能写CR2的其他位如EXTTRIG否则配置可能失效。此处delay_us(5)是硬性要求不可省略。5. 常见问题排查5条真实踩坑记录与定位方法在某跨平台系统调试中我们累计遇到27类ADC触发异常其中5类高频问题及解决路径如下。每条均按“现象→原因→解决”结构整理拒绝模糊描述。5.1 现象ADC_DR始终为0x0000示波器测PA0有正常模拟信号原因GPIOA时钟未使能导致PA0引脚处于高阻态ADC输入缓冲器无法获取信号。F103的ADC输入必须经过GPIO配置即使不用于数字功能。解决在RCC使能后立即配置GPIOA模式GPIOA-CRL ~GPIO_CRL_MODE0; // 清除PA0模式位 GPIOA-CRL | GPIO_CRL_MODE0_0; // MODE0 01b → 输入模式10MHz GPIOA-CRL ~GPIO_CRL_CNF0; // 清除CNF0 GPIOA-CRL | GPIO_CRL_CNF0_1; // CNF0 10b → 模拟输入关键5.2 现象DMA缓冲区数据全为0xFFFEADC上电复位值原因ADC1未真正启动转换。常见于ADC_CR2_EXTTRIG位未置1导致ADC虽在运行但对外部触发无响应DR寄存器保持复位值。解决用调试器查看ADC1-CR2寄存器确认bit22EXTTRIG为1。若为0检查ADC1-CR2 | ADC_CR2_EXTTRIG语句是否被执行断点验证。5.3 现象DMA传输完成中断TCIF永不触发但ADC1-DR有规律变化原因DMA通道未使能CCR.EN0或传输数量CNDTR设为0。CNDTR写入0会导致DMA立即置位TCIF但若CNDTR为非零值却未触发则大概率是CCR.EN未置1。解决调试时读取DMA1_Channel1-CCR确认bit0EN为1同时检查CNDTR值是否大于0。5.4 现象采样数据存在周期性跳变如每100ms出现一次-2048原因ADC参考电压VREF不稳定。F103的VREF引脚PA0旁的VREF若未接100nF去耦电容电源纹波会直接调制ADC结果。实测中未加电容时VREF纹波达15mVpp导致12位ADC最低位频繁翻转。解决在PCB上VREF引脚就近焊接100nF X7R陶瓷电容至GND电容ESR需1Ω。5.5 现象定时器触发频率正确但ADC采样率仅为理论值的1/2原因ADC时钟ADCCLK超频。F103的ADCCLK最大为14MHz若APB272MHz且RCC_CFGR中ADCPRE01b2分频则ADCCLK36MHz超出规格导致转换失败。手册规定超频后ADC可能返回随机值或锁定。解决强制ADCCLK≤14MHz。例如APB272MHz时设RCC-CFGR | RCC_CFGR_ADCPRE_04分频→ADCCLK18MHz仍超限应改用RCC_CFGR_ADCPRE_16分频→12MHzRCC-CFGR ~RCC_CFGR_ADCPRE; // 清零ADCPRE位 RCC-CFGR | RCC_CFGR_ADCPRE_1; // ADCPRE 10b → PCLK2分频66. 实时性验证与进阶技巧用示波器抓取触发抖动以及双缓冲防覆盖验证一个采集系统是否真正“实时”不能只看平均采样率必须测量触发抖动Jitter——即相邻两次触发脉冲的时间偏差。工业场景要求抖动1%采样周期。例如1kHz采样1ms周期抖动需10μs。下面给出可落地的验证方法与生产环境必备技巧。6.1 用示波器量化触发抖动TIM2_CH1复用为触发同步信号F103允许将TIM2的任意通道如CH1复用为TRGO的镜像输出从而用示波器直接观测触发边沿。配置PA0为TIM2_CH1复用推挽输出// PA0复用为TIM2_CH1非ADC模式 GPIOA-CRL ~GPIO_CRL_CNF0; // 清除CNF0 GPIOA-CRL | GPIO_CRL_CNF0_0; // CNF0 00b → 复用推挽 GPIOA-CRL ~GPIO_CRL_MODE0; // 清除MODE0 GPIOA-CRL | GPIO_CRL_MODE0_1; // MODE0 10b → 输出模式50MHz AFIO-MAPR ~AFIO_MAPR_TIM2_REMAP; // 不重映射TIM2_CH1在PA0 TIM2-CCER | TIM_CCER_CC1E; // 使能CH1输出 TIM2-CCMR1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1; // OC1M110b → 翻转模式 TIM2-CCR1 1; // CCR11 → 每次计数到1时翻转PA0 // 此时PA0上升沿严格对应TIM2_TRGO可用示波器测其周期稳定性技巧将示波器设为“无限余辉”模式触发源选PA0观察1000个周期的边沿分布。若所有边沿集中在1μs宽度内则抖动合格若呈散点状分布则需检查APB1时钟是否受其他外设干扰如USB、CAN。6.2 双缓冲DMA防数据覆盖生产环境零丢帧的终极保障单缓冲DMA在传输完成中断中处理数据时若处理时间超过采样间隔新数据会覆盖未读旧数据。解决方案是启用DMA双缓冲模式Double Buffer Mode让DMA在两个缓冲区间自动切换// 启用双缓冲需修改DMA配置仅DMA1_Channel1支持 DMA1_Channel1-CCR ~DMA_CCR_EN; // 先关闭DMA DMA1_Channel1-CMAR (uint32_t)buffer_a; // 初始内存地址指向buffer_a DMA1_Channel1-CPAR (uint32_t)ADC1-DR; DMA1_Channel1-CNDTR 128; // 每缓冲区128个字256字节 // 关键设置第二个缓冲区地址到CMAR2需解锁DMA寄存器 DMA1_Channel1-CMAR2 (uint32_t)buffer_b; // 第二缓冲区 DMA1_Channel1-CCR | DMA_CCR_DBM; // 使能双缓冲模式 DMA1_Channel1-CCR | DMA_CCR_EN; // 重新使能此时DMA行为变为填满buffer_a→触发TC中断→自动切到buffer_b→填满buffer_b→触发HT半传输中断→自动切回buffer_a。软件只需在TC中断中处理buffer_a在HT中断中处理buffer_b两者完全并行处理时间只要小于256×采样间隔即不丢帧。6.3 一个血泪教训永远在ADC启动前校准F103的ADC存在出厂偏移尤其在温度变化时。某次野外设备在-10℃环境下未校准的ADC读数漂移达±12LSB约5mV。手册规定每次上电或ADC时钟变更后必须执行校准ADC1-CR2 | ADC_CR2_CAL; // 开始校准自动清零CAL位 while(ADC1-CR2 ADC_CR2_CAL); // 等待校准完成约6个ADCCLK周期 // 校准后ADC_CR2.CAL0此时方可启动转换我一般会把校准放在ADC1-CR2 | ADC_CR2_ADON之后、ADC1-CR2 | ADC_CR2_EXTTRIG之前作为初始化硬性步骤。这行代码看似微小却是工业设备通过EMC测试的隐性门槛——希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表