[求助] 关于STM32利用PWM和SPWM驱动步进电机的问题

lizoyu   2016-3-27 22:18 楼主
最近在做毕业设计,其中涉及到了步进电机,本来用驱动器驱动得很顺利的,但是因为便携的需求改用了小得多驱动模块,tb买了用2个L9110的驱动模块,如下图. sku_153951_2.jpg 电路图如下: 原理图.jpg 首先,我根据最常见的三种励磁方式写了程序,实现的时序如下图所示,图从左到右分别是一相励磁,二相励磁,一-二相励磁(8拍). 四个通道从上到下分别是A+,B+,A-,B-. 2beat.PNG 4beat.PNG 8beat.PNG 但是将这三种励磁时序分别输出到驱动模块后,电机产生比较大的震动和噪声(与驱动器相比),只转动了1秒左右就停下来,震动依然持续. PWM程序代码如下: #include "stm32f10x.h" void RCC_Configuration(void); void GPIO_Configuration(void); void TIM_Configuration_8beat(void); void TIM_Configuration_2beat(void); void TIM_Configuration_4beat(void); void NVIC_Configuration(void); u16 LowBitVal; u16 HighBitVal; void Delay(u32 ms) { u16 count; while(ms) { count = 1800; while(count--); ms--; } } int main() { RCC_Configuration(); NVIC_Configuration(); GPIO_Configuration(); //选择励磁方式 //TIM_Configuration_8beat(); //TIM_Configuration_4beat(); //TIM_Configuration_2beat(); while(1); } void RCC_Configuration() { SystemInit(); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE); } void GPIO_Configuration() { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; //PA0: A+; PA1: B+; PA2: A-; PA3: B- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); } void NVIC_Configuration() { NVIC_InitTypeDef NVIC_InitStructure; #ifdef VECT_TAB_RAM NVIC_SetVectorTable(NVIC_VectTab_RAM,0x0); #else NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0); #endif NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } //一-二相励磁(8拍) void TIM_Configuration_8beat() { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; LowBitVal = 37500; //高电平时的比较值,即其持续时间 HighBitVal = 22500; //低电平时的比较值,即其持续时间 TIM_TimeBaseStructure.TIM_Period = 65535-1; TIM_TimeBaseStructure.TIM_Prescaler = 100-1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_PrescalerConfig(TIM2, 4, TIM_PSCReloadMode_Immediate); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; //使用比较输出翻转模式,计数到达比较值自动翻转,并触发中断 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_Pulse = 7500-1; //初始比较值设置,能够产生移相效果 TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_Pulse = 22500-1; TIM_OC2Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_Pulse = 37500-1; TIM_OC3Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_Pulse = 52500-1; TIM_OC4Init(TIM2, &TIM_OCInitStructure); //禁止计数溢出事件 TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE); TIM_Cmd(TIM2, ENABLE); } //一相励磁 void TIM_Configuration_2beat() { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; LowBitVal = 45000; HighBitVal = 15000; TIM_TimeBaseStructure.TIM_Period = 65535-1; TIM_TimeBaseStructure.TIM_Prescaler = 150-1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_PrescalerConfig(TIM2, 4, TIM_PSCReloadMode_Immediate); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_Pulse = 15000-1; TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_Pulse = 30000-1; TIM_OC2Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_Pulse = 45000-1; TIM_OC3Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_Pulse = 60000-1; TIM_OC4Init(TIM2, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE); TIM_Cmd(TIM2, ENABLE); } //二相励磁 void TIM_Configuration_4beat() { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; LowBitVal = 30000; HighBitVal = 30000; TIM_TimeBaseStructure.TIM_Period = 65535-1; TIM_TimeBaseStructure.TIM_Prescaler = 100-1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_PrescalerConfig(TIM2, 4, TIM_PSCReloadMode_Immediate); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_Pulse = 15000-1; TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_Pulse = 30000-1; TIM_OC2Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_Pulse = 45000-1; TIM_OC3Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_Pulse = 60000-1; TIM_OC4Init(TIM2, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable); TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE); TIM_Cmd(TIM2, ENABLE); } 其中断程序如下: void TIM2_IRQHandler(void) { u16 capture = 0; extern u16 LowBitVal; extern u16 HighBitVal; if(TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); capture = TIM_GetCapture1(TIM2); //通过控制高低电平时的比较值,控制了高低电平的持续时间,即占空比 if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) TIM_SetCompare1(TIM2, capture + HighBitVal-1);//自动翻转后电平为高电平时,比较值=原值+HighBitVal else TIM_SetCompare1(TIM2, capture + LowBitVal-1); //自动翻转后电平为低电平时,比较值=原值+LowBitVal } else if(TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC2); capture = TIM_GetCapture2(TIM2); if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)) TIM_SetCompare2(TIM2, capture + HighBitVal-1); else TIM_SetCompare2(TIM2, capture + LowBitVal-1); } else if(TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC3); capture = TIM_GetCapture3(TIM2); if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2)) TIM_SetCompare3(TIM2, capture + HighBitVal-1); else TIM_SetCompare3(TIM2, capture + LowBitVal-1); } else if(TIM_GetITStatus(TIM2, TIM_IT_CC4) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC4); capture = TIM_GetCapture4(TIM2); if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)) TIM_SetCompare4(TIM2, capture + HighBitVal-1); else TIM_SetCompare4(TIM2, capture + LowBitVal-1); } } PWM失败之后,我参考了论坛的STM32产生SPWM来驱动步进电机的教程,因为我不是相关专业的,不懂脉冲调制的原理,直接实现了SPWM时序如下图,直接用查表法,SPWM数据来自教程里的matlab程序. 同样,四个通道从上到下分别是A+,B+,A-,B-.可以看出A+和A-,B+和B-是互补的,而且A和B相差90度相位,符合教程的正弦波要求 SPWM.PNG 但是输出到驱动模块后,依然无法成功驱动步进电机,现象是:步进电机转动了一个小角度后自动回到原位置,然后不断重复.震动依然存在,但是比PWM的小. 程序代码如下:
  1. #include "stm32f10x.h"
  2. void RCC_Configuration(void);
  3. void GPIO_Configuration(void);
  4. void TIM_Configuration(void);
  5. void NVIC_Configuration(void);
  6. /*****************************************
  7. * 函数名 : main
  8. ******************************************/
  9. int main()
  10. {
  11. RCC_Configuration();
  12. GPIO_Configuration();
  13. NVIC_Configuration();
  14. TIM_Configuration();
  15. while(1);
  16. }
  17. /*****************************************
  18. * 函数名 : RCC_Configuration
  19. ******************************************/
  20. void RCC_Configuration()
  21. {
  22. SystemInit();
  23. //RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
  24. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_TIM1,ENABLE);
  25. }
  26. /*****************************************
  27. * 函数名 : GPIO_Configuration
  28. ******************************************/
  29. void GPIO_Configuration()
  30. {
  31. GPIO_InitTypeDef GPIO_InitStructure;
  32. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11; //PA8: A+; PA9: B+; PA10: A-; PA11: B-
  33. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  34. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  35. GPIO_Init(GPIOA, &GPIO_InitStructure);
  36. }
  37. /*****************************************
  38. * 函数名 : NVIC_Configuration
  39. ******************************************/
  40. void NVIC_Configuration()
  41. {
  42. NVIC_InitTypeDef NVIC_InitStructure;
  43. #ifdef VECT_TAB_RAM
  44. NVIC_SetVectorTable(NVIC_VectTab_RAM,0x0);
  45. #else
  46. NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0);
  47. #endif
  48. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  49. //TIM1更新事件NVIC设置
  50. NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
  51. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  52. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  53. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  54. NVIC_Init(&NVIC_InitStructure);
  55. }
  56. /*****************************************
  57. * 函数名 : TIM_Configuration
  58. ******************************************/
  59. void TIM_Configuration()
  60. {
  61. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  62. TIM_OCInitTypeDef TIM_OCInitStructure;
  63. TIM_TimeBaseStructure.TIM_Period = 16050 - 1;
  64. TIM_TimeBaseStructure.TIM_Prescaler = 100 - 1;
  65. TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  66. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  67. TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  68. TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
  69. TIM_ARRPreloadConfig(TIM1, ENABLE);
  70. TIM_ClearFlag(TIM1, TIM_FLAG_Update);
  71. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  72. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  73. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  74. TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
  75. TIM_OCInitStructure.TIM_Pulse = 0;
  76. TIM_OC1Init(TIM1, &TIM_OCInitStructure);
  77. TIM_OC2Init(TIM1, &TIM_OCInitStructure);
  78. TIM_OC3Init(TIM1, &TIM_OCInitStructure);
  79. TIM_OC4Init(TIM1, &TIM_OCInitStructure);
  80. TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
  81. TIM_CtrlPWMOutputs(TIM1, ENABLE);
  82. TIM_Cmd(TIM1, ENABLE);
  83. }
