当前位置:   article > 正文

【超硬货】FOC(电机矢量控制)程序分块细解_foc控制程序

foc控制程序

0、前言

永磁同步电机(PMSM)位置、速度、电流三闭环矢量控制(FOC)软件(以下简称“软件”)是为学习矢量控制算法而编写的,针对的是永磁同步电机,BLDC也可用,但是淘宝上面的电机大多数都是BLDC,传感器都是霍尔的(如果BLDC用FOC控制的话,扭矩是要比方波六步换相稍微低一些,但是声音会小很多而且扭矩平稳,所以我最喜欢玩霍尔BLDC的FOC控制),PMSM要比BLDC贵一些,理论上ABZ编码器foc最简单,hall_foc次之,无感foc转起来简单,控制得好比较难,当确认硬件板子没问题后,所有的电机控制都是参数问题了(建议一边学foc,一边理解这些参数)。

我有一套驱控(双驱,还顺便放了一个有刷控制接口和舵机控制接口)准备有偿提供,它既可以ABZ编码器控制,也可以BLDC方波控制,又可以BLDC霍尔FOC控制,又可以无感FOC控制 ,整套硬件采用模块化设计,驱控分离,预留多个冗余接口,单个驱动12个大mos管(单个 85V 160A),带有定时器BKE刹车和泄放电阻,最大可以支持功率为2KW的电机稳定运行(如果功率差距太大,采样电阻最好改一下),如果你不想管FOC的底层原理,只要求拿来就可调可用还有人指导,那这个再合适不过了,如果需要的朋友可以私信我,看最近是否有时间,本人有点考虑提供一对一的代码梳理服务。

首先说明,出此教程的目的,是为了让广大初学者可以挣脱淘宝CSDN海量教程的毒害,因为经常看到一些初学者搞了一堆资料,学了大半年,结果连程序都看不懂,实践的时候最多也就是按照教程接好线,通电看电机转一转,浪费的大量的时间和金钱,很多细节问题不清楚,没有解决问题的能力。

所以,我想以一套完整的、成熟的PMSM的FOC控制方案为例,从各个方面详细讲解了FOC的原理、实现方法等知识,几乎是逐条的分析了程序,又兼顾程序的系统层面,目的是让初学者看完我的资料,可以对FOC控制有一个类似游戏里面的“大地图”的概念,能达到这个目的,我就很满足了。知识是个触类旁通的东西,相信大家学完这个FOC算法后,肯定会对电机控制有一个更深的认识,其实也就是那么回事儿。

  1. 软件功能设计

1.1 总体框架

本文以程序中采用的最常见的id=0电流控制策略为例进行说明。

学习矢量控制,PMSM调速系统的框图必须要熟悉,如图1所示。其中红色虚线包围的为电流环。

                               图1  PMSM调速系统结构框图

    从框图中可以看出,FOC运算,需要知道电机的三相电流和转子位置,因此我们在设计中,需要采集、解算这两个量。另外,三闭环控制时,位置环的输出作为速度环的指令,速度环的输出作为电流环的指令,当然,这些指令,都可以来自上位机输入,形成单电流闭环、或者速度电流双闭环控制的形式。

硬件平台我们采用常见的DSP,资源如下:

  1. 处理器:TMS320F28335,晶振频率30MHz,工作主频150MHz;
  2. 存储器:TMS320F28335内部资源模块寄存器,片内18K×16位SARAM存储器,64K×16位Flash存储器;
  3. 中断源:Pwm1中断,8Khz;

软件的构成如图2所示。

图2 软件结构图

软件通过RS232与上位机实时通讯交互,在主循环中采用查询的方式接收上位机发送的系统工作模式、伺服指令,并将系统相关状态监测量实时反馈给上位机;为了保障系统安全,软件对输入指令进行了限幅,避免指令超限。

1.2 主要功能

软件的具体功能及实现需求如下:

 a)在控制电源上电后,完成各功能模块初始化;

 b)通过串口与上位机通讯。通过查询方式接收上位机数据,并进行解析;

 c)利用TI最新的SVPWM算法,产生占空比,驱动功率板;

 d)通过DSP内部自带ADC采集母线电压信号、相电流信号;

 e)完成位置闭环、速度闭环、电流闭环调节;

 f)系统状态监测、故障诊断与处理(工业应用中故障诊断、系统检测是很复杂的,为了突出本教程重点,故程序仅保留过压、欠压、过流保护功能);

控制软件全部功能描述见表1和表2所示 。

表1软件功能描述

序号

功能名称

说明

1

初始化功能

控制电源上电后对DSP相关硬件配置及变量进行初始化

2

SCI通信功能

与上位机通过RS232串口线进行实时通信

3

定时器功能

通过配置EPWM1获取125us周期定时,通过分频获取250us、1ms、10ms等周期定时

4

位置、速度、电流闭环调节

完成电机位置闭环、速度闭环、电流的闭环调节

5

状态监测与故障诊断

对系统工作状态,故障状态实时监测

表2 软件性能描述

序号

性能名称

说明

1

串口数据发送周期

通过串口向上位机发送数据的周期为1ms。

2

脉宽调制信号

PWM波载波频率为8kHz,死区时间为2us

3

MainPwmISR()中断程序执行时间

中断执行一次时间不能超过总中断时间的80%,即不超过100us。

1.3 程序接口

与FOC控制软件相关的硬件接口,包括RS232串口,片内ADC,外设I/O,数字I/O等,连接关系见图3所示,具体的IO配置表见附录I所示。

图3  DSP接口图

2、软件架构设计

暂且把这个架构叫《伺服软件迭代进化架构》吧。

伺服软件代码架构千奇百怪,初学者甚至是一些“老师傅”都很难辨别优劣,差的架构不利于代码的理解,更不利于知识的积累,跨平台移植代码更是劳民伤财,不利于个人、企业的发展。

故本人根据自己的理解,提出本架构,买家可以依托自己原有的技术积累,以此架构为平台,进行自身技术的积累,软件的迭代、进化,通过不断的学习、实践,凝结核心技术,形成多种成熟可靠的电机控制软件平台,逐步扩充自己的伺服软件型谱,提高个人薪资竞争力,同时,打造出值得信赖的伺服软件品牌。

2.1 架构规划

软件采用分层架构设计思想,5个层次。

(1)系统配置(CMD文件、LIB文件、启动文件、官方库文件……)。

