BLDC开发笔记6.利用硬件COM事件换相

前面我是用霍尔触发中断进行换相,这里将使用定时器的COM事件来换相。吐槽下看懂这个还真不容易。另外有什么不对的请帮忙指出。

什么是COM事件?

在中文参考手册中的13.3.14产生六步PWM输出有以下描述:

COM事件,其实就是为了让换相时相应的通道同时打开/关断,因为如果按前面在霍尔触发中断中写配置函数的话,是顺序进行的,有延时,不能做到同时配置。那么在切换的时候上一步的MOS状态多少会影响下一步(其实我感觉影响很小)。COM事件能让下一步的配置暂时缓存在影子寄存器中,等待COM事件触发生效,触发后就将相应的输出比较通道使能位同步更新,进行换相,然后可以在COM中断中配置下一步的通道状态。

我理解的使用COM事件另一个好处就是,能更灵活的对定时器进行同步操作。比如在ADC进行瞬时电流采样时候,就可以利用一个通用定时器去和高级定时器(输出PWM)同步,去触发ADC转换。方式更灵活。

需要注意的是,COM事件产生时,生效的是上一次在中断中的配置(硬件完成),在COM中断中配置的是下一步的状态。所以我们需要提前一相进行配置。

COM事件可由软件产生,也可以由硬件产生,这里主要解析硬件COM事件。

硬件COM事件产生的流程


上面是ST官方的主要流程框图,不过我觉得还是结合下面通用定时器功能框图解析比较清晰。

总的说就是用一个通用定时器去触发一个高级定时器。下面是官方参考手册中给出的产生COM事件流程。

  1. 三个hall信号经过异或后,任意一相跳变,都会产生脉冲信号IC1PS,先将计数值传给输入捕获寄存器1

  2. 计数器复位,重新从零开始计数。在下一个脉冲IC1PS到来之前,输入捕获寄存器1的值,就表示了两个霍尔之间的时间。

  3. 将输出比较通道2发出的PWM作为TRGO输入源,注意配置为PWM2模式(当CNT<CCR时输出无效电平),设置一个较小的占空比,将主定时器的TRGO作为另一个高级定时器(从定时器)的TRGI触发源,每一次主定时器的TRGO有上升沿时,就会在从定时器触发一次硬件COM事件,进行换相。

下面是中文参考手册实例:

这里也注明了,在COM事件到来时,要写入的是下一个步骤的通道配置。

用OC2REF作为TRGO好处就是能够自定义触发COM时间。

我用的是TIM3去触发TIM1产生COM事件。

TIM3初始化

TIM3的初始化和前面文章中用霍尔触发中断的初始化中,时基和输入捕获通道初始化是一样的,只不过加入输出比较通道2的初始化和COM事件的配置。

因为计数器一直在计数,而且计数器的ARR是有限的,所以这里就需要理清楚整个思路(ps:只是个人理解,可能我还没遇到这个情况,如果不对麻烦指出来)

  1. 刚开始,电机停止时候,初始设置PWM占空比为0,因为没有霍尔状态改变,计数器一直在计数,那么就一直产生溢出事件,不断触发COM事件,但是无所谓,因为没有霍尔状态改变,并不会捕获CNT的值,并且因为设置占空比为0,所以电机还是停转状态
  2. 电机启动,按照给定PWM占空比转动,TIM3利用TIM_TS_TI1F_ED正常触发COM事件,因为开始计数器一直计数,第一次产生捕获事件时候,并不知道会停在哪里,可能并不是我们想要的CNT值,但是我们可以将其过滤掉。我们通过设置反馈调整时间(50ms在滴答定时器进行转速计算和PI算法),其实就已经把这个值过滤掉了。之后正常捕获。关于电机转速慢到超出计数器ARR,则需要根据实际最低转速调整时基配置,保证长于霍尔传感器上的两次变化的时间间隔。触发延时变长其实是相对而言的,最大65535个周期我觉得是足以应对大部分情况的。
  3. 电机停止,同1,设置占空比为0等操作进行停转。
  4. 考虑堵转保护,我的思路是使能TIM3输入捕获中断,每次在里面将堵转标志清零,在COM中断中加一,如果超过一定数值就关闭PWM输出(这个数值不能太大,比如为5),这样做是因为两个中断理论上时间线上是不同的,会先触发输入捕获中断,再触发COM中断,不过需要控制输入捕获中断的执行时间,不要在里面进行太多事务处理,做到互不妨碍,这里我的配置应该有10us间隔时间,应该是足够的,这里待我完善。
        //使能Timx霍尔传感器接口,CH1CH2CH3异或输入
	TIM_SelectHallSensor(HALL_TIMx,ENABLE);  
	
	//输入触发源选择TIM_TS_TI1F_ED
	TIM_SelectInputTrigger(HALL_TIMx, TIM_TS_TI1F_ED);
	
	//从模式选择:复位模式
	TIM_SelectSlaveMode(HALL_TIMx, TIM_SlaveMode_Reset);
	
	//主从模式选择使能
	TIM_SelectMasterSlaveMode(HALL_TIMx, TIM_MasterSlaveMode_Enable); 
	
	//输出比较通道2配置
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;  //选择PWM模式2
        TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Reset;  
	TIM_OCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
        TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;  //高电平有效  
	TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High;
        TIM_OCInitStruct.TIM_OutputState = TIM_OutputNState_Disable;  //关闭,不用输出   
	TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Disable;   
	TIM_OCInitStruct.TIM_Pulse= 10;  //设置占空比
	TIM_OC2Init(HALL_TIMx, &TIM_OCInitStruct);
	
	//选择TRGO触发源为OC2Ref
	TIM_SelectOutputTrigger(HALL_TIMx,TIM_TRGOSource_OC2Ref);