其中断程序为: void TIM1_UP_IRQHandler(void) { #define TIM1_CH1_Pulse TIM1->CCR1 //A+ #define TIM1_CH2_Pulse TIM1->CCR2 //B+ #define TIM1_CH3_Pulse TIM1->CCR3 //A- #define TIM1_CH4_Pulse TIM1->CCR4 //B- //半个周期的正弦表 static vu16 SinwaveTable[128] = { 0,392,785,1177,1568,1958,2347,2735,3121,3505,3887,4267,4644,5018,5390,5758, 6122,6483,6840,7193,7542,7886,8225,8559,8889,9212,9531,9843,10150,10450,10744,11032, 11313,11587,11855,12115,12368,12613,12851,13081,13303,13517,13723,13921,14110,14291,14463,14627, 14782,14927,15064,15192,15311,15420,15520,15611,15692,15764,15826,15879,15922,15956,15980,15995, 16000,15995,15980,15956,15922,15879,15826,15764,15692,15611,15520,15420,15311,15192,15064,14927, 14782,14627,14463,14291,14110,13921,13723,13517,13303,13081,12851,12613,12368,12115,11855,11587, 11313,11032,10744,10450,10150,9843,9531,9212,8889,8559,8225,7886,7542,7193,6840,6483, 6122,5758,5390,5018,4644,4267,3887,3505,3121,2735,2347,1958,1568,1177,785,392 }; static u8 stepcounter_A = 0; static u8 stepcounter_B = 64; //控制相位 static u8 WhosTurnFlag_A = 1; static u8 WhosTurnFlag_B = 1; if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) { //A+和A-交替产生半个周期的SPWM,组成一个完整的正弦波 //A+ if (WhosTurnFlag_A) { if (stepcounter_A > 127) { stepcounter_A = 0; WhosTurnFlag_A = 0; } TIM1_CH1_Pulse = SinwaveTable[stepcounter_A]; stepcounter_A++; } //A- else { if (stepcounter_A > 127) { stepcounter_A = 0; WhosTurnFlag_A = 1; } TIM1_CH3_Pulse = SinwaveTable[stepcounter_A]; stepcounter_A++; } //B+和B-交替产生半个周期的SPWM,组成一个完整的正弦波 //B+ if (WhosTurnFlag_B) { if (stepcounter_B > 127) { stepcounter_B = 0; WhosTurnFlag_B = 0; } TIM1_CH2_Pulse = SinwaveTable[stepcounter_B]; stepcounter_B++; } //B- else { if (stepcounter_B > 127) { stepcounter_B = 0; WhosTurnFlag_B = 1; } TIM1_CH4_Pulse = SinwaveTable[stepcounter_B]; stepcounter_B++; } } TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update); } 对于一些基本问题的排除:1. 供电: 采用的是24V转5V降压模块,输出5V/3A,我用驱动器时使用0.3A即可驱动步进电机,所以供电应该没有问题; 2. 接线: 步进电机的接线方式与使用驱动器时的相同,由于驱动器能够顺利地驱动步进电机,所以接线应该没有问题; 3. 工作频率: 我需求的运转速度不高,不超过50Hz,而这种速度在驱动器上能够顺利运转,因此不是速度过高的问题. 希望论坛的各位大神能够帮我这个门外汉分析一下是哪里出了问题,事关毕业,感激不尽! 本帖最后由 lizoyu 于 2016-3-27 22:36 编辑

回复评论 (12)

输出的波形不对,感觉你每次输出了两个正弦半周期
点赞  2016-3-28 10:27
使用4Hz 8拍驱动
如果有示波器分别观察一下两个驱动器的A+输出吧,同理A-,B,B-,有问题应该会看到差异
点赞  2016-3-28 10:34
应该是时序的问题,楼主可以先写个简单代码把步进电机的单步运行弄明白,网上的一些开发板资料都有简单程序,PWM波足矣,代码没仔细看,给个建议
点赞  2016-3-28 10:37
我好像发现问题了.

TIM_PrescalerConfig(TIM2, 4, TIM_PSCReloadMode_Immediate);导致了预分频值失效,使得频率非常高.
点赞  2016-3-28 10:43
引用: huo_hu 发表于 2016-3-28 10:27
输出的波形不对,感觉你每次输出了两个正弦半周期

捕获1.JPG
这是放大后的一个SPWM波形,您看看是不是有问题?
点赞  2016-3-28 10:53
引用: tianshuihu 发表于 2016-3-28 10:34
使用4Hz 8拍驱动
如果有示波器分别观察一下两个驱动器的A+输出吧,同理A-,B,B-,有问题应该会看到差异

谢谢!我去实验室用示波器测一测
点赞  2016-3-28 10:56
引用: lizoyu 发表于 2016-3-28 10:43
我好像发现问题了.

TIM_PrescalerConfig(TIM2, 4, TIM_PSCReloadMode_Immediate);导致了预分频值失效,使 ...

频率恢复正常后,电机不震动了,但是不转动
点赞  2016-3-28 11:02
引用: lizoyu 发表于 2016-3-28 10:53
这是放大后的一个SPWM波形,您看看是不是有问题?

先不管数据,A输出到正弦极大B开始输出,你那个貌似A走过一个半周期了才开始出B
点赞  2016-3-28 16:46
在官网找了个文档,您看看呢。

DM00119042.pdf (491.61 KB)
(下载次数: 176, 2016-3-28 21:51 上传)

点赞  2016-3-28 21:51
看起来是时序需要重新调整下
点赞  2016-3-31 09:38
这不是废话吗,人家都说是时序不对导致电机不转,都把代码贴出来了要实际一点的建议,草
点赞  2016-11-11 16:28

这个通道四的初始比较值不太对吧。

点赞  2024-6-28 16:14
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复