(2)外设配置层(时钟、GPIO分配、ADC配置、PWM配置、SCI配置……)。

和硬件确定标准接口,明确所有接口的首选、备选方案。

根据无刷驱动特点,设置标准的底层配置模式,可实现快速切换

(3)模块驱动层(速度反馈解算、位置反馈解算、各种滤波、指令斜坡处理、SVPWM计算、ClarkPark变换、各种PID运算、串口通信、总线、故障诊断……)。

以功能划分模块,各模块之间采用水平逻辑,模块内采用自上而下的垂直逻辑。

每个模块自成体系,具有明确的输入、输出,设置自己的参数结构体,表征所有用到的中间变量、标志位信息,整个生命周期不能直接影响模块以外的任何变量、参数,必须采用参数传递的形式。

模块采用统一写法,设计时必须附带调用说明,参数设置说明,功能说明等文档。

模块之间相互独立,便于更新、升级。

通过项目验证的,进入CBB货架管理,通过多次飞试验证等,进行封包,进入系统配置层的LIB中。 

(4)控制算法层

a)前馈补偿、抗负载扰动、参数惯量(电机极对数、旋变极对数、旋变安装偏差、电阻、电感、转动惯量、负载转矩、反电动势系数、摩擦系数)、参数自整定(电流环、速度环PI参数)、电流解耦、死区补偿、执行机构末端抖振抑制、母线电压波动拟制补偿、再生制动、能量回馈、故障保护(常规故障保护、缺相运行、异常状态的再启动等技术)……

b)弱磁、直接转矩……

c)无位置算法(观测器、锁相环、高频注入……)

d)先进智能控制算法的研究应用(自抗扰、滑模变结构)……

根据算法需求,调用模块驱动层的函数,进行算法设计。

同样,算法之间平行、独立,自成体系,采用标准写法。

软件人员利用实验平台,验证先进的智能控制算法,不断的扩充算法层、优化、升级现有算法层中内容。

成熟稳定的算法可进入模块驱动层,无需修改,供直接调用。

(5)任务调度层实时任务采用中断机制,执行快速、慢速流程任务,非实时任务采用多任务调度机制,整个软件采用实时操作系统进行任务调度)。

中断任务中完成实时性较强的电流电压采集、控制算法和故障保护等任务,不同的实时任务运行于不同的控制周期;

多任务调度系统中完成通信、系统检测等实时性要求较弱的任务。

2.2 架构特点

(1)跨平台开发时,仅需移植系统配置层、外设配置层,内容很少,而核心的模块驱动层、软件算法层、任务调度层可无需修改,直接复制。

(2)模块按最小化功能划分,输入、输出、中间变量明确,各模块之间完全独立,使得功能函数的迁移完全自由,极大方便了协同工作,同时,给同一功能不同实现方法的对比分析提供了便利条件,有利于整个软件的迭代、进化。

(3)算法层模块化处理,使得软件可以很方便的借助Simulink工具对已有c形式的算法进行脱离硬件平台的仿真验证,模拟出很多硬件难以达到的边界条件,扩充了算法验证手段。同时,也更方便借助Simulink代码生成技术,对新算法先仿真,然后直接生成可用的c代码,加快了新算法、新控制策略的落地、应用。

(4)任务调度层模块化处理,单线程中任务跳转逻辑可采用Stateflow进行设计、验证,复杂系统的多进程任务,可采用RTOS进行调度、管理。

(5)组织架构清爽,模块界线清晰,方便排查问题。

(6)优秀的架构,使得软件的质量和可靠性得到保证。

2.3 实施步骤

选择1个具有代表性的型号,按以下步骤展开工作:

(1)以任务书为指导,按架构设计伺服软件框架。

输出:软件架构以及功能说明。

(2)从标准和产品特点出发,形成功能函数的编写模板。

输出:功能函数模板。

(3)梳理软硬件接口推荐表,进行软件底层配置的首选配置以及备选配置的快速切换设计,封装固定模式,预留可调模式的对外接口。

输出:推荐接口表;底层架构。

(4)按软件功能划分模块,梳理模块驱动层中所有函数的输入、输出、中间变量、激励要素等,并按c语言模板进行初次定型。

输出:功能函数表;每个函数的输入、输出要素表。

(5)实时任务、非实时任务流程设计,完成项目调试、交付,对软件进行评审。

输出:软件改进建议表。

(6)改进软件、新项目中继续验证优化,算法迭代、进化……

同时,梳理需求,搭建实验平台,帮助算法验证。

2.4 具体形式

(1)软件模块的组织形式见图4所示。

图4 代码组织形式

1_SystemConfigs(系统配置层)

   01_CMD

   02_LIB (TI自带库、自行封装库)

   03_Ti_Include

   04_Ti_Source

2_PeripheralConfigs(外设配置层)(时钟、片内外的外设、GPIO分配及状态)

   01_ADC_Init.c  

   01_ADC_Init.h

   02_ePWM_Init.c

   02_ePWM_Init.h

   03_SCI_Init.c

   03_SCI_Init.h

   04_GPIO_Init.c

   04_GPIO_Init.h

   ……….

3_Module_Drive(模块驱动层)

   01_Interface (转子位置角获取,三相电流获取,电压、温度等的获取)

   02_Driver(速度解算、PI运算、SVPWM计算、ClarkPark变换……)

   03_Diagnosis (故障诊断)

   04_Communication(串口通信、总线通信协议……)

05_MathFunction(斜坡处理、各种滤波、数学运算……)

……….

4_Algorithm(控制算法层)

   01_ParaEstimate(极对数、电阻、电感、负载、转动惯量、反电势系数等辨识)

   02_Self_Tuning(机械电气零位自整定,电流环、速度环等参数自整定)

   03_Control(FOC算法,解耦、补偿、抑制抖动、弱磁、DTC、自抗扰、滑模……)

   04_Sensorless(观测器、PLL、高频注入……)

……….

5_Multitask_Scheduing(任务调度层)

   01_Scheduing (实时任务中断内快速、慢速流程调度)

   02_OS-II (非实时任务,采用操作系统进行调度)

(移植的实时操作系统放置在系统配置层中)

(2)模块编写参考形式:

模块驱动层可参考TI的宏程序写法,采用1个H文件即可完成一个功能的编写,规范后直接调用即可。

/* =============File name:RAMPGEN.H  ===========*/

