这个源文件实际上包含该项目的入口函数main函数。那么如何找到入口函数呢?可以进入debug的simulator模式,选中run to main(),当执行完启动文件startup_cm3.s后就进入了main函数(注意在main函数之前还进入了SystemInit函数),如下图所示:
这个main函数主要架构如下:
int main(void)
{
HW_Init(); /*PDI(过程数据接口)初始化,这里用的是MCI接口*/
MainInit(); /*主函数初始化包含:ESC 和COE */
bRunApplication = TRUE; /*处于运行状态标志*/
do
{
MainLoop(); /*主循环函数,实现周期性和非周期性数据交换*/
} while (bRunApplication == TRUE); /*检查是否处于运行状态*/
HW_Release(); //无意义的函数
return 0; //设置正确返回
}
下面来依次看各个函数。
HW_Init(void)函数,定义了PDI接口,定义了ESC到PDI中断的映射:
ESC的PDI(过程数据接口)有这几种情况:
1. Up to 32 bit digital I/O——32位数字量I/O
2. Serial Peripheral Interface ——SPI总线
3. 8/16-bit synchronous/asynchronous Microcontroller Interface ——MCI接口
4. With FPGA, specific on-board-bus——带FPGA的,特定的on-board-bus
在倍福的手册中也可以看到0x0140寄存器描述了PDI过程数据接口的几种情况。
过程数据和参数是通过ESC上的DPRAM来交互的。为了保证数据的一致性,ESC硬件上会有一些 机制,比如同步管理器等。
HW_Init函数的解读也可以参考 https://blog.csdn.net/qq_15157841/article/details/51789250
UINT16 HW_Init(void)
{
UINT32 intMask = 0;
pEsc = (unsigned short *)ESC100StartAddress;//(unsigned short)MAKE_PTR_TO_ESC;定义指针pEsc,指向ESC物理内存起始地址
UINT16 u16PdiCtrl = 0;
/* we have to wait here, until the ESC is started */
do
{
HW_EscReadWord(u16PdiCtrl,ESC_PDI_CONTROL_OFFSET);//ESC_PDI_CONTROL_OFFSET==0x0140
} while (((u16PdiCtrl & 0xFF) < 0x8) || ((u16PdiCtrl & 0xFF) > 0xD) );//过程数据接口需要是MCI接口
HW_EscWriteDWord(intMask, ESC_AL_EVENTMASK_OFFSET);//ESC_AL_EVENTMASK_OFFSET==0x0204 AL Event masking of the AL Event Request register Events for mapping to PDI IRQ signal
/* enable the ESC-interrupt microcontroller specific,
the macro ENABLE_ESC_INT should be defined in ecat_def.h */
//------------ENABLE_ESC_INT();
return 0;
}
其中0x0204寄存器定义如下:
MainInit(void)函数,定义了EtherCAT从站接口,COE(CANopen Over EtherCAT)相关的配置。
/////////////////////////////////////////////////////////////////////////////////////////
/**
\param pObjectDictionary Pointer to application specific object dictionary.
NULL if no specific object are available.
\return 0 if initialization was successful
\brief This function initialize the EtherCAT Sample Code
*////////////////////////////////////////////////////////////////////////////////////////
UINT16 MainInit(void)
{
UINT16 Error = 0;
/* initialize the EtherCAT Slave Interface */
ECAT_Init();
/* initialize the objects */
COE_ObjInit();
/*Timer initialization*/
u16BusCycleCntMs = 0;
StartTimerCnt = 0;
bCycleTimeMeasurementStarted = FALSE;
/*indicate that the slave stack initialization finished*/
bInitFinished = TRUE;
/*Application Init need to be called from the application layer*/
return Error;
}
其中的ECAT_Init()函数用于初始化从站接口。
void ECAT_Init(void)
{
UINT8 i;
/*Get Maximum Number of SyncManagers and supported DPRAM size*/
HW_EscReadByte(nMaxSyncMan, ESC_SM_CHANNELS_OFFSET); //read register 0x0005 to get numbers of sync manager channel.
HW_EscReadWord(nMaxEscAddress, ESC_DPRAM_SIZE_OFFSET); //read register 0x0006 to get DPRAM size in KB
//get max address (register + DPRAM size in Byte (in the register it is stored in KB))
/* ECATCHANGE_START(V5.11) ESC1*/
nMaxEscAddress = (nMaxEscAddress << 10) + 0xFFF;//0xFFF? //convert DPRAM size to Byte,then add 0xFFF
/* ECATCHANGE_END(V5.11) ESC1*/
/* Get EEPROM loaded information */
UpdateEEPROMLoadedState();//read register 0x0502(bit11+12),return EEPROM load state = 0 or 1
/* disable all Sync Manager channels */
for (i = 0; i < nMaxSyncMan; i++)
{
/*ECATCHANGE_START(V5.11) HW1*/
DisableSyncManChannel(i);//write register 0x0807 + 8*channel
/*ECATCHANGE_END(V5.11) HW1*/
}
/* initialize the mailbox handler */
MBX_Init(); //write register 0x0807 + 8
/* initialize variables */
bApplEsmPending = FALSE;
bEcatWaitForAlControlRes = FALSE;
bEcatFirstOutputsReceived = FALSE;
bEcatOutputUpdateRunning = FALSE;
bEcatInputUpdateRunning = FALSE;
bWdTrigger = FALSE;
EcatWdValue = 0;
Sync0WdCounter = 0;
Sync0WdValue = 0;
Sync1WdCounter = 0;
Sync1WdValue = 0;
bDcSyncActive = FALSE;
bLocalErrorFlag = FALSE;
u16LocalErrorCode = 0x00;
u16ALEventMask = 0;
nPdOutputSize = 0;
nPdInputSize = 0;
/* initialize the AL Status register */
nAlStatus = STATE_INIT;
SetALStatus(nAlStatus, 0);
nEcatStateTrans = 0;
u8EcatErrorLed = LED_OFF;
/* ECATCHANGE_START(V5.11) ECAT5*/
bEscIntEnabled = FALSE;
/* ECATCHANGE_END(V5.11) ECAT5*/
/* initialize the COE part */
COE_Init();
}
COE_ObjInit函数用于COE对象字典的初始化。
void COE_ObjInit(void)
{
/* initialize the Sync Manager Output parameter object 0x1C32 */
sSyncManOutPar.subindex0 = 32;
/*
subindex 1 contains the actual synchronization mode, it could be written
from the master to switch between ECAT FreeRun and ECAT Synchron Mode
if the slave supports both modes,
in DC mode (selected by the DC registers) this value will be overwritten
with SYNCTYPE_DCSYNC0 or SYNCTYPE_DCSYNC1 */
/*default mode is ECAT Synchron Mode */
sSyncManOutPar.u16SyncType = SYNCTYPE_FREERUN;
/* subindex 2 contains the cycle time of the application,
in ECAT FreeRun mode it could be used for a timer interrupt to run the application,
in ECAT Synchron mode it could be written from the master with its local cycle time
that the slave can check if this cycle time is supported,
in DC Mode this value will be overwritten with the DC cycle time register */
sSyncManOutPar.u32CycleTime = 0;
/* only for DC Mode important: the subindex 3 contains the time shift between the
SYNC0 (SYNC1) signal and when the outputs are put to the hardware to allow the
master a very exactly calculation of delay times*/
sSyncManOutPar.u32ShiftTime = 0;
/* the subindex 4 contains the supported synchronization types */
sSyncManOutPar.u16SyncTypesSupported = SYNCTYPE_FREERUNSUPP /* ECAT FreeRun Mode is supported */
| SYNCTYPE_TIMESVARIABLE /* the execution times depend on the connected modules */
| SYNCTYPE_SYNCHRONSUPP /* ECAT Synchron Mode is supported */
| SYNCTYPE_DCSYNC0SUPP /* DC Sync0 Mode is supported */
| SYNCTYPE_DCSYNC1SUPP /* DC Sync1 Mode is supported */
| SYNCTYPE_SUBCYCLESUPP /*Subordinated application cycles supported*/
;
/* subindex 5 contains the minimum cycle time the slave is able to support,
will be calculated dynamically because it depends on the connected modules
(in this example we will make an online measurement in the ESC Interrupt Routine).
For the sample application this value is set to MIN_PD_CYCLE_TIME */
sSyncManOutPar.u32MinCycleTime = MIN_PD_CYCLE_TIME;
/* only for DC Mode important: subindex 6 contains the minimum delay time the slave
needs after receiving the SM2-event before the SYNC0(SYNC1) can be received without delays
will be calculated dynamically because it depends on the connected modules
(in this example we will make an online measurement in the ESC Interrupt Routine) */
sSyncManOutPar.u32CalcAndCopyTime = (PD_OUTPUT_CALC_AND_COPY_TIME);
/*subindex 8: trigger cycle time measurement*/
sSyncManOutPar.u16GetCycleTime = 0;
/*subindex 9: time from start driving outputs until outputs are valid*/
sSyncManOutPar.u32DelayTime = (PD_OUTPUT_DELAY_TIME);
/*subindex 32: indicates if a synchronisation error has occurred*/
sSyncManOutPar.u8SyncError = 0;
/*ECATCHANGE_START(V5.11) ECAT4*/
/* initialize the Sync Manager Input parameter object 0x1C33 */
sSyncManInPar.subindex0 = 32;
/* default mode is ECAT Synchron Mode, if output size > 0 the inputs are updated with the SM2-event */
sSyncManInPar.u16SyncType = SYNCTYPE_FREERUN;
/* subindex 2: same as 0x1C32:02 */
sSyncManInPar.u32CycleTime = sSyncManOutPar.u32CycleTime;
/* only for DC Mode important: subindex 3 contains the time shift between the
SYNC0 (SYNC1) signal and when the inputs are got to the hardware to allow the
master a very exactly calculation of delay times,
will be calculated dynamically because it depends on the connected modules
(in this example we will make an online measurement in the ESC Interrupt Routine) */
sSyncManInPar.u32ShiftTime = 0;
/* subindex 4: same as 0x1C32:04 */
sSyncManInPar.u16SyncTypesSupported = sSyncManOutPar.u16SyncTypesSupported;
/* subindex 5: same as 0x1C32:05 */
sSyncManInPar.u32MinCycleTime = MIN_PD_CYCLE_TIME;
/* subindex 6: delay read inputs, calculation and copy to SM buffer*/
sSyncManInPar.u32CalcAndCopyTime = (PD_INPUT_CALC_AND_COPY_TIME);
/*subindex 8: trigger cycle time measurement*/
sSyncManInPar.u16GetCycleTime = 0;
/*subindex 9: delay to prepare input latch*/
sSyncManInPar.u32DelayTime = (PD_INPUT_DELAY_TIME);
/*subindex 32: incremented if a synchronisation error has occurred*/
sSyncManInPar.u8SyncError = 0;
/*ECATCHANGE_END(V5.11) ECAT4*/
/*Indicate no user specified Sync mode*/
bSyncSetByUser = FALSE;
UINT16 result = COE_ObjDictionaryInit();
if(result != 0)
{
/*clear already linked objects*/
COE_ClearObjDictionary();
}
u8PendingSdo = 0;
bStoreCompleteAccess = FALSE;
u16StoreIndex = 0;
u8StoreSubindex = 0;
u32StoreDataSize = 0;
pStoreData = NULL;
pSdoPendFunc = NULL;
pSdoSegData = NULL;
}
这样就完成了MainInit()函数,处于运行状态了,下一步就开始跑应用层的任务,只要处于运行状态就循环进行MainLoop()函数。
MainLoop()函数如下:
void MainLoop(void)
{
/*return if initialization not finished */
if(bInitFinished == FALSE)
return;
/* FreeRun-Mode: bEscIntEnabled = FALSE, bDcSyncActive = FALSE
Synchron-Mode: bEscIntEnabled = TRUE, bDcSyncActive = FALSE
DC-Mode: bEscIntEnabled = TRUE, bDcSyncActive = TRUE */
/* SM-Synchronous, but not SM-event received */ /* DC-Synchronous */
if ((!bEscIntEnabled || !bEcatFirstOutputsReceived) && !bDcSyncActive)
{
/* if the application is running in ECAT Synchron Mode the function ECAT_Application is called
from the ESC interrupt routine (in mcihw.c or spihw.c),
in ECAT Synchron Mode it should be additionally checked, if the SM-event is received
at least once (bEcatFirstOutputsReceived = 1), otherwise no interrupt is generated
and the function ECAT_Application has to be called here (with interrupts disabled,
because the SM-event could be generated while executing ECAT_Application) */
if ( !bEscIntEnabled )
{
/* application is running in ECAT FreeRun Mode,
first we have to check, if outputs were received */
UINT16 ALEvent = HW_GetALEventRegister();//read 0x0220
ALEvent = SWAPWORD(ALEvent);
if ( ALEvent & PROCESS_OUTPUT_EVENT )//0x0400==0x0220[10]
{
/* set the flag for the state machine behaviour */
bEcatFirstOutputsReceived = TRUE;
if ( bEcatOutputUpdateRunning )
{
/* update the outputs */
PDO_OutputMapping();
}
}
else if ( nPdOutputSize == 0 )
{
/* if no outputs are transmitted, the watchdog must be reset, when the inputs were read */
if ( ALEvent & PROCESS_INPUT_EVENT ) //0x0800==0x0220[11]
{
/* Outputs were updated, set flag for watchdog monitoring */
bEcatFirstOutputsReceived = TRUE;
}
}
}
DISABLE_ESC_INT();
ECAT_Application();
if ( bEcatInputUpdateRunning )
{
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
PDO_InputMapping();
}
ENABLE_ESC_INT();
}
/* there is no interrupt routine for the hardware timer so check the timer register if the desired cycle elapsed*/
UINT32 CurTimer = (UINT32)HW_GetTimer();
if(CurTimer>= ECAT_TIMER_INC_P_MS) //1500
{
ECAT_CheckTimer();
HW_ClearTimer();
}
/* call EtherCAT functions */
ECAT_Main();
/* call lower prior application part */
COE_Main();
CheckIfEcatError();
}