【STM32-HAL库】一步步搭建出FOC矢量控制(附C代码)

说明

本文为无刷电机或PMSM电机驱动的简易代码,旨在分享一些个人调试过程的小心得,提供一个demo文件,程序仍有许多不完善的地方,建立起个人的FOC底层驱动,可以帮助快速熟悉FOC算法原理与使用方法,可以帮助验证新的电机控制算法。原理部分不再阐述。
整个部分共有PWM模块、ADC电流采集、定时器编码器配置、SVPWM模块、FOC核心、PID模块、电压限幅模块,其实有了PWM与SVPWM以及一些必要的数学变换,我们就可以开环使电机转起来了,加入电角度与电流采集作为反馈后,我们就能做到电流闭环,再加入速度PID就可以做到速度闭环,其他的模块只是这些目的的辅助手段罢了。

注意:
调试一定要注意安全!!!
使用带有保护的电源,调试时一定要限制电流在安全等级,开关放手边,随时断电!
程序里做了标幺,比如编码器S16格式,最终都是-32768—32768表示-PI — PI!!!

硬件相关:
(1)MCU为STM32F405RGT6
(2)引脚分配
PWM:TIM1–PA8、PA9、PA10、PB13、PB14、PB15
电流采样:IA–PA6、IB–PA7、IC–PC4
编码器: EA–PA0、EB–PA1
串口: PB6、PB7
(3)编码器为1250线,电机为PMSM、4対极
软件相关:
STM32CubeMX、Keil

如果自制硬件可参考:迷你FOC驱动器

参考资料:
(1)ST电机库
(2)PMSM的FOC 矢量控制算法调试流程,新手上手流程
(3)PMSM矢量控制算法调试流程
(4)FOC和SVPWM的C语言代码实现
(5)上官致远–深入理解无刷直流电机矢量控制技术–科学出版社

0、系统配置

将下列值加入到Cube的User Constants下,然后按照下面的图配置好基本外设。

#define CKTIM 168000000//定时器时钟频率
#define PWM_PRSC 0
#define PWM_FREQ 15000//PWM频率
#define PWM_PERIOD CKTIM/(2*PWM_FREQ*(PWM_PRSC+1))
#define REP_RATE 1 //电流环刷新频率为(REP_RATE+1)/(2*PWM_FREQ)
#define DEADTIME_NS 1000//死区时间ns
#define DEADTIME CKTIM/1000000/2*DEADTIME_NS/1000
#define POLE_PAIR_NUM 4//极对数
#define ENCODER_PPR 1250//编码器线数
#define ALIGNMENT_ANGLE 300
#define COUNTER_RESET (ALIGNMENT_ANGLE*4*ENCODER_PPR/360-1)/POLE_PAIR_NUM
#define ICx_FILTER 8

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1、电机有力了!(PWM模块)

高级定时器主要用于产生6路互补的PWM来驱动MOS管,加入死区防止电源导通,本文未使用刹车引脚。高级定时器1通道1、2、3用于产生PWM,通道4用于触发ADC电流采样,根据扇区的位置,灵活设置PWM占空比,进而选择合理的触发点,避免在噪声点采样。引脚配置与PWM极性请根据自己的硬件合理配置,如IR2101是高电平有效,而IR2103则是低端低有效,高端高有效。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

PWM测试

生成工程后,应首先对PWM模块进行测试,如果有示波器,先测试PWM是否正常(安全起见一路路测试),死区时间是否正确,然后主函数中加入下列代码,导通U相,注意:占空比一定不能设置的过大,防止电流过大,烧毁电机与驱动板。同理可测试其它相。测试完成后进入下一项。
当然,也可以通过这种方法知道你电机的极对数,导通一相后,用手转动电机一圈,感到有几次阻力,就是几对极。或者,不使用驱控板,先用万用表测试电机任意两相间的电阻,然后通合适的电压,如电阻为2欧,则可以通1V电压,然后用手转动电机一圈,感到有几次阻力,就是几对极。

  /* USER CODE BEGIN 2 */
  //此时电机应该是有阻力的
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
	HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_2);
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,400);//不能设置的过大
	__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2,5600);//5600为最大占空比
  /* USER CODE END 2 */