#ifndef __RAMPGEN_H__

#define __RAMPGEN_H__

typedef struct { _iq  Freq; // Input: Ramp frequency (pu)

       _iq  StepAngleMax; //Maximum step angle (pu)

         _iq  Angle; // Variable: Step angle (pu)

     _iq  Gain; // Input: Ramp gain (pu)

     _iq  Out;     // Output: Ramp signal (pu)

     _iq  Offset; // Input: Ramp offset (pu)

         } RAMPGEN;             

/*---------------  Object Initializers-----------------*/                       

#define RAMPGEN_DEFAULTS {0, \

  0, \

  0, \

  _IQ(1), \

  0, \

  _IQ(1), \

                         }

/*--------RAMP(Sawtooh) Generator Macro Definition------*/                                               

#define RG_MACRO(v) \

/* Compute the angle rate */ \

v.Angle += _IQmpy(v.StepAngleMax,v.Freq); \

/* Saturate the angle rate within (-1,1) */ \

if (v.Angle>_IQ(1.0)) \

v.Angle -= _IQ(1.0); \

else if (v.Angle<_IQ(-1.0)) \

v.Angle += _IQ(1.0); \

v.Out=v.Angle;

#endif // __RAMPGEN_H__

也可参考以下写法:

H文件:定义自己的结构体,设置结构体变量初始值,声明对应函数。

/*

 * CurrentGet.h

*/

#ifndef IPHASEGET_H_

#define IPHASEGET_H_

//======= 定义   三相电流采样   结构体变量类型   ===========================================

typedef struct

{

    float                IaTemp;  //A相电流,临时变量

    float                IbTemp;  //B相电流,临时变量

    float                IcTemp;  //C相电流,临时变量

    float                IaOffset;  //iA零偏

    float                IbOffset;  //iB零偏

    float                IcOffset;  //iC零偏

float               Ia;      //iA

float               Ib;      //iB

float               Ic;      //iC

}IPhaseStr;

#define IPhaseStr_DEFAULTS {0,0,0,\

    0,0,0,\

    0,0,0,\

                         }

//======= 函数声明   ===========================================

void GetIPhase(void);

#endif /* CURRENTGET_H_ */

对应的C文件:初始化结构体,函数自上而下垂直结构,输入、输出明确,不能直接使用其他函数的量,必须进行参数传递。对所有会用到、需观测的中间变量设置出来,供使用。

/*

 * CurrentGet.c

*/

#include "CurrentGet.h"

#include "Para_Settings.h"

IPhaseStr IPhase = IPhaseStr_DEFAULTS;

//************************************************************

  @ Description: 电机三相电流采样

  @ Param

  @ Return

//************************************************************

void GetIPhase()

{

IPhase.IaTemp = ((AdcRegs.ADCRESULT0>>4)*0.0182-44.46)*0.586;   

IPhase.IcTemp = ((AdcRegs.ADCRESULT1>>4)*0.0182-44.46)*0.586;   

IPhase.IaNew = (IPhase.IaNew*CURR_K1 + IPhase.IaTemp*CURR_K2);

IPhase.IcNew = (IPhase.IcNew*CURR_K1 + IPhase.IcTemp*CURR_K2);

IPhase.Ia = IPhase.IaNew-IPhase.IaOffset;            //得到iA

IPhase.Ic = IPhase.IcNew-IPhase.IcOffset;            //得到iC

IPhase.Ib = 0 - IPhase.Ia - IPhase.Ic;               //得到iB

}

// End of file.

//============================================================

3、详细设计

对于采用增量式编码器的电机,初次使用时,需要知道编码器零位和电机零位之间的安装偏差,具体原理和操作步骤见《02_ABZ编码器校准原理及教程》,本文档中默认,大家已经做好了校准工作了。

3.1主循环模块

3.1.1功能描述

主循环模块是程序运行时的主控流程,控制程序的流程走向。

软件在控制电源上电复位后执行主程序,主程序完成初始化后,开中断(触发周期125us),然后进入主循环等待中断的发生,同时在主循环中完成与上位机的实时通讯,翻转LED,提示用户系统正在正常运行中。

3.1.2流程逻辑

主程序主要完成初始化(相关功能模块初始配置,控制变量、结构体的初始化),开主中断,然后进入主循环等待中断发生。主循环中完成与上位机的实时通讯交互(查询接收上位机输入的工作指令),具体流程见图5所示。

图5 DSP主循环模块流程图

增量编码器,每次一上电,控制器不知道此时的转子位置,因此,每次重新上电,程序会先进入lsw=0的条件语句:

//------------转子定位模式----------------------------

if(lsw == 0)    //lsw为0时,抱轴,开环的,此时给定指令清0

{

lsw_cnt++;

eQEP1.ElecTheta = 0;         //电角度为0

EQep1Regs.QEPCTL.bit.SWI=1;  //转子定位过程编码器将转动,在定位结束后对eQEP模块重新初始化

MotorCtrl();

if(lsw_cnt > 3500) //抱轴时间到了,就切换lsw=1,进入控制模式

{

lsw_cnt = 0;

lsw = 1;

// CalibrateFlag = 1;  //校准电机零位时释放,正常工作时屏蔽此句。

}

} 

在lsw=0的条件下,程序流程为:拖动电机(ud给定一个安全值,uq=0,反Park角度设置为0),把电机的d轴拉到和电机A相对齐。这个过程持续3500*125us=437.5ms,然后程序会自动切换到lsw=1的条件,保证了编码器从“零”开始进行增量计数,就获得了准确的转子位置角。

完成这个过程后,程序的执行流程则见图6所示。

图6  lsw=1时软件执行流程图

3.2 PWM初始化及TZ保护模块初始化

3.2.1功能描述

矢量控制中,一般需要六路(3组)PWM输出,配置为两两互补带死区的导通形式。

void InitEPWM(void)

{

EALLOW;

SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; //所有ePWM的TBCLK信号停止

EDIS;

InitEPwm1();

InitEPwm2();

InitEPwm3();

EALLOW;

SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; //所有ePWM模块的时钟都开始于TBCLK的第一个上升沿

EDIS;

}

以上函数完成ePWM模块的配置:PWM初始化,配置PWM频率、死区,载波频率8kHz,死区时间为2us,对PWM1启动125us中断以及ePWM模块的错误联防保护功能。

3.2.2流程逻辑

