当前位置: 首页 > news >正文

【STM32】HAL库中的实现(三):PWM(脉冲宽度调制)

🔧 HAL库中的实现:PWM(脉冲宽度调制)

PWM(Pulse Width Modulation)是基于定时器(TIM)产生的周期性脉冲信号,广泛应用于:① 电机调速;② LED 亮度控制;③ 蜂鸣器频率控制;④ 模拟信号模拟(DAC)等等。

PWM 本质原理(基于定时器)

PWM 信号是 定时器的输出比较功能(Output Compare) 的一种模式,核心逻辑:

参数含义
PSC(Prescaler)预分频器,降低计数频率
ARR(Auto Reload Register)自动重装值,决定周期
CCR(Capture Compare Register)比较值,决定高电平时间

占空比 = CCR / ARR

PWM的初始化流程( STM32CubeMX )
总体实现思路:(使用 STM32CubeMX 配置)1.启用一个支持 PWM 的定时器(如 TIM3)2.设置为 PWM Generation CHx3.选择输出引脚(TIM3_CH1 → PA6 等)4.设置频率、占空比、极性等参数HAL中的常用API:
函数									作用
HAL_TIM_PWM_Init()					初始化定时器为 PWM 模式
HAL_TIM_PWM_ConfigChannel()			配置 PWM 输出通道
HAL_TIM_PWM_Start()					启动 PWM 输出
HAL_TIM_PWM_Stop()					停止 PWM 输出
__HAL_TIM_SET_COMPARE()				动态设置占空比
__HAL_TIM_SET_AUTORELOAD()			动态修改周期

一般通过 STM32CubeMX 配置,自动生成如下代码:

TIM_HandleTypeDef htim1;void MX_TIM1_Init(void)
{htim1.Instance = TIM1;htim1.Init.Prescaler = 71;         // 72MHz / (71+1) = 1MHzhtim1.Init.Period = 999;           // 每 1000 次递增,周期为 1mshtim1.Init.CounterMode = TIM_COUNTERMODE_UP;HAL_TIM_PWM_Init(&htim1);
}

然后配置 PWM 通道:

TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500;                 // 占空比 = 500 / 1000 = 50%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
HAL 启动 PWM 输出
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
该函数会设置:启动计数器(CEN 位)使能输出通道(CC1E 位)
动态修改占空比
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, new_value);
作用:修改 CCRx(比较寄存器)值控制 PWM 高电平时间长度

例如:使用 TIM1 通道1 产生 1KHz、占空比 25% 的 PWM

Prescaler = 71; // f = 1 MHz
Period = 999; // T = 1ms (1kHz)
Pulse = 250; // 占空比 25%


PWM 频率与占空比计算公式:
设系统时钟 f_clk = 72MHz
PWM频率 = f_clk / ((PSC + 1) * (ARR + 1))
占空比 = CCR / (ARR + 1)

PWM 模式图示(PWM1):

              
__|¯¯¯¯¯¯¯|________|¯¯¯¯¯¯¯|________|¯¯¯¯¯¯¯|<--  T  -->         (周期)
<-->      <--      (高电平时间 = CCRx)

PWM 实现呼吸灯(提供STM32F1 系列中断服务函数的实现代码)

目标:使用 定时器 PWM 输出,并通过周期性改变占空比 使 LED 灯呈现亮 → 暗 → 亮的效果 ,就像“呼吸”一样。

呼吸灯原理简介
使用 PWM 控制 LED 的亮度,通过改变 PWM 的占空比(CCR 值)实现 LED 的亮/暗过渡:- 占空比从 0% 增加到 100%:LED 从暗变亮- 占空比从 100% 减小到 0%:LED 从亮变暗- 循环往复,形成呼吸灯效果

实现结构:
1. 使用一个定时器(如 TIM2)生成 PWM
2. 使用另一个定时器(如 TIM3)定时改变占空比

也可以使用中断,在 stm32f1xx_it.c 中启用 SysTickTIM3 的中断,也能用它们来实现呼吸灯。

实现流程:
  1. 配置 TIM2 生成 PWM(在 MX_TIM2_Init() 中)
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71;       // 72MHz / (71+1) = 1MHz
htim2.Init.Period = 999;         // 1kHz PWM
HAL_TIM_PWM_Init(&htim2);sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;             // 初始占空比为 0%
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);  // 启动 PWM
  1. 使用 TIM3 定时改变 PWM 占空比

启用 TIM3 中断,每隔 1ms 调整一次占空比:

HAL_TIM_Base_Start_IT(&htim3);
  1. 修改 stm32f1xx_it.c 中的中断回调实现为呼吸灯逻辑:

/* USER CODE BEGIN 1 */ 区域添加以下内容:

