当前位置:   article > 正文

STM32_FOC_3_标定零电角度对应的编码器读数_spi stm32 foc

spi stm32 foc

1. 整体流程

1、令iq = 0, id = 合适值,假设电角度θ = 0

2、park逆变换求得iα和iβ

3、确定扇区

4、计算中间变量 X Y Z

5、根据扇区和中间变量得到 t_1st 和 t_2nd

6、当t_1st + t_2nd > Ts 进行 过调制处理。

7、根据扇区和t_1st 和 t_2st,得到Tcm1 Tcm2 Tcm3

8、根据Tcm1 Tcm2 Tcm3设置PWM对应三个通道的比较值

说白了,就是要实现电流环的固定输入的开环控制。

2. STM32CubeMx对PWM需要的配置

参考链接:STM32用cube配置PWM波输出,HAL库PWM_Drive World的博客-CSDN博客_hal库pwm

复习一下定时器挂再哪个APB上:STM32使用定时器实现<获取代码块运算时间>的功能heqiunong的博客-CSDN博客

我们用 TIM1和TIM8做电机的控制, 他们都挂在APB2上,也就是主频168MHz

另外,看下电路原理图

电机1 连的是 PC6 PC7 PC8、电机2 连的是 PA8 PA9 PA10, 因此先用钉子定一手

后续就用TIM1为例,继续讲解。也就是说TIM1的通道1 通道2 通道3 分别对应 电机的三相输入电压。继续看其他的配置,首先看下TIM1总体Mode的配置

 

 然后看看具体参数的配置

参考链接:STM32定时器三种中心对齐计数模式简介_茶话MCU的博客-CSDN博客

关于Counter Mode 有 Up / Down / Center Aligned mode 1、2、3

Center Aligned mode和Up还有Down的主要区别是:STM32的通用定时器和高级定时器除了支持单向的向上或向下计数模式外,还支持中心对齐计数模式,即一个计数周期内分别由向上计数和向下计数两个过程组成。讲更细一点,假设你的ARR寄存器设置为500, 你的计数器寄存器CNT会从0 -> 500,然后再从500 ->0。 假设比较寄存器CCR=400,当计数器寄存器向上计数到400时, PWM输出通道会发生一次电平翻转,当计数器寄存器从500向下计数时,再一次计数到400时,PWM输出通道又会发生一次电平翻转。

在我的理解中,如果默认时低电平, 那么输出波形就是长这样。

 如果默认时是高电平,那么输出波形应该就是长这样

那默认的电平到底是高电平还得低电平呢? 一探究竟。

手册上讲了,计数器和捕获/比较寄存器比较之后,会输出一个OC1REF

可以看到, 输出的时候有两个互补的输出 TIMx_CH1和TIMx_CH1N,继续看这里可能看不出来啥了,看一下Cube上的配置,发现通道可以配置 极性(CH Polarity)和 空闲状态(CH Idle State)

CSDN有好兄弟做了充分的实验专门讲这个:STM32Cube的PWM控制基础篇(三)定时器的PWM设置详解ASWaterbenben的博客-CSDN博客pwm空闲状态,让我们忘掉手册上讲的,就看这个就可以了,非常感谢作者的分享。

说白了, 主要看→ “CH Polarity”,如果 CH Polarity = High, 我们PWM的输出就是,平时是低电平,发生第一次翻转的后变成高电平。(如果是中心对称模式)发生第二次翻转后变成低电平。CH Idle State是说PWM不输出的时候默认的电平,这个其实不太重要。

下面说一下Center Aligned mode 1、2、3是个mode之间的区别

 

箭头表示事件标志

这里CCR=4,ARR=8。当选择中心对齐计数模式1时,只在向下计数过程中发生匹配动作时才置位比较事件标志CC4IF; 当选择中心对齐计数模式2时,只在向上计数过程中发生匹配动作时才置位比较事件标志CC4IF; 当选择中心对齐计数模式3时,在向上/向下计数过程中发生匹配动作时都会置位比较事件标志CC4IF。当然,比较事件标志被置位时可以触发中断或DMA请求。