TBCLKSYNC位可以用来同步所有使能的ePWM模块的基准时钟,在配置ePWM模块的过程中,遵循以下步骤:

(1)使能各个ePWM模块的时钟;

(2)将TBCLKSYNC清零,从而停止所有ePWM模块的时钟;

(3)对ePWM模块进行配置;

(4)将TBCLKSYNC置位。

ePWM模块一共有7个子模块,分别为:时间基准子模块(TB)、比较功能子模块(CC)、动作限定子模块(AQ)、死区产生子模块(DB)、斩波控制子模块(PC)、故障捕获子模块(TZ)、事件触发子模块(ET)。各子模块的主要功能如下:

a.时间基准(TB)

设定基准时钟TBCLK与时钟SYSCLKOUT之间的关系;

设定PWM时间基准计数器TBCLK的频率和周期;

设定时间基准计数器的工作模式:增计数、减计数、增减计数;

设定与其他ePWM模块之间的相位关系;

通过软件或者硬件方式同步所有ePWM模块的时间基准计数器,并设定同步后计数器的方向(增计数或者减计数);

设定时间基准计数器在仿真器挂起时的工作方式;

指定ePWM的同步输出信号的信号源:同步输入信号、时间计数器归零、时间计数器等于比较器B(CMPB)、不产生同步信号。

b.比较功能(CC)

指定EPWMxA和EPWMxB的占空比;

指定EPWMxA和EPWMxB输出脉冲发生状态翻转的时间。

c.动作限定(AQ)

无反应;

EPWMxA和/或EPWMxB得输出切换到高电平;

EPWMxA和/或EPWMxB得输出切换到低电平;

EPWMxA和/或EPWMxB得输出进行状态翻转。

d.死区产生(DB)

控制上下两个互补脉冲之间的死区时间;

设定上升沿延时时间;

设定下降沿延时时间;

不做处理。

e.斩波控制(PC)

产生斩波频率;

设定脉冲序列中第一个脉冲的宽度;

设定第二个及其以后脉冲的脉冲宽度;

不做处理。

f.故障捕获(TZ)

配置ePWM模块响应一个、全部或者不响应外部故障触发信号;

设定当外部故障触发信号出现时,强制EPWMxA和/或EPWMxB为高电平、低电平、高阻态、不做反应;

设定ePWM对外部故障触发信号的响应频率:单次响应,周期性响应;

使能外部故障触发信号产生中断;

不做处理。

g.事件触发(ET)

使能ePWM模块的中断功能;

使能ePWM模块产生ADC启动信号;

设定触发事件触发中断或ADC启动信号的频率:每次都触发、2次才触发、3次才触发;

挂起、置位或清除事件标志位。

在配置ePWM模块时,按照图7所示流程进行。 

 图7 EPWM模块

图6中,虚线包围的模块,可以根据实际需求,选择使用或者让PWM直接通过该模块。事件触发子模块用来处理事件基准计数器、比较功能子模块产生的时间,从而向CPU发出中断请求或者产生ADC启动信号SOCA或SOCB。

3.2.3配置说明

这里注意:配置PWM时,需要注意硬件上是否对PWM电平进行了翻转,高电平管子开通时采用“AHC”的死区方案,低电平管子开通时采用“ALC”的死区配置方案。

下面以一个例子(注意:例子和源代码配置的不一样,原理相同的)对上述内容进行说明,从程序解读,到配置,到波形占空比计算,到实验,供理解上述注意事项。

以ePWM1为例,程序如下:

EALLOW;

  EPwm1Regs.TBPRD = PWM_PeriodMax;              //9375

  EPwm1Regs.TBPHS.half.TBPHS = 0;                 //相位寄存器清0

  EPwm1Regs.TBCTR = 0;                            //计数器清0

  EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;  //增减计数

  EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;    //Master Mode,禁止相位装载功能

  EPwm1Regs.TBCTL.bit.PRDLD = TB_SHADOW;  //CTR=0时将映射寄存器中的值装入当前寄存器

  EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; //CTR=0时发出同步信号

  EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;  //1分频

  EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV1;     //1分频

//CC

  EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;  //CTR=0时将映射寄存器中的值装入当前寄存器

  EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;  //CTR=0时将映射寄存器中的值装入当前寄存器

  EPwm1Regs.CMPA.half.CMPA = PWM_HalfPerMax;  //50%;

  EPwm1Regs.CMPB = PWM_HalfPerMax;              //50%;

//AQ

  EPwm1Regs.AQCSFRC.bit.CSFA = AQ_NO_ACTION; //软件强制时,0:无动作,1:强制为低;2:强制为高;3:禁止强制,无动作。

  EPwm1Regs.AQCSFRC.bit.CSFB = AQ_NO_ACTION; //软件强制时,0:无动作,1:强制为低;2:强制为高;3:禁止强制,无动作。

  EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;

  EPwm1Regs.AQCTLA.bit.CAD = AQ_SET;

  EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR;

  EPwm1Regs.AQCTLB.bit.CBD = AQ_SET;

//DB

  EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE; //使能上升沿和下降沿的延时信号

  EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;// AHC:EPWM1B反转极性。即高电平有效,互补输出   

EPwm1Regs.DBCTL.bit.IN_MODE = DBB_ALL; //EPWM1B作为上升沿及下降沿延时的信号源。

      EPwm1Regs.DBFED =  PWM_Deadband;

      EPwm1Regs.DBRED =  PWM_Deadband;              //2us=2*150*1/150M

//PC

  EPwm1Regs.PCCTL.bit.CHPEN = CHP_DISABLE;      //禁止PWM斩波控制

ePWM2和ePWM3的配置相似。注意:ePWM1为主模块,发出同步信号,ePWM2和ePWM3为从模块,接收同步信号,从而实现3个逆变桥的同步控制。

程序由ePWM1模块产生中断信号,故它还需要如下配置:

  EPwm1Regs.ETSEL.bit.INTEN = 1; //使能

  EPwm1Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO; //TBCTR=0时产生EPWMx_INT中断

  EPwm1Regs.ETPS.bit.INTPRD = ET_1ST;     //每发生1次事件,产生中断信号EPWMx_INT

  EPwm1Regs.ETCLR.bit.INT = 1;            // Enable more interrupts

