基于stm32的PWM控制与多任务调度系统设计与实战
基于STM32的PWM控制与多任务调度系统设计与实战
PWM(脉冲宽度调制)技术详解
1. PWM基本概念
1.1 什么是PWM
PWM(Pulse Width Modulation,脉冲宽度调制)是一种通过数字信号模拟模拟信号的技术。它通过改变脉冲的宽度(占空比)来控制平均电压值,而不改变脉冲的频率。
1.2 PWM关键参数
- 频率(Frequency):PWM信号完整周期的重复速率
- 周期(Period):一个完整PWM波形的时间长度
- 占空比(Duty Cycle):高电平时间占整个周期的百分比
- 脉冲宽度(Pulse Width):高电平持续的时间
2. PWM工作原理
2.1 基本原理
一个PWM周期:
┌─────────────────┐
│ │
│ 高电平时间 │ 低电平时间
│ (Ton) │ (Toff)
└─────────────────┘
←───── 周期(T) ────→
占空比 = (Ton / T) × 100%
2.2 PWM控制模拟量原理
通过改变占空比,可以控制负载上的平均电压:
- 占空比0%:平均电压为0V
- 占空比50%:平均电压为电源电压的一半
- 占空比100%:平均电压等于电源电压
3. STM32中的PWM实现
3.1 STM32定时器PWM模式
STM32的通用定时器(TIM2-TIM5)和高级定时器都支持PWM输出。
3.2 关键寄存器
- ARR(Auto-Reload Register):决定PWM周期
- CCR(Capture/Compare Register):决定PWM占空比
- PSC(Prescaler):时钟预分频器
3.3 PWM频率计算
定时器时钟 = 系统时钟 / (PSC + 1)
PWM频率 = 定时器时钟 / (ARR + 1)
在我们的代码中:
- 系统时钟 = 72MHz
- PSC = 71 → 定时器时钟 = 72MHz / 72 = 1MHz
- ARR = 999 → PWM频率 = 1MHz / 1000 = 1kHz
3.4 PWM占空比计算
占空比 = (CCR / (ARR + 1)) × 100%
在我们的代码中,ARR=999,所以:
- CCR = 0 → 占空比0%
- CCR = 500 → 占空比50%
- CCR = 999 → 占空比100%
4. STM32CubeMX中PWM配置详解
4.1 定时器配置步骤
- 选择定时器:TIM3用于PA1,TIM2/TIM3备用
- 时钟源:内部时钟
- 通道配置:PWM Generation CH2
- 参数设置:
- Prescaler:71
- Counter Period:999
- Pulse:初始占空比
- CH Polarity:High/Low

- 选择定时器:TIM3用于PA1,TIM2/TIM3备用
- 时钟源:内部时钟
- 通道配置:PWM Generation CH3
- 参数设置:
- Prescaler:71
- Counter Period:999
- Pulse:初始占空比
- CH Polarity:High/Low

4.2 引脚配置
- PA1:TIM3_CH1,支持硬件PWM
- PC13:通常不支持硬件PWM,需要软件模拟
5. 硬件PWM vs 软件PWM
5.1 硬件PWM(TIM3控制PA1)
优点:
- 精度高,稳定性好
- 不占用CPU资源
- 频率和占空比精确可控
实现方式:
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pulse_value);
5.2 软件PWM(控制PC13)
优点:
- 可在任何GPIO上实现
- 配置简单
缺点:
- 占用CPU资源
- 精度和稳定性较差
实现方式:
// 软件PWM计数器
static uint32_t pwm_counter = 0;
// 在主循环中
if(pwm_counter < target_pulse) {
HAL_GPIO_WritePin(GPIOx, GPIO_PIN_x, LED_ON);
} else {
HAL_GPIO_WritePin(GPIOx, GPIO_PIN_x, LED_OFF);
}
pwm_counter = (pwm_counter + 1) % PWM_PERIOD;
6. 呼吸灯算法实现
6.1 线性呼吸算法
// 呼吸灯核心算法
pulse += direction * step;
if(pulse >= MAX_VALUE) {
pulse = MAX_VALUE;
direction = -1; // 改为递减
} else if(pulse <= MIN_VALUE) {
pulse = MIN_VALUE;
direction = 1; // 改为递增
}
6.2 呼吸灯效果调节
- 步长(step):控制亮度变化速度
- 延时时间:控制呼吸周期
- 最大/最小值:控制亮度范围
结果展示
视频
mmexport1760942956673
标题STM32多任务并发实现方案
1. 项目概述
本项目基于STM32微控制器,使用硬件定时器实现多任务并发功能,同时完成两个周期性任务:
- 任务1:每隔5秒从串口发送"hello windows!"
- 任务2:每隔2秒控制LED灯周期性闪烁
2. STM32CubeMX详细配置步骤
2.1 创建新项目
- 打开STM32CubeMX,点击"New Project"
- 选择对应的STM32系列芯片(如STM32F103C8T6)
- 在Pinout视图中开始配置引脚
2.2 系统核心配置
- SYS配置:
- Debug: Serial Wire(用于SWD调试)
- Timebase Source: SysTick(保持默认)

