STM32F767 PWM的基本用法

 本文介绍STM32F767 PWM的基本用法。

开发环境

硬件环境

  • 电脑:Windows 10 Home x64
  • Apollo STM32F767开发板(ST-LINK V2仿真器)

软件环境

  • Keil Version 5.24.1 (Pack Installer:Keil.STM32F7xx_DFP.2.9.0.pack)
  • STM32CubeMX Version 4.25.0(Packages Manager:STM32CubeF7)

TIM1主要特性

TIM1/TIM8为高级控制定时器,支持的PWM功能最为完善。本例将使用TIM1完成PWM实验:输出3对互补并带有死区的PWM

系统框图

主要特性

 本次主要列出PWM的相关特性。

  • 多达6个独立通道,可以用于PWM生成(边沿与中心对齐模式);
  • 可编程死区的互补输出;
  • 2个断路输出,用于将定时器的输出信号置于用户可选的安全配置中。

PWM的基本用法

PWM参数设置

 PWM的初始化代码总体如下所示。

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
80
81
82
83
84
/* TIM1 init function */
void MX_TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
htim1.Instance = TIM1;
htim1.Init.Prescaler = 216-1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 100-1;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 20;
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(__FILE__, __LINE__);
}
sConfigOC.Pulse = 30;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.Pulse = 40;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCNPolarity = TIM_OCPOLARITY_LOW;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 100;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
sBreakDeadTimeConfig.Break2Filter = 0;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_TIM_MspPostInit(&htim1);
}

 以下将对代码进行分析。

时基初始化

 时基初始化函数如下所示。

1
2
3
4
5
6
7
8
9
10
11
htim1.Instance = TIM1;
htim1.Init.Prescaler = 216-1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 100-1;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

 时基单元的初始化在timer实验中已经介绍,这里不再叙述,其功能如下:

  • TIM1使用APB2时钟,本例中APB2时钟频率为216MHz
  • 定时器周期值为100-1,则一个周期内共计数100次;
  • 定时器预分频器值为216-1,则时钟频率为1MHz
  • 定时器时钟频率为1MHz,计数值为100,则定时器周期/PWM频率10kHz
  • TIMx_CR1寄存器CKD位为0,则死区时钟相对于TIM输入时钟不分频

时钟源选择

 时钟源的选择通过如下函数实现。本例中时钟选择为内部时钟,即将TIMx_SMCR寄存器SMS控制位设置为0000

1
2
3
4
5
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