值得注意的是,本程序的配置方法是Epwm1B的波形来自ePWM1A的反转,并加入死区时间,我们给EPwm1Regs.CMPA.half.CMPA中装入值,产生ePWM1A波形(其实不用配置1B),然后通过死区模块,就产生了对称互补带死区的两路PWM波。死区采用AHC方式,即EPWM1b反转极性。高电平有效,互补输出。具体波形如图8所示:

图8  带死区的对称互补PWM波形产生原理

注意:该方式适用于低电平有效的驱动芯片。配好完成,主要保证比较值CMPA增大的时候,EPWM1A的占空比应该是增大的。

增减计数模式下,可以根据自己系统的中断周期和CPU主频计算出寄存器TBPRD中应该装入的值。

EPwm1Regs.TBPRD=9375; //150000k/9375/2=8k

配置好ePWM模块后,应该对其进行验证,确保配置的正确无误。

我们直接给3相桥臂进行占空比赋值:

Svpwmdq.Ta = 0.5;

Svpwmdq.Tb = -0.2;

Svpwmdq.Tc =  0;

即给ePWM模块的比较寄存器CMPA中设定值为:

EPwm1Regs. CMPA.half.CMPA = (int)(PWM_PERIOD_DEFAULT*(0.5 + 1)*0.5)=9375*75%.

EPwm2Regs. CMPA.half.CMPA =3750(40%)

EPwm3Regs. CMPA.half.CMPA =4688(50%)

此时不考虑死区,只考虑对称互补,6路PWM的占空比应该为:

表3  PWM的占空比

EPwm1A

EPwm1B

EPwm2A

EPwm2B

EPwm3A

EPwm3B

75%

25%

40%

60%

50%

50%

程序中通过死区模块,采用ALC:EPWMA反转极性即低电平有效,互补输出的方式(该方式用于低电平有效的驱动芯片),产生了带死区的互补PWM。

死区占空比为:150/9375=1.6%

因此最终的6路PWM占空比应该为:

表4  PWM的占空比

EPwm1A

EPwm1B

EPwm2A

EPwm2B

EPwm3A

EPwm3B

26.6%

76.6%

61.6%

41.6%

51.6%

51.6%

将程序烧录到硬件中,利用示波器观察波形分别如图9-图14所示: 

图9  EPWM1A波形

图10  EPWM1B波形

图11  EPWM2A波形

图12  EPWM2B波形

图13  EPWM3A波形

图14  EPWM3B波形

可见,实验和理论分析相符,配置的波形正确。

3.3 串口通信模块

3.3.1功能描述

串口主要完成和上位机的通信,接收上位机的控制指令,并将一些需要监视的变量回传给上位机。

本程序中设置波特率为460800pbs,8位数据位,1位停止位;无奇偶校验,全双工传送,设置RX FIFO中断深度1TX FIFO中断深度0

SCI内部串行时钟信号由低速外设时钟信号LSPCLK及波特率选择寄存器共同决定。BRR为16为波特率设定值,其高8位装入SCIHBAUD中,低8位装入SCILBAUD中,即可配制出不同的波特率。

若BBR=0, 

若1≤BRR≤65535,

因此,

3.3.2流程逻辑

这里对串口通信协议进行分析:

上位机给DSP发送9个字节,DSP给上位机回传14个字节的数据。这里仅分析烧录在DSP中的通信部分的代码,上位机中的通信代码原理和这个一样。

(1)先收上位机发来的数据(9个字节)

如果帧头是AA或者BB或者55,则接收数据。

RecData_ctrl_tmp[0],放置帧头;

RecData_ctrl_tmp[1]~ [7],放置数据;

RecData_ctrl_tmp[8],放置校验和;

2)数据拼接

Rec_mode.Head = RecData_ctrl_tmp[0],放置帧头

如果帧头是AABB:工作模式

Rec_mode.Data0=RecData_ctrl_tmp[1][2][3]; //数据(暂未用)

Rec_mode.Data1=RecData_ctrl_tmp[4][5][6];  //数据

temp = (int32)(([4]*65536) + ([5]*256) + [6]); //这是拼接过程

Rec_mode.Data1 = temp*0.01-10000;   //数据处理下,原因见下文分析

Rec_mode.CmdMode = RecData_ctrl_tmp[7];  //[7]是工作模式

如果帧头是55:参数配置模式

Rec_mode.FrameCnt = RecData_ctrl_tmp[2];  //帧计数

Rec_mode.ParaSet  = RecData_ctrl_tmp[3];  //指令

Rec_mode.CmdMode  = 0x04;    //参数配置时应停机

根据Rec_mode.ParaSet对不同的参数进行调整。

3)解析上位机指令

根据Rec_mode.CmdMode选择执行不同的流程。

0停机,1电流环,2速度环,3位置环,4参数配置,并将模式回传给上位机Snd_mode.CmdModeFdb

Rec_mode.Data1为上位机发来的具体指令值。

4)准备好要回传的数据

Snd_mode.Data0,Data1,Data2,Data3,Data4

可以将电机运行过程中需要监测的数据赋给Data0Data1Data2Data3Data4就能将数据传送给上位机,并用上位机保存下来,以便后续分析数据,处理问题,保存的数据利用Matlab画图分析特别方便,教程中给出了画图的M文件了。

这里一个有符号的数据占用了2个字节,可表示的数据范围为-32768~32768,为了能表示小数点后2位,故回传时将数据*100处理了,因此要回传的数据的大小范围在-32.768~32.768

//32位系统,short为2个字节。因此,Snd_mode.Data0只能是-327.68~326.68,小数点后两位。

//如果*10,则Snd_mode.Data0只能是-3276.8~3276.8,小数点后1位。

//如果*1,则Snd_mode.Data0只能是-32768~32668,只能是整数。

ftemp1=(short)(Snd_mode.Data0 *100.0);

SndData_ctrl_tmp[3]=(ftemp1>>8)&0x00FF;  //高8位放在[3]

SndData_ctrl_tmp[4]=ftemp1&0xFF;

(5)数据处理

如果收到的帧头是AA或BB

SndData_ctrl_tmp[0]=0xCC;

SndData_ctrl_tmp[1] = Snd_mode.CmdModeFdb;  //工作模式回传

SndData_ctrl_tmp[2] = Snd_mode.FaultStatus; //故障模式回传

ftemp1=(short)(Snd_mode.Data0 *100.0); SndData_ctrl_tmp[3]=(ftemp1>>8)&0x00FF;  //高8位放在[3]

