加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
README.txt 5.82 KB
一键复制 编辑 原始数据 按行查看 历史
liuxiang 提交于 2021-06-06 20:25 . 上传原理图
环境:
使用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, \
};
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化