extern TIM_HandleTypeDef htim2;uint16_t pwm_val = 0;
int8_t step = 1;void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3){// 修改占空比__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pwm_val);pwm_val += step;if(pwm_val >= 999)step = -1;else if(pwm_val <= 0)step = 1;}
}

编程实现思路
[TIM3 中断] ---> 每 1ms 触发一次↓
[改变 pwm_val] ---> 0~999~0 来回↓
[设置 TIM2 的 CCR1] ---> 改变占空比↓
[LED 呼吸效果] ---> 更改 step 步长:控制呼吸快慢;更改 TIM3 定时时间:调节呼吸频率加入非线性变化(如正弦表):更自然的呼吸效果CubeMX配置:
TIM2 ---> 	PWM Mode, 通道1启用,频率设置合适(如 1kHz)
TIM3 ---> 	基础定时器,启用中断,周期设置为 1ms
GPIO ---> 	TIM2_CH1 绑定的引脚(如 PA0)设置为 AF 推挽
NVIC ---> 	TIM3 中断启用,优先级配置合理LED 呼吸周期为 2 秒:pwm_val += step: 每次变化 1,共 1000 步每步 1ms → 1 秒变亮,1 秒变暗总共:2 秒一个完整呼吸周期作用:
PWM 定时器   ---> 产生恒定频率、可调占空比的信号
中断定时器    ---> 每 1ms 改变一次占空比,实现呼吸变化
HAL 接口	    --->  __HAL_TIM_SET_COMPARE 实时控制亮度
优化呼吸灯频率 ---> 可使用 DMA / LUT 表优化呼吸曲线

完整代码

📄 main.c

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "iwdg.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t U1SendData[] = {"hello world!"};
/* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */uint16_t pwmVal = 0;	//占空比/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_IWDG_Init();MX_TIM3_Init();/* USER CODE BEGIN 2 */HAL_UARTEx_ReceiveToIdle_IT(&huart1, U1RxData, U1RxDataSize);	//使能空闲中断HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);	//PWM初始化完毕之后,找到对应PWM的启动函数/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */HAL_UART_Transmit(&huart1, U1SendData, sizeof(U1SendData), 0xff);	//启动串口空闲中断的发送while (1){while(pwmVal < 500){HAL_IWDG_Refresh(&hiwdg);	//喂狗pwmVal++;//让LED灯以呼吸灯的频率闪烁__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, pwmVal);printf("pwmVal++: %d	\r\n",pwmVal);HAL_Delay(1);}while(pwmVal){HAL_IWDG_Refresh(&hiwdg);	//喂狗pwmVal--;//让LED灯以呼吸灯的频率闪烁__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, pwmVal);printf("pwmVal--: %d	\r\n",pwmVal);HAL_Delay(1);}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.LSIState = RCC_LSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

📄 stm32f1xx_it.c