- RCC配置:
- High Speed Clock (HSE): Crystal/Ceramic Resonator
- 启用外部高速时钟

- 时钟树配置:
- HSE输入频率:8MHz
- PLL倍频设置:×9
- 系统时钟:72MHz
- AHB预分频器:1
- APB1预分频器:2(36MHz)
- APB2预分频器:1(72MHz)

2.3 外设配置
GPIO配置
- 找到PA1引脚(或其他可用GPIO)我选择的是PA1
- 设置为GPIO_Output
- 配置如下:
- GPIO output level: Low
- GPIO mode: Output Push Pull
- GPIO Pull-up/Pull-down: No pull-up and no pull-down
- Maximum output speed: Low

USART配置
- 选择USART1
- 模式:Asynchronous
- 基本参数:
- Baud Rate: 115200
- Word Length: 8 Bits
- Parity: None
- Stop Bits: 1
- Over Sampling: 16 Samples

TIM2配置(5秒定时器)
-
选择TIM2
-
配置参数:
- Clock Source: Internal Clock
- Prescaler: 7199
- Counter Period: 4999
- auto-reload preload: Enable
- 计算公式:72MHz / (7199+1) = 10kHz,10kHz / 5000 = 2Hz(0.5秒周期)
- 实际使用软件计数实现5秒

-
NVIC设置:
- TIM2 global interrupt: Enable

- TIM2 global interrupt: Enable
TIM3配置(2秒定时器)
- 选择TIM3
- 配置参数:
- Clock Source: Internal Clock
- Prescaler: 7199
- Counter Period: 1999
- auto-reload preload: Enable
- 计算公式:72MHz / (7199+1) = 10kHz,10kHz / 2000 = 5Hz(0.2秒周期)
- 实际使用软件计数实现2秒

- NVIC设置:
- TIM3 global interrupt: Enable


- TIM3 global interrupt: Enable
2.4 项目生成设置
-
Project Manager:
- Project Name: MultiTask_Timer
- Project Location: 选择合适路径
- Toolchain/IDE: MDK-ARM V5
-
Code Generator:
- 选择"Copy only necessary library files"
- 选择"Generate peripheral initialization as pair of ‘.c/.h’"
- 选择"Set all free pins as analog"
-
点击"GENERATE CODE"生成项目