SndData_ctrl_tmp[4]=ftemp1&0xFF;

[5\6]Data1,[7\8]Data2,[9\10]Data3,[11\12] 放Data4

SndData_ctrl_tmp[13]放数据[1]~[12]的校验和。

如果收到的帧头是55

SndData_ctrl_tmp[0] = 0x55;

[1]~ [9]放PI参数 

SndData_ctrl_tmp[10] = Snd_mode.FaultStatus;故障码

SndData_ctrl_tmp[11] = Rec_mode.FrameCnt;  调参次数

SndData_ctrl_tmp[12] = Snd_mode.CmdModeFdb; //工作模式回传

SndData_ctrl_tmp[13]放数据[1]~[12]的校验和。

6)回传数据

每1ms,向上位机回传SndData_ctrl_tmp[1]~[14]

程序中设计的标志位,比如Rec_mode.flag_NewSnd_mode.flag_New等,是为了让数据传送有条不紊的进行,即以下几个流程按顺序执行。

    scic_ctrl_RecData();    //从测控串口接收数据

    RecData_Ctrl_Update();  //接收数据更新

    Message_Ctrl_Decode();  //数据解码

    Message_Ctrl_Encode();  //要发送的数据及回传数据编码

SndData_Ctrl_Update();  //发送数据更新

以上即为DSP程序中的通信部分,上位机中的代码和这个模式一模一样,学习时只需要掌握这个原理,就可以很方便的将代码移植到自己的程序中,实现电机运行过程数据的监控、分析,这对学习很有帮助。

3.4 片内AD采集处理模块

3.4.1功能描述

最基本的FOC只需要两相电流即可,为了硬件保护等,我们可能还要采集母线电压、电流、温度等信号。并对采样值进行滤波数据处理。

ADC模块具有多个分频器,以产生任何需要的ADC操作时钟,图15为ADC的时钟链结构。

图15 ADC时钟链结构图

3.4.2流程逻辑

片内AD一共采集了以下物理量:

AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 1; // A1-> Phase A Current

AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 9; // B1-> Phase B Current

AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 3; // A3-> Phase C Current

AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 15; // B7-> Phase A Voltage

AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 14; // B6-> Phase B Voltage

AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 12; // B4-> Phase C Voltage

AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 7; // A7-> DC Bus  Voltage

CONV01]~CONV07是要转换的顺序,后面的数字代含义为:0~7代表片内F28335芯片的ADCINA0~A7,8~15代表B0~B7。这个程序所使用的硬件部分,把电机的A相电流接在了DSP芯片的ADCINA1引脚,B相电流接在了DSP芯片的ADCINB1引脚,以此类推。

在主中断中,对采集的量进行处理。

三相电流处理程序为:

//**********************************************************************

/*

  @ Description: 电机三相电流采样

  @ Param

  @ Return

*/

//**********************************************************************

void GetIPhase()

{

IPhase.IaTemp = (AdcRegs.ADCRESULT1>>4)*0.00024414;   //iA

IPhase.IbTemp = (AdcRegs.ADCRESULT2>>4)*0.00024414;   //iB

IPhase.IcTemp = (AdcRegs.ADCRESULT3>>4)*0.00024414;   //iC

    if(MultCtrl.mult_mode == POWEROFF)

    {

        IPhase.IaOffset = IPhase.IaOffset*CURR_K1 + IPhase.IaTemp*CURR_K2;  //iA偏置电流

        IPhase.IbOffset = IPhase.IbOffset*CURR_K1 + IPhase.IbTemp*CURR_K2;  //iB偏置电流

        IPhase.IcOffset = IPhase.IcOffset*CURR_K1 + IPhase.IcTemp*CURR_K2;  //iC偏置电流

    }

IPhase.IaNew = (IPhase.IaNew*CURR_K1 + IPhase.IaTemp*CURR_K2);

IPhase.IbNew = (IPhase.IbNew*CURR_K1 + IPhase.IbTemp*CURR_K2);

IPhase.IcNew = (IPhase.IcNew*CURR_K1 + IPhase.IcTemp*CURR_K2);

IPhase.Ia = (IPhase.IaNew-IPhase.IaOffset)*2*0.909; //得到iA

IPhase.Ic = (IPhase.IcNew-IPhase.IcOffset)*2*0.909; //得到iC

    IPhase.Ib = 0 - IPhase.Ia - IPhase.Ic;     //得到iB

}

最终得到电机工作时的三相电流Ia、Ib、Ic,用于后续的Clark变换。

AD采样结果寄存器是12位的,即AdcMirror.ADCRESULT1范围为0-4095,相当于有4096个数字。

2^12=4096,给采样结果除以4096,也就是乘以0.00024414(对于整数,右移12位,也相当于除以4096),就把(0-4095)标幺为了0~1。

对于电机的电流是有正有负的,于是电流采样硬件电路设计的时候,理论上,会让电机电流为0时,调理电路输出的那个电压为AD量程的中位,系统上电后,电机未运行时,即在MultCtrl.mult_mode = POWEROFF时采集零偏电流并滤波,于是,电机没有电流时,采集的offsetA会得到0.5。

然后, IPhase.IaNew-IPhase.IaOffset,将(0-1)变换到(-0.5~0.5);然后乘以2,变换成(-1~1)

0.909是与280xx的芯片进行统一,因为那些芯片是0~3.3V的采样范围,而F28335的AD采样范围为0-3V。

标幺化是使不同的产品系列,不同的控制芯片使用同一套程序的必要手段,当如果你只想研究单个平台,不标幺也是可以的,只要在平台转移的时候会很费劲。

这里注意:AD采样的寄存器AdcMirror和AdcRegs差别是,AdcRegs不支持DMA访问,而AdcMirror支持。

母线电压的处理程序为:

//**********************************************************************

/*

  @ Description: 母线电压采集

  @ Param

  @ Return

*/

//**********************************************************************

void GetVbus()

{

Volt.Vbus=((AdcRegs.ADCRESULT7>>4)*0.00024414)*0.909*74.53;  //AD获取直流母线电压

}

原理一样的。

表5 片内AD采集的变量

序号

数据名称

数据标识

数据类型

输出数据

1

A相电流

IPhase.Ia

float

2

B相电流

IPhase.Ib

float

3

C相电流

IPhase.Ic

float

4

A相电压

程序中未使用

5

B相电压

6

C相电压

7

母线电压

