吊哥FOC学习笔记---第一步

吊哥FOC学习总结

本文基于硬石科技的STM32F407IGT6的主控板移植的,吊哥的主控是STM32F407VET6。这里需要大家注意!

1 开环FOC驱动

开环所需的部件是:

	1、TIM定时器,产生PWM信号,驱动L6234芯片,这里使用定时器1,即M1_PWM1、M1_PWM2、M1_PWM3的引脚分别为:PE9、PE11、PE13。
	2、L6234需要一个使能引脚,我们这里选择PE8作为M1_ENABLE。
	3、编码器部分,用的是绝对模式接口的编码器,SPI通讯模式,因此,我们使用SPI1,即PA5、PA6、PA7分别作为:M1_SPI_SCK、M1_SPI_MISO、M1_SPI_MOSI引脚。
	4、SPI通讯模式需要一个片选引脚,因此我们选择PA4引脚作为M1_SPI_CS。

​ 有了SPI通讯可以获取绝对式编码器的初始角度值,方便校验,使其为零度角位置。剩下的就是需要产生一个PWM信号,去驱动电机芯片L6234运行即可做成开环FOC系统。

​ 为了方便理解吊哥的FOC代码风格,首先吊哥是使用一个SYS_LED引入的,方便我们了解他的代码风格。接下来我也以先点亮SYS_LED为例子,来讲一下吊哥FOC的代码理解,方便大家看懂代码。

1.1 点亮SYS_LED

​ 这里其实灯的引脚每个板子都不一样,我用的硬石科技的板子,他有3个LED灯(LED1:PH9、LED2:PE5、LED3:PE6),我这里就用LED1来移植吧,即定义PH9为SYS_LED,大家可根据自己的开发板进行移植~

​ 具体配置引脚如下所示:
在这里插入图片描述在这里插入图片描述



​ 关于配置部分,这里介绍一下各个部分如何配置的:

1.1.1 定时器3路通道配置

关于定时器配置,主要是用到了定时器1的3路PWM信号,即PE9、PE11、PE13引脚,其模式配置如下图所示:
在这里插入图片描述

在这里插入图片描述


1.1.2 L6234使能引脚配置

​ L6234的三路PWM信号的使能端口全部都连接在一个引脚上:即PE8引脚,将其配置成GPIO_Output模式,因为是高电平有效,因此在初始化时,我们设置成输出电平为低电平,普通推挽输出,上拉,输出速度因为是长期的变化,因此我们设置成高,其具体配置如下图所示:

在这里插入图片描述


1.1.3 SPI配置

​ SPI通讯是用于跟编码器(TLE5012B E1000)进行通讯的,使用的是绝对式编码器接口,因此只需要上电校验一次即可。这里配置的引脚就是SPI1引脚,即PA5、PA6、PA7引脚,切由于SPI通讯需要一个片选引脚,我们这里选择了PA4作为片选引脚。SPI1的配置如下图所示:

在这里插入图片描述


1.1.4 SPI片选引脚配置

​ 前面说了SPI通讯需要片选引脚拉低才行,因此我们设置的片选引脚是连接到PA4引脚的,PA4配置成:GPIO_Output模式即可,

在这里插入图片描述


最后值得注意的是:我们的定时器是设置了更新中断的,这是方便后面开环的时候,在定时器更新中断中调用FOC控制函数。

1.1.5 定时器更新中断配置

也就是设置定时器1的更新中断为最高等级即可,因为是唯一一个中断,就设置成最高等级默认即可。配置图如下所示:

在这里插入图片描述


最后配置一下时钟树就可以了,如下图所示:

1.1.6 时钟树配置

在这里插入图片描述


上面就完成了所有的开环必备的配置了,接下来我们就可以开启我们的开环FOC之旅了~




1.2 接口函数导入

​ 我们在用STM32CubeMX生成的文件夹中,新建User文件夹,并在User文件夹中,导入相关函数,如下图所示:

在这里插入图片描述


​ 然后我们打开我们的MDK工程,把接口函数加入到我们的KEIL文件中,如下图所示:

在这里插入图片描述

上面其实我们就是导入了能点亮系统LED所需的文件,到时候后期开环的时候还需要加入一些函数,我们讲解开环的时候再介绍。

​ 加下来我们分析一下每一个函数接口的意义。

1.2.1 APP函数