3. 硬件定时器中断方案
3.1 核心特性
- 技术基础:使用STM32的TIM2和TIM3硬件定时器
- 中断机制:基于1ms定时器中断进行时间累积
- 任务触发:通过标志位在主循环中处理任务
- 精度:高精度,不受主循环执行时间影响
3.2 代码架构
宏定义和变量声明
/* USER CODE BEGIN PD */
// 定义LED引脚
#define LED_PIN GPIO_PIN_1
#define LED_PORT GPIOA
// 定时目标值 (基于1ms中断)
#define TIMER_5S_TARGET 5000 // 5秒 = 5000 × 1ms
#define TIMER_2S_TARGET 2000 // 2秒 = 2000 × 1ms
/* USER CODE END PD */
/* USER CODE BEGIN PV */
// 定时器相关变量
volatile uint32_t timer2_counter = 0;
volatile uint32_t timer3_counter = 0;
volatile uint8_t timer2_flag = 0;
volatile uint8_t timer3_flag = 0;
// 调试变量
uint32_t last_debug_time = 0;
uint32_t debug_counter = 0;
/* USER CODE END PV */
主函数初始化
int main(void)
{
/* MCU初始化 */
HAL_Init();
SystemClock_Config();
/* 外设初始化 */
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
MX_NVIC_Init();
/* USER CODE BEGIN 2 */
// 启动定时器
HAL_TIM_Base_Start_IT(&htim2); // TIM2用于5秒定时
HAL_TIM_Base_Start_IT(&htim3); // TIM3用于2秒定时
// 初始化LED为关闭状态
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
// LED硬件测试
char startup_msg[] = "系统启动,开始LED硬件测试...\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)startup_msg, strlen(startup_msg), HAL_MAX_DELAY);
// 快速闪烁测试LED
for(int i = 0; i < 6; i++) {
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
HAL_Delay(200);
}
char test_complete_msg[] = "LED硬件测试完成,开始定时器任务\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)test_complete_msg, strlen(test_complete_msg), HAL_MAX_DELAY);
/* USER CODE END 2 */
主循环任务处理
while (1)
{
uint32_t current_time = HAL_GetTick();
// 每秒输出调试信息
if (current_time - last_debug_time >= 1000) {
last_debug_time = current_time;
debug_counter++;
char debug_msg[128];
sprintf(debug_msg, "[%lu秒] T2计数:%lu T3计数:%lu T2标志:%d T3标志:%d 循环:%lu\r\n",
current_time/1000, timer2_counter, timer3_counter, timer2_flag, timer3_flag, debug_counter);
HAL_UART_Transmit(&huart1, (uint8_t*)debug_msg, strlen(debug_msg), HAL_MAX_DELAY);
}
// 处理5秒定时任务 - 串口发送
if (timer2_flag) {
timer2_flag = 0;
char hello_msg[] = "=== hello windows! ===\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)hello_msg, strlen(hello_msg), HAL_MAX_DELAY);
}
// 处理2秒定时任务 - LED闪烁
if (timer3_flag) {
timer3_flag = 0;
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
char led_msg[50];
sprintf(led_msg, "LED状态: %s\r\n", HAL_GPIO_ReadPin(LED_PORT, LED_PIN) ? "ON" : "OFF");
HAL_UART_Transmit(&huart1, (uint8_t*)led_msg, strlen(led_msg), HAL_MAX_DELAY);
}
// 这里可以添加其他后台任务
HAL_Delay(10); // 小延时,让出CPU时间处理其他任务
}
定时器中断回调函数
/* USER CODE BEGIN 4 */
/**
* @brief 定时器中断回调函数
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2) {
// 5秒定时器 - 累积计数
timer2_counter++;
if (timer2_counter >= TIMER_5S_TARGET) {
timer2_counter = 0;
timer2_flag = 1;
}
}
else if (htim->Instance == TIM3) {
// 2秒定时器 - 累积计数
timer3_counter++;
if (timer3_counter >= TIMER_2S_TARGET) {
timer3_counter = 0;
timer3_flag = 1;
}
}
}
/* USER CODE END 4 */
3.3 系统时钟配置
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_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();
}
}
3.4 NVIC中断配置
static void MX_NVIC_Init(void)
{
/* TIM2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/* TIM3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
}
4. 定时器配置原理
4.1 定时器计算公式
定时器频率 = 系统时钟 / (预分频器 + 1)
中断周期 = (计数器周期 + 1) / 定时器频率
4.2 TIM2配置(5秒定时基础)
- 系统时钟:72MHz
- 预分频器:7199
- 定时器频率:72MHz / 7200 = 10kHz
- 中断周期:5000 / 10kHz = 0.5秒
- 软件计数:10次中断 = 5秒
4.3 TIM3配置(2秒定时基础)
- 系统时钟:72MHz
- 预分频器:7199
- 定时器频率:72MHz / 7200 = 10kHz
- 中断周期:2000 / 10kHz = 0.2秒
- 软件计数:10次中断 = 2秒
5. 性能特点
5.1 优势
- ✅ 高精度定时:硬件定时器提供精确的时间基准
- ✅ 实时响应:中断机制确保及时响应
- ✅ 低CPU占用:主循环可处理其他任务
- ✅ 可扩展性:易于添加更多定时任务
- ✅ 稳定性:不受主循环执行时间影响
5.2 技术要点
- 中断优先级:合理设置NVIC优先级避免冲突
- 标志位处理:使用volatile变量确保数据一致性
- 资源管理:合理配置定时器参数避免资源浪费
- 错误处理:包含完整的错误处理机制
6. 测试验证
6.1 功能测试项目
- 系统启动测试:验证硬件初始化和LED测试
- 定时精度测试:检查5秒和2秒定时的准确性
- 多任务并发测试:验证两个任务能否同时正常运行
- 长时间运行测试:检查系统稳定性
6.2 预期输出结果
系统启动,开始LED硬件测试...
LED硬件测试完成,开始定时器任务
[1秒] T2计数:1000 T3计数:1000 T2标志:0 T3标志:0 循环:1
LED状态: ON
[2秒] T2计数:2000 T3计数:0 T2标志:0 T3标志:1 循环:2
[3秒] T2计数:3000 T3计数:1000 T2标志:0 T3标志:0 循环:3
LED状态: OFF
[4秒] T2计数:4000 T3计数:0 T2标志:0 T3标志:1 循环:4
=== hello windows! ===
[5秒] T2计数:0 T3计数:1000 T2标志:1 T3标志:0 循环:5
6.3测试结果

视频
1.LED
mmexport1760930152907
2.串口表现
mmexport1760930217986
7. 应用扩展建议
7.1 功能扩展
- 增加更多定时任务:使用其他定时器或优化现有定时器
- 实现任务优先级:在中断处理中添加优先级机制
- 添加参数配置:通过串口动态修改定时参数
7.2 优化方向
- 功耗优化:在空闲任务中进入低功耗模式
- 通信优化:使用DMA进行串口数据传输
- 可靠性提升:添加看门狗和异常恢复机制
8. 总结
综上所述,本报告系统地阐述了STM32嵌入式系统中两项核心技术的原理与实现:PWM(脉冲宽度调制) 与基于硬件定时器的多任务并发。
通过PWM技术,我们实现了对输出信号的精确模拟控制,从呼吸灯这一经典应用入手,深入剖析了其背后的数学模型、STM32的硬件配置方法以及关键参数的计算,展现了如何利用微控制器的定时器外设,以数字之道实现模拟控制之效。
进而,通过多任务并发项目,我们演示了如何利用STM32的硬件定时器中断,构建一个稳定、精准且低CPU占用的多任务调度系统。该方案成功实现了“5秒串口通信”与“2秒LED闪烁”任务的并行不悖,其设计思想——包括中断服务例程的轻量化、标志位通信机制以及主循环的任务分发——为开发更复杂的实时嵌入式应用奠定了坚实的基础。
从精确的PWM波形控制到可靠的多任务调度,本报告所呈现的不仅是具体的技术实现,更是一种嵌入式系统的设计哲学:高效地利用硬件资源,通过中断与主循环的协同,在有限的资源下构建出稳定、响应及时且易于维护的应用系统。
展望未来,在此基础之上,我们可以进一步集成更多的功能模块,如传感器数据采集、电机伺服控制、通信协议栈(如MQTT、Modbus)等,并引入实时操作系统(RTOS)来管理更复杂的任务调度与资源共享,从而将系统扩展至物联网节点、工业控制器、智能设备等更广阔的应用领域。
至此,STM32的探索之旅告一段落,但它所开启的嵌入式世界大门已然敞开。掌握了这些核心基础,开发者便拥有了将创新想法转化为现实产品的钥匙,能够在嵌入式开发的广阔天地中,构建出更多智能、高效的电子系统。
感谢友情链接
https://blog.csdn.net/weixin_64559251/article/details/127561710
https://blog.csdn.net/weixin_64559251/article/details/127581276
http://www.mcublog.cn/stm32/2021_01/stm32cubemx-dingshiqi-led/
http://www.mcublog.cn/stm32/2021_01/stm32cubemx-pwm-huxideng/
https://blog.csdn.net/zmhDD/article/details/111942507
https://blog.csdn.net/qq_45237293/article/details/111997424
更多推荐

所有评论(0)