//#define OC_TEST //enables test harness for offensive coordinator /**************************************************************************** Module SMOffensiveCoordinator.c Description OffensiveCoordinator state machine and module. Handles all writes and reads to and from the OC. Write commands are on a timer, reads are interrupt-driven. Commands are stored in a queue, and can be added with a certain priority. ****************************************************************************/ /*----------------------------- Include Files -----------------------------*/ #include "GlobalHeader.h" #include "SMOffensiveCoordinator.h" /*----------------------------- Module Defines ----------------------------*/ #define PENDING_PAUSE 37500 //100ms at 375kHz #define INPROGRESS_PAUSE 37500//100ms at 375kHz //Queue properties #define OC_QUEUE_LENGTH 4 // 32 bits/COMMAND_LENGTH #define OC_COMMAND_LENGTH 8 //in bits #define OC_COMMAND_TIMEOUT 15 //repetitions /*---------------------------- Module Functions ---------------------------*/ static Event_t DuringReady(Event_t Event); static Event_t DuringInProgress(Event_t Event); static Event_t DuringPending(Event_t Event); /*---------------------------- Module Variables ---------------------------*/ // state and timer variables static OCState_t CurrentState; static unsigned char OC_TimerExpiredFlag = 0; static unsigned char OC_ReadProcessedFlag = 0; //used to mask timer expired //SPI events flags and events are set and handled internally. static unsigned char SPI_NewRead; static unsigned char SPI_NewReadFlag = 0; //is there a new Response? static unsigned char SPI_NewResponse; //Stored response //OC Response flag and event are set internally but are for external use via query functions static unsigned char OC_NewResponseFlag = 0; //have a new response for the main program static unsigned char OC_NewResponse; //OC Queue is used internally but its length can be queried static unsigned long OC_CommandQueue = 0; //8bits per item, 32 bits = 4 item queue static unsigned char OC_CommandQueueLast = 0; //Index of last entry in command queue //OC Last Command stores command corresponding to most recent result. static unsigned char OC_CurrentCommand; static unsigned char OC_LastCommand; static unsigned char OC_CommandCounter; //Counts repetitions for timeout /*------------------------------ Module Code ------------------------------*/ /**************************************************************************** Function QueryOCTimerFlag Parameters none Returns 1 if the OC timer has expired and a response has been received from the OC. 0 otherwise Description Returns whether the OC timer has expired, masked by whether we have recevied a response from the OC. ****************************************************************************/ unsigned char QueryOCTimerFlag (void) { unsigned char FlagReturn = 0; if (OC_ReadProcessedFlag == 1) //read processed used to mask timer expired { DisableInterrupts; FlagReturn = OC_TimerExpiredFlag; OC_TimerExpiredFlag = 0; EnableInterrupts; } return FlagReturn; } /**************************************************************************** Function QueryOCReadFlag Parameters none Returns 1 if there is a new read from the SPI, 0 otherwise Description Checks for a new read on the SPI port. If so, it stores the value. ****************************************************************************/ unsigned char QueryOCReadFlag (void) { unsigned char ReturnVal = 0; DisableInterrupts; if (SPI_NewReadFlag == 1) { ReturnVal = 1; SPI_NewReadFlag = 0; SPI_NewResponse = SPI_NewRead; } EnableInterrupts; return ReturnVal; } /**************************************************************************** Function QueryOCQueueLength Parameters none Returns unsigned char length of queue Description Returns length of queue ****************************************************************************/ unsigned char QueryOCQueueLength (void) { return OC_CommandQueueLast; } /**************************************************************************** Function QueryOCCommandCounter Parameters none Returns 1 if we have waited for a response to a command for more than the timeout limit. Description Checks timeout for waiting for a response for a single command. ****************************************************************************/ unsigned char QueryOCCommandCounter (void) { return (OC_CommandCounter > OC_COMMAND_TIMEOUT); } /**************************************************************************** Function QueryOCNewResponseFlag Parameters none Returns 1 if the field has returned a response, 0 otherwise Description Checks whether the game master has responded to a command ****************************************************************************/ unsigned char QueryOCNewResponseFlag(void) { unsigned char ReturnVal; ReturnVal = OC_NewResponseFlag; OC_NewResponseFlag = 0; return ReturnVal; } /**************************************************************************** Function QueryOCResponse Parameters none Returns unsigned char - the response from the field Description Returns what the most recent response from the field was. ****************************************************************************/ unsigned char QueryOCResponse (void) { return OC_NewResponse; } /**************************************************************************** Function QueryOCLastCommand Parameters none Returns unsigned char - the command corresponding to the new response Description Returns the command corresponding to the most recent response. ****************************************************************************/ unsigned char QueryOCLastCommand (void) { return OC_LastCommand; } /**************************************************************************** Function OC_AddCommand Parameters unsigned char Command - 8bit command to add, unsigned char Priority Returns 1 if command successfully added to queue, 0 otherwise Description Adds Command to the command queue. If added with priority > 0, the command is added to at worst that spot. If priority == 0, the command is added to the end of the queue, unless the queue is full in which case the command is NOT added, and the function returns 0 for failure. ****************************************************************************/ unsigned char OC_AddCommand(unsigned char Command, unsigned char Priority) { unsigned char Success = 0; //trim priority to the queue length Priority = (Priority > OC_CommandQueueLast ? OC_CommandQueueLast : Priority); if (Priority == 0) //if no priority, { if (OC_CommandQueueLast < OC_QUEUE_LENGTH) //if queue not full { OC_CommandQueue += ((long)(Command) << (unsigned char)(OC_COMMAND_LENGTH*OC_CommandQueueLast)); //shift queue and add command OC_CommandQueueLast += 1; //update queue length Success = 1; } } else //if priority >0 { unsigned long FirstPart; unsigned long MiddlePart; unsigned long LastPart; unsigned char InsertPoint = OC_COMMAND_LENGTH*(Priority - 1); //where to put command //construct parts of queue before and after command, then rejoin them FirstPart = ((OC_CommandQueue >> InsertPoint) << InsertPoint); LastPart = OC_CommandQueue - FirstPart; MiddlePart = ((long)(Command) << (unsigned char)(InsertPoint)); OC_CommandQueue = (FirstPart<<OC_COMMAND_LENGTH) + MiddlePart + LastPart; OC_CommandQueueLast += (OC_CommandQueueLast < OC_QUEUE_LENGTH ? 1 : 0); //update queue length Success = 1; } return Success; } /**************************************************************************** Function OC_QueryDispenser Parameters unsigned char Dispenser to query, unsigned char Priority of command Returns success of AddCommand call. Description Adds a query dispenser command with priority Priority ****************************************************************************/ unsigned char OC_QueryDispenser(unsigned char Dispenser, unsigned char Priority) { unsigned char Command = (OC_QueryCommandBase | (OC_CommandDispenserMask & Dispenser)); return (OC_AddCommand(Command, Priority)); } /**************************************************************************** Function OC_RequestBall Parameters unsigned char Dispenser to get ball from, unsigned char Priority of command Returns success of AddCommand call. Description Adds a request ball command with priority Priority ****************************************************************************/ unsigned char OC_RequestBall(unsigned char Dispenser, unsigned char Priority) { unsigned char Command = (OC_RequestCommandBase | (OC_CommandDispenserMask & Dispenser)); return (OC_AddCommand(Command, Priority)); } /**************************************************************************** Function OC_WriteCommand Parameters unsigned char Command - command to write Returns 1 if successful write, 0 otherwise Description Writes a command to the OC. ****************************************************************************/ static unsigned char OC_WriteCommand (unsigned char Command) { if (((SPISR & _S12_SPTEF ) == _S12_SPTEF)) //check to make sure ok to write { OC_ReadProcessedFlag = 0; SPIDR = Command; //write command //printf("write %#x\r\n",Command); return 1; } return 0; } /**************************************************************************** Function RunOffensiveCoordinatorSM Parameters Event_t Returns Event_t Description Runs Offensive coordinator state machine. checks for new commands and responses from OC and Game Master. Resends appropriate commands in case of 'pending' response. ****************************************************************************/ Event_t RunOffensiveCoordinatorSM(Event_t CurrentEvent) { unsigned char MakeTransition = FALSE;/* are we making a state transition? */ OCState_t NextState = CurrentState; switch ( CurrentState ) { case OC_Ready : CurrentEvent = DuringReady(CurrentEvent); //process any events if ( CurrentEvent != EV_NO_EVENT ) //If an event is active { switch (CurrentEvent) { unsigned char NewCommand; case EV_OC_NewCommand : //If we have a new command to send NewCommand = (OC_CommandQueue % (1 << OC_COMMAND_LENGTH)); //pull new command from queue if (OC_WriteCommand(NewCommand)) //Tries to write new command { OC_CurrentCommand = NewCommand; //Reorganize Queue OC_CommandQueue >>= OC_COMMAND_LENGTH; OC_CommandQueueLast -= 1; OC_CommandCounter = 0; NextState = OC_InProgress;//Decide what the next state will be MakeTransition = TRUE; //mark that we are taking a transition } break; } } break; case OC_InProgress : CurrentEvent = DuringInProgress(CurrentEvent); //process any events if ( CurrentEvent != EV_NO_EVENT ) //If an event is active { switch (CurrentEvent) { case EV_OC_NewRead: //if we have a new read OC_ReadProcessedFlag = 1; //ok to transition now break; case EV_OC_TimerExpired : //If timer expired case EV_OC_CounterExpired: //(or if the counter is up) switch (SPI_NewResponse) { default: NextState = OC_Pending; //switch to pending state if (OC_WriteCommand(OC_DefaultCommand) == 0) ;//write dummy byte. if 0 then error break; } MakeTransition = TRUE; //mark that we are taking a transition break; } } break; case OC_Pending : CurrentEvent = DuringPending(CurrentEvent); //process any events if ( CurrentEvent != EV_NO_EVENT ) //If an event is active { switch (CurrentEvent) { case EV_OC_NewRead: OC_ReadProcessedFlag = 1; //ok to transition now break; case EV_OC_TimerExpired : //If timer expired switch (SPI_NewResponse) { case OC_LastRequestPending: //if no response from field yet if (OC_WriteCommand(0x74) == 0) ; //query status of last request // if 0 then error OC_CommandCounter += 1; //add to command counter NextState = OC_InProgress; //transition back to inprogress break; case OC_DispenserQueryPending0: //disp0 -- if we are querying a dispenser case OC_DispenserQueryPending1: //disp1 case OC_DispenserQueryPending2: //disp2 case OC_DispenserQueryPending3: //disp3 if (OC_WriteCommand(OC_CurrentCommand) == 0) ; //retry last request // if 0 then error OC_CommandCounter += 1; //add to command counter NextState = OC_InProgress; //transition back to inprogress break; default: //other cases OC_NewResponseFlag = 1; //indicate that we have a response from the field OC_NewResponse = SPI_NewResponse; OC_LastCommand = OC_CurrentCommand; NextState = OC_Ready; break; } MakeTransition = TRUE; //mark that we are taking a transition break; case EV_OC_CounterExpired : //if timer expired, counter limit exceeded OC_CommandCounter = 0; printf("timed out\r\n"); switch (SPI_NewResponse) { default: OC_NewResponseFlag = 1; //indicate that we have a response from the field OC_NewResponse = SPI_NewResponse; OC_LastCommand = OC_CurrentCommand; NextState = OC_Ready; break; } MakeTransition = TRUE; //mark that we are taking a transition break; } } break; } // If we are making a state transition if (MakeTransition == TRUE) { // Execute exit function for current state (void)RunOffensiveCoordinatorSM(EV_EXIT); CurrentState = NextState; //Modify state variable // Execute entry function for new state (void)RunOffensiveCoordinatorSM(EV_ENTRY); } return(CurrentEvent); } /**************************************************************************** Function StartOffensiveCoordinatorSM Parameters Event_t Returns none Description Starts OC state machine, initializes SPI and OutputCompare for timer. Output compare shares timer with the game timer. the prescales must match. ****************************************************************************/ void StartOffensiveCoordinatorSM ( Event_t CurrentEvent ) { CurrentState = OC_Ready; //SPI Init //Baud Rate SPIBR|=_S12_SPR1; //Divide by 8 pre-scale SPIBR|=(_S12_SPR2 | _S12_SPR0 | _S12_SPPR0 | _S12_SPPR1 | _S12_SPPR2); SPICR1|=(_S12_CPOL | _S12_CPHA); //Type 3 SPICR1|=_S12_MSTR; //Sets E128 as master SPICR1|=_S12_SPIE; //Receive interrupt enable SPICR1|=_S12_SPE; //Enable SPI SPICR1|=_S12_SSOE; SPICR2|=_S12_MODFEN; //shares timer with Game Timer system. Make sure timers match. //OC Output Compare Init TIM2_TSCR1 |= _S12_TEN; TIM2_TSCR2 |= (_S12_PR2 | _S12_PR1); //Prescale of 64. 375 kHz TIM2_TCTL1 &= ~(_S12_OL5 | _S12_OM5);// no output change TIM2_TIOS |= (_S12_IOS5); // output compare //start with OC disabled. enable when sending command TIM2_TFLG1 = (_S12_C5F); // call the entry function (if any) for the ENTRY_STATE (void)RunOffensiveCoordinatorSM(EV_ENTRY); } /**************************************************************************** Function QueryOffensiveCoordinatorStateSM Parameters none Returns OCState_t current machine state Description returns state of state machine ****************************************************************************/ OCState_t QueryOffensiveCoordinatorSM ( void ) { return(CurrentState); } /*************************************************************************** private functions ***************************************************************************/ /**************************************************************************** Function DuringReady Parameters Event_t Returns Event_t Description just wait. ****************************************************************************/ static Event_t DuringReady( Event_t Event) { // process EV_ENTRY & EV_EXIT events if ( Event == EV_ENTRY) { }else if ( Event == EV_EXIT) { }else // do the 'during' function for this state { } return(Event); } /**************************************************************************** Function DuringInProgress Parameters Event_t Returns Event_t Description On entry, set and enable timer. on exit, disable timer. ****************************************************************************/ static Event_t DuringInProgress(Event_t Event) { // process EV_ENTRY & EV_EXIT events if ( Event == EV_ENTRY) { TIM2_TC5 = TIM2_TCNT + INPROGRESS_PAUSE; //set timer TIM2_TIE |= _S12_C5I; //Interrupt enable }else if ( Event == EV_EXIT) { TIM2_TIE &= ~_S12_C5I; //Disable Interrupt }else { } return(Event); } /**************************************************************************** Function DuringPending Parameters Event_t Returns Event_t Description On entry, set and enable timer. on exit, disable timer. ****************************************************************************/ static Event_t DuringPending( Event_t Event) { // process EV_ENTRY & EV_EXIT events if ( Event == EV_ENTRY) { TIM2_TC5 = TIM2_TCNT + PENDING_PAUSE; //set timer TIM2_TIE |= _S12_C5I; //Interrupt enable }else if ( Event == EV_EXIT) { TIM2_TIE &= ~_S12_C5I; //Disable Interrupt }else // do the 'during' function for this state { } return(Event); } /**************************************************************************** Interrupt OC_Timer Description Sets flag when timer has expired for waiting between commands. ****************************************************************************/ void interrupt _Vec_tim2ch5 OC_Timer (void) { OC_TimerExpiredFlag = 1; TIM2_TFLG1 = _S12_C5F; } /**************************************************************************** Interrupt SPI_Interrupts Description If interrupt was due to read event, stores new read and sets flag. Interrupt does not trigger when write command complete. ****************************************************************************/ void interrupt _Vec_spi SPI_Interrupts(void) { if ((SPISR & _S12_SPIF ) == _S12_SPIF) { SPI_NewRead = SPIDR; SPI_NewReadFlag = 1; } } /**************************************************************************** Test Harness for OC module Description allows separate testing of OC by adding commands through keyboard input. ****************************************************************************/ #ifdef OC_TEST #include "ChkEvents.h" void main(void) { StartOffensiveCoordinatorSM(EV_ENTRY); TMRS12_Init(TMRS12_RATE_1MS); EnableInterrupts; printf("Init done\r\n"); while(1) { static unsigned int FirstLoop = 1; static unsigned int LastTime = 10000; Event_t CurrentEvent = CheckEvents(); if (CurrentEvent != EV_NO_EVENT) // printf("Event: %d\r\n", CurrentEvent); (void)RunOffensiveCoordinatorSM(CurrentEvent); if ( kbhit() != 0) // there was a key pressed { unsigned char KeyStroke = getchar(); switch ((KeyStroke)) { case 'q' : OC_AddCommand(0x78, 0); break; //game status case 'w' : OC_AddCommand(0b01110001, 0); break; //how many balls disp 1 case 'e' : OC_AddCommand(0b01000001, 0); break; //dispense ball disp 1 case 'r' : OC_AddCommand(0x4F, 0); break; //3pt size case 't' : OC_AddCommand(0x74, 0); break; //status of last request case 'a' : OC_QueryDispenser(0, 0); break; case 's' : OC_QueryDispenser(1, 2); break; case 'd' : OC_QueryDispenser(2, 0); break; case 'f' : OC_QueryDispenser(3, 2); break; case 'z' : OC_RequestBall(0, 0); break; case 'x' : OC_RequestBall(1, 0); break; case 'c' : OC_RequestBall(2, 0); break; case 'v' : OC_RequestBall(3, 0); break; } } if (CurrentEvent == EV_OC_NewResponse) { unsigned char LastCommand; unsigned char NewResponse; LastCommand = QueryOCLastCommand(); NewResponse = QueryOCResponse(); printf("Command 0x%x Response 0x%x \r\n",LastCommand, NewResponse); } } } #endif