APP:承接main.c文件和我们写的函数相连接的功能,也就是说在main.c中我们只需要在初始化中调用APPMain_Init()函数即可,在while(1)中调用APPMain_Loop()函数即可,我们改变函数仅在APP函数中进行修改,main.c中我们不修改,保持默认用STM32CubeMX产生的形势,如下图所示:

在这里插入图片描述

然后在APP函数中我们在调用我们的接口函数,所有APP函数就类似于一个中介,他起到连接main.c和我们写的接口函数的作用

在这里插入图片描述


APP函数中只有二个函数:

void APPMain_Init(void)
{
    MCUDriverMain_Init();
    PerDriverMain_Init();
    FunctionMain_Init();
}
void APPMain_Loop(void)
{
    MCUDriverMain_Loop();
    PerDriverMain_Loop();
    FunctionMain_Loop();
}

一个是初始化的函数,一个是循环调用函数。与main.c中的init和while(1)功能一致!


1.2.2 Framework函数

​ 总体而言,Framework中的函数,是一个完全脱离硬件,写的一个实现函数功能的函数,也就是说他不依赖任何硬件,如果是调用它,无论什么硬件,都可直接调用。

​ 依照LEDControl函数为例,其代码为:

#include "LEDControl.h"

volatile uint32_t gLED_TimeCNT;
/*************************************************************
** Function name:       LEDON
** Descriptions:        LED常亮
** Input parameters:    gLED:SLEDControl_Struct结构体指针
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void SetLEDON(PLEDControl_Struct gLED)
{
    gLED->state = LEDState_ON;
    gLED->SetLEDLeave(gLED->onLeave);
}
/*************************************************************
** Function name:       LEDFF
** Descriptions:        LED常灭
** Input parameters:    gLED:SLEDControl_Struct结构体指针
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void SetLEDOFF(PLEDControl_Struct gLED)
{
    gLED->state = LEDState_OFF;
    gLED->SetLEDLeave(!(gLED->onLeave));
}
/*************************************************************
** Function name:       LEDToggle
** Descriptions:        LED闪烁
** Input parameters:    gLED:SLEDControl_Struct结构体指针
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDToggle(PLEDControl_Struct gLED)
{
    gLED->onoff = !gLED->onoff;
    gLED->SetLEDLeave(gLED->onoff);
}
/*************************************************************
** Function name:       LEdRunCycle
** Descriptions:        LED周期执行函数
** Input parameters:    gLED:SLEDControl_Struct结构体指针
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDRunCycle(PLEDControl_Struct gLED)
{
    if(LED_TIMEOUT(gLED->cycle * 1000,gLED->startTime)) {
        gLED->startTime = LED_GETTIME();
        if(gLED->state == LEDState_Toggle) {
            LEDToggle(gLED);
        }
    }
}
/*************************************************************
** Function name:       LEDFlashing
** Descriptions:        LED闪烁
** Input parameters:    gLED:SLEDControl_Struct结构体指针
**                      cycle:闪烁周期(单位ms)
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void SetLEDFlashing(PLEDControl_Struct gLED,float cycle)
{
    gLED->state = LEDState_Toggle;
    gLED->cycle = cycle;
}
/*************************************************************
** Function name:       SetLedStatus
** Descriptions:        设置LEDz状态
** Input parameters:    gLED:SLEDControl_Struct结构体指针
**                      status:LED状态
**                      cycle:闪烁周期(单位ms)
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void SetLedStatus(PLEDControl_Struct gLED,uint8_t status,float cycle)
{
    switch(status) {
        case LEDState_OFF:
            SetLEDOFF(gLED);
            break;
        case LEDState_ON:
            SetLEDON(gLED);
            break;
        case LEDState_Toggle:
            SetLEDFlashing(gLED,cycle);
            break;
    }
}
#ifndef LEDControl_h
#define LEDControl_h
#include "stdint.h"

enum {
    LEDState_OFF = 0,
    LEDState_ON,
    LEDState_Toggle,
};

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引脚电平函数
};
typedef struct SLEDControl_Struct LEDControl_Struct;
typedef LEDControl_Struct* PLEDControl_Struct;

/*************************************************************
** Function name:       LED_EXPORT
** Descriptions:        x:对象名字
**                      xonLeave:LED点亮电平
**                      xSetLEDLeave:设置LED引脚电平函数指针
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
#define LED_EXPORT(x,xOnLeave,xSetLEDLeave)   	\
LEDControl_Struct x = {              			\
    .state = LEDState_OFF,                		\
    .onoff = 0,                      			\
    .cycle = 0.0,                    			\
    .onLeave = xOnLeave,             			\
	.startTime = 0,                  			\
    .SetLEDLeave = xSetLEDLeave,     			\
};
extern volatile uint32_t gLED_TimeCNT;

/*************************************************************
** Function name:       SysPower_TIMEBASE
** Descriptions:        时基,放在周期为1ms的函数里面执行
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
*************************************************************/
#define LED_TIMEBASE(ms)                \
        gLED_TimeCNT+= ms