PWM初始化

 PWM初始化代码如下所示。

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
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**
* @brief Initializes the TIM PWM Time Base according to the specified
* parameters in the TIM_HandleTypeDef and create the associated handle.
* @param htim pointer to a TIM_HandleTypeDef structure that contains
* the configuration information for TIM module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
{
/* Check the TIM handle allocation */
if(htim == NULL)
{
return HAL_ERROR;
}
/* Check the parameters */
assert_param(IS_TIM_INSTANCE(htim->Instance));
assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
if(htim->State == HAL_TIM_STATE_RESET)
{
/* Allocate lock resource and initialize it */
htim->Lock = HAL_UNLOCKED;
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
/* Reset interrupt callbacks to legacy week callbacks */
TIM_ResetCallback(htim);
if(htim->PWM_MspInitCallback == NULL)
{
htim->PWM_MspInitCallback = HAL_TIM_PWM_MspInit;
}
/* Init the low level hardware : GPIO, CLOCK, NVIC */
htim->PWM_MspInitCallback(htim);
#else
/* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
HAL_TIM_PWM_MspInit(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
}
/* Set the TIM state */
htim->State= HAL_TIM_STATE_BUSY;
/* Init the base time for the PWM */
TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* Initialize the TIM state*/
htim->State= HAL_TIM_STATE_READY;
return HAL_OK;
}

 实际上该函数并无实际意义。

  • htim->State在TIM时基初始化中已经更改为HAL_TIM_STATE_READY,所以判定条件以内函数不执行;
  • TIM_Base_SetConfig()函数已经在HAL_TIM_Base_Init()中首次调用,这里为二次调用,这期间时基配置并未进行任何更新。

主模式设置

 主模式设置代码如下所示。

1
2
3
4
5
6
7
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

 TIM1的主模式输出均为RESET状态,即只能通过寄存器TIMx_EGRUG位生成更新事件。TIM1不进行主从模式控制。

PWM通道配置

 PWM通道配置代码如下所示,这里以channel1为例。

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
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 20;
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(__FILE__, __LINE__);
}
sConfigOC.Pulse = 30;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.Pulse = 40;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCNPolarity = TIM_OCPOLARITY_LOW;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

 初始化代码配置了PWM的基本参数:

  • 模式为TIM_OCMODE_PWM1
  • 脉冲宽度20,考虑到周期值100,如果不考虑死区即占空比为20%
  • OCOCN的极性均为高电平
  • 禁用FastMode
  • 空闲状态下为低电平。

 PWM参数的配置通过HAL_TIM_PWM_ConfigChannel()函数实现。

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* @brief Initializes the TIM PWM channels according to the specified
* parameters in the TIM_OC_InitTypeDef.
* @param htim pointer to a TIM_HandleTypeDef structure that contains
* the configuration information for TIM module.
* @param sConfig TIM PWM configuration structure
* @param Channel TIM Channels to be enabled.
* This parameter can be one of the following values:
* @arg TIM_CHANNEL_1: TIM Channel 1 selected
* @arg TIM_CHANNEL_2: TIM Channel 2 selected
* @arg TIM_CHANNEL_3: TIM Channel 3 selected
* @arg TIM_CHANNEL_4: TIM Channel 4 selected
* @retval HAL status
*/
__weak HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim, TIM_OC_InitTypeDef* sConfig, uint32_t Channel)
{
__HAL_LOCK(htim);
/* Check the parameters */
assert_param(IS_TIM_CHANNELS(Channel));
assert_param(IS_TIM_PWM_MODE(sConfig->OCMode));
assert_param(IS_TIM_OC_POLARITY(sConfig->OCPolarity));
assert_param(IS_TIM_FAST_STATE(sConfig->OCFastMode));
htim->State = HAL_TIM_STATE_BUSY;
switch (Channel)
{
case TIM_CHANNEL_1:
{
assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
/* Configure the Channel 1 in PWM mode */
TIM_OC1_SetConfig(htim->Instance, sConfig);
/* Set the Preload enable bit for channel1 */
htim->Instance->CCMR1 |= TIM_CCMR1_OC1PE;
/* Configure the Output Fast mode */
htim->Instance->CCMR1 &= ~TIM_CCMR1_OC1FE;
htim->Instance->CCMR1 |= sConfig->OCFastMode;
}
break;
case TIM_CHANNEL_2:
{
assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
/* Configure the Channel 2 in PWM mode */
TIM_OC2_SetConfig(htim->Instance, sConfig);
/* Set the Preload enable bit for channel2 */
htim->Instance->CCMR1 |= TIM_CCMR1_OC2PE;
/* Configure the Output Fast mode */
htim->Instance->CCMR1 &= ~TIM_CCMR1_OC2FE;
htim->Instance->CCMR1 |= sConfig->OCFastMode << 8;
}
break;
case TIM_CHANNEL_3:
{
assert_param(IS_TIM_CC3_INSTANCE(htim->Instance));
/* Configure the Channel 3 in PWM mode */
TIM_OC3_SetConfig(htim->Instance, sConfig);
/* Set the Preload enable bit for channel3 */
htim->Instance->CCMR2 |= TIM_CCMR2_OC3PE;
/* Configure the Output Fast mode */
htim->Instance->CCMR2 &= ~TIM_CCMR2_OC3FE;
htim->Instance->CCMR2 |= sConfig->OCFastMode;
}
break;
case TIM_CHANNEL_4:
{
assert_param(IS_TIM_CC4_INSTANCE(htim->Instance));
/* Configure the Channel 4 in PWM mode */
TIM_OC4_SetConfig(htim->Instance, sConfig);
/* Set the Preload enable bit for channel4 */
htim->Instance->CCMR2 |= TIM_CCMR2_OC4PE;
/* Configure the Output Fast mode */
htim->Instance->CCMR2 &= ~TIM_CCMR2_OC4FE;
htim->Instance->CCMR2 |= sConfig->OCFastMode << 8;
}
break;
default:
break;
}
htim->State = HAL_TIM_STATE_READY;
__HAL_UNLOCK(htim);
return HAL_OK;
}
/**
* @brief Time Output Compare 1 configuration
* @param TIMx to select the TIM peripheral
* @param OC_Config The output configuration structure
* @retval None
*/
void TIM_OC1_SetConfig(TIM_TypeDef *TIMx, TIM_OC_InitTypeDef *OC_Config)
{
uint32_t tmpccmrx = 0;
uint32_t tmpccer = 0;
uint32_t tmpcr2 = 0;
/* Disable the Channel 1: Reset the CC1E Bit */
TIMx->CCER &= ~TIM_CCER_CC1E;
/* Get the TIMx CCER register value */
tmpccer = TIMx->CCER;
/* Get the TIMx CR2 register value */
tmpcr2 = TIMx->CR2;
/* Get the TIMx CCMR1 register value */
tmpccmrx = TIMx->CCMR1;
/* Reset the Output Compare Mode Bits */
tmpccmrx &= ~TIM_CCMR1_OC1M;
tmpccmrx &= ~TIM_CCMR1_CC1S;
/* Select the Output Compare Mode */
tmpccmrx |= OC_Config->OCMode;
/* Reset the Output Polarity level */
tmpccer &= ~TIM_CCER_CC1P;
/* Set the Output Compare Polarity */
tmpccer |= OC_Config->OCPolarity;
if(IS_TIM_ADVANCED_INSTANCE(TIMx) != RESET)
{
/* Reset the Output N Polarity level */
tmpccer &= ~TIM_CCER_CC1NP;
/* Set the Output N Polarity */
tmpccer |= OC_Config->OCNPolarity;
/* Reset the Output N State */
tmpccer &= ~TIM_CCER_CC1NE;
/* Reset the Output Compare and Output Compare N IDLE State */
tmpcr2 &= ~TIM_CR2_OIS1;
tmpcr2 &= ~TIM_CR2_OIS1N;
/* Set the Output Idle state */
tmpcr2 |= OC_Config->OCIdleState;
/* Set the Output N Idle state */
tmpcr2 |= OC_Config->OCNIdleState;
}
/* Write to TIMx CR2 */
TIMx->CR2 = tmpcr2;
/* Write to TIMx CCMR1 */
TIMx->CCMR1 = tmpccmrx;
/* Set the Capture Compare Register value */
TIMx->CCR1 = OC_Config->Pulse;
/* Write to TIMx CCER */
TIMx->CCER = tmpccer;
}

 代码功能如下:

  • TIMx->CCER寄存器CC1E位复位,禁用OC1
  • tmpccer保存TIMx->CCER值,tmpcr2保存TIMx->CR2值,tmpccmrx保存TIMx->CCMR1值;
  • tmpccmrx复位OC1MCC1S位;CC1S被复位后,代表OC1通道被设置为输出模式;
  • tmpccmrx写入OCMode,本例中OCMode配置为TIM_OCMODE_PWM1,即对应OC1M配置为0b0110,即为PWM模式1:当计数器处于增计数时,TIMx_CNT<TIMx_CCR1channel1active
  • tmpccer复位CC1P,并写入active状态的电平,本例为active对应高电平;
  • TIM1为高级控制定时器,可以输出互补PWM,则有tmpccer复位CC1NP位,并写入active状态的电平,本例为active对应高电平;
  • tmpccer复位CC1NE位,则OC1N输出关闭;
  • tmpcr2复位OIS1OIS1N位,并写入OC输出空闲的状态,本例对应均为输出空闲状态为复位状态;
  • TIMx->CR2寄存器写入tmpcr2值;
  • TIMx->CCMR1寄存器写入tmpccmrx值;
  • TIMx->CCR1寄存器写入脉宽值,决定了PWM的占空比;
  • TIMx->CCER寄存器写入tmpccer值;
  • TIMx->CCMR1寄存器置位OC1PE位,使能OC1预装载;
  • TIMx->CCMR1寄存器复位OC1PE并写入OCFastMode值,本例为禁止fast模式。fast模式进对于输入比较有效,此处配置无意义;
  • 注意channel1channel2配置OC有效电平为高电平channel3配置OC有效电平为低电平

