/*********************************************************************** * * Copyright © Gaetano Borriello 1998 -- All Rights Reserved * * CSE477 - Digital Systems Design - Winter 1998 * Laboratory Assignment #3 * * DESCRIPTION: Application for controlling the speed of two DC motors * connected to a 68HC11 EVB board including a constraint * to lock the motors at the same speed. * * REVISION HISTORY: * 01/29/98 Gaetano Borriello * **********************************************************************/ #include // all the system toolbox headers #include // serial port functions #include "CSE477Lab3.rsr.h" // application resource defines /*********************************************************************** * Defines for this module **********************************************************************/ #define FALSE 0 #define TRUE 1 #define BaudRate 9600 #define SerRcvQueueSize 100 #define FramingByte 'Z' #define NoLock 0 #define LockLeft 1 #define LockRight 2 /*********************************************************************** * Global variables for this module **********************************************************************/ static MenuBarPtr currentMenu = NULL; // ptr to current menu static Word currentView; static Char serRcvQueue[SerRcvQueueSize]; //serial receive queue static UInt serRefNum; static Boolean serLineMode; static Boolean serLineStatus; static Boolean gotFramingByte; static Long numBytesRcvd; /*********************************************************************** * Prototypes for internal functions **********************************************************************/ static void StartApplication(void); static void StopApplication(void); static void SetCurrentMenu(Word rscID); static Boolean MainFormHandleEvent(EventPtr event); static FieldPtr GetFocusObjectPtr (void); static void EditDoMenuCommand (Word command); static void EventLoop(void); DWord PilotMain (Word cmd, Ptr cmdPtr, Word launchFlags); static Err SendDesiredValues(void); static void Update(void); static void SerialInit(void); /*********************************************************************** * FUNCTION: StartApplication * DESCRIPTION: This routine sets up the initial state of the application. * PARAMETERS: None. * RETURNED: Nothing. ***********************************************************************/ static void StartApplication(void) { FormPtr frm; // initialize and draw main CSE477Lab3 form frm = FrmInitForm(CSE477Lab3Form); FrmSetActiveForm(frm); FrmDrawForm(frm); // set the current menu to be the CSE477Lab3 menu bar SetCurrentMenu(CSE477Lab3MenuBar); // initialize the serial port SerialInit(); } /*********************************************************************** * FUNCTION: StopApplication * DESCRIPTION: This routine cleans up upon exit * PARAMETERS: None. * RETURNED: Nothing. ***********************************************************************/ static void StopApplication(void) { // close the serial connection SerClose(serRefNum); // close all CSE477Lab3 forms that may be open (main and info) FrmCloseAllForms(); } /*********************************************************************** * FUNCTION: SetCurrentMenu * DESCRIPTION: This routine loads the specified menu resource and makes * it the current menu. * PARAMETERS: rscID - resource id of the new menu * RETURNED: nothing ***********************************************************************/ static void SetCurrentMenu(Word rscID) { // dispose of any existing current menu. if (currentMenu) MenuDispose(currentMenu); // set the current menu and remember it. currentMenu = MenuInit(rscID); } /*********************************************************************** * FUNCTION: MainFormHandleEvent * DESCRIPTION: Handles processing of events for the ̉mainÓ form. * PARAMETERS: event - the most recent event. * RETURNED: True if the event is handled, false otherwise. ***********************************************************************/ static Boolean MainFormHandleEvent(EventPtr event) { Boolean handled = false; FormPtr frm; ControlPtr cbx; // get the current form so that it can be redrawn later frm = FrmGetActiveForm(); // handle an event switch (event->eType) { // handle events from check boxes case ctlSelectEvent: // lock the desired speeds for both motors to that for the left motor if (event->data.ctlSelect.controlID == CSE477Lab3LeftLockCheckbox) { // if we are checking the box then we need to verify that we // aren't locked to the other motor if (CtlGetValue(event->data.ctlSelect.pControl) == TRUE) { // get a control pointer to the right lock check box cbx = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, CSE477Lab3RightLockCheckbox)); // uncheck the box if the right one is already checked if (CtlGetValue(cbx) == TRUE) CtlSetValue(event->data.ctlSelect.pControl, FALSE); } } // lock the desired speeds for both motors to that for the right motor if (event->data.ctlSelect.controlID == CSE477Lab3RightLockCheckbox) { // if we are checking the box then we need to verify that we // aren't locked to the other motor if (CtlGetValue(event->data.ctlSelect.pControl) == TRUE) { // get a control pointer to the left lock check box cbx = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, CSE477Lab3LeftLockCheckbox)); // uncheck the box if the left one is already checked if (CtlGetValue(cbx) == TRUE) CtlSetValue(event->data.ctlSelect.pControl, FALSE); } } // record that the event was handled handled = true; break; // handle event from menus case menuEvent: // erase the current menu from the screen MenuEraseStatus(currentMenu); // handle the menu event EditDoMenuCommand(event->data.menu.itemID); // record that the event was handled handled = true; break; } return handled; } /*********************************************************************** * FUNCTION: GetFocusObjectPtr * DESCRIPTION: This routine returns a pointer to the field object, in * the current form, that has the focus. * PARAMETERS: nothing * RETURNED: pointer to a field object or NULL if there is no focus ***********************************************************************/ static FieldPtr GetFocusObjectPtr (void) { FormPtr frm; Word focus; // get a pointer to the form and its object with the current focus frm = FrmGetActiveForm(); focus = FrmGetFocus(frm); // return the pointer if (focus == noFocus) return (NULL); else return (FrmGetObjectPtr (frm, focus)); } /*********************************************************************** * FUNCTION: EditDoMenuCommand * DESCRIPTION: This routine performs the menu command specified. * PARAMETERS: command - menu item id * RETURNED: nothing ***********************************************************************/ static void EditDoMenuCommand (Word command) { FormPtr frm; FieldPtr fld; switch (command) { // Edit menu items case EditCut: fld = GetFocusObjectPtr(); if (fld) FldCut(fld); break; case EditCopy: fld = GetFocusObjectPtr(); if (fld) FldCopy(fld); break; case EditPaste: fld = GetFocusObjectPtr(); if (fld) FldPaste(fld); break; case EditUndo: fld = GetFocusObjectPtr(); if (fld) FldUndo(fld); break; case EditSelectAll: fld = GetFocusObjectPtr(); if (fld) FldSetSelection(fld, 0, FldGetTextLength(fld)); break; case EditKeyboard: SysKeyboardDialog(kbdAlpha); break; case EditGraffiti: SysGraffitiReferenceDialog(referenceDefault); break; // Info menu items case InfoGetInfo: // get the pointer to the info form frm = FrmInitForm(CSE477Lab3InfoForm); // bring it up as a dialog FrmDoDialog(frm); // delete it after the dialog completes FrmDeleteForm(frm); break; } } /*********************************************************************** * FUNCTION: EventLoop * DESCRIPTION: A simple loop that obtains events from the Event * Manager and passes them on to various applications and * system event handlers before passing them on to * FrmHandleEvent for default processing. * PARAMETERS: None. * RETURNED: Nothing. ***********************************************************************/ static void EventLoop(void) { EventType event; Word error; // process events (except for the application stop event) do { // Get the next available event. EvtGetEvent(&event, evtWaitForever); // Give the system a chance to handle the event. if (! SysHandleEvent(&event)) // Give the menu bar a chance to update and handle the event. if (! MenuHandleEvent(currentMenu, &event, &error)) // Give the application a chance to handle the event. if (! MainFormHandleEvent(&event)) // Let the form object provide default handling of the event. FrmHandleEvent(FrmGetActiveForm(), &event); // check if characters have arrived on the serial port Update(); } while (event.eType != appStopEvent); } /*********************************************************************** * FUNCTION: PilotMain * DESCRIPTION: This function is the equivalent of a main() function * under standard ̉CÓ. It is called by the Emulator to * begin execution of this application. * PARAMETERS: cmd - command specifying how to launch the application. * cmdPBP - parameter block for the command. * launchFlags - flags used to configure the launch. * RETURNED: Any applicable error code. ***********************************************************************/ DWord PilotMain(Word cmd, Ptr cmdPBP, Word launchFlags) { // Check for a normal launch. if (cmd == sysAppLaunchCmdNormalLaunch) { // Set up initial form. StartApplication(); // Start up the event loop. EventLoop(); // Clean up StopApplication(); } return 0; } /*********************************************************************** * FUNCTION: SendDesiredValues * DESCRIPTION: This routines sends a packet with the context of the * send buffer as payload. * PARAMETERS: nothing * RETURNED: an error if one occured ***********************************************************************/ static Err SendDesiredValues(void) { FormPtr frm; FieldPtr lfld, rfld; ControlPtr lcbx, rcbx; Err err = 0; Long ldesired, rdesired; Char strToSend[6]; // get the pointers to the form // and the receive buffer field on the form frm = FrmGetActiveForm(); lfld = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, CSE477Lab3LeftDesiredField)); rfld = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, CSE477Lab3RightDesiredField)); lcbx = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, CSE477Lab3LeftLockCheckbox)); rcbx = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, CSE477Lab3RightLockCheckbox)); // convert the desired values to integers (0 is the default if they are empty) if (lfld->text == NULL) ldesired = 0; else ldesired = StrAToI(lfld->text); if (rfld->text == NULL) rdesired = 0; else rdesired = StrAToI(rfld->text); // update the values if the motors are locked to each other if (CtlGetValue(lcbx) == TRUE) rdesired = ldesired; if (CtlGetValue(rcbx) == TRUE) ldesired = rdesired; // for the packet of bytes to send consisting of a framing byte followed // by four data bytes (2 bytes each for the desired values for each motor) strToSend[0] = FramingByte; // framing byte strToSend[1] = (Char) (ldesired >> 8); strToSend[2] = (Char) (ldesired); strToSend[3] = (Char) (rdesired >> 8); strToSend[4] = (Char) (rdesired); // WinDrawInvertedChars(strToSend, 5, 30, 50); // send the desired values over the serial port SerSend(serRefNum, strToSend, 5, &err); return(err); } /*********************************************************************** * FUNCTION: Update * DESCRIPTION: This routine gets bytes received on the serial port and * puts them in a buffer. When it finds a sequence of 5 bytes * starting with an "FF" byte, it uses the 2nd and 3rd bytes * as a new 16-bit value for the actual speed of the left * motor and the 4th and 5th bytes as a new 16-bit value for * the actual speed of the right motor. * PARAMETERS: None. * RETURNED: nothing ***********************************************************************/ static void Update(void) { FormPtr frm; FieldPtr lfld, rfld; Err err = 0; static Char bytesRcvd[SerRcvQueueSize]; ULong num = 0; UInt i; Boolean done = FALSE; Char lfldChars[6], rfldChars[6]; // get pointers to the form and the two fields for displaying actual speeds frm = FrmGetActiveForm(); lfld = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, CSE477Lab3LeftActualField)); rfld = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, CSE477Lab3RightActualField)); // receive as many bytes as come in until one is received // with all 1s and then 4 more have been received after that containing // the values of the left and right motor speeds (2 bytes each). // receive at least one byte and wait at most 0 seconds for it SerReceive(serRefNum, serRcvQueue, 1, 0, &err); if (err != serErrTimeOut) { // if it didn't time out, then a character was in the queue if (gotFramingByte == TRUE) { // insert the byte received into the receive buffer field bytesRcvd[numBytesRcvd] = serRcvQueue[0]; bytesRcvd[numBytesRcvd+1] = '\0'; // and increment the number of bytes received so far numBytesRcvd = numBytesRcvd + 1; // check to see if we now have 4 bytes since we saw the framing byte if (numBytesRcvd == 4) { // convert the first two bytes to the number to be displayed // for the actual value of the left motor speed StrIToA(lfldChars, (Long) ((bytesRcvd[0] << 8) + bytesRcvd[1]) ); // WinDrawInvertedChars(lfldChars, StrLen(lfldChars), 30, 140); FldDelete(lfld, 0, lfld->textLen); FldInsert(lfld, lfldChars, StrLen(lfldChars)); // convert the second two bytes to the number to be displayed // for the actual value of the right motor speed StrIToA(rfldChars, (Long) ((bytesRcvd[2] << 8) + bytesRcvd[3]) ); // WinDrawInvertedChars(rfldChars, StrLen(rfldChars), 110, 140); FldDelete(rfld, 0, rfld->textLen); FldInsert(rfld, rfldChars, StrLen(rfldChars)); // reset things for the next time numBytesRcvd = 0; gotFramingByte = FALSE; // send the desired speed values over the serial port SendDesiredValues(); } } else { // if the byte is the framing byte then set its flag to true // so that we'll interpret the next 4 bytes as the actual motor // speed values if (serRcvQueue[0] == FramingByte) { gotFramingByte = TRUE; } } } return; } /*********************************************************************** * FUNCTION: SerialInit * DESCRIPTION: This routine initializes the serial line. * PARAMETERS: none * RETURNED: nothing ***********************************************************************/ static void SerialInit(void) { int i; Err err = 0; SerSettingsType settings; UInt *MiscReg; gotFramingByte = FALSE; numBytesRcvd = 0; // clear the receive buffer for (i=0; i < SerRcvQueueSize; i++) serRcvQueue[i] = '\0'; // find the system serial library err = SysLibFind("Serial Library", &serRefNum); ErrFatalDisplayIf(err, "Can't find Serial Library!"); if (err) SerClose(serRefNum); // open the serial connection at the specified baud rate err = SerOpen(serRefNum, 0, BaudRate); ErrFatalDisplayIf(err, "Problem opening the serial port!"); if (err) SerClose(serRefNum); // get the settings right settings.baudRate = BaudRate; settings.ctsTimeout = 0; settings.flags = serSettingsFlagStopBits1 | serSettingsFlagBitsPerChar8; SerSetSettings(serRefNum, &settings); // put the pilot into the proper serial mode MiscReg = (UInt *)0xFFFFF908; *MiscReg = *MiscReg & 0xFFDF; // turn off bit 5 (IrDA DISABLE) *MiscReg = *MiscReg & 0xFF7F; // turn off bit 7 (RTS CONT) *MiscReg = *MiscReg & 0xFFBF; // turn off bit 6 (RTS) return; }