跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C

STM32 温度采样定时器触发配置示例

基于 STM32 定时器触发 ADC 转换并结合 DMA 传输数据的温度采样方案。通过硬件触发确保采样精度,利用 DMA 降低 CPU 占用。主要步骤包括配置定时器输出 TRGO 信号、设置 ADC 外部触发源及内部温度传感器通道、配置 DMA 循环模式搬运数据。该方案适用于对实时性和稳定性要求较高的工业控制场景。

链路追踪发布于 2026/3/22更新于 2026/6/228 浏览

STM32 温度采样定时器触发配置示例

在嵌入式系统中,传统的软件轮询加延时方式采集温度数据存在隐患。例如主循环延时期间 CPU 无法处理其他任务,且多任务环境下难以保证精确的采样周期。为了解决这些问题,可以采用硬件协同的方式。

为什么非要用硬件触发?

对于需要高实时性、长期稳定运行的应用,软件轮询是行不通的。工业级系统要求确定性的行为:第 n 次和第 n+1 次采样的间隔必须严格相等。

STM32 的高级定时器(如 TIM2/TIM3)不仅可以计时,还能输出一个叫 TRGO(Trigger Output)的信号。这个信号可以像'发令枪'一样,精准地告诉 ADC:'现在开始转换!'整个过程不需要 CPU 参与,即使主程序正在处理 Wi-Fi 协议栈或者跑 FreeRTOS 的任务,也不会影响采样节奏。

定时器怎么当'发令员'?一步步带你配置

我们以 TIM3 为例,目标是让它每 1ms 发出一次 TRGO 脉冲,驱动 ADC 启动一次转换。

假设系统主频 72MHz,APB1 总线时钟也是 72MHz,我们要实现 1kHz 的触发频率(即周期 1ms)。

关键参数计算如下:

  • 计数器时钟 = 72MHz / (PSC + 1)
  • 目标更新频率 = 1kHz → 周期 = 1ms = 1000μs
  • 所以:(PSC + 1) × (ARR + 1) / 72,000,000 = 0.001

取 PSC = 71 → 分频后时钟为 1MHz(每个计数 1μs) 取 ARR = 999 → 溢出时间 = 1000 × 1μs = 1ms

接下来设置 TRGO 信号源为'更新事件'(Update Event),这样每次溢出就会输出一个上升沿。

void TIM3_ConfigForADC(void) {
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    TIM_TimeBaseInitTypeDef timerInit;
    timerInit.TIM_Period = 1000 - 1; // ARR
    timerInit.TIM_Prescaler = 72 - 1; // PSC
    timerInit.TIM_ClockDivision = 0;
    timerInit.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &timerInit);
    // 关键一步:选择主模式触发源为'更新事件'
    TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
    // 启动定时器
    TIM_Cmd(TIM3, ENABLE);
}

这段代码执行完之后,TIM3 就开始自由运行了。它会自动每 1ms 打一枪,告诉 ADC:'该你干活了。'

注:除了更新事件,TRGO 还可以来自比较匹配、捕获等事件,适用于更复杂的同步场景。

ADC 准备好了吗?让它只听 TIM 的话

接下来轮到 ADC 登场。我们需要让 ADC 工作在'外部触发 + 单次转换'模式,并指定由 TIM3_TRGO 作为触发源。

STM32F1 系列中,ADC1 支持多个外部触发源,其中就包括 ADC_ExternalTrigConv_T3_TRGO。

此外,我们要采集的是内部温度传感器,它连接在 ADC 通道 16 上。使用前必须显式启用该功能。

void ADC1_TempSensor_Init(void) {
    // 配置 ADC 时钟:通常建议不超过 14MHz
    RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 72MHz / 6 = 12MHz
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // 若使用外部 NTC 传感器,需配置对应引脚为模拟输入
    GPIO_InitTypeDef gpioInit;
    gpioInit.GPIO_Pin = GPIO_Pin_0;
    gpioInit.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &gpioInit);
    
    ADC_InitTypeDef adcInit;
    ADC_StructInit(&adcInit); // 先初始化默认值
    adcInit.ADC_Mode = ADC_Mode_Independent;
    adcInit.ADC_ScanConvMode = DISABLE; // 单通道
    adcInit.ADC_ContinuousConvMode = DISABLE; // 非连续,由外部触发控制
    adcInit.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; // 触发源
    adcInit.ADC_DataAlign = ADC_DataAlign_Right;
    adcInit.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &adcInit);
    
    // ⚠️ 必须调用此函数才能启用内部温度传感器
    ADC_TempSensorVrefintCmd(ENABLE);
    
    // 配置规则通道:通道 16(温度传感器),采样时间尽量长些以提高精度
    ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5);
    
    // 可选:执行 ADC 校准(推荐上电时做一次)
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));
    ADC_Cmd(ADC1, ENABLE);
}