Break与DeadTime配置

BreakDeadTime配置代码如下所示。其中最主要的设置为设置死区时间为100

 在时基初始化中,PWM死区的时钟相对TIM输入时钟不进行分频,本例中即为216MHz(PWM死区的时钟周期定义为Tdts)。死区的设置通过TIMx_BDTR寄存器的DTG位实现:

  • $DTG[7:5]=0xx$时,$DT=DTG[7:0] \cdot Tdt$g,其中 $Tdtg=Tdts$,则等效可以设置的$Tdts$周期数为0~127;
  • $DTG[7:5]=10x$时,$DT=(64+DTG[5:0]) \cdot Tdtg$,其中$Tdtg=2 \cdot Tdts$,则等效可以设置的$Tdts$周期数为128~254;
  • $DTG[7:5]=11x$时,$DT=(32+DTG[ 4:0]) \cdot Tdtg$,其中$Tdtg=8 \cdot Tdts$,则等效可以设置的$Tdts$周期数为256~504;
  • $DTG[7:5]=111$时,$DT=(32+DTG[ 4:0]) \cdot Tdtg$,其中$Tdtg= 16 \cdot Tdts$,则等效可以设置的$Tdts$周期数为512~1008;
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
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 100;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
sBreakDeadTimeConfig.Break2Filter = 0;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**
* @brief Configures the Break feature, dead time, Lock level, OSSI/OSSR State
* and the AOE(automatic output enable).
* @param htim pointer to a TIM_HandleTypeDef structure that contains
* the configuration information for TIM module.
* @param sBreakDeadTimeConfig pointer to a TIM_ConfigBreakDeadConfig_TypeDef structure that
* contains the BDTR Register configuration information for the TIM peripheral.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIMEx_ConfigBreakDeadTime(TIM_HandleTypeDef *htim,
TIM_BreakDeadTimeConfigTypeDef * sBreakDeadTimeConfig)
{
uint32_t tmpbdtr = 0;
/* Check the parameters */
assert_param(IS_TIM_BREAK_INSTANCE(htim->Instance));
assert_param(IS_TIM_OSSR_STATE(sBreakDeadTimeConfig->OffStateRunMode));
assert_param(IS_TIM_OSSI_STATE(sBreakDeadTimeConfig->OffStateIDLEMode));
assert_param(IS_TIM_LOCK_LEVEL(sBreakDeadTimeConfig->LockLevel));
assert_param(IS_TIM_DEADTIME(sBreakDeadTimeConfig->DeadTime));
assert_param(IS_TIM_BREAK_STATE(sBreakDeadTimeConfig->BreakState));
assert_param(IS_TIM_BREAK_POLARITY(sBreakDeadTimeConfig->BreakPolarity));
assert_param(IS_TIM_BREAK_FILTER(sBreakDeadTimeConfig->BreakFilter));
assert_param(IS_TIM_AUTOMATIC_OUTPUT_STATE(sBreakDeadTimeConfig->AutomaticOutput));
assert_param(IS_TIM_BREAK2_STATE(sBreakDeadTimeConfig->Break2State));
assert_param(IS_TIM_BREAK2_POLARITY(sBreakDeadTimeConfig->Break2Polarity));
assert_param(IS_TIM_BREAK_FILTER(sBreakDeadTimeConfig->Break2Filter));
/* Check input state */
__HAL_LOCK(htim);
/* Set the Lock level, the Break enable Bit and the Polarity, the OSSR State,
the OSSI State, the dead time value and the Automatic Output Enable Bit */
/* Set the BDTR bits */
MODIFY_REG(tmpbdtr, TIM_BDTR_DTG, sBreakDeadTimeConfig->DeadTime);
MODIFY_REG(tmpbdtr, TIM_BDTR_LOCK, sBreakDeadTimeConfig->LockLevel);
MODIFY_REG(tmpbdtr, TIM_BDTR_OSSI, sBreakDeadTimeConfig->OffStateIDLEMode);
MODIFY_REG(tmpbdtr, TIM_BDTR_OSSR, sBreakDeadTimeConfig->OffStateRunMode);
MODIFY_REG(tmpbdtr, TIM_BDTR_BKE, sBreakDeadTimeConfig->BreakState);
MODIFY_REG(tmpbdtr, TIM_BDTR_BKP, sBreakDeadTimeConfig->BreakPolarity);
MODIFY_REG(tmpbdtr, TIM_BDTR_AOE, sBreakDeadTimeConfig->AutomaticOutput);
MODIFY_REG(tmpbdtr, TIM_BDTR_MOE, sBreakDeadTimeConfig->AutomaticOutput);
MODIFY_REG(tmpbdtr, TIM_BDTR_BKF, (sBreakDeadTimeConfig->BreakFilter << BDTR_BKF_SHIFT));
if (IS_TIM_BKIN2_INSTANCE(htim->Instance))
{
assert_param(IS_TIM_BREAK2_STATE(sBreakDeadTimeConfig->Break2State));
assert_param(IS_TIM_BREAK2_POLARITY(sBreakDeadTimeConfig->Break2Polarity));
assert_param(IS_TIM_BREAK_FILTER(sBreakDeadTimeConfig->Break2Filter));
/* Set the BREAK2 input related BDTR bits */
MODIFY_REG(tmpbdtr, TIM_BDTR_BK2F, (sBreakDeadTimeConfig->Break2Filter << BDTR_BK2F_SHIFT));
MODIFY_REG(tmpbdtr, TIM_BDTR_BK2E, sBreakDeadTimeConfig->Break2State);
MODIFY_REG(tmpbdtr, TIM_BDTR_BK2P, sBreakDeadTimeConfig->Break2Polarity);
}
/* Set TIMx_BDTR */
htim->Instance->BDTR = tmpbdtr;
__HAL_UNLOCK(htim);
return HAL_OK;
}

 对代码的分析如下:

  • 死区时间设置为100,即为0b0110 0100,所以对应的死区时间为100个$Tdts$周期数,即为463ns
  • 运行模式下与空闲模式下处于无效状态时,设置为禁止OC/OCN输出
  • 锁定配置不生效;
  • Break功能本例不涉及,全部禁用状态。

