/************************************************************************************************************************ * control.c * Control module * * The control module is hardware agnostic, not related to a specific hardware module, to enable it to deal with hardware and system changes easily. * The control module contains a high priority task that connects the hardware drivers * below to the control algorithms of the high level system modules above. * * The control task is invoked by a message from a 1 millisecond timer interrupt. * The module is based on system components registered in the control module, supplying callback hooks for information gathering and distribution. * All control clients (drivers and modules) will not perform long procedures that will block the control task. * * Control interfaces: * System Devices list: * * All control system input devices will be listed in a system-shared enumareted list, to create a common language between the modules and the drivers. * (The list will include all hardware devices, but for this module only control input devices are valid) * Drivers: * Device registration: a driver that initializes a control input device will call the ControlDeviceRegister function with the deviceId, * and a control callback hook. This callback is called with a deviceId as the parameter, and returns an unsigned 32bit integer * as a read value and a call status. The callback will be a non-blocking call, and will indicate in the status value if the data is valid. * * It is the responsibility of the device driver to update the control information according to the module hardware requirements. * If the polling of the information is immediate, it can be collected at the callback call (e.g. local GPI). * * Module registration – control: a module is registering to receive the value of the input from a specific device. * The module indicates what will be the desired frequency of the device polling (in milliseconds). * It supplies a callback routine that will receive the deviceId, the control value status and the control value. * The callback will be a non-blocking call. * The polling frequency is one of a specific list of frequencies: 1/10/100/1000 Hz. (others - TBD) * * * Registering a request for control information from a device that is not registered will be rejected. * The device registration process must be performed before module registration process. * * Unregistering: when control information is not needed, the module will unregister the device polling request from the control module. * There is a separate call for control hooks and for report hooks. * The unregistering command contains the callback function pointer, to enable distribution of control * information of the same device to more than one destination. * **************************************************************************************************************************/ ////////////////////////////////State machine operation//////////////////////////////////// //the state machine operation is used to operate in runtime correct profile flow execution //by recieved esign flow of the user from the UI /////////////////////////////////////////////////////////////////////////////////////////// #include "include.h" #include #include #include #include "drivers/FPGA/Full_Vme/FPGA_Programming_Up.h" #include "drivers/adc_sampling/adc.h" #include "drivers/FPGA/FPGA_GPIO/FPGA_GPIO.h" #include "drivers/FPGA/FPGA_SPI_Comm.h" #include "Modules/General/buttons.h" #include "Modules/General/GeneralHardware.h" #include "Modules/AlarmHandling/AlarmHandling.h" #include "StateMachines/Printing/PrintingSTM.h" #include "control.h" #include "MillisecTask.h" #define DURATION_LIMIT 6 /******************** Definitions ********************************************/ #define MAX_TANGO_CONTROL_DEVICES 100 /******************** STRUCTURES AND ENUMs ********************************************/ typedef struct { uint32_t PartId; // the identity of the inspected/controlled part in the Devices enum. bool ControlActive; uint32_t Parameter1; uint16_t IfIndex; uint32_t StartTick; DataReadCBFunction ControlDataReadPtr; ControlCBFunction ControlCallbackPtr; uint32_t ControlTiming; uint32_t LastCalled; char *Name; }ControlDeviceStruc; typedef enum { OneMillisec, }controlMessages; typedef struct ControlMessage{ uint16_t messageId; uint16_t msglen; uint32_t tick; uint8_t messageData[20]; }ControlMessageStruc; int ControlPhaseDelay = 300; //the control task enters only after data gathering in the millisecond task is finished. //this parameters defines how many microseconds in the delay. it is used only on starting the control loop on the first time /******************** GLOBAL PARAMETERS ********************************************/ Mailbox_Handle ControlMsgQ = NULL; Mailbox_Handle TenControlMsgQ = NULL; bool ControlRestart; static GateMutex_Handle gateControlDB; Task_Handle Control_Task_Handle; ControlDeviceStruc ControlArray[MAX_TANGO_CONTROL_DEVICES]; uint32_t ControlDatalog[MAX_TANGO_CONTROL_DEVICES]; #define MAX_BACKLOG_SIZE 100 uint16_t ControlBacklog[MAX_BACKLOG_SIZE]={0}; uint32_t ControlTime[MAX_TANGO_CONTROL_DEVICES]={0}; uint16_t backlogindex = 0; uint32_t Control_timerBase = TIMER0_BASE; //Timer handle uint32_t MaxHighDevices = 0xFF; /******************** Functions ********************************************/ void OneMilliSecondFunction(UArg arg0); //********************************************************************** /******************** CODE ********************************************/ //********************************************************************** uint32_t TemplateDataReadCBFunction (uint32_t deviceID, uint32_t Parameter1) { return 0; } void ControlInit(void) { int Device_i; Error_Block eb; //Mailbox_Params_init(&ControlMsgQ); ControlMsgQ = Mailbox_create(sizeof(ControlMessageStruc), 1, NULL,NULL); TenControlMsgQ = Mailbox_create(sizeof(ControlMessageStruc), 1, NULL,NULL); ControlRestart = false; memset(ControlDatalog,0,sizeof(uint32_t)*MAX_TANGO_CONTROL_DEVICES); MaxHighDevices = 0xFF; for (Device_i = 0; Device_i < MAX_TANGO_CONTROL_DEVICES; Device_i++) { ControlArray[Device_i].ControlActive = false; ControlArray[Device_i].ControlCallbackPtr = NULL; ControlArray[Device_i].ControlDataReadPtr = NULL; ControlArray[Device_i].ControlTiming = eNoControl; ControlArray[Device_i].Name = NULL; } gateControlDB = GateMutex_create(NULL, &eb); if (gateControlDB == NULL) { System_abort("Could not create USB Wait gate"); } ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); ROM_TimerConfigure(Control_timerBase, TIMER_CFG_PERIODIC); // 32 bits Timer //TimerIntRegister(Control_timerBase, TIMER_A, Timer0Isr); // Registering isr ROM_TimerEnable(Control_timerBase, TIMER_A); ROM_IntEnable(INT_TIMER0A); ROM_TimerIntEnable(Control_timerBase, TIMER_TIMA_TIMEOUT); //ADCAcquireInit(); return; } void ControlStop(void) { ControlRestart = false; ADCAcquireStop(); } int FPGA_ReInit_Count = 0; ///avoid too many reinitializations of motors as happens when FPGA is corrupted uint32_t ControlActivityLed( uint32_t Parameter1) { static bool flag = false; static uint8_t counter; const uint8_t Blink_Freq = 7;//odd number if (flag==true) { COMM_RED_LED_ON; ACTIVITY_RED_LED_OFF; // Heaters indication - all the Heaters OFF if(FPGA_WD_Occurred == true) { if (FPGABurningActive == false) { FPGA_WD_Occurred = false; AlarmHandlingSetAlarm(EVENT_TYPE__FPGA_WATCHDOG_ACTIVATED,true); if (JobIsActive()) { Report("Hardware Failure Error - abort job!",__FILE__,__LINE__,EVENT_TYPE__FPGA_WATCHDOG_ACTIVATED,RpError, 0,0); JobEndReason = JOB_MOTOR_ALARM; usnprintf(AlarmReasonStr, 100, "Hardware Failure Error"); SendJobProgress(0.0,0,false, "Hardware Failure Error"); AbortJob("FPGA Watchdog Error"); } if (FPGA_ReInit_Count++<20) { ReportWithPackageFilter(FPGAFilter, "FPGA Watchdog Error",__FILE__,__LINE__,0,RpError, 0,0); ACTIVITY_GREEN_LED_ON; MotorConfiguredTimeout = 100; FPGA_SetMotorsInit(); Motor_ReconfigAllMotors(); } } } else ACTIVITY_GREEN_LED_OFF; if(power.color == colorOFF) Pannel_Leds(POWER_ON_OFF,MODE_OFF); if(jog.color == colorOFF) Pannel_Leds(THREAD_JOGGING,MODE_OFF); if(load.color == colorOFF) Pannel_Leds(THREAD_JOGGING,MODE_OFF); if(cart1.color == colorOFF) Pannel_Leds(CART_1,MODE_OFF); if(cart2.color == colorOFF) Pannel_Leds(CART_2,MODE_OFF); if(cart3.color == colorOFF) Pannel_Leds(CART_3,MODE_OFF); if(power.color == colorON) Pannel_Leds(POWER_ON_OFF,MODE_ON); if(jog.color == colorON) Pannel_Leds(THREAD_JOGGING,MODE_ON); if(load.color == colorON) Pannel_Leds(THREAD_JOGGING,MODE_ON); if(cart1.color == colorON) Pannel_Leds(CART_1,MODE_ON); if(cart2.color == colorON) Pannel_Leds(CART_2,MODE_ON); if(cart3.color == colorON) Pannel_Leds(CART_3,MODE_ON); if(power.color == fastBILNK) Pannel_Leds(POWER_ON_OFF,MODE_OFF); else if((power.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(POWER_ON_OFF,MODE_OFF); } /////////////////////////////////////////////////////////// if(jog.color == fastBILNK) Pannel_Leds(THREAD_JOGGING,MODE_OFF); else if((jog.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(THREAD_JOGGING,MODE_OFF); } /////////////////////////////////////////////////////////// if(load.color == fastBILNK) Pannel_Leds(THREAD_LOAD,MODE_OFF); else if((load.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(THREAD_LOAD,MODE_OFF); } /////////////////////////////////////////////////////////// if(cart1.color == fastBILNK) Pannel_Leds(CART_1,MODE_OFF); else if((cart1.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(CART_1,MODE_OFF); } /////////////////////////////////////////////////////////// if(cart2.color == fastBILNK) Pannel_Leds(CART_2,MODE_OFF); else if((cart2.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(CART_2,MODE_OFF); } /////////////////////////////////////////////////////////// if(cart3.color == fastBILNK) Pannel_Leds(CART_3,MODE_OFF); else if((cart3.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(CART_3,MODE_OFF); } flag = false; } else { COMM_RED_LED_OFF; if (HeaterActive > 0)// Blink the led on heating ACTIVITY_RED_LED_ON;// Heaters indication - at least one of the Heaters is ON ACTIVITY_GREEN_LED_OFF; if(power.color == fastBILNK) Pannel_Leds(POWER_ON_OFF,MODE_ON); else if((power.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(POWER_ON_OFF,MODE_ON); } /////////////////////////////////////////////////////////// if(jog.color == fastBILNK) Pannel_Leds(THREAD_JOGGING,MODE_ON); else if((jog.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(THREAD_JOGGING,MODE_ON); } /////////////////////////////////////////////////////////// if(load.color == fastBILNK) Pannel_Leds(THREAD_LOAD,MODE_ON); else if((load.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(THREAD_LOAD,MODE_ON); } /////////////////////////////////////////////////////////// if(cart1.color == fastBILNK) Pannel_Leds(CART_1,MODE_ON); else if((cart1.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(CART_1,MODE_ON); } /////////////////////////////////////////////////////////// if(cart2.color == fastBILNK) Pannel_Leds(CART_2,MODE_ON); else if((cart2.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(CART_2,MODE_ON); } /////////////////////////////////////////////////////////// if(cart3.color == fastBILNK) Pannel_Leds(CART_3,MODE_ON); else if((cart3.color == BLINK) && (counter % Blink_Freq == 0) ) { Pannel_Leds(CART_3,MODE_ON); } flag = true; } if (counter < 0xFF) counter++; else counter = Blink_Freq + 1; return OK; } uint32_t ControlEmptyCBFunction(uint32_t IfIndex, uint32_t ReadValue) { return OK; } void ControlStart(void) { if (ControlRestart == false) { ControlRestart = true; ROM_TimerLoadSet(Control_timerBase, TIMER_A,120000+(ControlPhaseDelay*120)/*one millisecond*/); TimerEnable(Control_timerBase, TIMER_A); ADCAcquireStart(0,1); AddControlCallback("ControlActivityLed", ControlEmptyCBFunction, eHundredMillisecond, ControlActivityLed,0, 0, 0 ); SysCtlDelay(12000000); MillisecStart(); } } /************************************************************************************************************************************************ * the control task reads the data from the devices every millisecond. * (the data might be old data, if it is polled in a slower rate) * for every polled device, there is a need to add a Data read callback, with a device id. the read value is always 32bits unsigned integer * if there is a need to run a control function based on the read data, then the hardware module will add a control function, specifying the control calling rate * both these callbacks can be removed. if a new call is arriving, it invalidates the previous one (no dual control or data) * ***************************************************************************************************************************************************/ uint32_t AddControlCallback(char* Name, ControlCBFunction Callback, uint32_t CtrlFrequency, DataReadCBFunction DriverfPtr, uint16_t IfIndex, uint32_t Parameter1, uint32_t Parameter2 ) { assert(Callback); assert(DriverfPtr); unsigned int key; uint32_t device_i; uint32_t deviceId = 0xFF; if (CtrlFrequency == eOneMillisecond) { for(device_i = 0;device_i < MAX_TANGO_CONTROL_DEVICES;device_i++) { if (ControlArray[device_i].ControlActive == false) { deviceId = device_i; break; } } if (MaxHighDevices == 0xFF) MaxHighDevices = deviceId; else { if ((deviceId!=0xFF )&&(deviceId> MaxHighDevices)) MaxHighDevices = deviceId; } } else { for(device_i = MAX_TANGO_CONTROL_DEVICES-1;device_i > 0;device_i--) { if (ControlArray[device_i].ControlActive == false) { deviceId = device_i; break; } } } if (deviceId == 0xFF) { LOG_ERROR(deviceId, "Add Callback failed"); AlarmHandlingSetAlarm(EVENT_TYPE__FPGA_WATCHDOG_ACTIVATED,true); return 0xFF; } key = GateMutex_enter(gateControlDB); ControlArray[deviceId].ControlTiming = CtrlFrequency; ControlArray[deviceId].ControlCallbackPtr = Callback; ControlArray[deviceId].ControlActive = true; ControlArray[deviceId].ControlDataReadPtr = DriverfPtr; ControlArray[deviceId].Parameter1 = Parameter1; ControlArray[deviceId].IfIndex = IfIndex; ControlArray[deviceId].StartTick = millisecondCounter; ControlArray[deviceId].Name = Name; GateMutex_leave(gateControlDB, key); //LOG_ERROR(deviceId, "Add Callback"); return deviceId; } int SafeRemoveHighControlCallback(uint32_t deviceId , ControlCBFunction Callback) { if (RemoveControlCallback(deviceId, Callback )!=OK) { Report("Fixing Remove control ",__FILE__,__LINE__,(int)GetControlDevice_i(),RpWarning,(int)deviceId,0); if (RemoveControlCallback(GetControlDevice_i(),Callback)==OK) { Report("Remove control callback fixed",ControlArray[GetControlDevice_i()].Name,__LINE__,(int)GetControlDevice_i(),RpWarning,(int)deviceId,0); } else { Report("Remove control callback failed",ControlArray[GetControlDevice_i()].Name,__LINE__,(int)GetControlDevice_i(),RpWarning,(int)deviceId,0); return ERROR; } } return OK; } int SafeRemoveControlCallback(uint32_t deviceId , ControlCBFunction Callback) { if (RemoveControlCallback(deviceId, Callback )!=OK) { Report("Fixing Remove control ",__FILE__,__LINE__,(int)GetControlLowDevice_i(),RpWarning,(int)deviceId,0); if (RemoveControlCallback(GetControlLowDevice_i(),Callback)==OK) { Report("Remove control callback fixed",ControlArray[GetControlDevice_i()].Name,__LINE__,(int)GetControlLowDevice_i(),RpWarning,(int)deviceId,0); } else { Report("Remove control callback failed",ControlArray[GetControlDevice_i()].Name,__LINE__,(int)GetControlLowDevice_i(),RpWarning,(int)deviceId,0); return ERROR; } } return OK; } int RemoveControlCallback(uint32_t deviceId , ControlCBFunction Callback) { if (deviceId == 0xFF) return ERROR; assert(deviceId < MAX_TANGO_CONTROL_DEVICES); unsigned int key; if (Callback == ControlArray[deviceId].ControlCallbackPtr) { key = GateMutex_enter(gateControlDB); ControlArray[deviceId].ControlTiming = eNoControl; ControlArray[deviceId].ControlCallbackPtr = NULL; ControlArray[deviceId].ControlDataReadPtr = NULL; ControlArray[deviceId].ControlActive = false; ControlArray[deviceId].Parameter1 = 0; ControlArray[deviceId].IfIndex = 0; //LOG_ERROR(deviceId, "Remove Callback "); GateMutex_leave(gateControlDB, key); return OK; } else { LOG_ERROR(deviceId, "Remove Callback failed"); return ERROR; } } uint32_t millisecondCounter = 0; void OneMilliSecondControlInterrupt(UArg arg0) { ControlMessageStruc ControlMessage; //uint32_t TenmillisecondCounter = 0; ROM_IntMasterDisable(); ROM_TimerIntClear(Control_timerBase, TIMER_TIMA_TIMEOUT); // Clear the timer interrupt if (ControlRestart == true) { ROM_TimerLoadSet(Control_timerBase, TIMER_A,120000/*one millisecond*/); } else { ROM_IntDisable(INT_TIMER0A); ROM_IntMasterEnable(); //ROM_TimerDisable(Control_timerBase, TIMER_A); return; } //send message to the control task ControlMessage.messageId = OneMillisec; ControlMessage.tick = millisecondCounter++; ControlMessage.msglen = sizeof(ControlMessageStruc); if (ControlMsgQ != NULL) Mailbox_post(ControlMsgQ , &ControlMessage, BIOS_NO_WAIT); //if (TenmillisecondCounter) { //ControlMessage.tick = TenmillisecondCounter; if (TenControlMsgQ != NULL) Mailbox_post(TenControlMsgQ , &ControlMessage, BIOS_NO_WAIT); } if (millisecondCounter == 1000000000) millisecondCounter = 0; // // Enable all interrupts. // ROM_IntMasterEnable(); return ; } uint32_t ControlDevice_i; uint32_t ControlLowDevice_i; uint32_t GetControlDevice_i(void) { return ControlDevice_i; } uint32_t GetControlLowDevice_i(void) { return ControlLowDevice_i; } ControlCBFunction GetControlCallbackFuncPtr(uint32_t ControlId) { if (ControlArray[ControlId].ControlActive) return ControlArray[ControlId].ControlCallbackPtr; else return NULL; } uint32_t ControlLoop(uint32_t tick) { if (MaxHighDevices == 0xFF) return OK; for (ControlDevice_i = 0; ControlDevice_i <= MaxHighDevices;ControlDevice_i++) //for (ControlDevice_i = 0; ControlDevice_i < MAX_TANGO_CONTROL_DEVICES;ControlDevice_i++) { if (ControlArray[ControlDevice_i].ControlActive) { ControlBacklog[backlogindex]=ControlDevice_i; if ( ++backlogindex >= MAX_BACKLOG_SIZE) backlogindex = 0; switch (ControlArray[ControlDevice_i].ControlTiming) { case eOneMillisecond: if(ControlArray[ControlDevice_i].ControlDataReadPtr) ControlDatalog[ControlDevice_i] = ControlArray[ControlDevice_i].ControlDataReadPtr( ControlArray[ControlDevice_i].Parameter1); else LOG_ERROR (ControlDevice_i, "Invalid callback ptr"); if(ControlArray[ControlDevice_i].ControlCallbackPtr) ControlArray[ControlDevice_i].ControlCallbackPtr(ControlArray[ControlDevice_i].IfIndex, ControlDatalog[ControlDevice_i]); else LOG_ERROR (ControlDevice_i, "Invalid callback ptr"); break; default: break; } //switch } //if control active } //for //ROM_IntMasterEnable(); return OK; } uint32_t prevtick = 0; #ifdef CONTROL_DEBUG void ResetControlTime(void) { memset(ControlTime,0,sizeof(ControlTime)); } #endif uint32_t ControlLowLoop(uint32_t tick) { uint32_t skipped_ticks = 0; #ifdef CONTROL_DEBUG uint32_t tempp,tempq,delta; uint32_t sys_ticks_start = msec_millisecondCounter,sys_ticks_end,max = 0,dev = 0; #endif if (tick-prevtick>1) { skipped_ticks = tick-prevtick-1; //if (tick-prevtick>10) // Report("ControlLowLoop skipped",__FILE__,tick,(int)prevtick,RpWarning,(int)skipped_ticks,0); } prevtick = tick; for (ControlLowDevice_i = 0; ControlLowDevice_i < MAX_TANGO_CONTROL_DEVICES;ControlLowDevice_i++) { if (ControlArray[ControlLowDevice_i].ControlActive) { if (tick - ControlArray[ControlLowDevice_i].StartTick<=skipped_ticks) continue; if (tick - ControlArray[ControlLowDevice_i].LastCalled<=skipped_ticks) continue; if (ControlArray[ControlLowDevice_i].ControlTiming == eOneMillisecond) continue; if (ControlArray[ControlLowDevice_i].StartTick == tick) continue; ControlArray[ControlLowDevice_i].LastCalled = tick; if (((tick - ControlArray[ControlLowDevice_i].StartTick)%ControlArray[ControlLowDevice_i].ControlTiming)<=skipped_ticks) // run the control on exact intervals { ControlBacklog[backlogindex]=ControlLowDevice_i; if ( ++backlogindex >= MAX_BACKLOG_SIZE) backlogindex = 0; #ifdef CONTROL_DEBUG tempp = HibernateRTCSSGet(); #endif if(ControlArray[ControlLowDevice_i].ControlDataReadPtr) ControlDatalog[ControlLowDevice_i] = ControlArray[ControlLowDevice_i].ControlDataReadPtr( ControlArray[ControlLowDevice_i].Parameter1); else LOG_ERROR (ControlLowDevice_i, "Invalid callback ptr"); if(ControlArray[ControlLowDevice_i].ControlCallbackPtr) ControlArray[ControlLowDevice_i].ControlCallbackPtr(ControlArray[ControlLowDevice_i].IfIndex, ControlDatalog[ControlLowDevice_i]); else LOG_ERROR (ControlLowDevice_i, "Invalid callback ptr"); #ifdef CONTROL_DEBUG tempq = HibernateRTCSSGet(); if (tempq < tempp) { delta = (32768 - tempp) + tempq + 1; } else delta = tempq - tempp; if (ControlTime[ControlLowDevice_i] DURATION_LIMIT) { Report("ControlLowLoop long",__FILE__,sys_ticks_end-sys_ticks_start,(int)sys_ticks_end,RpWarning,(int)sys_ticks_start,0); for (ControlLowDevice_i = 0; ControlLowDevice_i < MAX_TANGO_CONTROL_DEVICES;ControlLowDevice_i++) { if (ControlTime[ControlLowDevice_i]>max) { max = ControlTime[ControlLowDevice_i]; dev = ControlLowDevice_i; } } //Report(ControlArray[dev].Name,__FILE__,__LINE__,dev,RpWarning,max,0); } #endif //ROM_IntMasterEnable(); return OK; } /****************************************************************************** * ======== messageTsk ======== * Task for this function is created statically. See the project's .cfg file. * this message task is created statically in system initialization, ******************************************************************************/ void controlTask(UArg arg0, UArg arg1) { ControlMessageStruc ControlMessage; //char str[60]; //uint16_t length; //Clock_setTimeout(HostKAClock, 1000); //Clock_start(HostKAClock); Control_Task_Handle = Task_self(); while(1) { Mailbox_pend(ControlMsgQ , &ControlMessage, BIOS_WAIT_FOREVER); switch (ControlMessage.messageId) { case OneMillisec: ControlLoop(ControlMessage.tick); break; default: break; } } } /****************************************************************************** * ======== messageTsk ======== * Task for this function is created statically. See the project's .cfg file. * this message task is created statically in system initialization, ******************************************************************************/ void controlLowTask(UArg arg0, UArg arg1) { ControlMessageStruc ControlLowMessage; //char str[60]; //uint16_t length; //Clock_setTimeout(HostKAClock, 1000); //Clock_start(HostKAClock); Control_Task_Handle = Task_self(); while(1) { Mailbox_pend(TenControlMsgQ , &ControlLowMessage, BIOS_WAIT_FOREVER); switch (ControlLowMessage.messageId) { case OneMillisec: ControlLowLoop(ControlLowMessage.tick); break; default: break; } } }