再看看PWM的其他配置

 这些都没管,那配置就剩下

这个配置的理解前面也提到了,网上的兄弟讲得很到位了:STM32Cube的PWM控制基础篇(三)定时器的PWM设置详解ASWaterbenben的博客-CSDN博客pwm空闲状态

PWM mode1 和 PWM mode2 是互补波形。

Pulse是用于配置初始的脉冲宽度。 占空比=(Pulse/自动重载值)*100%

Fast Mode网上的兄弟也没搞清楚。

If CH Polarity = High, 我们PWM的输出就是,平时是低电平,发生第一次翻转的后变成高电平。(如果是中心对称模式)发生第二次翻转后变成低电平。

CH Idle State是说PWM不输出的时候默认的电平,这个其实不太重要

那么PWM的配置就这样了。

3. STM32工程里面需要做哪些工作

首先工程里面需要打开一下 PWM,代码如下

  1. HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
  2. HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
  3. HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);
  4. HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1);
  5. HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_2);
  6. HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_3);

另外,电机芯片(L6230Q),有几个引脚需要使能, STM32_MOTOR_EN1和STM32_MOTOR_DIAGEN1

电机2:STM32_MOTOR_EN2和STM32_MOTOR_DIAGEN2

 

 PA4 PA5 PA11 PA12配置成输出模式, 这些引脚拉高就是Enable电机, 拉低就是Disable电机。