不加驱动板时50%占空比波形与1000ns死区
在这里插入图片描述
在这里插入图片描述

2、让电机转起来吧!(SVPWM)

在主函数头文件main.h中加入下面定义,这在后面都会用到。

typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef __IO uint32_t  vu32;
typedef __IO uint16_t vu16;
typedef __IO uint8_t  vu8;

#define U8_MAX     ((u8)255)
#define S8_MAX     ((s8)127)
#define S8_MIN     ((s8)-128)
#define U16_MAX    ((u16)65535u)
#define S16_MAX    ((s16)32767)
#define S16_MIN    ((s16)-32768)
#define U32_MAX    ((u32)4294967295uL)
#define S32_MAX    ((s32)2147483647)
#define S32_MIN    ((s32)-2147483648)

加入下面代码,主要是数学变换中的Clark变换、Park变换、反Park变换,以及SVPWM模块。


//结构体定义
typedef struct 
{
   
  s16 qI_Component1;
  s16 qI_Component2;
} Curr_Components;


typedef struct 
{
   
  s16 qV_Component1;
  s16 qV_Component2;
} Volt_Components;

typedef struct     //电压值结构体
{
   
  s16 hCos;
  s16 hSin;
} Trig_Components;  //存放角度sin和cos函数值的结构体

typedef struct
{
   
    s16 hKp_Gain;			   //比例系数
    u16 hKp_Divisor;		   //比例系数因子
    s16 hKi_Gain;		       //积分系数
    u16 hKi_Divisor;  	       //积分系数因子
    s16 hLower_Limit_Output;   //总输出下限
    s16 hUpper_Limit_Output;   //总输出上限
    s32 wLower_Limit_Integral; //积分项下限
    s32 wUpper_Limit_Integral; //积分项上限
    s32 wIntegral;			   //积分累积和
    s16 hKd_Gain;			   //微分系数
    u16 hKd_Divisor;		   //微分系数因子
    s32 wPreviousError;	       //上次误差
} PID_Struct_t;
 




//数学变换部分
#define S16_MAX    ((s16)32767)
#define S16_MIN    ((s16)-32768)
#define divSQRT_3	(s16)0x49E6      //1/sqrt(3)的Q15格式,1/sqrt(3)*2^15=18918=0x49E6 
#define SIN_MASK  0x0300
#define U0_90     0x0200
#define U90_180   0x0300
#define U180_270  0x0000
#define U270_360  0x0100
#define SQRT_3		1.732051
#define T		    (PWM_PERIOD * 4)
#define T_SQRT3     (u16)(T * SQRT_3)
//SVPWM部分
#define SECTOR_1	(u32)1
#define SECTOR_2	(u32)2
#define SECTOR_3	(u32)3
#define SECTOR_4	(u32)4
#define SECTOR_5	(u32)5
#define SECTOR_6	(u32)6
#define PWM2_MODE 0
#define PWM1_MODE 1
#define TW_AFTER ((u16)(((DEADTIME_NS+MAX_TNTR_NS)*168uL)/1000ul))
#define TW_BEFORE (((u16)(((((u16)(SAMPLING_TIME_NS)))*168uL)/1000ul))+1)
#define TNOISE_NS 1550     //2.55usec
#define TRISE_NS 1550     //2.55usec
#define SAMPLING_TIME_NS   700  //700ns
#define SAMPLING_TIME (u16)(((u16)(SAMPLING_TIME_NS) * 168uL)/1000uL) 
#define TNOISE (u16)((((u16)(TNOISE_NS)) * 168uL)/1000uL)
#define TRISE (u16)((((u16)(TRISE_NS)) * 168uL)/1000uL)
#define TDEAD (u16)((DEADTIME_NS * 168uL)/1000uL)

#if (TNOISE_NS > TRISE_NS)
  #define MAX_TNTR_NS TNOISE_NS
