一、学习背景和目标
1.1 学习背景
在嵌入式系统开发中,测量外部信号的频率和占空比是一项非常常见的需求。无论是电机转速检测、遥控器信号解码,还是传感器数据采集,都离不开对脉冲信号的精确测量。传统的测量方法往往需要额外的硬件计数器或复杂的逻辑分析仪,不仅成本高,而且使用不便。
STM32 系列单片机内置了功能强大的定时器(Timer)模块,配合输入捕获(Input Capture)功能,可以轻松实现对 PWM 信号频率和占空比的精确测量。本实验基于 STM32F103 微控制器,利用 TIM2 产生 PWM 信号,再通过 TIM3 的输入捕获功能对信号进行测量,最终在 OLED 屏幕上实时显示测量结果。
1.2 学习目标
通过本实验,你将掌握以下核心知识与技能:
- 理解 PWM 信号的基本原理:掌握频率、占空比的概念及其计算方法。
- 掌握 STM32 定时器的基本配置:学会使用标准外设库配置定时器的时基单元。
- 掌握 PWM 输出模式:学会使用 TIM2 的 PWM 模式生成可调频率、可调占空比的方波信号。
- 掌握输入捕获功能:学会使用 TIM3 的输入捕获模式测量外部信号的频率和占空比。
- 掌握 OLED 显示技术:学会在 OLED 屏幕上实时显示测量数据。
- 培养系统调试能力:通过实际测量验证理论计算,提升嵌入式开发的综合实践能力。
二、接线图
三、代码块
main.h
#include"stm32f10x.h"// Device header#include"delay.h"#include"OLED.h"#include"pwm.h"#include"IC.h"intmain(){OLED_Init();pwm_init();IC_init();pwm_setpsc(7200-1);//ƵÂÊ=72M/(PSC+1)(ARR+1)pwm_setcompare(70);OLED_ShowString(1,1,"Freq:");OLED_ShowString(1,12," HZ");OLED_ShowString(2,1,"Duty:");OLED_ShowString(2,12," %");while(1){OLED_ShowNum(1,6,get_fre(),6);//×îµÍ´ó¸ÅÊÇ15,×î´ó1000000;OLED_ShowNum(2,6,get_duty(),6);}}pwm.c
#include"stm32f10x.h"// Device headervoidpwm_init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);TIM_InternalClockConfig(TIM2);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period=100-1;//ARRTIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse=50;//CCRTIM_OC1Init(TIM2,&TIM_OCInitStructure);TIM_Cmd(TIM2,ENABLE);}voidpwm_setcompare(uint16_tnum){TIM_SetCompare1(TIM2,num);}voidpwm_setpsc(uint16_tpsc){TIM_PrescalerConfig(TIM2,psc,TIM_PSCReloadMode_Immediate);}IC.c
#include “stm32f10x.h” // Device header
void IC_init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); TIM_InternalClockConfig(TIM3); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); TIM_ICInitTypeDef TIM_struct; TIM_struct.TIM_Channel=TIM_Channel_1; TIM_struct.TIM_ICFilter=0xF; TIM_struct.TIM_ICPolarity=TIM_ICPolarity_Rising; TIM_struct.TIM_ICPrescaler=TIM_ICPSC_DIV1; TIM_struct.TIM_ICSelection=TIM_ICSelection_DirectTI; TIM_PWMIConfig(TIM3,&TIM_struct); //µÈЧÓÚ£ºTIM_struct.TIM_Channel=TIM_Channel_2; //TIM_struct.TIM_ICFilter=0xF; //TIM_struct.TIM_ICPolarity=TIM_ICPolarity_Falling; //TIM_struct.TIM_ICPrescaler=TIM_ICPSC_DIV1; //TIM_struct.TIM_ICSelection=TIM_ICSelection_IndirectTI; //TIM_ICInit(TIM3,&TIM_struct); TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1); TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset); TIM_Cmd(TIM3,ENABLE);}
uint32_t get_fre(void)
{
return 1000000/(TIM_GetCapture1(TIM3)+1);
}
uint32_t get_duty(void)
{
return (TIM_GetCapture2(TIM3)+1)*100/(TIM_GetCapture1(TIM3)+1);
}
四、知识点详解
4.1 PWM 信号产生原理
本实验使用 TIM2 的 PWM 输出模式产生被测信号。PWM 波形的核心参数有两个:
- 频率(Frequency):由定时器的时钟源、预分频器(PSC)和自动重装载值(ARR)共同决定,计算公式为
频率 = 72MHz / ((PSC+1) × (ARR+1))。 - 占空比(Duty Cycle):由比较寄存器(CCR)与自动重装载值(ARR)的比值决定,计算公式为
占空比 = CCR / (ARR+1) × 100%。
在pwm_init()中,TIM_Period = 100 - 1设置 ARR 为 99,TIM_Prescaler = 720 - 1设置 PSC 为 719,因此默认输出频率为72MHz / (720 × 100) = 1000Hz,默认占空比为50 / 100 = 50%。pwm_setpsc()和pwm_setcompare()分别用于动态调整频率和占空比。
4.2 输入捕获测量原理
本实验使用 TIM3 的输入捕获功能测量外部信号的频率和占空比,核心思路如下:
- 频率测量:利用定时器的从模式复位功能,在每次捕获到上升沿时,计数器自动清零并重新计数。因此,
TIM_GetCapture1(TIM3)返回的即为一个完整周期的计数值,频率计算公式为频率 = 72MHz / (PSC+1) / (捕获值+1)。本实验中 PSC 设置为 71,故get_fre()返回1000000 / (捕获值+1)。 - 占空比测量:通过
TIM_PWMIConfig()配置 PWM 输入模式,通道 1 捕获上升沿(记录周期),通道 2 自动捕获下降沿(记录高电平时间)。占空比计算公式为占空比 = 高电平时间 / 周期 × 100%,即get_duty()中的(TIM_GetCapture2(TIM3)+1) × 100 / (TIM_GetCapture1(TIM3)+1)。
4.3 定时器从模式复位
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1)将 TI1 的上升沿作为触发源,TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset)使计数器在触发时自动复位。这一机制保证了每次捕获到的计数值都对应一个完整的信号周期,无需软件干预即可连续测量。
4.4 OLED 显示
OLED 屏幕通过OLED_ShowString()和OLED_ShowNum()实时刷新频率与占空比数据。主循环中不断调用get_fre()和get_duty()获取最新测量值并显示,实现动态监测。
五、实验不足与改进方向
5.1 测量精度受限
- 低频测量误差大:当被测信号频率较低时,计数器可能溢出(ARR 最大为 65535),导致测量结果不准确。当前代码未处理计数器溢出情况。
- 高频测量分辨率不足:PSC 固定为 71,当频率较高时,一个周期内的计数值较小,量化误差相对增大。
5.2 功能单一
- 仅支持单通道测量,无法同时测量多路信号。
- 未实现超量程提示,当信号超出可测范围时,OLED 显示结果可能失真。
5.3 缺少校准与滤波
- 未对输入信号进行软件滤波,易受噪声干扰,导致测量值抖动。
- 未提供校准功能,测量结果可能存在系统偏差。
5.4 改进方向
- 增加计数器溢出处理,扩展低频测量范围。
- 根据信号频率动态切换预分频系数,提高测量分辨率。
- 加入软件滤波算法(如多次采样取平均),提升测量稳定性。
- 扩展多通道测量,支持同时监测多路 PWM 信号。