到这里,ADC 已经处于'待命'状态,只等 TIM3 的 TRGO 信号一到,立刻启动一次转换。

数据去哪了?让 DMA 默默搬走,别吵 CPU

想象一下:每 1ms 产生一个温度值,一天就是 86400 个数据。如果你每次都让 CPU 亲自去读 ADC_DR 寄存器,那它啥也别干了。

解决办法就是引入第三位主角:DMA(Direct Memory Access)。

我们配置 DMA,在每次 ADC 转换完成时,自动把结果从 ADC1->DR 搬到内存中的缓冲区。整个过程无需 CPU 干预,真正做到'采样归采样,处理归处理'。

而且我们可以开启循环模式(Circular Mode),当缓冲区满后自动从头覆盖,非常适合长期监控。

#define SAMPLE_BUFFER_SIZE 10
uint16_t adc_buffer[SAMPLE_BUFFER_SIZE];

void DMA_ConfigForADC(void) {
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    DMA_InitTypeDef dmaInit;
    DMA_DeInit(DMA1_Channel1);
    dmaInit.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 源地址
    dmaInit.DMA_Memory0BaseAddr = (uint32_t)adc_buffer; // 目标地址
    dmaInit.DMA_DIR = DMA_DIR_PeripheralToMemory; // 外设→内存
    dmaInit.DMA_BufferSize = SAMPLE_BUFFER_SIZE; // 缓冲大小
    dmaInit.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不变
    dmaInit.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址递增
    dmaInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    dmaInit.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    dmaInit.DMA_Mode = DMA_Mode_Circular; // 循环模式
    dmaInit.DMA_Priority = DMA_Priority_High;
    dmaInit.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_Init(DMA1_Channel1, &dmaInit);
    
    // 使能 ADC 的 DMA 请求
    ADC_DMACmd(ADC1, ENABLE);
    DMA_Cmd(DMA1_Channel1, ENABLE);
}

这样一来,只要系统运行着,adc_buffer[] 就会被持续填充最新采样值。

你可以在 DMA 传输一半或全部完成时触发中断,进行批量处理:

void DMA1_Channel1_IRQHandler(void) {
    if (DMA_GetITStatus(DMA1_IT_TC)) {
        // Transfer Complete
        float avg = 0;
        for (int i = 0; i < SAMPLE_BUFFER_SIZE; i++) {
            uint16_t raw = adc_buffer[i];
            float voltage = raw * 3.3f / 4095.0f; // 转换为电压(12 位 ADC)
            float temp = (voltage - 1.42f) / 0.0043f + 30.0f; // 查手册公式
            avg += temp;
        }
        avg /= SAMPLE_BUFFER_SIZE;
        // 发送到串口或 LCD
        printf("Temperature: %.2f°C\r\n", avg);
        DMA_ClearITPendingBit(DMA1_IT_TC);
    }
}

提示:实际应用中建议加入滑动平均滤波或一阶 IIR 滤波,进一步平滑噪声。

系统架构图:三位一体的自动化流水线

最终系统的数据流清晰明了:

[TIM3] │ (每 1ms 发出 TRGO 信号)
▼
[ADC1] ← 内部温度传感器(Channel 16)
│ (转换完成)
▼
[DMA1] → 自动写入 adc_buffer[N]
└─▶ 半传输/全传输中断 → 温度计算 → 显示/报警/上传

这条链路由三个外设协同完成:

  • TIM3:节拍控制器,提供精准时钟源;
  • ADC1:感知单元,负责模数转换;
  • DMA1:搬运工,悄无声息地转移数据。

CPU 唯一要做的事,就是在合适的时候看看结果,其余时间可以全力投入 PID 控制、网络通信、人机交互等核心业务。

实战经验分享:那些手册不会告诉你的坑

坑点 1:忘了开内部温度传感器供电

很多开发者发现读出来的一直是 0 或固定值,原因就是没调用:

ADC_TempSensorVrefintCmd(ENABLE);

这个函数不仅启用通道 16,还会打开内部参考电压路径,否则传感器没电!

坑点 2:采样时间太短导致精度下降

内部温度传感器阻抗较高,建议使用最长采样时间:

ADC_SampleTime_239Cycles5

否则可能因充电不足造成测量误差达±5°C 以上。

坑点 3:DMA 缓冲区未对齐或越界