#else
  #define MAX_TNTR_NS TRISE_NS
#endif

//函数声明

//数学变换
Curr_Components Clarke(Curr_Components Curr_Input);
Trig_Components Trig_Functions(s16 hAngle);
Curr_Components Park(Curr_Components Curr_Input, s16 Theta);
Volt_Components Rev_Park(Volt_Components Volt_Input);
//SVPWM
void SVPWM_3ShuntCalcDutyCycles (Volt_Components Stat_Volt_Input);
//FOC核心
void FOC_Model(void);
//系统初始化
void motor_init(void);

//变量定义部分
Trig_Components Vector_Components;
u8 bSector;
u8 PWM4Direction=PWM2_MODE;
s16 cnt = S16_MIN;//开环调试变量

//FOC相关
Trig_Components Vector_Components;
Curr_Components Stat_Curr_a_b;            
Curr_Components Stat_Curr_alfa_beta;       
Curr_Components Stat_Curr_q_d;             
Curr_Components Stat_Curr_q_d_ref_ref;   //电流环的给定值,用于电流环Id,Iq和前馈电流控制的给定值
Volt_Components Stat_Volt_q_d;             
Volt_Components Stat_Volt_alfa_beta; 
PID_Struct_t PID_Torque_InitStructure;
PID_Struct_t PID_Flux_InitStructure
### STM32 实现无感 FOC 控制的源代码实例 对于希望在 STM32 平台上实施无传感器磁场定向控制 (FOC) 的开发者来说,可以参考多个开源项目和官方文档。这些资源提供了详细的指导和支持材料。 #### 使用 STM32CubeMX 和 HAL 初始化项目配置 为了简化开发流程并确保兼容性,建议利用 STM32CubeMX 工具来生成初始工程框架以及必要的外设驱动程序。这一步骤能够自动完成大部分底层硬件抽象层(HAL)函数调用,使开发者能更专注于应用逻辑的设计上[^1]。 ```cpp // main.c 中的部分代码片段用于展示如何启动定时器中断服务例程以产生PWM信号 #include "main.h" void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM8_PWM_Init(void); int main(void){ /* Reset of all peripherals, Initializes the Flash interface and Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM8_PWM_Init(); while (1){} } /* TIM8 init function */ static void MX_TIM8_PWM_Init(void){ __HAL_RCC_TIM8_CLK_ENABLE(); htim8.Instance = TIM8; htim8.Init.Prescaler = 99; // 设置预分频系数为100-1=99 htim8.Init.CounterMode = TIM_COUNTERMODE_UP; htim8.Init.Period = 999; // 自动重装载值设定为1000-1=999 ... } ``` #### 关键技术要点解析 - **三相逆变桥臂电流采样电路设计** 需要精确测量电机绕组内的瞬态电流变化情况作为反馈量输入给控制系统进行闭环调节操作。通常采用分流电阻法配合高精度ADC转换模块实现这一功能。 - ** Clarke & Park 变换算法计算空间矢量坐标变换矩阵** 将静止两轴αβ坐标系下的电压/电流向量映射至旋转d-q直角坐标系下以便后续处理运算更加直观简便。此过程涉及到复杂的三角函数求解,在实际编程时可借助CORDIC算法提高效率减少浮点数乘除次数[^2]。 - ** PI 调节器参数整定方法论探讨** 合理选取比例积分环节增益Kp、Ki数值大小直接影响着整个系统的动态响应特性和平稳度表现效果。一般而言,可以通过Ziegler-Nichols经验公式或者临界振荡边界测试等方式初步确定理论最优解范围再结合实验验证不断调整优化直至满足预期性能指标为止[^3]。 #### 开发工具链推荐 考虑到 C++ 编程语言具备面向对象特性的优势有助于构建大型复杂工程项目架构体系;同时也能更好地发挥现代编译器内置优化机制带来的运行速度提升效益。因此强烈建议选用支持标准模板(STL)扩展特性的IDE环境比如 Keil MDK 或者 IAR Embedded Workbench 来开展具体工作实践任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值