Volt.Vbus

float

    1. 中断处理模块

3.5.1功能描述

EPWM1产生的主中断,每次事件触发1次中断,因此中断触发周期为125us,该模块执行时间不能超过125us的80%即100us,因为还要留时间去执行非中断中的任务。

该模块完成状态机机制下各子函数定时计数器时间分配任务(根据上位机指令,选择要执行的流程)。

3.5.2流程逻辑

中断服务程序流程图见图16所示,中断服务程序中主要完成(故障保护不分析):

(a)获取转子位置角度(每120us);

(b)获取电机三相电流(每120us);

(c)通过分频获取其他周期定时;

(d)每120us执行电流闭环控制;

(e)每500us执行速度闭环控制(如果被选择);

(f)每1ms执行位置闭环控制(如果被选择);

其中电流闭环、速度电流双闭环、位置速度电流3闭环执行流程分别如图16.1、图16.2、图16.3所示。

图16 中断服务程序流程图

图16.1 电流环子程序流程图

图16.2 速度环子程序流程图

图16.3 位置闭环子程序

其中三个环中使用的MotorCtrl()流程如图16.4所示。

图16.4  子程序

MotorCtrl函数中,对变换使用的转子位置角进行了判断,目的是,在开环拉电机时,直接使用给定的theta = 0.0(调试时,也可以改变theta值,观察电机转向)将电机拉到指定的位置。正常工作时,再使用编码器计算得到的eQEP1.ElecTheta

同理,下面的这两段代码也是为了开环拉电机和校准编码器零位使用的,正常工作时,程序不会执行这两段代码。

if(lsw==0)

{

IparkU.Ds = 0.15;                    //d轴给定安全电压

IparkU.Qs = 0.0;                     //q轴给定电压为0

}

//校准零偏

if(CalibrateFlag==1)

{

IparkU.Ds = 0.0;                    //d轴给定安全电压

IparkU.Qs = 0.0;                    //q轴给定电压为0

}

定时计数器配置功能:使用EPWM1产生的主中断周期作为时间基准,使用TimerCtrl结构体定义的变量TimerCnt自加,得到125us到500ms不同的定时计数器,当TimerCnt变量累加到所需定时计数器的预设值时,设置时间标识位变量置1,执行相应的功能函数,函数执行完成后该变量清零。

软件定时器功能见表6。

表6 定时器功能

功能名称

定时器功能

输入

激励事件

ePWM1产生中断(125us)

数据

处理

125us周期定时到,标志位置1;定时计数器值累加,判断其他如250us、1ms、5ms、10ms、500ms等周期定时是否到。

输出

各定时器标志位

说明

标志位会在对应函数执行后清0

    1. 转子位置角获取模块

3.6.1功能描述

电机用的是增量式正交编码器,因此本程序采用的EQEP模块来采集电机的转子位置角,该模块就是一个片内外设的配置应用问题,没有技术上的难度。

3.6.2流程逻辑

(1)EQEP模块的初始化配置

void InitEqep1()

{

EQep1Regs.QUPRD = 1500000;// Unit Timer for 100Hz at 150 MHz SYSCLKOUT

EQep1Regs.QDECCTL.bit.QSRC = 00; // 00:正交计数,01:方向计数,10:增计数,11:减计数

EQep1Regs.QDECCTL.bit.XCR=0;// 0:上升沿和下降沿都计数,1:上升沿计数

EQep1Regs.QEPCTL.bit.FREE_SOFT=2;  // 仿真挂起对其无影响

EQep1Regs.QEPCTL.bit.PCRM = 00;// 00:索引事件发生时复位,01:计数最大时复位 // 10:第一次索引事件时复位,11:单位时间输出时间时复位

EQep1Regs.QEPCTL.bit.IEI=0x10; // 10:在QEPI上升沿初始化位置计数器,11:在下降沿,0x:无动作

EQep1Regs.QEPCTL.bit.IEL=0x01; // 01:在QEPI上升沿将QPOSCNT的值锁存到QPOSILAT中,10:下降沿,11:软件发起一次事件

EQep1Regs.QEPCTL.bit.QPEN=1; // 使能QEP位置计数器

EQep1Regs.QEPCTL.bit.QCLM=1; // 0:在CPU读取位置计数器的值时锁存,1:定时器基准单元超时事件时锁存

EQep1Regs.QEPCTL.bit.UTE=1; // 使能eQEP定时器基准单元

EQep1Regs.QPOSCTL.all=0x0000;       // eQEP位置比较单元寄存器

EQep1Regs.QCAPCTL.bit.UPPS = 5;    // 单元位置事件预分频,UPEVNT=QCLK/(2的5次方)

EQep1Regs.QCAPCTL.bit.CCPS = 7; // 捕获单元定时器时钟预分频,CAPCLK=SYSCLKOUT/(2的7次方)

EQep1Regs.QCAPCTL.bit.CEN = 1; // 捕获单元使能

EQep1Regs.QPOSMAX = eQEP1.Encoder_N;   // 码盘一周脉冲数的4倍(根据倍频的倍数而定,这里用4倍频)

InitEqep1Gpio();

}

(2)转子位置角的获取

void eQEP1_pos_get()

{

   //Check the rotational direction   QEPSTS为QEP的状态寄存器

eQEP1.DirectionQep = EQep1Regs.QEPSTS.bit.QDF; //正交方向标志

   //Check the position counter for EQep1   QPOSCNT为QEP的位置计数器,32位,根据方向增减。

   //根据编码器零位和电机转子零位的角度偏差,对位置计数器的值进行补偿

   eQEP1.RawTheta = EQep1Regs.QPOSCNT + eQEP1.CalibrateAngle;

   //RawTheta为电机实际角度,用码盘计数值表示,范围0-4000。

   //RawTheta只能在(0-QPOSMAX)之间,超过了则进行处理。

   if(eQEP1.RawTheta < 0)

   {

   eQEP1.RawTheta = eQEP1.RawTheta - EQep1Regs.QPOSMAX;

   }

   else if(eQEP1.RawTheta > EQep1Regs.QPOSMAX)

   {

   eQEP1.RawTheta = eQEP1.RawTheta - EQep1Regs.QPOSMAX;

   }

   //Compute the mechanical angle

   eQEP1.MechTheta = eQEP1.Mech_Scaler * eQEP1.RawTheta; //码盘计数值,归一化处理为0-1

   //Compute the electrical angle   floor(x)返回不大于X的最大整数.

   //以码盘归一化读数形式表示的电机的电角度0-1

eQEP1.RawElecTheta = (eQEP1.PolePairs * eQEP1.MechTheta)-floor(eQEP1.PolePairs * eQEP1.MechTheta);

   // Check an index occurrence    //索引事件锁存中断发生了

   if (EQep1Regs.QFLG.bit.IEL == 1)

   {

   eQEP1.Index_sync_flag = 0x00F0;    //输出标志位

   eQEP1.QepCountIndex = EQep1Regs.QPOSILAT;

       EQep1Regs.QCLR.bit.IEL=1;  //Clear interrupt flag 清除中断标志

   }

//得到-pi~+pi形式的转子位置角

   eQEP1.ElecTheta = eQEP1.RawElecTheta * PI2;

   if(eQEP1.ElecTheta > PI)

   {

   eQEP1.ElecTheta -= PI2;

   }

}

