分享如何使用stm32的定时器互补PWM通道,驱动IPM模块产生三相相差120°的正弦波,可产生旋转磁场驱动三相电机

2019-07-20 23:20发布

经过了几天的摸索,下午电机终于转起来了。
从论坛里面找到很多有用的资源和建议,程序也是摸索着写的。
但是论坛里面没有相关的程序,偶尔有几个帖子是相关的,楼主也是发完贴就失踪了
在此,把我的程序和大家分享一下,欢迎提建议。

首先说几点:
1、我是做DTC控制的,做这个旋转磁场只是为了验证一下硬件平台能不能用,所以程序里面没有写SPWM闭环控制算法,是一个纯开环的,但是大家可以在这个基础上写各种闭环算法。
2、我用的是stm32f103的timer1,因为是rct6型号,好像就这一个高级计时器,说一个基本知识,就是只有高级计时器才能产生互补通道,在三相控制驱动电机的时候,每个桥臂的上下桥臂不会同时打开
3、我的IPM模块是IPM100RLA060,是低电平导通的,我之前没做过强电,第一次做,默认是高电平导通,炸了有7、8个保险丝之后才发现了这个问题,等于每次上电都是310v直接短路,那段时间一上电就觉得有东西要炸,好在最后发现了这个问题,之后就一直顺利,我说这个的原因是,因为是低电平导通,所以程序里面的死区我必须用高电平,但是据我的摸索,死区要设置高电平,只能TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity=TIM_OCNPolarity_Low; 
这样的话,在每次给CCR赋值的时候就不能直接赋sin表里面的值,就需要赋值周期值减去sin表。
具体的困惑写在了这个帖子里,有盆友发现了解决方案的可以留言告诉我,非常感谢
http://www.openedv.com/posts/list/0/51941.htm?privmsg=26154&&sysid=4#296170

