赞
踩
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对应三个通道的比较值
说白了,就是要实现电流环的固定输入的开环控制。
参考链接: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的配置就这样了。
首先工程里面需要打开一下 PWM,代码如下
- HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
- HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
- HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);
- HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1);
- HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_2);
- 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电机。
配置完啦,就可以上代码啦
- // motor/motor.c
- #include "motor.h"
-
- void motor_stop(void){
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_RESET);
- }
-
- void motor_start(void){
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
- }
-
- // motor/motor.h
- #ifndef __USERPROGRAM_MOTOR_H
- #define __USERPROGRAM_MOTOR_H
-
- #include "gpio.h"
- #include "stm32f405xx.h"
-
- void motor_stop(void);
- void motor_start(void);
-
- #endif
-
- // control/current.c
- #include "current.h"
-
- #include "arm_math.h"
-
- float iq = 0.0f;
- float id = 0.0f;
-
- float theta = 0.0f;
- float sin_theta = 0.0f;
- float cos_theta = 0.0f;
-
- float V_Alpha = 0.0f;
- float V_Beta = 0.0f;
-
- float SectorJudgmentFactor1 = 0.0;
- float SectorJudgmentFactor2 = 0.0;
-
- int SectorNum = 0;
-
- float MiddleTerm_X = 0.0f;
- float MiddleTerm_Y = 0.0f;
- float MiddleTerm_Z = 0.0f;
-
- float t_first = 0.0f;
- float t_second = 0.0f;
- float t_firstaddsecond = 0.0f;
- float t_a = 0.0f;
- float t_b = 0.0f;
- float t_c = 0.0f;
-
- float t_cm1 = 0.0f;
- float t_cm2 = 0.0f;
- float t_cm3 = 0.0f;
-
- uint16_t TIM1_CH1_CMP_VAL = 0,
- TIM1_CH2_CMP_VAL = 0,
- TIM1_CH3_CMP_VAL = 0; // 重新装载到比较寄存器的值
-
- void Get_EncoderOffset_Of_Zero_Electric_Angle_Method_1(void){
-
- float TempVar1 = 0.0f;
- float TempVar2 = 0.0f;
-
- id = 1;
-
- theta = 0.0f;
- sin_theta = arm_sin_f32(theta);
- cos_theta = arm_cos_f32(theta);
-
-
- // Anti-Park
- V_Alpha = (id*cos_theta - iq*sin_theta);
- V_Beta = (id*sin_theta + iq*cos_theta);
-
- // 确定扇区
- TempVar1 = V_Alpha * 0.86602540378444f;
- TempVar2 = V_Beta * 0.5f;
-
- SectorJudgmentFactor1 = TempVar1 - TempVar2;
- SectorJudgmentFactor2 = - TempVar1 - TempVar2;
-
- SectorNum = 0;
- if( V_Beta >= 0)
- SectorNum = SectorNum + 1;
- if( SectorJudgmentFactor1 >= 0 )
- SectorNum = SectorNum + 2;
- if( SectorJudgmentFactor2 >= 0 )
- SectorNum = SectorNum + 4;
-
- // 计算中间变量X Y Z
- MiddleTerm_X = V_Beta;
- MiddleTerm_Y = TempVar1 + TempVar2;
- MiddleTerm_Z = -TempVar1 + TempVar2;
-
- // ? where is √3*Ts/Vdc ?
-
- // 作用时间计算
- switch(SectorNum){
- case 0:
- t_cm1 = 0;
- t_cm2 = 0;
- t_cm3 = 0;
- break;
- case 1: /*Sector 1: t_1st = Z and t_2nd = Y (Tcm1/2/3 ---> Tb,Ta,Tc)*/
- t_first = MiddleTerm_Z;
- t_second = MiddleTerm_Y;
-
- // 过调制处理
- if(t_first + t_second > 1){
- t_firstaddsecond = t_first + t_second;
- t_first = t_first / t_firstaddsecond;
- t_second = t_second / t_firstaddsecond;
- }
-
- t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
- t_b = t_a + t_first;
- t_c = t_b + t_second;
-
- t_cm1 = t_b;
- t_cm2 = t_a;
- t_cm3 = t_c;
- break;
- case 2: /*Sector 2: t_1st = Y and t_2nd = -X (Tcm1/2/3 ---> Ta,Tc,Tb)*/
- t_first = MiddleTerm_Y;
- t_second = -MiddleTerm_X;
- // 过调制处理
- if(t_first + t_second > 1){
- t_firstaddsecond = t_first + t_second;
- t_first = t_first / t_firstaddsecond;
- t_second = t_second / t_firstaddsecond;
- }
- t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
- t_b = t_a + t_first;
- t_c = t_b + t_second;
- t_cm1 = t_a;
- t_cm2 = t_c;
- t_cm3 = t_b;
- break;
- case 3: /*Sector 3: t_1st = -Z and t_2nd = X (Tcm1/2/3 ---> Ta,Tb,Tc)*/
- t_first = -MiddleTerm_Z;
- t_second = MiddleTerm_X;
-
- // 过调制处理
- if(t_first + t_second > 1){
- t_firstaddsecond = t_first + t_second;
- t_first = t_first / t_firstaddsecond;
- t_second = t_second / t_firstaddsecond;
- }
-
- t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
- t_b = t_a + t_first;
- t_c = t_b + t_second;
- t_cm1 = t_a;
- t_cm2 = t_b;
- t_cm3 = t_c;
- break;
- case 4: /*Sector 4: t_1st = -X and t_2nd = Z (Tcm1/2/3 ---> Tc,Tb,Ta)*/
- t_first = -MiddleTerm_X;
- t_second = MiddleTerm_Z;
- // 过调制处理
- if(t_first + t_second > 1){
- t_firstaddsecond = t_first + t_second;
- t_first = t_first / t_firstaddsecond;
- t_second = t_second / t_firstaddsecond;
- }
- t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
- t_b = t_a + t_first;
- t_c = t_b + t_second;
- t_cm1 = t_c;
- t_cm2 = t_b;
- t_cm3 = t_a;
- break;
- case 5: /*Sector 5: t_1st = X and t_2nd = -Y (Tcm1/2/3 ---> Tc,Ta,Tb)*/
- t_first = MiddleTerm_X;
- t_second = -MiddleTerm_Y;
- // 过调制处理
- if(t_first + t_second > 1){
- t_firstaddsecond = t_first + t_second;
- t_first = t_first / t_firstaddsecond;
- t_second = t_second / t_firstaddsecond;
- }
- t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
- t_b = t_a + t_first;
- t_c = t_b + t_second;
- t_cm1 = t_c;
- t_cm2 = t_a;
- t_cm3 = t_b;
- break;
- case 6: /*Sector 6: t_1st = -Y and t_2nd = -Z (Tcm1/2/3 ---> Tb,Tc,Ta)*/
- t_first = -MiddleTerm_Y;
- t_second = -MiddleTerm_Z;
- // 过调制处理
- if(t_first + t_second > 1){
- t_firstaddsecond = t_first + t_second;
- t_first = t_first / t_firstaddsecond;
- t_second = t_second / t_firstaddsecond;
- }
- t_a = (1 - t_first - t_second)*0.5f; // ? why not (Ts - t_first - t_second) / 4
- t_b = t_a + t_first;
- t_c = t_b + t_second;
- t_cm1 = t_b;
- t_cm2 = t_c;
- t_cm3 = t_a;
- break;
-
- default:
- break;
- }
-
- TIM1_CH1_CMP_VAL = (uint16_t)((t_cm1)*500.0f);
- TIM1_CH2_CMP_VAL = (uint16_t)((t_cm2)*500.0f);
- TIM1_CH3_CMP_VAL = (uint16_t)((t_cm3)*500.0f);
-
- TIM1->CCR1 = TIM1_CH1_CMP_VAL;
- TIM1->CCR2 = TIM1_CH2_CMP_VAL;
- TIM1->CCR3 = TIM1_CH3_CMP_VAL;
-
-
- }
-
-
-
- // control/current.h
- #ifndef __USERPROGRAM_CURRENT_H
- #define __USERPROGRAM_CURRENT_H
-
- #include "stm32f405xx.h"
- #include "tim.h"
-
- void Get_EncoderOffset_Of_Zero_Electric_Angle_Method_1(void);
-
- #endif
-
-
-
-
- // main.c
- // 部分代码
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_DMA_Init();
- MX_USART1_UART_Init();
- MX_TIM3_Init();
- MX_TIM2_Init();
- MX_SPI1_Init();
- MX_SPI2_Init();
- MX_SPI3_Init();
- MX_ADC1_Init();
- MX_ADC2_Init();
- MX_TIM1_Init();
- MX_TIM8_Init();
- /* USER CODE BEGIN 2 */
- HAL_TIM_Base_Start_IT(&htim3);
- userinit();
- __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); // 使能串口空闲中断
- HAL_UART_Receive_DMA(&huart1, AngelaRxBuffer,16); // 启串口DMA接收
-
- // 两轴电机对应的PWM使能
- HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); // ★★★ ← 重点看这里
- HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
- HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);
- HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1);
- HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_2);
- HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_3);
-
- // HAL_UART_Receive_IT(&huart1,value,16);
-
- /* 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、这种方法电机会发烫,有点废手,读完编码器数据抓紧断电,各位兄弟
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。