14.3 高级定时器项目:PWM互补输出实验

# 14.3 高级定时器项目:PWM互补输出实验

本实验通过STM32的高级定时器TIM1生成带死区时间的互补PWM信号,结合示波器验证波形参数,并通过OLED实时显示中断计数。
核心功能

  • 互补PWM输出:TIM1_CH1(PA8)与TIM1_CH1N(PB13)输出互补信号。
  • 死区时间控制:避免上下桥臂直通,确保电路安全。
  • 频率与占空比调节:PWM频率150kHz,占空比可调。

# 14.3.1 STM32CubeMX配置

# 1. 建立新工程

  • 芯片型号:STM32F103C8T6;
  • 项目命名:14 TIM_PWMN ;
  • 主频:72MHz ;
  • 调试接口:SWD。

# 2. 配置TIM1

# 1. 使能TIM1互补输出通道

①点击“Pinout & Configuration”标签页→②在左侧导航栏中点击“Timers”→③双击“TIM1”进行配置→④选择“Clock Source”为“Internal Clock”→⑤选择“Channle1”为“PWM Generation CH1 CH1N”→⑥将自动配置的“TIM1_CH1N”配置为PB13引脚。

开启TIM1互补输出通道

# 2. 配置TIM1工作参数

  • PSC:0;
  • Counter Mode:Up;
  • ARR:480、Pulse:200;
  • Internal Clcok Division:No Division;
  • Repetition Counter:0;
  • Auto-reload preload:Disable;
  • 其他参数保持不变。
开启TIM1互补输出通道

# 3. 配置死区时间管理参数

  • BRK State(刹车输入状态):Disable;
  • Automatic Output State(自动输出状态):Disable、OSSR:Disable;
  • OSSI:Disable;
  • lock configuratio(写保护配置)Off;
  • Dead Time(死区时间):43。
死区时间管理参数

# 4. 配置I2C1

# 5. 引脚分配与复用

  • PA8自动配置为TIM1_CH1;
  • PB13配置为TIM1_CH1N;
  • PB9配置为I2C1_SDA;
  • PB8配置为I2C1_SCL;
  • 若引脚未自动分配或分配到其他引脚,需手动点击选择功能。
死区时间管理参数

# 6. 生成代码

参考第4章,在“Project Manager”标签页中的“Code Generator”导航栏,勾选为每个外设生成单独的“.c”和“.h”文件的选项。 最后,点击“Project→Generate Code”或工具栏的齿轮图标,再或者按下“Ctrl+S”快捷键保存,生成工程代码。在弹出的对话框中勾选“Open project”选项,即可进入代码编辑界面。经过上述的操作,STM32CubeIDE软件配置基本完成。


# 14.3.2 高级定时器项目软件设计

# 1. 初始化函数(代码清单14‑2)

