N32H487 6Steps例程详解
GaoSheng Lv4

本文会对国民的H487的定时器六步换相例程做一个相对详尽的讲解
图片1

1
2
3
4
5
6
7
8
9
void RCC_Configuration(void)
{
/* TIMx, GPIOx and AFIO clocks enable */
RCC_EnableAHBPeriphClk(TIMx_CLK,ENABLE);
RCC_EnableAHB1PeriphClk(TIMx_CH1_GPIO_CLK | TIMx_CH2_GPIO_CLK |\
TIMx_CH3_GPIO_CLK | TIMx_CH1N_GPIO_CLK |\
TIMx_CH2N_GPIO_CLK | TIMx_CH3N_GPIO_CLK,ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO,ENABLE);
}

第一句是开启ATIM的时钟,使用的HCLK
后面两句是开启GPIO时钟和打开复用功能时钟。
之前用过国民技术N32的朋友可能会观察到为什么这里的高级定时器叫ATIM(Advanced-control Timers)。之前的G和L系列都是没有这个区分的
图片2
通过UM手册中的时钟可以观察到ATIM可以选择两个时钟,SYSCLK和HCLK,对应的API是RCC_ConfigATimClk
图片3
那为什么工程代码直接就给ATIM配置HCLK,没有进行选择呢?
打开UM手册中时钟配置寄存器 2 (RCC_CFG2)的部分
图片4
由于复位值是0x 0000 7000。其中第28位是1.即默认选择HCLK作用ATIM的时钟源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    /* NVIC Configuration */
NVIC_Configuration();

