别再死记硬背了!用STM32F103C8T6和CubeMX玩转定时器,从LED闪烁到PWM测量一次搞定
STM32F103C8T6定时器实战从CubeMX配置到PWM测量的全流程指南当你第一次拿到STM32开发板时那些密密麻麻的定时器功能是否让你望而生畏传统的学习方式往往从寄存器开始让人陷入枯燥的理论泥潭。今天我们将打破常规用一块F103C8T6最小系统板和CubeMX通过五个看得见效果的实验带你轻松玩转定时器。1. 环境搭建与基础认知工欲善其事必先利其器。在开始实验前我们需要准备好以下工具链硬件准备STM32F103C8T6最小系统板蓝色板USB转TTL串口模块用于调试输出LED灯若干建议不同颜色杜邦线若干按键开关用于输入捕获实验软件环境Keil MDK-ARM建议V5.25以上STM32CubeMX最新版ST-Link驱动或其他调试器驱动安装完软件后建议先运行一次CubeMX检查是否安装了对应的F1系列HAL库。如果没有可以通过Help - Manage embedded software packages进行安装。提示初学者常犯的错误是时钟树配置不当。F103C8T6内部高速时钟(HSI)为8MHz通常我们会通过PLL倍频到72MHz作为系统主频。2. 定时器中断实现LED闪烁让我们从最简单的定时器中断开始实现LED以500ms间隔闪烁的效果。2.1 CubeMX基础配置打开CubeMX新建工程选择STM32F103C8系列具体型号选择C8T6。首先配置时钟树在Pinout视图选择RCC-High Speed Clock(HSE)为Crystal/Ceramic Resonator切换到Clock Configuration标签将HSE输入频率设为8MHz外部晶振频率设置PLL Source Mux为HSE调整PLLMUL为x9使系统时钟达到72MHz接下来配置GPIO找到PA6引脚设置为GPIO_Output用户标签命名为LED方便代码识别定时器配置是关键步骤激活TIM2时钟源选择Internal Clock配置Prescaler(PSC)为719972MHz/(71991)10kHz设置Counter Period(ARR)为499910kHz/(49991)2Hz开启TIM2全局中断注意定时器预分频器和自动重装载值都需要减1设置这是STM32定时器的特性。例如要实现1kHz频率实际PSC应设为71(72MHz/721MHz)ARR设为999(1MHz/10001kHz)。2.2 代码实现与调试生成代码后在Keil中打开工程。我们需要添加的代码非常简洁/* 主函数中启用定时器中断 */ HAL_TIM_Base_Start_IT(htim2); /* 定时器中断回调函数 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM2) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6); } }编译下载后你应该能看到PA6上的LED以精确的500ms间隔闪烁。如果LED没有反应检查以下常见问题是否忘记使能定时器中断GPIO引脚配置是否正确时钟树配置是否生效可通过SystemCoreClock变量查看3. PWM实现呼吸灯效果掌握了基础定时器后我们来挑战PWM输出实现呼吸灯效果。3.1 CubeMX的PWM配置继续在之前的工程上修改保留TIM2配置增加PWM Generation CH1设置Pulse(初始占空比)为0确认CH1 Mode为PWM mode 1新增一个GPIO(如PA0)作为对比LED关键参数说明Pulse决定PWM的初始占空比范围0-ARR值ModePWM mode 1表示计数器小于比较值时输出有效电平CH Polarity决定有效电平是高还是低3.2 动态调整PWM占空比生成代码后添加以下逻辑实现呼吸灯uint16_t pwmDuty 0; // PWM占空比控制变量 HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); // 启动PWM while(1) { // 渐亮过程 while(pwmDuty htim2.Instance-ARR) { pwmDuty; __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, pwmDuty); HAL_Delay(5); } // 渐暗过程 while(pwmDuty 0) { pwmDuty--; __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, pwmDuty); HAL_Delay(5); } }通过示波器观察PA6引脚可以看到占空比从0%到100%平滑变化的PWM波形。调整HAL_Delay的参数可以改变呼吸速度。4. 输入捕获测量按键时长定时器不仅能输出信号还能精确测量输入信号的脉宽。下面我们实现按键按下时长的测量。4.1 输入捕获硬件连接将按键一端接地另一端接PA0TIM2_CH1同时接一个10kΩ上拉电阻到3.3V。这样按键未按下时PA0为高电平按下时为低电平。4.2 CubeMX输入捕获配置配置PA0为TIM2_CH1在TIM2配置中选择Channel1为Input Capture direct mode设置IC Selection为Direct设置ICPolarity为Falling捕获下降沿开启TIM2全局中断4.3 代码实现我们需要使用两个变量来记录捕获状态和值volatile uint32_t captureStart 0; volatile uint32_t pulseWidth 0; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM2) { if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_SET) { // 上升沿 captureStart HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); } else { // 下降沿 pulseWidth HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1) - captureStart; __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); } } }在主循环中添加显示逻辑可以通过串口或OLED显示测量到的脉宽值。记得在主函数中启动输入捕获HAL_TIM_IC_Start_IT(htim2, TIM_CHANNEL_1);5. 进阶PWM输入模式测量频率对于需要精确测量PWM频率和占空比的场景STM32提供了专门的PWM输入模式。5.1 硬件连接使用两个定时器TIM2产生PWM信号接PA0TIM1测量该PWM信号PA8接TIM1_CH15.2 CubeMX配置配置TIM2为PWM输出同第3节配置TIM1Channel1为Input Capture direct modeChannel2为Input Capture indirect mode触发源选择TI1FP1从模式选择Reset ModeIC1和IC2都选择触发信号TI1FP15.3 代码实现uint32_t ic1Value 0, ic2Value 0; float dutyCycle 0, frequency 0; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM1) { if(htim-Channel HAL_TIM_ACTIVE_CHANNEL_1) { // 周期 ic1Value HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); } else if(htim-Channel HAL_TIM_ACTIVE_CHANNEL_2) { // 占空比 ic2Value HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); // 计算频率和占空比 frequency 72000000.0f / (htim1.Instance-PSC 1) / ic1Value; dutyCycle (float)ic2Value / ic1Value * 100; printf(Frequency: %.2f Hz, Duty: %.1f%%\r\n, frequency, dutyCycle); } } }在主函数中启动两个定时器HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(htim1, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(htim1, TIM_CHANNEL_2);通过调整TIM2的ARR和CCR值可以产生不同频率和占空比的PWM波TIM1会精确测量这些参数并通过串口输出。