SVPWM-实战
接SVPWM原理分析-基于STM32 MC SDK 5.0这篇文章,分析了基本原理和代码
本章节讲实战操作,通过本文可以了解到程序的完整运行流程。,
1.硬件篇,要调试首先要了解基本的硬件状态,本文调试使用的是STM32F302的主控。
MOS管驱动部分如图1所示
配置接口如下:
PA7 ------> TIM1_CH1N
PB0 ------> TIM1_CH2N
PB1 ------> TIM1_CH3N
PA8 ------> TIM1_CH1
PA9 ------> TIM1_CH2
PA10 ------> TIM1_CH3
配置高级定时器TIM1 产生6路互补的PWM,带刹车保护,详细配置代码如下,
把下面的程序段拷贝到main.c 中直接可以输出PWM波形(BKIN下拉),方便读者验证
先看主程序:
Volt_Components Vtest; int8_t bSector; int32_t wX, wY, wZ, wUAlpha, wUBeta; int16_t hTimePhA=0, hTimePhB=0, hTimePhC=0; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM1_Init(); Vtest.qV_Component1=-5000; Vtest.qV_Component2=-2000; while (1) { HAL_Delay(500); HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); CALC_SVPWM( Vtest); }
第一部分,定时器的初始化,如何判断初始化正确:
static void MX_TIM1_Init(void) { /* USER CODE BEGIN TIM1_Init 0 */ /* USER CODE END TIM1_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; /* USER CODE BEGIN TIM1_Init 1 */ /* USER CODE END TIM1_Init 1 */ htim1.Instance = TIM1;//设置频率为16k htim1.Init.Prescaler = ((TIM_CLOCK_DIVIDER) - 1);//分频系数为0 htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;//TIM中央对齐模式1计数模式 htim1.Init.Period = ((PWM_PERIOD_CYCLES) / 2);/*Period max 4500 设置了在下一个更新事件装入活动的自动重装载寄存器周期的值。 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV2;/*设置定时器时钟CK_INT频率与死区发生器以及数字滤波器采样时钟频率分频化。Val ue:*/ htim1.Init.RepetitionCounter = REP_RATE;/*是否使用重复定时器, 当该值不为0的时候,计数器计数值达到周期数时,该值减1,计数器重新计数,当该值减到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(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC4REF; sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; 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(); } if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) { Error_Handler(); } sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_1; sBreakDeadTimeConfig.DeadTime = DEADTIME; sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW; sBreakDeadTimeConfig.BreakFilter = 0; sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE; sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_LOW; sBreakDeadTimeConfig.Break2Filter = 0; sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM1_Init 2 */ /* USER CODE END TIM1_Init 2 */ HAL_TIM_MspPostInit(&htim1); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3); HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_3); // TIM1->CCR1=1000;// // TIM1->CCR2=4500/2; // TIM1->CCR3=75; }
以下内容是重点,关于确认TIM定制器的配置是否ok
HAL_TIM_MspPostInit(&htim1); //GPIO 初始化 //以下是测试代码:判断GPIO的初始化是否OK,可以判断死区的设置时间是否正确 HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3); HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_3); TIM1->CCR1=1000;// TIM1->CCR2=4500/2; TIM1->CCR3=75;
PWM输出占空比的范围测试:
htim1.Init.Period =4500 ,频率16K,对应的周期为:62.5uS,设定死区时间1000ns=1us。约占1.6%
占空比范围测试:
序号 |
CCRX的值 |
TIM1_CH1N 占空比大小 |
TIM1_CH1N 高电平时间 |
TIM1_CH1 占空比大小 |
TIM1_CH1 高电平时间 |
备注 |
1 |
100 |
0.64% |
0.4uS |
96.15% |
60us |
|
2 |
500 |
9.615% |
6us |
87.18% |
54.4us |
|
3 |
1000 |
21.05% |
13.2us |
76.28% |
47.6us |
|
4 |
2250 |
48.72% |
30.40 |
48.41% |
30.4us |
|
5 |
4420 |
96.79% |
60.40us |
0.32% |
0.2us |
|
6 |
4450 |
97.12% |
(62.5-1.8)us |
0 |
0 |
超限制 |
7 |
4490 |
98.08% |
(62.5-1.2)us |
0 |
0 |
|
8 |
70 |
0 |
0 |
96.8% |
(62.5-2)us |
|
9 |
5 |
0 |
0 |
98.4% |
(62.5-1)us |
|
从以上实验可知,超出4420之后TIM1_CH1N输出高电平,TIM1_CH1输出低电平
实测在4420-4500和 0-100,存在一部分单桥臂占空比变化区间
通过以上测试可以得到CCRX的值的变化范围,极其对应占空比的大小变化范围。
结论如下:CCRX的变化为为:100-4400,对应的占空比变化范围为:0.64%~96.79%。
如何判断死区时间是否合适?
CH1,CH2为单片机的输出信号,CH3为MOS输出信号,可以看到,时序上有明显的滞后
滞后的主要原因在于MOS驱动电路的延时,驱动芯片采购:IRS2005驱动芯片
有驱动芯片可以得到,延时时间在160ns-220ns(驱动延时)左右。实际的延时误差值在235ns(包含两部分,驱动延时和mos开启延时)
通道2位PWM输出,通道3位驱动芯片输出端,延时位186ns。
CH1,CH2 位MCU输出波形,CH3和CH4位驱动芯片输出波形。
CH1 MOS输出的波形, CH2为TIM1_CH1N,CH3为TIM1_CH1。实测占空比如下,调节TIM1_CH1的占空比其实就是调整
MOS输出的占空比。上图是CCRX的值为100是的数据。
当CCRX=2500时的波形如下所示:
当CCRX=4100时的波形如下所示:
通过以上可以看到,CCRX的值和MOS输出的波形的占空比成正比,机CCRX值越大,MOS输出的占空比越大。
死区时间的本质是为了防止上下管同时导通,造成MOS烧毁,或者损耗增大, 软件设置死区时间为1000ns.
#define DEADTIME_NS ((uint16_t)1000) //死区时间(ns),范围:0-3500,死区时间:
#define DEADTIME_NS ((uint16_t)1500) //死区时间(ns),范围:0-3500,死区时间:
可以看到死区时间明显改变,死区时间的大小和MOS的性能相关性比较大,一般来说推荐设置为1000ns,可以可以根据实际情况具体调整,判断的依据,就是死区时间要大于MOS管的开区和关闭时间,即CH1的波形的上升时间和下降时间。
上升时间:100ns,下降时间100ns,选择不同 MOS参数会有差异。
以上讲了这么多,主要是梳理了一下PWM从MCU的引脚输出到控制MOS的输出,整个流程,同时也回答了,如何判断MOS死区时间设置是否争取的问题。整个流程供大家参考。以上提供的丰富的波形,方便大家调试才考使用。
3.第三部分,回到正题,验证第一篇文章中SVPWM函数输入的Vα和Vβ到3路占空比的计算公式,是否正确。
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
|
/* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ #define SYSCLK_FREQ 72000000uL #define TIM_CLOCK_DIVIDER 1 #define ADV_TIM_CLK_MHz 144 #define ADC_CLK_MHz 12 #define HALL_TIM_CLK 72000000uL #define PWM_FREQUENCY 16000 #define SW_DEADTIME_NS 1000 /*!< Dead-time to be inserted by FW, only if low side signals are enabled */ #define DEAD_TIME_ADV_TIM_CLK_MHz (ADV_TIM_CLK_MHz * TIM_CLOCK_DIVIDER)
#define REP_RATE (1) //该参数可以调整电流环的刷新频率,刷新周期:(REP_RATE + 1)/(2*PWM_FREQ) 秒 #define PWM_PERIOD_CYCLES (uint16_t)(ADV_TIM_CLK_MHz*\ (unsigned long long)1000000u/((uint16_t)(PWM_FREQUENCY)))
#define DEADTIME_NS ((uint16_t)1000) //死区时间(ns),范围:0-3500,死区时间:
#define DEADTIME (uint16_t)((unsigned long long)SYSCLK_FREQ/ 1*(unsigned long long)DEADTIME_NS/1000000000uL) /* USER CODE END PM */ //SVPWM波形 #define SQRT_3 1.732051 //根号3 #define T (PWM_PERIOD_CYCLES * 2) //TIM1 ARR值的4倍((PWM_PERIOD_CYCLES) / 2) #define T_SQRT3 (uint16_t)(T * SQRT_3) #define SECTOR_1 (uint32_t)1 #define SECTOR_2 (uint32_t)2 #define SECTOR_3 (uint32_t)3 #define SECTOR_4 (uint32_t)4 #define SECTOR_5 (uint32_t)5 #define SECTOR_6 (uint32_t)6
void CALC_SVPWM(Volt_Components Stat_Volt_Input) { // int8_t bSector; // int32_t wX, wY, wZ, wUAlpha, wUBeta; // int16_t hTimePhA=0, hTimePhB=0, hTimePhC=0;
wUAlpha = Stat_Volt_Input.qV_Component1 * T_SQRT3; wUBeta = -(Stat_Volt_Input.qV_Component2 * T);
wX = wUBeta; wY = (wUBeta + wUAlpha)/2; wZ = (wUBeta - wUAlpha)/2;
//下面是查找定子电流的扇区号 if (wY<0) { if (wZ<0) { bSector = SECTOR_5; } else // wZ >= 0 if (wX<=0) { bSector = SECTOR_4; } else // wX > 0 { bSector = SECTOR_3; } } else // wY > 0 { if (wZ>=0) { bSector = SECTOR_2; } else // wZ < 0 if (wX<=0) { bSector = SECTOR_6; } else // wX > 0 { bSector = SECTOR_1; } }
switch(bSector) //根据所在扇区号,计算三相占空比 { case SECTOR_1: case SECTOR_4: hTimePhA = (T/8) + ((((T + wX) - wZ)/2)/131072); hTimePhB = hTimePhA + wZ/131072; hTimePhC = hTimePhB - wX/131072; break; case SECTOR_2: case SECTOR_5: hTimePhA = (T/8) + ((((T + wY) - wZ)/2)/131072); hTimePhB = hTimePhA + wZ/131072; hTimePhC = hTimePhA - wY/131072; break;
case SECTOR_3: case SECTOR_6: hTimePhA = (T/8) + ((((T - wX) + wY)/2)/131072); hTimePhC = hTimePhA - wY/131072; hTimePhB = hTimePhC + wX/131072; break; default: break; }
TIM1->CCR1 = hTimePhA; TIM1->CCR2 = hTimePhB; TIM1->CCR3 = hTimePhC; }
|
代码分析可以,Vtest.qV_Component1即为Vα Vtest.qV_Component2即为Vβ,下文直接用Vα ,Vβ表示
Vα ,Vβ值的变化范围为:-32767~+32767。可以根据上表的数据大小范围,推算出不用扇区并计算出对应的hTimePhA,hTimePhB和hTimePhC的值。
注意Vα,Vβ(未转换)的值模值不可超过32767,超出范围的电压矢量不可以生成。当控制矢量在空间旋转 360°后,逆变器就能输出一个周期的正弦波电压。
扇区 |
Vα |
Vβ |
hTimePhA |
hTimePhB |
hTimePhC |
V1 |
V2 |
V3 |
A占空比 |
C占空比 |
D占空比 |
备注 |
I |
1000 |
-1000 |
2343 |
2293 |
2156 |
12.37 |
11.95 |
11.03 |
51.92% |
51.04% |
47.92% |
占空比1>2>3 |
I |
20000 |
-5000 |
3600 |
1576 |
890 |
19.28 |
8.421 |
4.755 |
80.13% |
35.14% |
19.81% |
图21 |
II |
2000 |
-5000 |
2487 |
2593 |
1906 |
13.16 |
13.63 |
9.66 |
55.13% |
57.51% |
42.31% |
占空比2>1>3 |
Ii |
2000 |
-20000 |
2487 |
3622 |
876 |
12.97 |
19.24 |
4.093 |
55.13% |
80.51% |
19.55% |
图22 |
III |
-2000 |
-2000 |
2063 |
2473 |
2163 |
10.59 |
12.52 |
11.32 |
45.69% |
53.99% |
48.08% |
占空比2>3>1 |
III |
-30000 |
-2000 |
398 |
4102 |
3828 |
2.384 |
21.95 |
20.73 |
8.654% |
91.05% |
85.26% |
图23 |
IV |
-2000 |
2000 |
2063 |
2163 |
2437 |
10.59 |
10.98 |
12.86 |
45.69 |
48.08 |
53.99 |
占空比3>2>1 |
IV |
-20000 |
5000 |
890 |
2925 |
3611 |
5.118 |
15.43 |
19.18 |
19.55 |
65.96 |
80.19 |
图24 |
V |
-2000 |
5000 |
2013 |
1908 |
2594 |
10.36 |
9.753 |
13.65 |
44.55 |
42.49 |
57.69 |
占空比3>1>2 |
V |
-2000 |
30000 |
2013 |
191 |
4310 |
10.37 |
1.062 |
23.14 |
44.55 |
4.167 |
95.83 |
图25 |
VI |
2000 |
3000 |
2471 |
2029 |
2440 |
13.00 |
10.43 |
12.79 |
54.81 |
45.05 |
54.17 |
占空比1>3>2 |
VI |
20000 |
3000 |
3542 |
959 |
1370 |
18.94 |
5.201 |
7.017 |
78.59 |
21.41 |
30.45 |
Tu 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
hTimePhx=4500X占空比。
图21
图22
图23
图24
图25
图26
这样理论分析就和实际的计算统一了,扇区也对上了。总结如下所示
4.通过以上3个部分,完成了Vα到Vβ的每个转换过程的分析,通过实战了解每个过程的变化。存在问题,欢迎大家指正
、