前言
因为比赛的一些需要学习FOC电机控制技术,但个人水平有限一窍不通,在网上找资料研究了大半个月后,又跟着灯哥的教程看了一遍虽然脑子感觉都会了,但是自己去写一个FOC算法时时候还是遇到了不少坑于是打算记录一下,也是作为注册CSDN以来的第一篇博客。
开发环境和平台
控制板:野火指南者F103VET6
驱动板和电机:灯哥V3P套件
驱动程序使用HAL库编写
HAL库配置
时钟之类的配置这里就不展开了,都是常规操作
既然要驱动FOC电机那么PWM肯定是少不了的,我们首先进行PWM输出的配置:我们选择定时器TIM3的通道2,3,4就是PB0,PB1,PB5三个引脚输出PWM信号这三个引脚正好连接着led也方便我们观察现象。

定时器的相关配置如下图所示我们用15khz的pwm信号作为载波

串口配置我们选择串口一、异步通讯,用于向vofa发送数据


然后生成代码即可
FOC算法的编写
主要参考了dengfoc的相关算法
.C文件
#include "BY_FOC.h"
#include "bsp_UART.h"
#define voltage_power_supply 12.6
#define Pi 3.1415926
float Ualpha,Ubeta,Ua,Ub,Uc;
float APWM,BPWM,CPWM;
/**********************系统运行时间***************************/
__STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void)
{
return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));
}
static uint32_t getCurrentMicros(void)
{
/* Ensure COUNTFLAG is reset by reading SysTick control and status register */
GXT_SYSTICK_IsActiveCounterFlag();
uint32_t m = HAL_GetTick();
const uint32_t tms = SysTick->LOAD + 1;
__IO uint32_t u = tms - SysTick->VAL;
if (GXT_SYSTICK_IsActiveCounterFlag()) {
m = HAL_GetTick();
u = tms - SysTick->VAL;
}
return (m * 1000 + (u * 1000) / tms);
}
//获取系统时间,单位us
uint32_t micros(void)
{
return getCurrentMicros();
}
/**************************************************************/
/* Clark与Park变换*/
void ClarkAndPark(float Uq,float Ud,float angle_el)
{
Ualpha = -Uq*sin(angle_el);
Ubeta = Uq*cos(angle_el);
Ua = Ualpha + voltage_power_supply / 2;
Ub = (sqrt(3)*Ubeta - Ualpha) / 2 + voltage_power_supply / 2;
Uc = -(Ualpha + sqrt(3)*Ubeta) / 2 + voltage_power_supply / 2;
APWM=Ua/voltage_power_supply;
BPWM=Ub/voltage_power_supply;
CPWM=Uc/voltage_power_supply;
printf("%lf,%lf,%lf\n",APWM,BPWM,CPWM);
}
float electricalAngle(float shaft_angle, int pole_pairs)
{
return (shaft_angle * pole_pairs);
}
// 归一化角度到 [0,2PI]
float normalizeAngle(float angle){
float a = fmod(angle, 2*Pi); //取余运算可以用于归一化,列出特殊值例子算便知
return a >= 0 ? a : (a + 2*Pi);
}
uint32_t now=0,last=0;
float E_A,m_a=0;
double Tval;
void test(void)
{
now=micros();
Tval=(double)(now-last)/1000000;
m_a=normalizeAngle(m_a+(10*Pi)*Tval*7);
// printf("%lf\n",m_a);
E_A= electricalAngle(m_a, 7);
ClarkAndPark(5,0,m_a);
last=now;
}
开环控制的代码还是比较简单的,主要就是PARK变换和CLARK变换和一些简单的数学运算
H文件
#ifndef __BY_FOC_H_
#define __BY_FOC_H_
#include "stm32f1xx.h"
#include "math.h"
void ClarkAndPark(float Uq,float Ud,float angle_el);
float electricalAngle(float shaft_angle, int pole_pairs) ;
float normalizeAngle(float angle);
void test(void);
#endif
主函数
extern float APWM,BPWM,CPWM;
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
test();
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,4799*APWM);
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_3,4799*BPWM);
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,4799*CPWM);
}
/* USER CODE END 3 */
}
代码运行
我们打开vofa就可以得到这样的三条相位相差120度的正弦波

我们的电机也愉快的转起来了
foc开环控制
1174

被折叠的 条评论
为什么被折叠?