COM中断服务函数

void TIM1_TRG_COM_IRQHandler(void)
{
  if(TIM_GetITStatus(BLDC_TIMx, TIM_IT_COM) !=RESET)
  {      
      //检测霍尔状态,换相
      HALL_Position_commutate();  
      
      //清楚标志位
      TIM_ClearITPendingBit(BLDC_TIMx, TIM_IT_COM);
  }
}

TIM1初始化

TIM1的初始化和前面文章中用输出六步PWM的初始化中,时基和输出比较通道初始化是一样的,加入COM事件配置,注意换相函数需要提前一相配置。

	//输入触发源选择,选择定时器3,这里可以试下,如果选择错误,TIM1是不会由PWM输出的,进而验证
        TIM_SelectInputTrigger(BLDC_TIMx,TIM_TS_ITR2);  

	//从模式,选择复位模式
	TIM_SelectSlaveMode(BLDC_TIMx, TIM_SlaveMode_Reset); 
	
        //COM事件配置
        /*配置ccxe,ccxne,ocxm是预装载的,只有com事件产生才更新,这个预装载和CCRX值的预装载不同,CCRX预装载由
        TIM_OC1PreloadConfig 配置,配置的寄存器不同*/
	TIM_CCPreloadControl(BLDC_TIMx, ENABLE); 

	//使能com事件
        TIM_SelectCOM(BLDC_TIMx, ENABLE);  
	
	//中断优先级配置
	NVIC_InitStruct.NVIC_IRQChannel = TIM1_TRG_COM_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStruct);
	TIM_ITConfig(BLDC_TIMx, TIM_IT_COM ,ENABLE);
	TIM_ClearITPendingBit( BLDC_TIMx, TIM_IT_COM);

然后在COM中断中检测霍尔相位,进行换相。

启动函数

启动的时候由于没有霍尔状态改变来触发,需要软件触发COM事件。

void BLDC_Start(void)
{
        TIM_SetCompare1(BLDC_TIMx,0);
	TIM_SetCompare2(BLDC_TIMx,0);
	TIM_SetCompare3(BLDC_TIMx,0);
	TIM_CCxCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCx_Enable);
	TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Enable);
	TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Enable);
	TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCxN_Enable);
	TIM_CCxCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCx_Enable);
	TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCxN_Enable);
	TIM_GenerateEvent(BLDC_TIMx, TIM_EventSource_COM);  //软件触发COM事件生效配置
	TIM_ClearFlag(BLDC_TIMx, TIM_FLAG_COM);
	
	Delay_nms(5);  //等待boost电容充电完成,大于大概4-5个电容充电时间常数即可
	
        HALL_Position_commutate();  //检测霍尔状态,换相
        TIM_GenerateEvent(BLDC_TIMx, TIM_EventSource_COM);  
	TIM_ClearFlag(BLDC_TIMx, TIM_FLAG_COM);
}
原文地址:https://www.cnblogs.com/ckk-blog/p/14015327.html