void NVIC_Configuration(void)
{
NVIC_InitType NVIC_InitStructure;

/* Enable the TIMx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIMx_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

配置ATIM的中断等级

1
2
/* GPIO Configuration */
GPIO_Configuration();

图片5
将所使用到的GPIO全部复用到对应的ATIM通道上面
接下来是SysTick的配置

1
2
3
4
5
6
7
8
9
10
11
12
void SysTick_Configuration(void)
{
/* Setup SysTick Timer for 10 msec interrupts ,(SystemCoreClock) / 100) cannot be greater than 0xFFFFFF */
if (SysTick_Config((SystemCoreClock) / 100))
{
/* Capture error */
while (1)
;
}

NVIC_SetPriority(SysTick_IRQn, 0x0);
}

配置systick每10ms触发一次
这里调用SysTick_Config可以展开说说,在core_cm4.h文件中可以看到

1
#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U)

这里表示只有当__Vendor_SysTickConfig被定义,且被定义为0时,才会调用CMSIS的函数。可以看到国民的配置文件中就是这么做的
图片6
补充一点,现在这种情况下,编译后在map文件中无法到SysTick_Config的函数,这是因为该函数被__STATIC_INLINE修饰,已经被内联到调用处

Period(Auto-Reload Register)为2400(需要注意的是这个寄存器是16位)。
周期为外设寄存器的频率除以ARR,即240M/2400 == 100KHz
占空比(CCR/ARR)分别是1200/2400、600/2400、300/2400
图片7

1
TIM_OCInitStructure.OutputNState = TIM_OUTPUT_NSTATE_ENABLE;

打开互补输出

1
2
    /* interrupt Enable */
TIM_ConfigInt(TIMx, TIM_INT_COM, ENABLE);

使能COM中断
图片8

1
2
/* Main Output Enable */
TIM_EnableCtrlPwmOutputs(TIMx, ENABLE);

使能PWM输出。H48X有一点比较特殊:
H48X:
图片9
除了高级定时器ATIM1/2/3外,通用定时器GTIM8/9/10打开PWM竟然也需要这个API。
在别的系列中只有高级定时器有这个额外的操作,以G45X为例:
图片10

翻阅UM手册发现竟然是原因国民H48x系列的GTIM能支持一对CH互补输出
图片11
TIM_EnableCtrlPwmOutputs的其实就是置起TIMx_BKDT(刹车、死区寄存器)的第八位,将OC和OCN使能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void SysTick_Handler(void)
{
if(temp==9)
{
/* Generate TIM1 COM event by software */
TIM_GenerateEvent(TIMx, TIM_EVT_SRC_COM);
temp=0;
}
else
{
temp++;
}

}

Systick中断10ms触发一次,这里进十次中断后使用TIM_GenerateEvent来触发COM事件
图片12
定时器中断中是对这6Step过程中每个PWM的开关做的控制

1
2
TIM_EnableCapCmpCh(TIMx, TIM_CH_3, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_3, TIM_CAP_CMP_N_DISABLE);

这个API可以对定时器的单独的CH做一个开关控制
使用逻辑分析仪查看波形:
图片13
CH1:
图片14
波形与预期相符
下面是完整代码:

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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
int main(void)
{
/* System Clocks Configuration */
RCC_Configuration();

/* NVIC Configuration */
NVIC_Configuration();

/* GPIO Configuration */
GPIO_Configuration();

/* SysTick Configuration */
SysTick_Configuration();

/*
The TIMx peripheral offers the possibility to program in advance the
configuration for the next TIMx outputs behaviour (step) and change the configuration
of all the channels at the same time. This operation is possible when the COM
(commutation) event is used.
The COM event can be generated by software by setting the COM bit in the TIMx_EVTGEN
register or by hardware (on TRC rising edge).
In this example, a software COM event is generated each 100 ms: using the Systick
interrupt.
The TIMx is configured in Timing Mode, each time a COM event occurs,
a new TIMx configuration will be set in advance.
The following Table describes the TIMx Channels states:
-----------------------------------------------
| Step1 | Step2 | Step3 | Step4 | Step5 | Step6 |
----------------------------------------------------------
|Channel1 | 1 | 0 | 0 | 0 | 0 | 1 |
----------------------------------------------------------
|Channel1N | 0 | 0 | 1 | 1 | 0 | 0 |
----------------------------------------------------------
|Channel2 | 0 | 0 | 0 | 1 | 1 | 0 |
----------------------------------------------------------
|Channel2N | 1 | 1 | 0 | 0 | 0 | 0 |
----------------------------------------------------------
|Channel3 | 0 | 1 | 1 | 0 | 0 | 0 |
----------------------------------------------------------
|Channel3N | 0 | 0 | 0 | 0 | 1 | 1 |
----------------------------------------------------------
*/

/* Time Base configuration */
TIM_InitTimBaseStruct(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.Prescaler = 0;
TIM_TimeBaseStructure.CounterMode = TIM_CNT_MODE_UP;
TIM_TimeBaseStructure.Period = 2400;
TIM_TimeBaseStructure.ClkDiv = TIM_CLK_DIV1;
TIM_TimeBaseStructure.RepetCnt = 0;
TIM_InitTimeBase(TIMx, &TIM_TimeBaseStructure);

/* Channel 1, 2 and 3 Configuration in PWM mode */
TIM_InitOcStruct(&TIM_OCInitStructure);
TIM_OCInitStructure.OCMode = TIM_OCMODE_TIMING;
TIM_OCInitStructure.OutputState = TIM_OUTPUT_STATE_ENABLE;
TIM_OCInitStructure.OutputNState = TIM_OUTPUT_NSTATE_ENABLE;
TIM_OCInitStructure.Pulse = 1200;
TIM_OCInitStructure.OCPolarity = TIM_OC_POLARITY_HIGH;
TIM_OCInitStructure.OCNPolarity = TIM_OCN_POLARITY_HIGH;
TIM_OCInitStructure.OCIdleState = TIM_OC_IDLE_STATE_SET;
TIM_OCInitStructure.OCNIdleState = TIM_OCN_IDLE_STATE_SET;
TIM_InitOc1(TIMx, &TIM_OCInitStructure);

TIM_OCInitStructure.Pulse = 600;
TIM_InitOc2(TIMx, &TIM_OCInitStructure);

TIM_OCInitStructure.Pulse = 300;
TIM_InitOc3(TIMx, &TIM_OCInitStructure);

/* Automatic Output enable, Break, dead time and lock configuration*/
TIM_InitBkdtStruct(&TIM_BDTRInitStructure);
TIM_BDTRInitStructure.OSSRState = TIM_OSSR_STATE_ENABLE;
TIM_BDTRInitStructure.OSSIState = TIM_OSSI_STATE_ENABLE;
TIM_BDTRInitStructure.LOCKLevel = TIM_LOCK_LEVEL_OFF;
TIM_BDTRInitStructure.DeadTime = 1;
TIM_BDTRInitStructure.Break = TIM_BREAK_IN_DISABLE;
TIM_BDTRInitStructure.AutomaticOutput = TIM_AUTO_OUTPUT_ENABLE;

TIM_ConfigBkdt(TIMx, &TIM_BDTRInitStructure);

TIM_EnableCapCmpPreloadControl(TIMx, ENABLE);

/* interrupt Enable */
TIM_ConfigInt(TIMx, TIM_INT_COM, ENABLE);

/* TIM1 counter enable */
TIM_Enable(TIMx, ENABLE);

/* Main Output Enable */
TIM_EnableCtrlPwmOutputs(TIMx, ENABLE);

while (1);

}

/**
*\*\name RCC_Configuration.
*\*\fun Configures the different system clocks.
*\*\param none
*\*\return none
**/
void RCC_Configuration(void)
{
/* TIMx, GPIOx and AFIO clocks enable */
RCC_EnableAHBPeriphClk(TIMx_CLK,ENABLE);
RCC_EnableAHB1PeriphClk(TIMx_CH1_GPIO_CLK | TIMx_CH2_GPIO_CLK |\
TIMx_CH3_GPIO_CLK | TIMx_CH1N_GPIO_CLK |\
TIMx_CH2N_GPIO_CLK | TIMx_CH3N_GPIO_CLK,ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO,ENABLE);
}

/**
*\*\name NVIC_Configuration.
*\*\fun Configures the nested vectored interrupt controller.
*\*\param none
*\*\return none
**/
void NVIC_Configuration(void)
{
NVIC_InitType NVIC_InitStructure;

/* Enable the TIMx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIMx_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

/**
*\*\name GPIO_Configuration.
*\*\fun Configures the GPIO pins.
*\*\param none
*\*\return none
**/
void GPIO_Configuration(void)
{
GPIO_InitType GPIO_InitStructure;

GPIO_InitStruct(&GPIO_InitStructure);
/* GPIOx Configuration: Pin of TIMx */
GPIO_InitStructure.Pin = TIMx_CH1_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.GPIO_Slew_Rate = GPIO_SLEW_RATE_SLOW;
GPIO_InitStructure.GPIO_Alternate = TIMx_CH1_GPIO_AF;
GPIO_InitPeripheral(TIMx_CH1_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.Pin = TIMx_CH2_GPIO_PIN;
GPIO_InitStructure.GPIO_Alternate = TIMx_CH2_GPIO_AF;
GPIO_InitPeripheral(TIMx_CH2_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.Pin = TIMx_CH3_GPIO_PIN;
GPIO_InitStructure.GPIO_Alternate = TIMx_CH3_GPIO_AF;
GPIO_InitPeripheral(TIMx_CH3_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.Pin = TIMx_CH1N_GPIO_PIN;
GPIO_InitStructure.GPIO_Alternate = TIMx_CH1N_GPIO_AF;
GPIO_InitPeripheral(TIMx_CH1N_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.Pin = TIMx_CH2N_GPIO_PIN;
GPIO_InitStructure.GPIO_Alternate = TIMx_CH2N_GPIO_AF;
GPIO_InitPeripheral(TIMx_CH2N_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.Pin = TIMx_CH3N_GPIO_PIN;
GPIO_InitStructure.GPIO_Alternate = TIMx_CH3N_GPIO_AF;
GPIO_InitPeripheral(TIMx_CH3N_GPIO_PORT, &GPIO_InitStructure);

}

/**
*\*\name SysTick_Configuration.
*\*\fun Configures the SysTick.
*\*\param none
*\*\return none
**/
void SysTick_Configuration(void)
{
/* Setup SysTick Timer for 10 msec interrupts ,(SystemCoreClock) / 100) cannot be greater than 0xFFFFFF */
if (SysTick_Config((SystemCoreClock) / 100))
{
/* Capture error */
while (1)
;
}

NVIC_SetPriority(SysTick_IRQn, 0x0);
}

void SysTick_Handler(void)
{
if(temp==9)
{
/* Generate TIM1 COM event by software */
TIM_GenerateEvent(TIMx, TIM_EVT_SRC_COM);
temp=0;
}
else
{
temp++;
}

}

/** N32H47X Peripherals Interrupt Handlers, interrupt handler's name please refer
to the startup file (startup_n32h4xx.s) **/

/**
*\*\name TIMx_IRQHandler.
*\*\fun This function handles TIMx interrupts.
*\*\param none
*\*\return none
**/
void TIMx_IRQHandler(void)
{
/* Clear TIMx COM pending bit */
TIM_ClrIntPendingBit(TIMx, TIM_INT_COM);

if (step == 1)
{
/* Next step: Step 2 Configuration ---------------------------- */
/* Channel3 configuration */
TIM_EnableCapCmpCh(TIMx, TIM_CH_3, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_3, TIM_CAP_CMP_N_DISABLE);

/* Channel1 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_1, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_1, TIM_CAP_CMP_ENABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_1, TIM_CAP_CMP_N_DISABLE);

/* Channel2 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_2, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_2, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_2, TIM_CAP_CMP_N_ENABLE);
step++;
}
else if (step == 2)
{
/* Next step: Step 3 Configuration ---------------------------- */
/* Channel2 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_2, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_2, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_2, TIM_CAP_CMP_N_ENABLE);

/* Channel3 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_3, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_3, TIM_CAP_CMP_ENABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_3, TIM_CAP_CMP_N_DISABLE);

/* Channel1 configuration */
TIM_EnableCapCmpCh(TIMx, TIM_CH_1, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_1, TIM_CAP_CMP_N_DISABLE);
step++;
}
else if (step == 3)
{
/* Next step: Step 4 Configuration ---------------------------- */
/* Channel3 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_3, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_3, TIM_CAP_CMP_ENABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_3, TIM_CAP_CMP_N_DISABLE);

/* Channel2 configuration */
TIM_EnableCapCmpCh(TIMx, TIM_CH_2, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_2, TIM_CAP_CMP_N_DISABLE);

/* Channel1 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_1, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_1, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_1, TIM_CAP_CMP_N_ENABLE);
step++;
}
else if (step == 4)
{
/* Next step: Step 5 Configuration ---------------------------- */
/* Channel3 configuration */
TIM_EnableCapCmpCh(TIMx, TIM_CH_3, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_3, TIM_CAP_CMP_N_DISABLE);

/* Channel1 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_1, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_1, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_1, TIM_CAP_CMP_N_ENABLE);

/* Channel2 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_2, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_2, TIM_CAP_CMP_ENABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_2, TIM_CAP_CMP_N_DISABLE);
step++;
}
else if (step == 5)
{
/* Next step: Step 6 Configuration ---------------------------- */
/* Channel3 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_3, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_3, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_3, TIM_CAP_CMP_N_ENABLE);

/* Channel1 configuration */
TIM_EnableCapCmpCh(TIMx, TIM_CH_1, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_1, TIM_CAP_CMP_N_DISABLE);

/* Channel2 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_2, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_2, TIM_CAP_CMP_ENABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_2, TIM_CAP_CMP_N_DISABLE);
step++;
}
else if(step == 6)
{
/* Next step: Step 1 Configuration ---------------------------- */
/* Channel1 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_1, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_1, TIM_CAP_CMP_ENABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_2, TIM_CAP_CMP_N_DISABLE);

/* Channel3 configuration */
TIM_SelectOcMode(TIMx, TIM_CH_3, TIM_OCMODE_PWM1);
TIM_EnableCapCmpCh(TIMx, TIM_CH_3, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_3, TIM_CAP_CMP_N_ENABLE);

/* Channel2 configuration */
TIM_EnableCapCmpCh(TIMx, TIM_CH_2, TIM_CAP_CMP_DISABLE);
TIM_EnableCapCmpChN(TIMx, TIM_CH_2, TIM_CAP_CMP_N_DISABLE);
step = 1;
}
}
本站由 提供部署服务