1 Star 0 Fork 234

C_Coder/FOC教程

forked from 刘祥/FOC教程 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
环境:
使用HAL库编程,
Documents:文档
FOC-407-Demo:自制电路板V1.0.0 双路无刷驱动代码
ODrive:ODrive硬件平台,单路驱动代码

芯片资源使用:
PWM:
使用中央对其模式2,先向上计数再向下计数,只在向上计数时产生溢出中断。
使用PWM模式1,向上计数 CNT<CCR为有效电平,有效电平为高。
ODrive:使能通道4的比较中断,通道4的CCR为通道1 2 3中CCR最大值+1us。1us是为了让AD采样更稳定
FOC-407-Demo:使用溢出中断,再中断执行函数里选择上溢中断
其中FOC-407-Demo代码中使用主从定时器来将两个电机的电流环计算时间分开,互不干扰。

ABZ编码器:
采用ABZ正交增量编码器
每次上电需要做电气角度校准
利用Z轴每圈做一次清零

绝对式编码器:
只在第一次上电和拆卸电机的时候做校准
芯片选择TLE5012B1000

电流采样:
电流采样使用单次扫描采样,DMA+中断
在定时器1通道4中断中触发ADC采样


控制流程:
定时器中断,进行一次ADC采样.
ADC采样完成触发DMA中断,在DMA中完成FOC电流环。

FOC控制流程:(FOC.c)
1.获取BC两相电流,求出A相电流
2.获取当前电气角度。
3.通过Clarke变换和Park变换求出实际电流 IQ和ID
4.通过与目标IQ ID对比和PID计算,得出要输出的UQ UD
5.将UQ UD做Park反变化得出Uα和Uβ
6.将U阿尔法和Uβ送入SVPWM生成模块。

SVPWM控制流程:(Svpwm.c.c)
1.获取Uα和Uβ
2.通过U阿尔法和Uβ计算当前所在扇区
4.使用7段式PWM计算每个矢量的作用时常
5.通过矢量作用时长计算出定时器的高电平时间
6.通过定时器每个高电平时间计算出每个通道的CCR值
7.挑选出CCR最大值送给通道4,准备下一次定时器中断

定时器比较中断:
打开ADC进行一次AD采样

DMA中断:
读取AD数据进行FOC控制

编码器Z轴中断:
校准角度值


文件介绍:
所有文件均放置在User目录下
APP:应用程序总入口,实现初始化流程管理,循环执行管理。
Function:放置功能程序,目前没有实现,如放置T型加减速。
MCUDriver :放置芯片外设代码,如SPI,GPIO,TIM,ADC
Framework:放置代码库,将驱动代码和硬件平台剥离出来。
PeripheralsDriver:放置驱动程序,将硬件平台代码和代码库结合起来,实现具体功能,即(MCUDriver + Framework)
RTT :使用Segger RTT打印调试,也可以稍作修改改为串口打印。

已知BUG
1.双路FOC代码中(FOC-407-Demo)CUBE生成的代码初始化顺序会导致ADC2无法进入DMA中断,因此外设初始化顺序要修改成我代码中的那样。
2.无法通过SPI与DRV8301通讯,看了数据手册也没有调试通过,哪位老哥有经验望分享。
3.没有做刹车处理

声明:
1.受硬件平台影响,代码可能不能直接运行,但可以参考。
2.先调试SVPWM再调试电流采样,再闭环,SVPWM即可实现电机旋转。
3.闭环先调电流环再调速度环
4.电流环先调试ID再调试IQ
6.有疑问的地方欢迎骚扰,有错误的地方欢迎批评。

联系:
QQ:965552797@qq.com


代码编写风格:
我们实现一个功能其实是分为三个步骤
1.配置MCU引脚
2.配置传感器逻辑功能,即让传感器运行起来,或是通讯协议,或是电机控制,或是时间控制。
3.根据传感器的功能做一些小的逻辑应用,或是LED闪烁,或是电机转速控制。
其实我们发现1和3是受硬件平台和我们要实现的功能影响,需要不断修改,但步骤2是不变的,针对一个传感器来说无论你使用什么硬件平台步骤2是不需要变化的,举个例子来说TLE5012B编码器的SPi通讯逻辑是不变的。
因此我们将步骤2抽象出来,针对TLE5012B我们用结构体的方式表示这类传感器,我们假设其有SPI传输函数,SPI读取函数,SPI_CS引脚控制函数,微秒延时函数,有了这些函数之后我们就能使用这个传感器了。
但是现在这些函数都是虚拟的,我们使用这些假函数先把传感器的逻辑写出来,然后我们在去MCU那里把真正的SPI通讯实现了,然后把这些真正的函数地址传给TLE5012B结构体即可。


在单路FOC代码中会给人一种感觉,这样编写比较麻烦,累赘。但是在双路FOC中这样编写的优点就体现出来了。


LEDControl举例说明
我们使用板子习惯会先点亮一个LED,但我们会发现每次开发板点亮一个LED都要重新编写函数,如果在加一些闪烁效果就更恶心,为了不影响主循环的实时性我们甚至要开一个定时器中断,
真是苦不堪言,在本次代码中我们声明一个LED结构体:
struct SLEDControl_Struct {
    uint8_t state;//LED运行状态 0:LED常灭 1:LED常亮 2:闪烁
    uint8_t onoff;//当前LED状态
    float cycle;//闪烁周期(单位ms)
    uint8_t onLeave;//点亮电平
    uint32_t startTime;
    void(*SetLEDLeave)(uint8_t leave);//设置LED引脚电平函数
};

针对LED,我们要知道点亮电平,引脚电平控制,因此我们便在结构体中声明这两个函数,利用虚拟函数完成逻辑功能如:
void SetLEDON(PLEDControl_Struct gLED)
{
    gLED->state = LEDState_ON;
    gLED->SetLEDLeave(gLED->onLeave);
}

然后我们再去实现这个引脚控制函数
void SetLedLeave(uint8_t leave)
{
	HAL_GPIO_WritePin(SYS_LED_GPIO_Port, SYS_LED_Pin, leave);
}

最后通过接口把这个SetLedLeave函数地址传给结构体 
LED_EXPORT(gSysLed,1,SetLedLeave);

#define LED_EXPORT(x,xOnLeave,xSetLEDLeave)   	\
LEDControl_Struct x = {              			\
    .state = LEDState_OFF,                		\
    .onoff = 0,                      			\
    .cycle = 0.0,                    			\
    .onLeave = xOnLeave,             			\
	.startTime = 0,                  			\
    .SetLEDLeave = xSetLEDLeave,     			\
};

空文件

简介

暂无描述 展开 收起
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/C__Coder/foc-tutorial.git
git@gitee.com:C__Coder/foc-tutorial.git
C__Coder
foc-tutorial
FOC教程
main

搜索帮助

0d507c66 1850385 C8b1a773 1850385