因为没有做低通滤波电路,所以,没图,就是直接在逻辑分析仪上看的,看完直接驱动电机很稳定,忘了截逻辑分析仪的图,
但是确实验证过了。
[mw_shl_code=c,true]/********************************************************************************* *Copyright(C) *FileName: PWM.c *Author: 陶涛 *Version: v1.1 *Date: 2015/01/16 *Description: 本程序为stm32的高级计时器timer1生成PWM的API,rct6型号上测试过 *Version: *Function List: *History: <Author> <Date> <Version> <Description> taotao 2015/05/08 1.0 大部分函数改自原子大哥 taotao 2015/05/09 1.1 添加了SPWM的API,以定时器3作为中断,加载新的CCR,占空比值 *Others: 只有高级计时器可以输出互补波 TIM1模块是增强型的定时器模块,可以产生3组6路PWM,同时每组2路PWM为互补,并可以带有死区,可以用来驱动H桥。 硬件上的引脚连接: PA8---TIM1_CH1 PB13---TIM1_CH1N PA9---TIM1_CH2 PB14---TIM1_CH2N PA10--TIM1_CH3 PB15---TIM1_CH3N **********************************************************************************/ #include <WM.h> #include "GPIO.h " //测试用led灯 //主函数初始化中,需要做delay_init(72);和TIM1_PWM_Init(3600-1,0); //预分频数为0 PWM频率=72000/(3600-1+1)=20Khz 周期3600 //这几个占空比更改参数,需要配合TIM1_PWM_Init中的arr使用 u16 CCR1_Val=1800; //设置TIM1通道1输出占空比50% u16 CCR2_Val=900; //设置TIM1通道1输出占空比25% u16 CCR3_Val=450; //设置TIM1通道1输出占空比12.5% void TIM1_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; //第一步:配置时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //使能定时器3时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE); //使能GPIO和服用功能时钟 // RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //引脚重映射要开这个时钟 //第二步,配置gpio口 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10; //PWM输出在PA8,9,10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化GPIO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //PWM输出在PA7 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIO //第三步,定时器基本配置 TIM_TimeBaseStructure.TIM_Period = arr; //设置在自动重装载周期值,周期周期周期周期周期周期周期周期周期周期周期周期 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值 TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim,// 采样分频 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式 TIM_TimeBaseStructure.TIM_RepetitionCounter=0; //重复寄存器,用于自动更新pwm占空比 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //第四步pwm输出配置,TIM_OCInitStructure共七项配置 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2; //设置为pwm1输出模式 /*:PWM模式1- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平; 在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0), 否则为有效电平(OC1REF=1)。 PWM模式2- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平; 在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平。*/ TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low; //设置输出极性//输出极性和互补极性的有效电平为低 //Polarity_Low实际上是往极性控制位写1,所以会输出高电平 TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //使能该通道输出//比较输出使能 //下面几个参数(除了pulse)是高级定时器才会用到,通用定时器不用配置 TIM_OCInitStructure.TIM_OCNPolarity=TIM_OCNPolarity_Low; //设置互补端输出极性//输出极性和互补极性的有效电平为低 TIM_OCInitStructure.TIM_OutputNState=TIM_OutputNState_Enable; //使能互补端输出 TIM_OCInitStructure.TIM_OCIdleState=TIM_OCIdleState_Reset; //死区后输出状态 TIM_OCInitStructure.TIM_OCNIdleState=TIM_OCNIdleState_Set; //死区后互补端输出状态 //设置通道1捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_Pulse=CCR1_Val; TIM_OC1Init(TIM1,&TIM_OCInitStructure); //设置通道1 //设置通道2捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_Pulse=CCR2_Val; TIM_OC2Init(TIM1,&TIM_OCInitStructure); //设置通道3捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_Pulse=CCR3_Val; TIM_OC3Init(TIM1,&TIM_OCInitStructure); //第五步,死区和刹车功能配置,高级定时器才有的,通用定时器不用配置TIM_BDTRInitStructure,共七项配置 TIM_BDTRInitStructure.TIM_OSSRState=TIM_OSSRState_Disable; //运行模式下输出选择//运行模式下“关闭状态”使能 TIM_BDTRInitStructure.TIM_OSSIState=TIM_OSSIState_Disable; //空闲模式下输出选择//关闭模式下“关闭状态”使能 TIM_BDTRInitStructure.TIM_LOCKLevel=TIM_LOCKLevel_OFF; //锁定设置 TIM_BDTRInitStructure.TIM_DeadTime=0xA0; //死区时间设置 TIM_BDTRInitStructure.TIM_Break=TIM_Break_Disable; //刹车功能不使能 TIM_BDTRInitStructure.TIM_BreakPolarity=TIM_BreakPolarity_High; //刹车输入极性 //刹车输入高电平有效 TIM_BDTRInitStructure.TIM_AutomaticOutput=TIM_AutomaticOutput_Enable; //自动输出使能 TIM_BDTRConfig(TIM1,&TIM_BDTRInitStructure); //第六步,使能端的打开 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //使能TIMx在CCR1上的预装载寄存器,这句的功能是让改变CCR之后马上有效 ,立刻改变占空比 TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); //使能TIMx在CCR2上的预装载寄存器 TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable); //使能TIMx在CCR3上的预装载寄存器,这句的功能是让改变CCR之后马上有效 ,立刻改变占空比 TIM_ARRPreloadConfig(TIM1,ENABLE); //使能TIMx在ARR上的预装载寄存器?//立刻改变频率 TIM_Cmd(TIM1,ENABLE); //打开TIM1 //下面这句是高级定时器才有的,输出pwm必须打开 TIM_CtrlPWMOutputs(TIM1,ENABLE); //pwm输出使能,一定要记得打开 } void TIM3_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能 //定时器TIM3初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断 //中断优先级NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器 TIM_Cmd(TIM3, ENABLE); //使能TIMx } #define Sample_NUM 33 //采样点 unsigned int SinTab[Sample_NUM]={0x0400,0x04C2,0x057C,0x0629,0x06C2,0x0741,0x07A3,0x07E2,0x07FE,0x07F5, 0x07C7,0x0776,0x0705,0x0678,0x05D5,0x0520,0x0461,0x039F,0x02E0,0x022B, 0x0188,0x00FB,0x008A,0x0039,0x000B,0x0002,0x001E,0x005D,0x00BF,0x013E, 0x01D7,0x0284,0x033E}; unsigned char SinTab_i = 0; //定时器3中断服务程序 void TIM3_IRQHandler(void) //TIM3中断 { unsigned int SinTab_j,SinTab_k; if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否 { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIMx更新中断标志 SinTab_j=SinTab_i+Sample_NUM/3; //200/3 相差120° SinTab_k=SinTab_i+Sample_NUM*2/3; //400/3 相差240° SinTab_i++; if(SinTab_i == Sample_NUM){SinTab_i = 0;} if(SinTab_j >= Sample_NUM){SinTab_j = SinTab_j-Sample_NUM;} if(SinTab_k >= Sample_NUM){SinTab_k = SinTab_k-Sample_NUM;} TIM1->CCR1 = 7200-SinTab[SinTab_i]*2; //配合主函数的 TIM1_PWM_Init(7200-1,6-1); 使用 TIM1->CCR2 = 7200-SinTab[SinTab_j]*2; TIM1->CCR3 = 7200-SinTab[SinTab_k]*2; } } [/mw_shl_code]
主函数里面只要写
TIM1_PWM_Init(7200-1,6-1);
TIM3_Int_Init(39,719); 
初始化就行了,再开中断

程序写的不好,请大家多多指教!共同学习!
sin表的数据是用论坛里面下的一个软件生成的,也可以自己算。
为了方便大家,我把这个软件再传一下,原帖我一下找不到了,在这里也感谢原作者





友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
24条回答
w541230
1楼-- · 2019-07-22 23:02
 精彩回答 2  元偷偷看……
18377006655
2楼-- · 2019-07-23 01:17
因为我看不怎么懂库函数,我现在都是用寄存器来写的
w541230
3楼-- · 2019-07-23 04:44
18377006655 发表于 2016-8-10 12:31
因为我看不怎么懂库函数,我现在都是用寄存器来写的

           你在做什么东东啊?
林春霞
4楼-- · 2019-07-23 06:30
DTC啊,好啊。我做FOC的,之前捣鼓过一下PMSM SDK 4.2库,最近在做一个BLDC方波无感AD检测反电势的控制器。要不咱们加个QQ,交流学习一下,970253146
SDUGFK
5楼-- · 2019-07-23 07:24
 精彩回答 2  元偷偷看……
dossdesmend
6楼-- · 2019-07-23 10:04
 精彩回答 2  元偷偷看……

一周热门 更多>