/*************************************************************
** Function name:       SysPower_GETTIME
** Descriptions:        获取起始时间
** Input parameters:    None
** Output parameters:   None
** Returned value:      (uint32_t)起始时间
*************************************************************/
#define LED_GETTIME(void)                        \
    gLED_TimeCNT

/*************************************************************
** Function name:       SysPower_TIMEOUT
** Descriptions:        检查超时
** Input parameters:    timeOut:(uint32_t)超时时间
**                      startTime:(uint32_t)开始的时间
** Output parameters:   None
** Returned value:      false,未超时,true,超时
*************************************************************/
#define LED_TIMEOUT(timeOut,startTime)           ((gLED_TimeCNT - startTime) >= timeOut ? 1 : 0)

void SetLedStatus(PLEDControl_Struct gLED,uint8_t status,float cycle);
void LEDRunCycle(PLEDControl_Struct gLED);
#endif /* LEDControl_h */

我们会发现其实这个函数中,有6个函数,分别作用为:

#include "LEDControl.h"

volatile uint32_t gLED_TimeCNT;

void SetLEDON(PLEDControl_Struct gLED);                                       ->设置LED点亮
void SetLEDOFF(PLEDControl_Struct gLED);							          ->设置LED熄灭
void LEDToggle(PLEDControl_Struct gLED);			 					      ->设置LED翻转
void LEDRunCycle(PLEDControl_Struct gLED);								      ->设置LED周期性的执行函数
void SetLEDFlashing(PLEDControl_Struct gLED,float cycle);				      ->设置LED闪烁
void SetLedStatus(PLEDControl_Struct gLED,uint8_t status,float cycle);		  ->设置LED状态

在LEDControl.c函数中,其实我们正在需要调用的函数就只有两个函数:

void SetLedStatus(PLEDControl_Struct gLED,uint8_t status,float cycle);		  ->设置LED状态
void LEDRunCycle(PLEDControl_Struct gLED);								      ->设置LED周期性的执行函数

因为在这两个函数中已经包括了其他三个函数,并且实现了一切功能:比如点亮LED、熄灭LED、周期性闪烁LED灯;

有人会问,既然脱离了硬件层,那我们如何通过我们的硬件进行调用呢?

这就是吊哥牛逼的地方,他使用了一个函数叫做LED_EXPORT(x,xOnLeave,xSetLEDLeave),他将这个脱离硬件层的函数中所有未知量作为LED_EXPORT的形参,我们调用的时候,赋我们的硬件实参,就可以间接性的给这个Framework中的函数赋值!!!不得不佩服这个思路!!!


1.2.3 MCUDriver函数

​ 这个函数主要是调用Hal库中的函数,实现我们所需要的功能,比如说点亮LED灯,我们就使用:

/*************************************************************
** Function name:      SetLedLeave
** Descriptions:       设置LED引脚电平 0:点亮
** Input parameters:   leave:0:低电平 1:高电平
** Output parameters:  none
** Returned value:     none
** Created by:         none
** Created date:       none
*************************************************************/
void SetLedLeave(uint8_t leave)
{
	HAL_GPIO_WritePin(SYS_LED_GPIO_Port, SYS_LED_Pin, leave);
}

这个函数主要是硬件相关的调用。

好了,有了脱离硬件层的代码Framework中的函数,又有了硬件层的代码MCUDriver,那怎么结合在一起?这个时候就出来了一个函数叫:PeripheralsDriver,这个函数的主要目的就是:Framework中的函数+MCUDriver中的函数 == PeripheralsDriver中的函数!!!

1.2.4 PeripheralsDriver函数

首先比如是点亮LED灯,他就弄了一个文件专门结合Framework中的代码和MCUDriver中的代码:LEDConfig.c文件,其内容为:

#include "LEDConfig.h"

#include "LEDControl.h"
#include "LEDGPIO.h"

LED_EXPORT(gSysLed,1,SetLedLeave);   //调用Framework中的接口函数,作为实参赋值给Framework中的形参
									//其中SetLedLeave就说调用MCUDriver中的函数,作为实参赋值给Framework中函数的形参