/* USER CODE BEGIN Header */
/********************************************************************************* @file    stm32f1xx_it.c* @brief   Interrupt Service Routines.******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usart.h"
#include "iwdg.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD *//* USER CODE END TD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV *//* USER CODE END EV *//******************************************************************************/
/*           Cortex-M3 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/*** @brief This function handles Non maskable interrupt.*/
void NMI_Handler(void)
{/* USER CODE BEGIN NonMaskableInt_IRQn 0 *//* USER CODE END NonMaskableInt_IRQn 0 *//* USER CODE BEGIN NonMaskableInt_IRQn 1 */while (1){}/* USER CODE END NonMaskableInt_IRQn 1 */
}/*** @brief This function handles Hard fault interrupt.*/
void HardFault_Handler(void)
{/* USER CODE BEGIN HardFault_IRQn 0 *//* USER CODE END HardFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_HardFault_IRQn 0 *//* USER CODE END W1_HardFault_IRQn 0 */}
}/*** @brief This function handles Memory management fault.*/
void MemManage_Handler(void)
{/* USER CODE BEGIN MemoryManagement_IRQn 0 *//* USER CODE END MemoryManagement_IRQn 0 */while (1){/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 *//* USER CODE END W1_MemoryManagement_IRQn 0 */}
}/*** @brief This function handles Prefetch fault, memory access fault.*/
void BusFault_Handler(void)
{/* USER CODE BEGIN BusFault_IRQn 0 *//* USER CODE END BusFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_BusFault_IRQn 0 *//* USER CODE END W1_BusFault_IRQn 0 */}
}/*** @brief This function handles Undefined instruction or illegal state.*/
void UsageFault_Handler(void)
{/* USER CODE BEGIN UsageFault_IRQn 0 *//* USER CODE END UsageFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_UsageFault_IRQn 0 *//* USER CODE END W1_UsageFault_IRQn 0 */}
}/*** @brief This function handles System service call via SWI instruction.*/
void SVC_Handler(void)
{/* USER CODE BEGIN SVCall_IRQn 0 *//* USER CODE END SVCall_IRQn 0 *//* USER CODE BEGIN SVCall_IRQn 1 *//* USER CODE END SVCall_IRQn 1 */
}/*** @brief This function handles Debug monitor.*/
void DebugMon_Handler(void)
{/* USER CODE BEGIN DebugMonitor_IRQn 0 *//* USER CODE END DebugMonitor_IRQn 0 *//* USER CODE BEGIN DebugMonitor_IRQn 1 *//* USER CODE END DebugMonitor_IRQn 1 */
}/*** @brief This function handles Pendable request for system service.*/
void PendSV_Handler(void)
{/* USER CODE BEGIN PendSV_IRQn 0 *//* USER CODE END PendSV_IRQn 0 *//* USER CODE BEGIN PendSV_IRQn 1 *//* USER CODE END PendSV_IRQn 1 */
}/*** @brief This function handles System tick timer.*/
void SysTick_Handler(void)
{/* USER CODE BEGIN SysTick_IRQn 0 *//* USER CODE END SysTick_IRQn 0 */HAL_IncTick();/* USER CODE BEGIN SysTick_IRQn 1 *//* USER CODE END SysTick_IRQn 1 */
}/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f1xx.s).                    */
/******************************************************************************//*** @brief This function handles EXTI line0 interrupt.*/
void EXTI0_IRQHandler(void)
{/* USER CODE BEGIN EXTI0_IRQn 0 *//* USER CODE END EXTI0_IRQn 0 */HAL_GPIO_EXTI_IRQHandler(PA0_Key_Pin);/* USER CODE BEGIN EXTI0_IRQn 1 *//* USER CODE END EXTI0_IRQn 1 */
}/*** @brief This function handles USART1 global interrupt.*/
void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 *//* USER CODE END USART1_IRQn 1 */
}/* USER CODE BEGIN 1 */void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == PA0_Key_Pin){if( HAL_GPIO_ReadPin(PA0_Key_GPIO_Port, PA0_Key_Pin) == GPIO_PIN_RESET){HAL_GPIO_TogglePin(GPIOC, LED_G_Pin);	//红灯的状态翻转
//			HAL_IWDG_Refresh(&hiwdg);		//进行喂狗
//			printf("Iwdg Count = %d \r\n", Count++);}}
}void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{U1RxLen = Size;HAL_UARTEx_ReceiveToIdle_IT(&huart1, U1RxData, U1RxDataSize);	//启动串口空闲中断的接收U1RxFlag = 1;
}/*
//TIM的中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//	static uint32_t Count = 0;if(htim->Instance == TIM3)	//判断产生中断的哪一个中断回调函数{HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin);	//绿灯的状态翻转//		HAL_IWDG_Refresh(&hiwdg);		//进行喂狗
//		printf("Iwdg Count = %d \r\n", Count++);}
}
*//* USER CODE END 1 */

以上。STM32 HAL 库通过对定时器的封装,提供了标准化的 PWM 控制方式,适用于各种电平控制、电机驱动、LED 等场景,配合 CubeMX 可以快速开发出稳定可靠的 PWM 应用。

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!

http://www.lryc.cn/news/611408.html

相关文章:

  • 浮雕软件Artcam安装包百度云网盘下载与安装指南
  • 内部排序算法总结(考研向)
  • [验证回文串]
  • C#案例实战
  • LeetCode——2411. 按位或最大的最小子数组长度
  • MCP与Function Calling
  • MySQL间隙锁在查询时锁定的范围
  • MinIO02-Docker安装
  • AI编程新时代:从氛围编程到上下文编程的深度实践和思考
  • GPS信号捕获尝试(上)
  • 快接龙 | 要如何对用户的接龙频次进行系统硬控
  • MongoDB 从3.4.0升级到4.0.0完整指南实战-优雅草蜻蜓I即时通讯水银版成功升级-卓伊凡|bigniu
  • 【文本左右对齐】
  • 【web自动化测试】实战
  • Python基础框架
  • WebRTC音视频编码模块深度解析:从编解码器到自适应码率控制(2025技术实践)
  • 前端包管理器深度对比
  • 普通树状数组
  • 贪心算法学习 1
  • Zabbix 企业级高级应用
  • 风丘助力混合动力汽车工况测试:精准采集整车信号解决方案
  • VNC连接VirtualBox中的Ubuntu24.04 desktop图形化(GUI)界面
  • 2025年渗透测试面试题总结-01(题目+回答)
  • GitHub Models:为开源AI项目解决推理难题,让AI更易用、更普及
  • css初学者第三天
  • MySQL 如何优化慢查询
  • Redis中的sdshdr的len和alloc那块的知识点详解
  • 前端记录项目中用到的js
  • python可视化--Seaborn图形绘制方法和技巧,Bokeh图形绘制方法和技巧
  • 最新基于Python科研数据可视化实践技术