原理如下:
以下代码实现了利用TIM3的CH3以及CH4输出两路移相的PWM
void TIM3_PWMShiftInit(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
/**********************TIM3 GPIO配置*****************************/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
/**********************初始化TimBase结构体*************************/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 3; //一个周期翻转一次电平,Frequence = 36000000/PSC/(ARR+1);
TIM_TimeBaseInitStruct.TIM_Prescaler = 7199;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
/**********************初始化TIM3 OC结构体*************************/
TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 0; //第一个周期,CCR与CNT不会进行比较
TIM_OC3Init(TIM3,&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_Pulse = 1; //移相度数 = 180*CCR/(ARR+1)
TIM_OC4Init(TIM3,&TIM_OCInitStruct);
TIM_Cmd(TIM3, ENABLE);
}
示波器抓取的波形:
CH1:TIM3 CH3
CH2:TIM3 CH4
然后将上述程序中的以下代码:
TIM_OCInitStruct.TIM_Pulse = 1; //移相度数 = CCR/(ARR+1)
TIM_OC4Init(TIM3,&TIM_OCInitStruct);
改成:
TIM_OCInitStruct.TIM_Pulse = 3; //移相度数 = CCR/(ARR+1)
TIM_OC4Init(TIM3,&TIM_OCInitStruct);
波形如下图:
需要注意几点:
1、初始电平,通过观察TIM_OCInitStruct.TIM_Pulse = 1; 与 TIM_OCInitStruct.TIM_Pulse = 3;
这两种情况下的示波器波形图可以知道,TIM_OCInitStruct.TIM_Pulse = 0; 时,理论上示波器CH1通道上波形应该为
现在的CH1通道的互补波形。可以理解为,在第一个ARR周期,示波器CH1上的波形并没有翻转,如此现在的波形为
理论上波形的互补波形就符合实际了。所以,此时CH1波形在第一个ARR周期为低电平,而CH2在第一个ARR周期中
1/4为低电平,剩下3/4为高电平。
2、不能通过修改 TIM_OCInitStruct.TIM_Pulse = 1; 中结构体成员TIM_Pulse 的值,来实现180度的移相。最大的移相
角度根据:
移相度数 = 180*CCR/(ARR+1)
当CCR=ARR时获得最大移相角度,角度=ARR/ARR+1。通过程序以及示波器波形可知,TIM_Pulse最大值为ARR的值,
在以上程序为“3”。为3时,理论上波形应该移相:180*(TIM_Pulse/(ARR+1))= 180*(3/4)= 135度。
但由上述第一条事项可知,在TIM_Pulse=0;时,第一个ARR周期电平不发生翻转(文章的最后有证明),所以
现在示波器上看到的移相角度为180+135=315度。因为ARR为3时CNT从0计数到3共4个计数周期,而CCR即
TIM_Pulse的值为3,CNT计数到3的瞬间产生中断事件,此时经过3个计数周期,他们之间相差一个计数周期。如果
要实现180度移相,也就是互补输出。只需将两通道的其他配置一致,输出极性(TIM_OCPolarity)相反即可。
3、因为一个ARR周期翻转一次电平,少于PWM模式的2次。所以采用这种模式出来的PWM频率为PWM模式下的一半。
计算频率时用36000000而不是72000000(定时器分频系数为1情况下):
Frequence = 36000000/PSC/(ARR+1);
4、此模式下输出的PWM占空比为50%不可调。如果要改变占空比,可以开启CCR中断,在中断中改变对应通道的CCR的值
实现占空比的改变。
/*****************************************利用中断更改占空比的程序*******************************************/
//CH3链表
struct TIM_CH3CCR
{
u16 CCRValue;
struct TIM_CH3CCR *next;
};
struct TIM_CH3CCR CCR3a,CCR3b,*p3;
//CH4链表
struct TIM_CH4CCR
{
u16 CCRValue;
struct TIM_CH4CCR *next;
};
struct TIM_CH4CCR CCR4a,CCR4b,*p4;
void TIM3_PWMShiftInit(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//初始化链表
CCR3a.CCRValue = 0;
CCR3b.CCRValue = 9000;
CCR4a.CCRValue = 18000;
CCR4b.CCRValue = 27000;
CCR3a.next = &CCR3b;
CCR3b.next = &CCR3a;
CCR4a.next = &CCR4b;
CCR4b.next = &CCR4a;
p3 = &CCR3a;
p4 = &CCR4a;
/***********************NVIC配置*****************************/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStruct);
/**********************TIM3 GPIO配置*****************************/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
/**********************初始化TimBase结构体*************************/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 35999; //一个周期翻转两次电平,Frequence = 72000000/PSC/(ARR+1);
TIM_TimeBaseInitStruct.TIM_Prescaler = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
/**********************初始化TIM3 OC结构体*************************/
TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 0; //第一个周期,电平不翻转
TIM_OC3Init(TIM3,&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_Pulse = 18000; //移相度数 = 360*CCR/(ARR+1)
TIM_OC4Init(TIM3,&TIM_OCInitStruct);
TIM_ClearITPendingBit(TIM3, TIM_IT_CC3|TIM_IT_CC4);
TIM_ITConfig(TIM3, TIM_IT_CC3|TIM_IT_CC4,ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=DISABLE)
{
p3 = p3->next;
TIM3->CCR3 = p3->CCRValue;
TIM_ClearITPendingBit(TIM3, TIM_IT_CC3);
}
else
{
p4 = p4->next;
TIM3->CCR4 = p4->CCRValue;
TIM_ClearITPendingBit(TIM3, TIM_IT_CC4);
}
}
出来波形如图:
示波器CH1: TIM3 CH3
示波器CH2: TIM3 CH4
1、我们设定的频率为72000000/PSC/(ARR+1) = 72000000/1/36000 = 2000Hz。与示波器探测出来相符。
2、CH3起始CCR = 0,CH4 CCR=18000;根据:移相度数 = 360*CCR/(ARR+1),可知移相为180度。与示波器相符
3、CH3设定CCR为:0、9000,CH4为:18000,27000。所以占空比为25%。
注意几点:
1、频率算法,因为一个ARR周期触发2次,而不是原本的一次,所以公式为72000000/PSC/(ARR+1)而不是36000000。
2、移相角度算法,也是因为一个ARR周期触发两次电平翻转,所以不是180*CCR/(ARR+1),而是360。
3、因为是在中断中处理移相电平的翻转,所以注意不要让两通道中断时间叠加一起,且这种方法需要消耗CPU资源,
高频率下易受到其他中断的干扰。
4、CCR设为0则第一个ARR周期电平不翻转,看以下示波器图得以证明:
图1,示波器调为单次触发模式,第一段高电平为TIM初始化,初始化完毕后开始输出PWM。
图2,为上述中断更改CCR程序中,屏蔽了中断的允许位开启代码后的波形:
//TIM_ClearITPendingBit(TIM3, TIM_IT_CC3|TIM_IT_CC4);
//TIM_ITConfig(TIM3, TIM_IT_CC3|TIM_IT_CC4,ENABLE);
其他代码不变。
图3,为正常的中断更改CCR实现调节占空比的程序的波形图。