/*************************************************************
** Function name:       LEDConfigSetYellowLedStatus
** Descriptions:        设置绿色LED状态
** Input parameters:    status:LED状态 0:熄灭 1:常亮 2:闪烁
**                      cycle:周期单位秒
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfigSetSysLedStatus(uint8_t status, float cycle)
{
    SetLedStatus(&gSysLed,status,cycle);		//调用Framework中的接口函数
}
/*************************************************************
** Function name:       LEDConfig_Init
** Descriptions:        led初始化
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfig_Init(void)
{
	LEDConfigSetSysLedStatus(LEDConfig_Toggle,1);
}
/*************************************************************
** Function name:       LEDConfig_Loop
** Descriptions:        led周期循环函数
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfig_Loop(void)
{
		LEDRunCycle(&gSysLed);	//调用Framework中的接口函数
}

其中初始化中的函数LEDConfigSetSysLedStatus(LEDConfig_Toggle,1)中的1是不是很熟悉,没错,他就是Framework中的cycle值,这里给的是1ms,如果好奇的同学可以试试改变这个1会怎么样?

大家可以试试将上面的代码改成下面的:

//情况1
#include "LEDConfig.h"

#include "LEDControl.h"
#include "LEDGPIO.h"

LED_EXPORT(gSysLed,1,SetLedLeave);   //调用Framework中的接口函数,作为实参赋值给Framework中的形参
									//其中SetLedLeave就说调用MCUDriver中的函数,作为实参赋值给Framework中函数的形参

/*************************************************************
** Function name:       LEDConfigSetYellowLedStatus
** Descriptions:        设置绿色LED状态
** Input parameters:    status:LED状态 0:熄灭 1:常亮 2:闪烁
**                      cycle:周期单位秒
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfigSetSysLedStatus(uint8_t status, float cycle)
{
    SetLedStatus(&gSysLed,status,cycle);		//调用Framework中的接口函数
}
/*************************************************************
** Function name:       LEDConfig_Init
** Descriptions:        led初始化
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfig_Init(void)
{
	//LEDConfigSetSysLedStatus(LEDConfig_Toggle,1);
    LEDConfigSetSysLedStatus(LEDConfig_ON,1);
}
/*************************************************************
** Function name:       LEDConfig_Loop
** Descriptions:        led周期循环函数
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfig_Loop(void)
{
		//LEDRunCycle(&gSysLed);	//调用Framework中的接口函数
}
//情况2
#include "LEDConfig.h"

#include "LEDControl.h"
#include "LEDGPIO.h"

LED_EXPORT(gSysLed,1,SetLedLeave);   //调用Framework中的接口函数,作为实参赋值给Framework中的形参
									//其中SetLedLeave就说调用MCUDriver中的函数,作为实参赋值给Framework中函数的形参

/*************************************************************
** Function name:       LEDConfigSetYellowLedStatus
** Descriptions:        设置绿色LED状态
** Input parameters:    status:LED状态 0:熄灭 1:常亮 2:闪烁
**                      cycle:周期单位秒
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfigSetSysLedStatus(uint8_t status, float cycle)
{
    SetLedStatus(&gSysLed,status,cycle);		//调用Framework中的接口函数
}
/*************************************************************
** Function name:       LEDConfig_Init
** Descriptions:        led初始化
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfig_Init(void)
{
	//LEDConfigSetSysLedStatus(LEDConfig_Toggle,1);
    LEDConfigSetSysLedStatus(LEDConfig_OFF,1);
}
/*************************************************************
** Function name:       LEDConfig_Loop
** Descriptions:        led周期循环函数
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfig_Loop(void)
{
		//LEDRunCycle(&gSysLed);	//调用Framework中的接口函数
}
//情况3
#include "LEDConfig.h"

#include "LEDControl.h"
#include "LEDGPIO.h"

LED_EXPORT(gSysLed,1,SetLedLeave);   //调用Framework中的接口函数,作为实参赋值给Framework中的形参
									//其中SetLedLeave就说调用MCUDriver中的函数,作为实参赋值给Framework中函数的形参

/*************************************************************
** Function name:       LEDConfigSetYellowLedStatus
** Descriptions:        设置绿色LED状态
** Input parameters:    status:LED状态 0:熄灭 1:常亮 2:闪烁
**                      cycle:周期单位秒
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfigSetSysLedStatus(uint8_t status, float cycle)
{
    SetLedStatus(&gSysLed,status,cycle);		//调用Framework中的接口函数
}
/*************************************************************
** Function name:       LEDConfig_Init
** Descriptions:        led初始化
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfig_Init(void)
{
	LEDConfigSetSysLedStatus(LEDConfig_Toggle,2);
}
/*************************************************************
** Function name:       LEDConfig_Loop
** Descriptions:        led周期循环函数
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void LEDConfig_Loop(void)
{
	LEDRunCycle(&gSysLed);	//调用Framework中的接口函数
}

试试上面的代码,你看看这三种情况的效果,针对你LED的情况,你也许就能领悟到这个调用的风格是多牛逼!!!在这里再说一句,吊哥牛逼!


在最后补充一句,当调用LED闪烁的时候,别忘记把Framework中LEDControl.h中的#define LED_TIMEBASE(ms) gLED_TimeCNT+= ms这个函数放在系统滴答定时器的中断服务函数中,并赋值ms=1,表示没1ms,这个gLED_TimeCNT的值+1,这里主要是调用LEDContrl.c中的

void LEDRunCycle(PLEDControl_Struct gLED)
{
if(LED_TIMEOUT(gLED->cycle * 1000,gLED->startTime)) {
gLED->startTime = LED_GETTIME();
if(gLED->state == LEDState_Toggle) {
LEDToggle(gLED);
}
}
}

这个函数中的技数时间,LED_TIMEOUT(gLED->cycle * 1000,gLED->startTime)这个函数乘以1000,就是表明1ms时间gLED_TimeCNT加1,要+1000次1,那1ms+1,+1000个1是不是1s呢,所有就实现了1s闪烁LED灯的效果~~~大家慢慢领悟吧

下面把滴答定时器中断服务函数中的代码放出来,方便大家借鉴。

在这里插入图片描述

烧写完代码,你的LED灯就会闪烁了~~~第一步就成功了!


今天就到这里,希望对大家有帮助,我也是小菜鸡,初学者,之所以发出来,是希望能帮助到跟我一样的同学,少走弯路~~~

最后,谢谢吊哥的无私开源代码~~~谢谢大佬!!!

DRunCycle(PLEDControl_Struct gLED)
{
if(LED_TIMEOUT(gLED->cycle * 1000,gLED->startTime)) {
gLED->startTime = LED_GETTIME();
if(gLED->state == LEDState_Toggle) {
LEDToggle(gLED);
}
}
}

这个函数中的技数时间,LED_TIMEOUT(gLED->cycle * 1000,gLED->startTime)这个函数乘以1000,就是表明1ms时间gLED_TimeCNT加1,要+1000次1,那1ms+1,+1000个1是不是1s呢,所有就实现了1s闪烁LED灯的效果~~~大家慢慢领悟吧

下面把滴答定时器中断服务函数中的代码放出来,方便大家借鉴。

在这里插入图片描述

烧写完代码,你的LED灯就会闪烁了~~~第一步就成功了!


今天就到这里,希望对大家有帮助,我也是小菜鸡,初学者,之所以发出来,是希望能帮助到跟我一样的同学,少走弯路~~~

最后,谢谢吊哥的无私开源代码~~~谢谢大佬!!!

版权声明:本文为CSDN博主「Lagligelang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Lagligelang/article/details/120942772

生成海报
点赞 0

Lagligelang

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

RT-Thread Studio移植LAN8720A驱动

RTT网络协议栈驱动移植(霸天虎) 1、新建工程 ​ 工程路径不含中文路径名,工程名用纯英文不含任何符号。 2、用CubeMx配置板子外设 2.1、配置时钟 ​ 按照自己板子配置相应时钟。

Lin总线通信在STM32作为主机代码以及从机程序

距离上次做资料准备已经过去六天了。最近在学车,上周末就没有开电脑。这周开始进行了Lin通信的代码整理,目前是可以正常通信的了,采用的是增强型校验方式。后期再进一步跟进研究。。。更新一博,留

4路红外循迹模块使用教程

4路红外循迹模块使用教程 个人原创博客:点击浏览模块详细信息: 工作电压:DC 3.3V~5V 工作电流:尽量选择1A以上电源供电 工作温度:-10℃~50℃ 安装孔

HAL库串口中断

一,配置串口初始化 void MX_USART1_UART_Init(void) {huart1.Instance USART1;huart1.Init.BaudRate 115200;huart1.Init.WordLen