确保 adc_buffer 数组长度与 DMA 配置一致,且避免在中断中频繁操作浮点运算(可在主循环处理)。

秘籍:如何获得更高精度?

ST 出厂时会在特定温度点(如 30°C 和 110°C)记录对应的 ADC 值,保存在芯片的 OTP 区域。你可以读取这些校准值进行线性修正,显著提升准确性。

例如:

// 假设已知:
// TS_CAL1 @ 30°C -> *(uint16_t*)0x1FFFF7B8
// TS_CAL2 @ 110°C -> *(uint16_t*)0x1FFFF7C2
int16_t raw_temp = adc_value;
float temp_c = ((float)(raw_temp - TS_CAL1) * (110.0f - 30.0f)) / (TS_CAL2 - TS_CAL1) + 30.0f;

这种架构适合哪些应用场景?

应用领域是否适用说明
电池管理系统(BMS)✅ 强烈推荐需要长时间稳定采样,防止过热
电机驱动温控✅ 推荐实时性强,配合 PWM 同步采样
智能家电(空调、烤箱)✅ 适用提升用户体验一致性
物联网终端节点✅ 推荐低功耗、少干扰
医疗设备恒温控制✅ 高度推荐对安全性和可靠性要求极高

相比之下,仅用于偶尔查看室温的小玩具项目,可以用软件轮询;但凡是涉及安全性、稳定性、实时性的工业级产品,这套方案几乎是标配。

总结与延伸思考

通过本文的讲解,你应该已经掌握了一套完整的、基于硬件协同的温度采样设计范式:

✅ 用定时器触发 → 解决时序抖动 ✅ 用 ADC 外部触发 → 实现非阻塞采集 ✅ 用 DMA 搬运数据 → 彻底解放 CPU

三者结合,构成了 STM32 嵌入式系统中最经典的'黄金三角'架构之一。

但这还不是终点。你可以在此基础上继续扩展:

  • 改用多个 ADC + 多通道扫描,实现温度阵列监测;
  • 动态调整 TIM 周期,实现自适应采样率(温变快时高频,稳态时低频);
  • 结合 RTC 实现带时间戳的日志记录;
  • 加入边缘计算逻辑,本地判断是否超温并触发保护动作。

嵌入式系统的魅力就在于:用最少的资源,做最可靠的事。

而这套定时器+ADC+DMA 的组合拳,正是通往高效、稳健系统设计的关键一步。

目录

  1. STM32 温度采样定时器触发配置示例
  2. 为什么非要用硬件触发?
  3. 定时器怎么当“发令员”?一步步带你配置
  4. ADC 准备好了吗?让它只听 TIM 的话
  5. 数据去哪了?让 DMA 默默搬走,别吵 CPU
  6. 系统架构图:三位一体的自动化流水线
  7. 实战经验分享:那些手册不会告诉你的坑
  8. 坑点 1:忘了开内部温度传感器供电
  9. 坑点 2:采样时间太短导致精度下降
  10. 坑点 3:DMA 缓冲区未对齐或越界
  11. 秘籍:如何获得更高精度?
  12. 这种架构适合哪些应用场景?
  13. 总结与延伸思考
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Milvus 部署架构选型与 Linux Docker 一键部署实战
  • Java 与 C++ 对比:应用场景、学习难度与就业方向
  • VSCode 插件 GitLens 安装与版本管理指南
  • 如何在 Android 上使用 MGit 管理 Git 仓库
  • Linux 底层核心精讲:环境变量、命令行参数与程序地址空间全解析
  • AI 时代的技术服务机会:从 OpenClaw 上门安装看信息差红利
  • OpenAI 指控 DeepSeek 模型蒸馏,字节发布 Seedance 2.0 与 Java 26 现状
  • Python dotenv 库 load_dotenv() 使用指南:环境变量管理与安全实践
  • C++ 核心技术点解析:从多态到智能指针
  • Ubuntu 24.04 本地部署 Open WebUI 与 Ollama 完整指南
  • 创新思维与创新管理:唐兴通课程核心内容与 AI 时代应用
  • Android Framework 核心原理与源码解析指南
  • Python 自动化测试工具 Playwright 核心功能与实战详解
  • 网络安全零基础入门指南:学习误区、路线与自学利弊分析
  • 自然语言处理:高级应用、前沿模型与实战开发
  • Web 自动化测试入门指南:从概念到 Selenium 实战
  • Python 在 CentOS 系统上的安装、配置与部署实战
  • MCPHost:命令行下大模型与外部工具交互的实践
  • Linux 进程间通信:命名管道(FIFO)实战与原理
  • C++ STL vector 常用函数接口详解

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online