看以上程序过程中,注意下,获取的转子位置角度是以哪种形式表示的,程序最后将电角度转化成了-pi~+pi。

(3)转速的计算

采用正交编码器的转速解算,有M法测速和T法测速,也有M/T法测速。TI官方对此有很完整的注释了,这里不再赘述。

不懂的同学,可以百度,也可以参看《深入理解无刷直流电机矢量控制技术》这本书的63-68页。

我这里粘贴下具体内容。

本程序包含了M法测速和T法测速,使用的是M法测速。

void eQEP1_speed_get(EQEP_POS_SPEED_GET *pV)

{

   //M法,适用于高速,固定时间读位置变化

   float tmp1;

//   unsigned long t2_t1;

   if(EQep1Regs.QFLG.bit.UTO==1)  //If unit timeout 单位时间事件中断发生

   {

      pV->Position_k = 1.0 * EQep1Regs.QPOSLAT; //当索引事件发生时,位置计数器中的值会加载到QPOSLAT中

      if(pV->DirectionQep == 0)   // POSCNT is counting down  减计数

      {

          if(pV->Position_k > pV->Position_k_1)

  {

           tmp1 = -(pV->Encoder_N - (pV->Position_k - pV->Position_k_1));

  }

          else

  {

           tmp1 = pV->Position_k - pV->Position_k_1;

  }// x2-x1 should be negative 减计数时,差值应该是负值

      }

      else if(pV->DirectionQep==1) // POSCNT is counting up  增计数

  {

      if(pV->Position_k < pV->Position_k_1)

  {

       tmp1 = pV->Encoder_N - (pV->Position_k_1 - pV->Position_k);

  }

          else

  {

           tmp1 = pV->Position_k - pV->Position_k_1;

  }// x2-x1 should be positive  增计数时,差值应该是正值

  }

  if(tmp1 > pV->Encoder_N * 1.0)

  {

  pV->Speed_Mr_Rpm = pV->BaseRpm;

  }

      else if(tmp1 < pV->Encoder_N * (-1.0))

  {

       pV->Speed_Mr_Rpm = -pV->BaseRpm;

  }

      else

  {

       pV->Speed_Mr_Rpm = tmp1 * pV->Speed_Mr_Rpm_Scaler;  //得到实际转速rpm

  }

  pV->Speed_Mr_Rpm = 0.25 * pV->Speed_Mr_Rpm_Last + 0.75 * pV->Speed_Mr_Rpm; //转速滤波

      pV->Speed_Mr = pV->Speed_Mr_Rpm / pV->BaseRpm;

      pV->Speed_Mr_Rpm_Last = pV->Speed_Mr_Rpm;  //保存当前转速,用于下一个周期的计算

      pV->Position_k_1 = pV->Position_k;         //更新码盘值

      EQep1Regs.QCLR.bit.UTO = 1; // Clear interrupt flag

}

方法和TI官方的一样,我是参考了符晓的代码。

解释如下:

    1. 其他说明

#if FLASH

MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);

InitFlash();  // Call the flash wrapper init function

#endif

这几句是将FLASH中的程序COPYRAM中运行,通常的目的是加快程序的运行速度,通常有两种情况需要这样去操作:
1、程序中对基要求比较高的函数,如中断;
2、程序需要对FLASH进行操作,这时就要把程序先复制到RAM中运行然后才能对FLASH操作。
RamfuncsLoadStart、RamfuncsLoadEnd、RamfuncsRunStart这三个变量是在CMD文件中创建的,创建方式如下:
            LOAD_START(RamfuncsLoadStart),
            LOAD_END(RamfuncsLoadEnd),
            RUN_START(RamfuncsRunStart),
分别表示了装载函数的首地址,装载函数的结束地址和装载函数的运行地址;
执行完MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);后,便将FLASH中相关的程序COPY到了RAM中,之后的程序运行时,只要调用FLASH中RamfuncsLoadStart地址开始的相关函数,系统都会自动地指向RAM中相应的函数入口地址运行。

4、附录

为方便不同控制板开发程序,梳理程序IO接口以及配置表7所示。

表7 控制软件接口描述

序号

接口

说明

引脚设置

1

外设GPIO0~GPIO5

电机6路PWM波输出(电平有效)

上拉,作为PWM功能引脚

A作为信号输入,AHC互补带死区

2

数字GPIO8

Led

D400,系统工作指示灯,高熄灭

3

数字GPIO10

Led

D401,LED,高熄灭

4

外设GPIO12(TZ1)

IR2136的fault引脚  

上拉,作为TZ功能引脚,异步输入

5

数字GPIO21

Led

D402,LED,高熄灭

6

数字GPIO52

IR2136使能引脚 

低电平使能

7

外设GPIO18

SCITX

用于串口(SCI-b)通讯

上拉、作为功能引脚

外设GPIO19

SCIRX

上拉、异步输入、作为功能引脚

8

外设GPIO30

CANrx

CAN通信

外设GPIO31

CANtx

9

外设GPIO50

EQEP1A

EQEP

外设GPIO52

EQEP1B

外设GPIO53

EQEP1I

10

ADINA0

电流IA采样通道

片内ADC

ADINB1

电流IB采样通道

ADINA3

电流IC采样通道

ADINB7

A相电压

ADINB6

B相电压

ADINB4

C相电压

ADINA7

母线电压

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/600260
推荐阅读
相关标签
  

闽ICP备14008679号