六花出品 必属精品,软件上实现多相位好像很方便,请问泥之前做的硬件多相交错buck是怎么实现让多个yc3843相位差实现同步的呢?还是说把单个ic的控制信号分为三相?
多相交错技术是低压大电流电源里面必备的技术之一,不光可以突破单个电感电流容量的限制,而且还能分散热点,更重要的是极大降低了有效值电流还提高了效率。多相交错电源里面每一路pwm都是需要相差一定角度的,比如三路交错就是需要三个相位之间相差120度的信号。本帖忽略匀流问题(实际中必须处理下否则相之间负载不平衡会让负载大的相烧掉)。如果启用了互补输出还可以做到三相交错的互补PWM。
STM32单片机是一种很常见性价比很高的32位单片机,最低端的STM32F0系列也有ADC,众多定时器等外设,于是本帖使用STM32F030K6为例,讲解高速多路交错PWM的产生方式,同样的原理也可以拓展到STM32F1/F3/F4系列和其他系列的单片机里面。在单片机里面有这么一种东西叫做时钟树,描述的是单片机内部每个模块的时钟频率,来源还有可用的分频/倍频选项,STM32F030K6的时钟树如图:
这个图是在STM32 CubeMX里面自动给出的,这个软件很不错,可以用图形界面来生成时钟树以及其他外设的初始化代码,再也不用死坑datasheet啦 哈哈哈(雾
STM32所有定时器的时钟都是接到APB1 Timer Clocks这里的,也就是说所有的定时器都是在同一时刻对Counter进行累加的。如果配置三个定时器为PWM输出模式,频率相同,但是开始计数的时间人为的叉开,就可以达到多相PWM的目的。我们可以配置三个定时器为同频率的PWM输出,然后再用一个定时器对这三个定时器在特定时间进行使能即可。GPIO的占用如下图:
Othervoid hw_init(void) {
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
SystemClock_Config();
GPIO_Init();
Timer1_Init();
Timer16_Init();
Timer17_Init();
Timer14_Init();
Timer_Sync_1_16_17();
}
GPIO_Init();是把GPIO口变成PWM输出的,参见服见里面的platform.c这里就不贴出了。
TIM1, TIM16, TIM17的初始化代码很相似,这里就只贴TIM1的代码了:
Ruststatic void Timer1_Init(void) {
__HAL_RCC_TIM1_CLK_ENABLE();//启用TIM1外射的时钟,如果没开时钟根本不工作也不能被配置的哦
TIM1->CR1 = 0;
TIM1->CR2 = 0;
TIM1->DIER = 0;
TIM1->ARR = TIM_ARR_SW; // Auto-reload value : 100kHz overflow
TIM1->PSC = 0;
TIM1->CCR2 = 0;
TIM1->CCMR1 = 0x6800; // 110: PWM mode 1, up counting.
TIM1->CCER = 0x0050; // CH2 output enable with complement, active high
TIM1->BDTR = TIM_BDTR_DTG_Msk & TIM_DEADTIME; // Set dead-time
}
这部分代码是很典型的把定时器配置成向上计数的PWM模式1并加互补死区的操作,就不详细解释了,如果有不懂可以参见RM0091(股沟得到)
重点来了!TIM14被用作了同步TIM1,TIM16和TIM17,但是这个同步并没有用到任何硬件上的同步(硬件也没有提供啊QAQ)
Otherstatic void Timer14_Init(void) {
__HAL_RCC_TIM14_CLK_ENABLE();
TIM14->CR1 = 0;
TIM14->DIER = TIM_DIER_UIE;
TIM14->ARR = TIM_ARR_PHSYNC; // Auto-reload value : 300kHz overflow
TIM14->PSC = 0;
NVIC_EnableIRQ(TIM14_IRQn);
}
首先还是开启时钟,这个千万别忘,很常见的错误,然后TIM14被配置成了自动重装模式,以300KHz的频率触发中断。300KHz的一个周期正好是我们想要的相位差的时间。Timer_Sync_1_16_17()首先确保TIM1,TIM14,TIM16和TIM17的计数器都是0,然后启动TIM14并等待TIM14被停止。
Otherstatic void Timer_Sync_1_16_17(void) {
extern volatile uint32_t* tim_cr1;
// Clear counter value
TIM14->CNT = 0;
TIM1->CNT = 0;
TIM16->CNT = 0;
TIM17->CNT = 0;
// Enable the synchronizer timer
TIM14->CR1 |= 1;
// Wait until the synchronization is done
while (tim_cr1 > 0);
while那会让程序卡住,但是还是会进定时器中断的。TIM14中断的程序如下:
Otherstatic uint32_t* tim_cr1 = &(TIM1->CR1);
void TIM14_IRQHandler(void) {
// This can be commented out, since update is the only source of interrupt
if(TIM14->SR & TIM_SR_UIF) {
TIM14->SR &= ~TIM_SR_UIF; // clear UIF flag
if (tim_cr1 > 0) {
*tim_cr1 |= TIM_CR1_CEN;
// Set the next timer to be enabled
if (tim_cr1 == &(TIM1->CR1)) {
tim_cr1 = &(TIM16->CR1);
} else if (tim_cr1 == &(TIM16->CR1)) {
tim_cr1 = &(TIM17->CR1);
} else if (tim_cr1 == &(TIM17->CR1)) {
// All timers have been synchronized to the correct phase order
tim_cr1 = 0; // No more CR1 to be set
// Disable the timer
TIM14->CR1 &=~ TIM_CR1_CEN;
}
} else {
// Execute control routine here
}
} // if(TIM14->SR & TIM_SR_UIF)
}
在单片机刚上电之后,tim_cr1会被设置为TIM1的CR1寄存器的地址,这样做是告诉单片机我们下次进中断需要开启TIM1并在开启TIM1之后为下次进中断开启TIM16做准备,就是把tim_cr1会被设置为TIM16的CR1寄存器的地址。这段代码确保了从进入中断到相应的CR1寄存器最低位被置1(定时器计数使能位)的指令条数完全相等,于是TIM1,TIM16,TIM17会依次在正确的时间被打开,完成三个定时器的同步产生正确的相位差。当TIM17被开启之后,tim_cr1会被置0(标识为无效状态,这样当TIM14作为其他用途用的时候会跳过同步TIM1-17的代码),然后TIM14的也会被禁用。退出中断程序后,while (tim_cr1 > 0); 这里也能够继续执行了。TIM14在同步TIM1,TIM16,TIM17完成之后可以用作其他用途比如执行反馈环的计算等。之后的TIM14中断处理程序写在"} // if(TIM14->SR & TIM_SR_UIF)"这行之后。static void Timer_Sync_1_16_17(void) 的后半段重新配置了TIM14:
Rust // Wait until the synchronization is done
while (tim_cr1 > 0);
// Reconfigure timer14 for executing control routine
TIM14->CR1 = 0;
TIM14->DIER = TIM_DIER_UIE;
TIM14->ARR = TIM_ARR_CON; // Auto-reload value : 50kHz overflow
TIM14->PSC = 0;
TIM14->CNT = 0;
// Enable the timer again
TIM14->CR1 |= TIM_CR1_CEN;
}
下面三个宏分别是启用PWM输出,禁用PWM输出还有设置占空比:
Other__STATIC_INLINE void TIM_PWM_ENABLE() {
TIM1->BDTR |= TIM_BDTR_MOE;
TIM16->BDTR |= TIM_BDTR_MOE;
TIM17->BDTR |= TIM_BDTR_MOE;
}
__STATIC_INLINE void TIM_PWM_DISABLE() {
TIM1->BDTR &=~ TIM_BDTR_MOE;
TIM16->BDTR &=~ TIM_BDTR_MOE;
TIM17->BDTR &=~ TIM_BDTR_MOE;
}
__STATIC_INLINE void TIM_PWM_SETVAL(uint32_t val) {
TIM1->CCR2 = val;
TIM16->CCR1 = val;
TIM17->CCR1 = val;
}
这个是实验效果:
有的同学可能想问,为啥这么多直接操作寄存器的?全部换成HAL不好吗?我感觉在这种需要对硬件行为进行精确把控的情况下使用HAL是不明智的选择,HAL的实现可能会在函数调用的时候产生短暂的无效输出状态,有可能会导致MOS管的误导通,这对于电源是灾难性的,会booooom
代码在附件里面,HAL库需要手动从Cube里考进来,当然如果把HAL的时钟GPIO初始化换成其他库或者寄存器操作也可以不要HAL。于是大家好好玩233333
六花出品 必属精品,软件上实现多相位好像很方便,请问泥之前做的硬件多相交错buck是怎么实现让多个yc3843相位差实现同步的呢?还是说把单个ic的控制信号分为三相?
200字以内,仅用于支线交流,主线讨论请采用回复功能。