硬件初始化

 硬件初始化函数如下所示。硬件初始化即为对PWM IO的初始化,这里不再说明。

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
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(timHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspPostInit 0 */
/* USER CODE END TIM1_MspPostInit 0 */
/**TIM1 GPIO Configuration
PE11 ------> TIM1_CH2
PE13 ------> TIM1_CH3
PB13 ------> TIM1_CH1N
PB14 ------> TIM1_CH2N
PB15 ------> TIM1_CH3N
PA8 ------> TIM1_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM1_MspPostInit 1 */
/* USER CODE END TIM1_MspPostInit 1 */
}
}

使能PWM输出

 使能PWM输出的函数如下所示。

1
2
3
4
5
6
7
8
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1); // turn on complementary channel
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2); // turn on complementary channel
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3); // turn on complementary channel

 以channel1为例,其具体实现如下。首先为PWM信号生成使能。

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
/**
* @brief Starts the PWM signal generation.
* @param htim pointer to a TIM_HandleTypeDef structure that contains
* the configuration information for TIM module.
* @param Channel TIM Channels to be enabled.
* This parameter can be one of the following values:
* @arg TIM_CHANNEL_1: TIM Channel 1 selected
* @arg TIM_CHANNEL_2: TIM Channel 2 selected
* @arg TIM_CHANNEL_3: TIM Channel 3 selected
* @arg TIM_CHANNEL_4: TIM Channel 4 selected
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
/* Check the parameters */
assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
/* Enable the Capture compare channel */
TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
if(IS_TIM_ADVANCED_INSTANCE(htim->Instance) != RESET)
{
/* Enable the main output */
__HAL_TIM_MOE_ENABLE(htim);
}
/* Enable the Peripheral */
__HAL_TIM_ENABLE(htim);
/* Return function status */
return HAL_OK;
}
void TIM_CCxChannelCmd(TIM_TypeDef* TIMx, uint32_t Channel, uint32_t ChannelState)
{
uint32_t tmp = 0;
/* Check the parameters */
assert_param(IS_TIM_CC1_INSTANCE(TIMx));
assert_param(IS_TIM_CHANNELS(Channel));
tmp = TIM_CCER_CC1E << Channel;
/* Reset the CCxE Bit */
TIMx->CCER &= ~tmp;
/* Set or reset the CCxE Bit */
TIMx->CCER |= (uint32_t)(ChannelState << Channel);
}
/**
* @brief Enable the TIM main Output.
* @param __HANDLE__ TIM handle
* @retval None
*/
#define __HAL_TIM_MOE_ENABLE(__HANDLE__) ((__HANDLE__)->Instance->BDTR|=(TIM_BDTR_MOE))
/**
* @brief Enable the TIM peripheral.
* @param __HANDLE__ TIM handle
* @retval None
*/
#define __HAL_TIM_ENABLE(__HANDLE__) ((__HANDLE__)->Instance->CR1|=(TIM_CR1_CEN))

 代码功能主要为:

  • TIMx_CCER寄存器CCxE位置位,代表OC开启,信号输出到对应引脚;
  • TIMx_BDTR寄存器MOE位置位,主输出使能开启,当CCxECCxNE使能时,则使能OCOCN
  • TIMx_CR1寄存器CEN置位,使能计数器

 PWM的互补对使能如下。

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
/**
* @brief Starts the PWM signal generation on the complementary output.
* @param htim pointer to a TIM_HandleTypeDef structure that contains
* the configuration information for TIM module.
* @param Channel TIM Channel to be enabled.
* This parameter can be one of the following values:
* @arg TIM_CHANNEL_1: TIM Channel 1 selected
* @arg TIM_CHANNEL_2: TIM Channel 2 selected
* @arg TIM_CHANNEL_3: TIM Channel 3 selected
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIMEx_PWMN_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
/* Check the parameters */
assert_param(IS_TIM_CCXN_INSTANCE(htim->Instance, Channel));
/* Enable the complementary PWM output */
TIM_CCxNChannelCmd(htim->Instance, Channel, TIM_CCxN_ENABLE);
/* Enable the Main Output */
__HAL_TIM_MOE_ENABLE(htim);
/* Enable the Peripheral */
__HAL_TIM_ENABLE(htim);
/* Return function status */
return HAL_OK;
}

 代码功能主要为:

  • TIMx_CCER寄存器CCxNE位置位,代表OCN开启,信号输出到对应引脚;
  • 与PWM主信号一致,使能MOE位并使能定时器。

测试程序

 按照上述配置进行测试,得到的波形信息如下。

  • 其中占空比分别接近20%、30%、60%(channel3为反相);
  • 死区时间约为460ns,与理论值接近。
文章目录
  1. 1. 开发环境
    1. 1.1. 硬件环境
    2. 1.2. 软件环境
  2. 2. TIM1主要特性
    1. 2.1. 系统框图
    2. 2.2. 主要特性
  3. 3. PWM的基本用法
    1. 3.1. PWM参数设置
    2. 3.2. 时基初始化
    3. 3.3. 时钟源选择
    4. 3.4. PWM初始化
    5. 3.5. 主模式设置
    6. 3.6. PWM通道配置
    7. 3.7. Break与DeadTime配置
    8. 3.8. 硬件初始化
    9. 3.9. 使能PWM输出
    10. 3.10. 测试程序
|