配置完啦,就可以上代码啦

  1. // motor/motor.c
  2. #include "motor.h"
  3. void motor_stop(void){
  4. HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_RESET);
  5. }
  6. void motor_start(void){
  7. HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
  8. }
  9. // motor/motor.h
  10. #ifndef __USERPROGRAM_MOTOR_H
  11. #define __USERPROGRAM_MOTOR_H
  12. #include "gpio.h"
  13. #include "stm32f405xx.h"
  14. void motor_stop(void);
  15. void motor_start(void);
  16. #endif
  17. // control/current.c
  18. #include "current.h"
  19. #include "arm_math.h"
  20. float iq = 0.0f;
  21. float id = 0.0f;
  22. float theta = 0.0f;
  23. float sin_theta = 0.0f;
  24. float cos_theta = 0.0f;
  25. float V_Alpha = 0.0f;
  26. float V_Beta = 0.0f;
  27. float SectorJudgmentFactor1 = 0.0;
  28. float SectorJudgmentFactor2 = 0.0;
  29. int SectorNum = 0;
  30. float MiddleTerm_X = 0.0f;
  31. float MiddleTerm_Y = 0.0f;
  32. float MiddleTerm_Z = 0.0f;
  33. float t_first = 0.0f;
  34. float t_second = 0.0f;
  35. float t_firstaddsecond = 0.0f;
  36. float t_a = 0.0f;
  37. float t_b = 0.0f;
  38. float t_c = 0.0f;
  39. float t_cm1 = 0.0f;
  40. float t_cm2 = 0.0f;
  41. float t_cm3 = 0.0f;
  42. uint16_t TIM1_CH1_CMP_VAL = 0,
  43. TIM1_CH2_CMP_VAL = 0,
  44. TIM1_CH3_CMP_VAL = 0; // 重新装载到比较寄存器的值
  45. void Get_EncoderOffset_Of_Zero_Electric_Angle_Method_1(void){
  46. float TempVar1 = 0.0f;
  47. float TempVar2 = 0.0f;
  48. id = 1;
  49. theta = 0.0f;
  50. sin_theta = arm_sin_f32(theta);
  51. cos_theta = arm_cos_f32(theta);
  52. // Anti-Park
  53. V_Alpha = (id*cos_theta - iq*sin_theta);
  54. V_Beta = (id*sin_theta + iq*cos_theta);
  55. // 确定扇区
  56. TempVar1 = V_Alpha * 0.86602540378444f;
  57. TempVar2 = V_Beta * 0.5f;
  58. SectorJudgmentFactor1 = TempVar1 - TempVar2;
  59. SectorJudgmentFactor2 = - TempVar1 - TempVar2;
  60. SectorNum = 0;
  61. if( V_Beta >= 0)
  62. SectorNum = SectorNum + 1;
  63. if( SectorJudgmentFactor1 >= 0 )
  64. SectorNum = SectorNum + 2;
  65. if( SectorJudgmentFactor2 >= 0 )
  66. SectorNum = SectorNum + 4;
  67. // 计算中间变量X Y Z
  68. MiddleTerm_X = V_Beta;
  69. MiddleTerm_Y = TempVar1 + TempVar2;
  70. MiddleTerm_Z = -TempVar1 + TempVar2;
  71. // ? where is √3*Ts/Vdc ?
  72. // 作用时间计算
  73. switch(SectorNum){
  74. case 0:
  75. t_cm1 = 0;
  76. t_cm2 = 0;
  77. t_cm3 = 0;
  78. break;
  79. case 1: /*Sector 1: t_1st = Z and t_2nd = Y (Tcm1/2/3 ---> Tb,Ta,Tc)*/
  80. t_first = MiddleTerm_Z;
  81. t_second = MiddleTerm_Y;
  82. // 过调制处理
  83. if(t_first + t_second > 1){
  84. t_firstaddsecond = t_first + t_second;
  85. t_first = t_first / t_firstaddsecond;
  86. t_second = t_second / t_firstaddsecond;
  87. }
  88. t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
  89. t_b = t_a + t_first;
  90. t_c = t_b + t_second;
  91. t_cm1 = t_b;
  92. t_cm2 = t_a;
  93. t_cm3 = t_c;
  94. break;
  95. case 2: /*Sector 2: t_1st = Y and t_2nd = -X (Tcm1/2/3 ---> Ta,Tc,Tb)*/
  96. t_first = MiddleTerm_Y;
  97. t_second = -MiddleTerm_X;
  98. // 过调制处理
  99. if(t_first + t_second > 1){
  100. t_firstaddsecond = t_first + t_second;
  101. t_first = t_first / t_firstaddsecond;
  102. t_second = t_second / t_firstaddsecond;
  103. }
  104. t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
  105. t_b = t_a + t_first;
  106. t_c = t_b + t_second;
  107. t_cm1 = t_a;
  108. t_cm2 = t_c;
  109. t_cm3 = t_b;
  110. break;
  111. case 3: /*Sector 3: t_1st = -Z and t_2nd = X (Tcm1/2/3 ---> Ta,Tb,Tc)*/
  112. t_first = -MiddleTerm_Z;
  113. t_second = MiddleTerm_X;
  114. // 过调制处理
  115. if(t_first + t_second > 1){
  116. t_firstaddsecond = t_first + t_second;
  117. t_first = t_first / t_firstaddsecond;
  118. t_second = t_second / t_firstaddsecond;
  119. }
  120. t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
  121. t_b = t_a + t_first;
  122. t_c = t_b + t_second;
  123. t_cm1 = t_a;
  124. t_cm2 = t_b;
  125. t_cm3 = t_c;
  126. break;
  127. case 4: /*Sector 4: t_1st = -X and t_2nd = Z (Tcm1/2/3 ---> Tc,Tb,Ta)*/
  128. t_first = -MiddleTerm_X;
  129. t_second = MiddleTerm_Z;
  130. // 过调制处理
  131. if(t_first + t_second > 1){
  132. t_firstaddsecond = t_first + t_second;
  133. t_first = t_first / t_firstaddsecond;
  134. t_second = t_second / t_firstaddsecond;
  135. }
  136. t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
  137. t_b = t_a + t_first;
  138. t_c = t_b + t_second;
  139. t_cm1 = t_c;
  140. t_cm2 = t_b;
  141. t_cm3 = t_a;
  142. break;
  143. case 5: /*Sector 5: t_1st = X and t_2nd = -Y (Tcm1/2/3 ---> Tc,Ta,Tb)*/
  144. t_first = MiddleTerm_X;
  145. t_second = -MiddleTerm_Y;
  146. // 过调制处理
  147. if(t_first + t_second > 1){
  148. t_firstaddsecond = t_first + t_second;
  149. t_first = t_first / t_firstaddsecond;
  150. t_second = t_second / t_firstaddsecond;
  151. }
  152. t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
  153. t_b = t_a + t_first;
  154. t_c = t_b + t_second;
  155. t_cm1 = t_c;
  156. t_cm2 = t_a;
  157. t_cm3 = t_b;
  158. break;
  159. case 6: /*Sector 6: t_1st = -Y and t_2nd = -Z (Tcm1/2/3 ---> Tb,Tc,Ta)*/
  160. t_first = -MiddleTerm_Y;
  161. t_second = -MiddleTerm_Z;
  162. // 过调制处理
  163. if(t_first + t_second > 1){
  164. t_firstaddsecond = t_first + t_second;
  165. t_first = t_first / t_firstaddsecond;
  166. t_second = t_second / t_firstaddsecond;
  167. }
  168. t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
  169. t_b = t_a + t_first;
  170. t_c = t_b + t_second;
  171. t_cm1 = t_b;
  172. t_cm2 = t_c;
  173. t_cm3 = t_a;
  174. break;
  175. default:
  176. break;
  177. }
  178. TIM1_CH1_CMP_VAL = (uint16_t)((t_cm1)*500.0f);
  179. TIM1_CH2_CMP_VAL = (uint16_t)((t_cm2)*500.0f);
  180. TIM1_CH3_CMP_VAL = (uint16_t)((t_cm3)*500.0f);
  181. TIM1->CCR1 = TIM1_CH1_CMP_VAL;
  182. TIM1->CCR2 = TIM1_CH2_CMP_VAL;
  183. TIM1->CCR3 = TIM1_CH3_CMP_VAL;
  184. }
  185. // control/current.h
  186. #ifndef __USERPROGRAM_CURRENT_H
  187. #define __USERPROGRAM_CURRENT_H
  188. #include "stm32f405xx.h"
  189. #include "tim.h"
  190. void Get_EncoderOffset_Of_Zero_Electric_Angle_Method_1(void);
  191. #endif
  192. // main.c
  193. // 部分代码
  194. /* Initialize all configured peripherals */
  195. MX_GPIO_Init();
  196. MX_DMA_Init();
  197. MX_USART1_UART_Init();
  198. MX_TIM3_Init();
  199. MX_TIM2_Init();
  200. MX_SPI1_Init();
  201. MX_SPI2_Init();
  202. MX_SPI3_Init();
  203. MX_ADC1_Init();
  204. MX_ADC2_Init();
  205. MX_TIM1_Init();
  206. MX_TIM8_Init();
  207. /* USER CODE BEGIN 2 */
  208. HAL_TIM_Base_Start_IT(&htim3);
  209. userinit();
  210. __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); // 使能串口空闲中断
  211. HAL_UART_Receive_DMA(&huart1, AngelaRxBuffer,16); // 启串口DMA接收
  212. // 两轴电机对应的PWM使能
  213. HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); // ★★★ ← 重点看这里
  214. HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
  215. HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);
  216. HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1);
  217. HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_2);
  218. HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_3);
  219. // HAL_UART_Receive_IT(&huart1,value,16);
  220. /* USER CODE END 2 */

一些理解性的东西:

1、比较/捕获 CCR寄存器的值,并不是越大占空比就越大,而是越小占空比越到

2、 令iq=1,id=1,θ=0的方法。与直接A相电压输入高电平,B相和C相电压输入低电平的方法,是等价的。

3、关于频率的问题, 168MHz主频的TIM1和TIM8,经过2分频后,变成了84MHz,如果ARR计数周期设置为500的话,计数是中心对齐模式1,0 → 500 → 0。也就是完成一次PWM操作的频率为 84MHz/1000 = 84kHz。但电流环控制频率不必达到 84kHz, 比如你10k的执行频率, 那么就是说,可能会有8个周期,你的PWM输出是一样的,这很容易理解。

4、这种方法电机会发烫,有点废手,读完编码器数据抓紧断电,各位兄弟

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/248028
推荐阅读
相关标签
  

闽ICP备14008679号