STM32 PMSM 霍尔 FOC 学习笔记——从 CubeMX 配置到闭环控制

发布时间:2026/9/4 17:45:09
STM32 PMSM 霍尔 FOC 学习笔记——从 CubeMX 配置到闭环控制 一、项目介绍本项目使用 STM32 实现 PMSM 的 FOC 控制通过 ADC 采集三相电流利用 Clarke/Park 变换获得 Id、Iq使用三霍尔传感器获取转子电角度及转速TIM1 产生三相互补 PWM 驱动逆变器。电机首先采用开环方式启动转速稳定后切换至霍尔角度最终加入速度环实现速度闭环控制。本文主要作为个人学习记录重点整理 CubeMX 外设配置、FOC 程序结构以及调试过程中遇到的问题。总框图如下注意Id_ref0二、 STM32CubeMX 配置2.1 时钟配置使能RCC和SYS然后倍频到168KHZ2.2 TIM1——三相 PWM将通道1-3配置为pwm生成通道4配置成产生pwm但是不输出因为通道4我们用来触发ADC采集Prescaler (PSC) 0预分频系数。计数器时钟为定时器时钟 ÷ (PSC1)这里不分频。Counter Mode Center Aligned mode 1中心对齐计数。计数器从 0 数到 8400再反向数到 0常用于中心对齐 PWM。Dithering Disable关闭抖动功能。不开启更高精度的周期/占空比微调。Counter Period (ARR) 8400计数周期上限。中心对齐模式下一个完整周期包含向上和向下计数。Internal Clock Division (CKD) No Division定时器内部采样时钟不分频主要影响数字滤波和死区时间并非 PSC 计数时钟。Repetition Counter (RCR) 0每个计数周期都产生更新事件增大后可隔若干周期再更新。Auto-reload preload Disable修改 ARR 后立即生效启用时新 ARR 会等到下一次更新事件再生效避免周期中途突变。当按以上配置时PSC0ARR8400PWM周期160M/(8400*2)10KHZCCR1 → U相占空比CCR2 → V相占空比CCR3 → W相占空比这里配置一下死区时间感兴趣的可以去了解一下怎么计算通道1-3默认配置即可4通道占空比为8400-1模式选择模式2用于触发ADC采集TRGO OC4REF → ADC用来触发ADC采集三相电流2.3 ADC 三相电流采样配置首先配置OPAMP1OPAMP2OPAMP3为Standalone独立模式指片内运放作为普通运算放大器使用参考原理图的ADC采集配置ADC的通道ADC2_IN9采集U相电流ADC2_IN5采集V相电流ADC1_IN5采集W相电流ADC1使能通道5设置为单端采样测量ADC_IN5相对于模拟地VSSA/GND的电压注意引脚为PB14ADC2分别使能通道9和5注意引脚是PC3PC4配置ADC1和ADC2的注入组ADC2需要设置为扫描模式其他默认即可重点是触发转换为TIM1的TIGO然后开启ADC1和ADC2的中断2.4 TIM3——霍尔传感器配置根据原理图可知霍尔传感器接在TIM3上TIM3中选择XOR ON / Hall Sensor Mode,启用霍尔传感器分频为168-1此时每计一个数为1MHZ就是1usARR拉到最大因为我们是用ARR的值得到时间的最后配置NVIC开启中断最后配置一下你用的通信后直接生成工程三、FOC 核心代码分析3.1 PMSM控制状态机与主控制流程pmsm.c首先我们需要建立pmsm.c和pmsm.h这2个文件这个函数就是最重要的函数其他的封装文件都是服务于这个文件其中有2个函数最重要第一个就是motor_Init()void Motor_Init(void) { /* adc配置 */ HAL_OPAMP_Start(hopamp1); //使能OPAMP1 HAL_OPAMP_Start(hopamp2); //使能OPAMP2 HAL_OPAMP_Start(hopamp3); //使能OPAMP3 HAL_Delay(10U); // 等待运放稳定 HAL_ADCEx_Calibration_Start(hadc1,ADC_SINGLE_ENDED); //校准ADC1的注入组通道 HAL_ADCEx_Calibration_Start(hadc2,ADC_SINGLE_ENDED); //校准ADC2的注入组通道 __HAL_ADC_CLEAR_FLAG(hadc1,ADC_FLAG_JEOC); //清除ADC1的JEOCcubemx会上电后自动清除可不写 __HAL_ADC_CLEAR_FLAG(hadc2,ADC_FLAG_JEOC); //清除ADC2的JEOCcubemx会上电后自动清除可不写 HAL_ADCEx_InjectedStart(hadc1); //开启ADC1注入组 HAL_ADCEx_InjectedStart_IT(hadc2); //开启ADC2注入组中断 HAL_TIM_Base_Start(htim1); //开启TIM1的时基单元 HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_4); //开启TIM1的四通道开始触发ADC采集电流 /* 等待ADC校准 */ while (current_offset_ready 0U) { } Hall_Init(); //开启霍尔中断当霍尔值发生改变时触发中断 PID_Init(); //初始化PID例如KpKbKi上限下限误差累积速度环和电流环 FOC_Init(); //初始化UdcTpwm电机的初始化方向 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_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_3); rtX.Id_ref 0.0f; rtX.Iq_ref 1.5f; rtX.Speed_ref150; motor_control_enabled 1U; //初始化完成电机启动标志位当电流校准完成参数初始化后即可启动 }然后第二个函数就是Motor_Run(),由ADC触发调用在adc.c中调用此行代码就是当ADC2采集完成后触发中断执行Motor_Run()注意adc.h文件中是包含main.h的为了使adc.c能够使用此函数我们在main.h写下如此代码foc.h和pid.h和hall.h等会一一介绍。/* USER CODE BEGIN 1 */ void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc-Instance ADC2) { Motor_Run(); } } /* USER CODE END 1 *//* USER CODE BEGIN Includes */ #include pmsm.h #include adc.h #include opamp.h #include tim.h #include foc.h #include pid.h #include hall.h /* USER CODE END Includes */首先定义一些变量在pmsm.c里static uint32_t current_offset_sum[3]; static uint16_t current_offset_count; static volatile uint8_t current_offset_ready; static volatile uint8_t motor_control_enabled; static float theta; float current_offset_adc[3]; float cnt;pmsm.h定义这些变量pmsm.h里还需要包含main.hpmsm.c要包含pmsm.h其他文件也是类似的接下来不再赘述本文的结构体命名后需要在.h文件中加上extern表示外部可调用还有函数如声明要记得volatile在这里可以简单理解为告诉编译器这个变量可能会被中断等地方突然修改每次使用时都必须重新从内存读取不能偷懒使用之前的值。#define CURRENT_ADC_TO_AMP (3.3f / 4095.0f / 10.0f / 0.003f) #define CURRENT_OFFSET_SAMPLES 1024U extern float current_offset_adc[3];接下来我们解释Motor_Init()void Motor_Run(void) { /* PWM三相输出关闭时采集零电流偏置 */ if (current_offset_ready 0U) { current_offset_sum[0] ADC1-JDR1; current_offset_sum[1] ADC2-JDR1; current_offset_sum[2] ADC2-JDR2; current_offset_count; if (current_offset_count CURRENT_OFFSET_SAMPLES) { current_offset_adc[0] (float)current_offset_sum[0] / CURRENT_OFFSET_SAMPLES; current_offset_adc[1] (float)current_offset_sum[1] / CURRENT_OFFSET_SAMPLES; current_offset_adc[2] (float)current_offset_sum[2] / CURRENT_OFFSET_SAMPLES; current_offset_ready 1U; //当电流校准完成后将标志位设置为1然后motor_Init继续运行 } return; } /* 等待电机启动标志为1 */ if (motor_control_enabled 0U) { return; } if(Hall.angle2*PI) { Hall.angle-2*PI; } else if(Hall.angle0) { Hall.angle2*PI; } /* 开环控制一段时间 */ if(cnt6000) { cnt; if(rtX.DirCW) theta0.01f; else if(rtX.DirCCW) theta-0.01f; if(theta2*PI) theta-2*PI; else if(theta0) theta2*PI; rtX.thetatheta; } /* 切到闭环角度 */ else { Hall.flag1; rtX.thetaHall.angle; if(rtX.Iq_ref 0.5f) rtX.Iq_ref 0.5f; else if(rtX.Iq_ref 2.5f) rtX.Iq_ref 2.5f; } FOC_Run(); //在此函数里会采集ADC得到电流再执行clarkesvpwm等等最后得到占空比给TIM1 }3.2 FOC核心算法实现foc.c接下来我们就来了解FOC_Run()和FOC_Init()是如何执行的,它在代码全部在foc.c里面首先是在foc.h定义相应的结构体变量typedef struct { float theta; /* 转子角度 */ float Udc; /* 电机供电电压 */ float Tpwm; /* 电机PWM周期 */ uint8_t Dir; /* 电机转向 */ float Id_ref; /* Id的目标电流 */ float Iq_ref; /* Iq的目标电路 */ float Speed_ref; /* 电机的目标速度 */ } ExtX; typedef struct { float Vd; /* PI控制器输出d轴电压 */ float Vq; /* PI控制器输出q轴电压 */ } Voltage_DQ_DEF; typedef struct { float Cos; /* 转子角度计算Cos值 */ float Sin; /* 转子角度计算Sin值 */ } Transf_Cos_Sin_DEF; typedef struct { float Valpha; /* RevPark变换输出Alpha轴电压 */ float Vbeta; /* RevPark变换输出Beta轴电压 */ } Voltage_Alpha_Beta_DEF; typedef struct { float Ia; /* 电机U项电流 */ float Ib; /* 电机V项电流 */ float Ic; /* 电机W项电流 */ } Current_ABC_DEF; typedef struct { float Ialpha; /* Clarke变换输出Alpha项电流 */ float Ibeta; /* Clarke变换输出Beta项电流 */ } Current_Alpha_Beta_DEF; typedef struct { float Id; /* Park变换输出d轴电流 */ float Iq; /* Park变换输出q轴电流 */ } Current_DQ_DEF;接着在foc.c中为结构体命名ExtX rtX; /* 输入结构体变量 */ Transf_Cos_Sin_DEF Transf_Cos_Sin; /* 当前角度计算三角函数 */ Voltage_DQ_DEF Voltage_DQ; /* DQ电压输出 */ Voltage_Alpha_Beta_DEF Voltage_Alpha_Beta; /* RevPark变换输出 */ Current_ABC_DEF Current_Iabc; /* 当前UVW三项电流 */ Current_Alpha_Beta_DEF Current_Ialpha_beta; /* Clarke变换输出 */ Current_DQ_DEF Current_Idq; /* Park变换输出 */现在我们编写FOC_Init()void FOC_Init(void) { rtX.Udc 24; //设置电机电压为24V rtX.Tpwm 8400*2; //PWM周期为16800次计数 rtX.Dir CCW; //定义电机的初始方向可通过按键来切换方向 }现在来编写FOC_Run()void FOC_Run(void) { Current_Iabc.Ia ((float)ADC2-JDR1 - current_offset_adc[0]) * CURRENT_ADC_TO_AMP; /* 获取当前U项电流 */ Current_Iabc.Ib ((float)ADC2-JDR2 - current_offset_adc[1]) * CURRENT_ADC_TO_AMP; /* 获取当前V项电流 */ Current_Iabc.Ic ((float)ADC1-JDR1 - current_offset_adc[2]) * CURRENT_ADC_TO_AMP; /* 获取当前W项电流 */ Clarke_Transf(Current_Iabc, Current_Ialpha_beta); /* Clarke变换 */ Angle_To_Cos_Sin(rtX.theta, Transf_Cos_Sin); /* 计算三角函数 */ Park_Transf(Current_Ialpha_beta, Transf_Cos_Sin, Current_Idq); /* Park变换 */ /* 电流环 */ Current_PID_Calc(rtX.Id_ref,Current_Idq.Id,Voltage_DQ.Vd,Current_D_PID); Current_PID_Calc(rtX.Iq_ref,Current_Idq.Iq,Voltage_DQ.Vq,Current_Q_PID); Rev_Park_Transf(Voltage_DQ, Transf_Cos_Sin, Voltage_Alpha_Beta); /* Revpark变换 */ SVPWM_Calc(Voltage_Alpha_Beta, rtX.Udc, rtX.Tpwm); /* SVPWM */ }Clarke变换void Clarke_Transf(Current_ABC_DEF Current_abc, Current_Alpha_Beta_DEF* Current_alpha_beta) { Current_alpha_beta-Ialpha Current_abc.Ia; Current_alpha_beta-Ibeta (Current_abc.Ia Current_abc.Ib * 2.0F) * 0.577350269F; }COS_SIN值计算void Angle_To_Cos_Sin(float angle, Transf_Cos_Sin_DEF* cos_sin) { cos_sin-Cos cosf(angle); cos_sin-Sin sinf(angle); }PARK变换void Park_Transf(Current_Alpha_Beta_DEF Current_alpha_beta, Transf_Cos_Sin_DEF cos_sin, Current_DQ_DEF* current_dq) { current_dq-Id Current_alpha_beta.Ialpha * cos_sin.Cos Current_alpha_beta.Ibeta * cos_sin.Sin; current_dq-Iq -Current_alpha_beta.Ialpha * cos_sin.Sin Current_alpha_beta.Ibeta * cos_sin.Cos; }反PARK变换void Rev_Park_Transf(Voltage_DQ_DEF v_dq, Transf_Cos_Sin_DEF cos_sin, Voltage_Alpha_Beta_DEF* v_alpha_beta) { v_alpha_beta-Valpha cos_sin.Cos * v_dq.Vd - cos_sin.Sin * v_dq.Vq; v_alpha_beta-Vbeta cos_sin.Sin * v_dq.Vd cos_sin.Cos * v_dq.Vq; }SVPWM计算void SVPWM_Calc(Voltage_Alpha_Beta_DEF v_alpha_beta, float Udc, float Tpwm) { int sector; float Tx,Ty,T,Ta,Tb,Tc; // 矢量扇区选择 (N4C2BA) sector 0; if (v_alpha_beta.Vbeta 0.0F) sector 1; if ((1.73205078F * v_alpha_beta.Valpha - v_alpha_beta.Vbeta) / 2.0F 0.0F) sector 2; if ((-1.73205078F * v_alpha_beta.Valpha - v_alpha_beta.Vbeta) / 2.0F 0.0F) sector 4; // 计算矢量作用时间 switch (sector) { case 1: Tx (-1.5F * v_alpha_beta.Valpha 0.866025388F * v_alpha_beta.Vbeta) * (Tpwm / Udc); Ty (1.5F * v_alpha_beta.Valpha 0.866025388F * v_alpha_beta.Vbeta) * (Tpwm / Udc); break; case 2: Tx (1.5F * v_alpha_beta.Valpha 0.866025388F * v_alpha_beta.Vbeta) * (Tpwm / Udc); Ty -(1.73205078F * v_alpha_beta.Vbeta * Tpwm / Udc); break; case 3: Tx -((-1.5F * v_alpha_beta.Valpha 0.866025388F * v_alpha_beta.Vbeta) * (Tpwm / Udc)); Ty 1.73205078F * v_alpha_beta.Vbeta * Tpwm / Udc; break; case 4: Tx -(1.73205078F * v_alpha_beta.Vbeta * Tpwm / Udc); Ty (-1.5F * v_alpha_beta.Valpha 0.866025388F * v_alpha_beta.Vbeta) * (Tpwm/ Udc); break; case 5: Tx 1.73205078F * v_alpha_beta.Vbeta * Tpwm / Udc; Ty -((1.5F * v_alpha_beta.Valpha 0.866025388F * v_alpha_beta.Vbeta) * (Tpwm/ Udc)); break; default: Tx -((1.5F * v_alpha_beta.Valpha 0.866025388F * v_alpha_beta.Vbeta) * (Tpwm/ Udc)); Ty -((-1.5F * v_alpha_beta.Valpha 0.866025388F * v_alpha_beta.Vbeta) * (Tpwm/ Udc)); break; } // 过调制 T Tx Ty; if (T Tpwm) { Tx Tx * Tpwm / T; Ty Ty * Tpwm / T; } // 扇区切换时间点 Ta (Tpwm - (Tx Ty)) / 4.0F; Tb Tx / 2.0F Ta; Tc Ty / 2.0F Tb; switch (sector) { case 1: TIM1-CCR1 Tb; TIM1-CCR2 Ta; TIM1-CCR3 Tc; break; case 2: TIM1-CCR1 Ta; TIM1-CCR2 Tc; TIM1-CCR3 Tb; break; case 3: TIM1-CCR1 Ta; TIM1-CCR2 Tb; TIM1-CCR3 Tc; break; case 4: TIM1-CCR1 Tc; TIM1-CCR2 Tb; TIM1-CCR3 Ta; break; case 5: TIM1-CCR1 Tc; TIM1-CCR2 Ta; TIM1-CCR3 Tb; break; case 6: TIM1-CCR1 Tb; TIM1-CCR2 Tc; TIM1-CCR3 Ta; break; } }剩下我们还有pid的函数还没介绍稍后再介绍3.3 霍尔角度与速度计算hall.c这里先介绍hall_Init()void Hall_Init(void) { Hall.flag0; /* 开始速度pid环标志位加入闭环之前为0当开环切闭环瞬间为1 */ __HAL_TIM_ENABLE_IT(htim3,TIM_IT_TRIGGER); /* 开启 TIM3 的触发中断允许霍尔触发事件产生中断。 */ HAL_TIMEx_HallSensor_Start_IT(htim3); /* 启动 TIM3 的霍尔传感器模式并开启中断方式检测霍尔信号。 */ }这里再介绍Hall的结构体typedef struct { uint8_t flag; //当为1时开启速度环pid uint8_t state; //当前霍尔状态 uint8_t state_last; //上一次的霍尔值 float angle; //当前角度 float angle_inc; //角度增量 uint32_t ccr; //当前的时间用于计算速度 float speed; //当前速度 float speed_filter; //滤波后的速度更精准 uint8_t dir; //当前方向不是人为给定而是计算得出 }Hall_Typedef;接下来我们介绍霍尔中断函数每当霍尔值发生改变便会进入该程序void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim) { Hall.ccrTIM3-CCR1; //获得上一次霍尔跳变到这些霍尔跳变的时间 __HAL_TIM_SET_COUNTER(htim3,0); //清定时器3的ccr /* 获得霍尔当前值 */ Hall.state HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_6); Hall.state | HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_7)1; Hall.state | HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)2; /* 判断当前霍尔值然后可以得出角度和当前速度 */ //CW:645132 CCW:623154 if(Hall.state 6) { if(Hall.state_last 2) { Hall.angle 2.41f; //当霍尔跳变时2.41rad是当前角度由vofa上位机软件测得 Hall.dir CW; //判断方向不是给定的方向 Hall.speed 2*PI/Hall.ccr*1000000; //计算当前速度 Hall.ccr 0; //将ccr的值清0 } else if(Hall.state_last 4) { Hall.angle 3.01f; Hall.dir CCW; Hall.speed 2*PI/Hall.ccr*1000000; Hall.ccr 0; } } if(Hall.state 4) { if(Hall.state_last 6) { Hall.angle 3.59f; Hall.dir CW; } else if(Hall.state_last 5) { Hall.angle 3.89f; Hall.dir CCW; } } if(Hall.state 5) { if(Hall.state_last 4) { Hall.angle 4.53f; Hall.dir CW; } else if(Hall.state_last 1) { Hall.angle 4.87f; Hall.dir CCW; } } if(Hall.state 1) { if(Hall.state_last 5) { Hall.angle 5.36f; Hall.dir CW; } else if(Hall.state_last 3) { Hall.angle 6.05f; Hall.dir CCW; } } if(Hall.state 3) { if(Hall.state_last 1) { Hall.angle 0.10f; Hall.dir CW; } else if(Hall.state_last 2) { Hall.angle 1.03f; Hall.dir CCW; } } if(Hall.state 2) { if(Hall.state_last 3) { Hall.angle 1.05f; Hall.dir CW; } else if(Hall.state_last 6) { Hall.angle 2.10f; Hall.dir CCW; } } Hall.state_last Hall.state; }接下来介绍速度滤波函数该函数由systick中断调用每1ms产生一次中断也就是1KHZPWM的频率为10KHZvoid SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); /* USER CODE BEGIN SysTick_IRQn 1 */ SysTick_ISR(); //调用速度滤波函数 /* USER CODE END SysTick_IRQn 1 */ }下面是速度滤波函数函数的编写void SysTick_ISR(void) { Hall_SpeedFilter(); } /* 速度滤波角度增量计算开启pid */ static void Hall_SpeedFilter(void) { static uint8_t i0; static float speed_mean,speed[10]; /* 速度滤波 */ speed[i]Hall.speed; if(i10) { i0; speed_mean0; BubbleSort(speed[0],10); for(uint8_t j2;j8;j) { speed_meanspeed[j]; } speed_mean/6.0f; } Hall.speed_filter0.8f*speed_mean0.2f*Hall.speed; Hall.angle_incHall.speed_filter*0.0001f; //角度增量的计算 if(Hall.flag1) //当闭环标志位等于1开启速度环 Speed_PID_Calc(rtX.Speed_ref,Hall.speed_filter,rtX.Iq_ref,Speed_PID); } /* 冒泡排序 */ void BubbleSort(float* speed, uint8_t length) { for (int i 0; i length - 1; i) for (int j 0; j length - 1 - i; j) { if (speed[j] speed[j 1]) { float temp speed[j]; speed[j] speed[j 1]; speed[j 1] temp; } } }3.4PI控制器实现pid.c首先还是介绍结构体命名后记得加上extern外部调用typedef struct { float Kp; /* Kp */ float Ki; /* Ki */ float Kb; /* Kb */ float Up_Limit; /* 输出上限 */ float Dowm_Limit; /* 输出下限 */ float I_Sum; /* 电流积分 */ } Current_PID_DEF; typedef struct { float Kp; /* Kp */ float Ki; /* Ki */ float Kb; /* Kb */ float Up_Limit; /* 输出上限 */ float Dowm_Limit; /* 输出下限 */ float I_Sum; /* 电流积分 */ } Speed_PID_DEF;开始编写pid的初始化Current_PID_DEF Current_D_PID; Current_PID_DEF Current_Q_PID; Speed_PID_DEF Speed_PID; void PID_Init(void) { /* PID参数控制 */ Current_D_PID.Kp 1.0f; Current_D_PID.Ki 0.005f; Current_D_PID.Kb 0.005f; Current_D_PID.Up_Limit 3.0f; Current_D_PID.Dowm_Limit -3.0f; Current_D_PID.I_Sum 0; Current_Q_PID.Kp 1.0f; Current_Q_PID.Ki 0.005f; Current_Q_PID.Kb 0.005f; Current_Q_PID.Up_Limit 3.0f; Current_Q_PID.Dowm_Limit -3.0f; Current_Q_PID.I_Sum 0; Speed_PID.Kp 0.12f; Speed_PID.Ki 0.0002f; Speed_PID.Kb 1.0f; Speed_PID.Up_Limit 3.0f; Speed_PID.Dowm_Limit -3.0f; Speed_PID.I_Sum 0; }电流环void Current_PID_Calc(float ref, float input, float* output, Current_PID_DEF* current_pid) { float temp; float err; /* 误差 */ /* 计算误差 */ err ref - input; /* PI控制 */ temp(current_pid-Kp * err)(current_pid-Ki*current_pid-I_Sum); /* 输出限幅 */ if (temp current_pid-Up_Limit) *output current_pid-Up_Limit; else if (temp current_pid-Dowm_Limit) *output current_pid-Dowm_Limit; else *output temp; current_pid-I_Sum err(*output-temp)*current_pid-Kb; }速度环void Speed_PID_Calc(float ref, float input, float* output, Speed_PID_DEF* speed_pid) { float temp; float err; /* 误差 */ /* 计算误差 */ err ref - input; /* PI控制 */ temp(speed_pid-Kp * err)(speed_pid-Ki*speed_pid-I_Sum); /* 输出限幅 */ if (temp speed_pid-Up_Limit) *output speed_pid-Up_Limit; else if (temp speed_pid-Dowm_Limit) *output speed_pid-Dowm_Limit; else *output temp; speed_pid-I_Sum err(*output-temp)*speed_pid-Kb; }最后记得在.h文件中声明外部可调用电流环会在Motor_Run()中被调用而速度环在systick中断被调用所以说电流环是频率是10KHZ速度环的频率是1KHZ最后你们也可以设置按键控制电机方向的切换速度的加减这里先不介绍四、实验结果与运行效果VOFA观察 Id、Iq 电流环Ia,Ib,Ic的电流目标速度与当前速度当前电角度屏幕录制 2026-09-03 231034