static void MX_TIM1_Init(void)
{
  // 定义并初始化定时器时钟配置结构体
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  // 定义并初始化定时器主模式配置结构体
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  // 定义并初始化定时器输出比较配置结构体
  TIM_OC_InitTypeDef sConfigOC = {0};
  // 定义并初始化定时器断路和死区时间配置结构体
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
  htim1.Instance = TIM1; // 配置TIM1定时器的实例
  htim1.Init.Prescaler = 0; // 设置预分频器为0,表示时钟频率不分频
  // 设置计数器模式为向上计数
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 480; // 设置自动重装载寄存器的值为480
  // 设置时钟分频为不分频
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  // 设置重复计数器的值为0(用于高级定时器)
  htim1.Init.RepetitionCounter = 0;
  // 禁用自动重装载预装载功能
 htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }// 初始化定时器基础配置,如果初始化失败则调用错误处理函数
  // 配置定时器时钟源为内部时钟
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }// 应用时钟源配置,如果配置失败则调用错误处理函数
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }// 初始化定时器的PWM模式,如果初始化失败则调用错误处理函数
  // 配置定时器的主模式,设置主输出触发为复位
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  // 禁用主从模式
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if(HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }// 应用主模式配置,如果配置失败则调用错误处理函数
  sConfigOC.OCMode = TIM_OCMODE_PWM1; // 配置PWM模式为PWM模式1
  sConfigOC.Pulse = 200; // 设置PWM的脉冲宽度为200
  // 设置输出极性为高电平有效
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  // 设置互补输出极性为高电平有效
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  // 禁用输出比较快速模式
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  // 设置输出空闲状态为复位状态
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  // 设置互补输出空闲状态为复位状态
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if(HAL_TIM_PWM_ConfigChannel(&htim1,&sConfigOC,TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }// 配置TIM1的通道1为PWM输出,如果配置失败则调用错误处理函数
  // 配置断路和死区时间
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; 
// 禁用运行模式下的关闭状态
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; 
// 禁用空闲模式下的关闭状态
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; 
// 禁用锁定级别
  sBreakDeadTimeConfig.DeadTime = 43; // 设置死区时间为43个时钟周期
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; 
// 禁用断路功能
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; // 设置断路极性为高电平有效
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; // 禁用自动输出功能
  if(HAL_TIMEx_ConfigBreakDeadTime(&htim1,
&sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }// 应用断路和死区时间配置,如果配置失败则调用错误处理函数
  // 初始化定时器的MSP(MCU Specific Package)后处理
  HAL_TIM_MspPostInit(&htim1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

# 2. 主函数代码(代码清单14‑3)

在main函数的while(1)主循环前,通过HAL_TIM_Base_Start_IT( )启动TIM1的定时器中断,使用HAL_TIM_PWM_Start( )和HAL_TIMEx_PWMN_Start( )启动TIM1的PWM和PWMN输出。主循环中,程序不断调用OLED_NewFrame( )、OLED_PrintString( )和OLED_ShowFrame( )刷新OLED屏幕,显示指定字符串。

#include "font.h"
#include <stdio.h>
#include <string.h>
#include "oled.h"
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM1_Init();
  MX_I2C1_Init();
  HAL_Delay(20);
  OLED_Init();
  HAL_TIM_Base_Start_IT(&htim1);
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
  HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);
  while (1)
  {
    OLED_NewFrame();
    OLED_PrintString(1,1, message, &font16x16, OLED_COLOR_NORMAL);
    OLED_ShowFrame();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 3. 中断回调函数(代码清单14‑4)

在定时器更新中断回调函数中,变量counter为中断回调函数执行的次数,用于初步判断定时器频率是否正确,若每隔一秒发送一次,则定时器频率正确。由于int型是16为数据,最大值为65535,这里定义变量要用long int型,其为32位数据。

/* USER CODE BEGIN 0 */
long int counter=0; //1s计数变量
int i=0;
char message[20];
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 
{
  if(htim == &htim1)                   //6.666us   150k
  {
    counter++;
    if(counter==150000)              //1s
    {
      counter=0;
      sprintf(message,"i:%d",i++);
    }
  }
}
/* USER CODE END 0 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 14.3.3 高级定时器项目下载验证

# 现象与步骤

  1. 烧录程序:通过ST-LINK下载代码至开发板。
  2. 示波器观测
    • PA8(TIM1_CH1):PWM波形,频率149.7kHz。
    • PB13(TIM1_CH1N):互补PWM波形,死区时间约600ns。
  3. OLED显示:每秒更新中断计数(如Count: 5)。

# 硬件实现结果 硬件实现结果

关键说明

  • 频率计算
    $$ f_{\text{PWM}} = \frac{\text{CLK}}{(\text{PSC}+1) \times (\text{ARR}+1)} = \frac{72 \times 10^6}{(0+1) \times (480+1)} \approx 149.7 , \text{kHz} $$
  • 死区时间:43个时钟周期对应约0.597ns(72MHz时钟)。
  • 应用场景:适用于电机驱动、半桥/全桥电路等需避免直通的场景。