
Example software of using the mbed-rtos to control a simple vehicle's on board computer
Dependencies: MCP23017 WattBob_TextLCD mbed-rtos mbed
Revision 9:c236eaaacf08, committed 2016-03-30
- Comitter:
- sk398
- Date:
- Wed Mar 30 13:10:09 2016 +0000
- Parent:
- 8:6fad4bd89240
- Child:
- 10:95793013ef87
- Commit message:
- All Tasks operational. All Code Commented.; ; Final Submission Revision;
Changed in this revision
main.cpp | Show annotated file Show diff for this revision Revisions of this file |
--- a/main.cpp Wed Mar 30 09:47:45 2016 +0000 +++ b/main.cpp Wed Mar 30 13:10:09 2016 +0000 @@ -9,13 +9,23 @@ Date: March 2016 - Function: This is the main runner containing the Cyclic executive - There are 7 defined tasks and several Auxillary components - which are logically ran periodically at their required time - by a Cyclic Executive sequencer. - Ticks, or slots, to this Cyclic Executive are provided by - a ticker ever 25ms, and then using logical expressions, the - correct task is initiated and allocated the required time. + Function: This code operates to simulate the operation of a Car's + control system. It does so by taking external input for + elements of a Car; Light Indicators, Engine State and Pedal values + and using a simple Car Simulation model, derives a speed + from which several tasks are included to act upon. + An average speed is generated at a given frequency + and a total distance is estimated using this average speed. + Output comes in the form of LED's when specific indicators + are true, and on a LCD screen and Servo Wiper to indicate speed. + Furthermore, a PC Connection is established and a data dump of + current Accelerometer, Brake and Average speed is presented + to the user every 20 seconds. + + This system is constructed using the MBED-RTOS and + as such, each Task has an associated frequency, found in main, below. + Note that no priority is given to the tasks and no + extra scheduler controls the synchronisation of tasks, ##################################################################### */ @@ -85,6 +95,9 @@ // Raw Data Structure // ---------------------------------------------------------------------------- + +// CarRawData is a global memory area and contaims an instance of the Raw data +// populated by Task1 and can be accessed through locking Mutex - rawDataMutex typedef struct { bool EngineState; @@ -92,85 +105,111 @@ float RawBraking; } CarRawData; +// Create Mutex to control access to CarRawData instance Mutex rawDataMutex; + +// Create Instance of CarRawData CarRawData rawData; // Speed Data Structure // ---------------------------------------------------------------------------- + +// CarSpeedData is a global memory area and contains an instance of the calculated +// raw speed values and the index to the next available element in the array to be +// written to. rawSpeed is calculated by the CarSim and can be accessed through +// locking Mutex - SpeedMutex typedef struct { float rawSpeed[3]; int counter; } CarSpeedData; -float lastSpeed; +// Create Mutex to control access to CarSpeedData instance +Mutex SpeedMutex; -Mutex SpeedMutex; +// Create instance of CarSpeedData CarSpeedData speedData; // Filtered Data Structure // ---------------------------------------------------------------------------- -typedef struct -{ - float AverageSpeed; -} CarFilteredParams; -Mutex filteredParamsMutex; -CarFilteredParams filteredParams; +float AverageSpeed; +float totalDistance; -float totalDistance; +// Create Mutex to control access to FilteredData +Mutex filteredDataMutex; // Mail Queue Structure // ---------------------------------------------------------------------------- + +// PCDumpData is a global memory area which is populated by Taak7 and used as +// the structure within a MailQueue. Data contained is a copy from the current +// state of the Control System at a given instance of time. typedef struct { float currentAverageSpeed; float currentAccelerometer; float currentBraking; -} PCDump_t; +} PCDumpData; + +// Construct a 100 Element Mail Queue structure +Mail<PCDumpData,100> Memory_Dump; -Mail<PCDump_t,100> Memory_Dump; +// Define a Counter to trakc number of entries to Mail Queue +int MailQueueCounter; +// Create Mutex to control access to the MailQueueCounter variable Mutex MailMutex; -int MailQueueCounter; // ============================================================================ // Car Simulation // ============================================================================ +// The CarSimulator Task updates the rawSpeed parameter at a frequenct of 20Hz void CarSimulator(void const *arg) { float newSpeed; + // Load Shared resources into local variables within the Task + // Shared Resources are; Accelerometer value, Braking value and Engine State rawDataMutex.lock(); float currentAccelerometer = rawData.RawAccelerometer; float currentBrake = rawData.RawBraking; bool currentEngineState = rawData.EngineState; rawDataMutex.unlock(); + // Run simple model which estimates the speed, as a fraction of the MAX SPEED + // based on the percentage of either accelerator or brake pressed by the user + // Further, newSpeed is set to 0 if the currentEngineState is equal to 0 (Engine off) newSpeed = currentAccelerometer*MAX_SPEED*(1-currentBrake)*currentEngineState; + // Check Speed Counter's range, if out of bounds of array length, reset Counter + // to 0 + // Data contained within Shared Resource therefore the SpeedMutex is used to control access SpeedMutex.lock(); if(speedData.counter > 2) { speedData.counter = 0; } + // Output a rawSpeed value to the next available index of rawSpeed[] and increment Counter speedData.rawSpeed[speedData.counter] = newSpeed; + speedData.counter = speedData.counter++; SpeedMutex.unlock(); - - speedData.counter = speedData.counter++; } // ============================================================================ // Control System Tasks // ============================================================================ - +// Task1_ReadRawData gathers external inputs and updates the rawData structure +// It operates at a frequency of 10Hz void Task1_ReadRawData(void const *arg) { -// PCConn.printf("Task1\r\n"); + // Lock Shared Resource - rawData + // Update rawData elements directly from AnalogIn channel values + // Unlock Shares Resource rawDataMutex.lock(); rawData.RawBraking = Brake.read(); rawData.RawAccelerometer = Accelerometer.read(); @@ -178,15 +217,22 @@ } - +// Task2_ReadEngineState updates the rawData structure and operates ar a frequency of 2Hz void Task2_ReadEngineState(void const *arg) { -// PCConn.printf("Task2\r\n"); + // Get external input from DigitalIn Channel and store in local variable bool currentEngineState = EngineState.read(); + + // Lock Shared Resource - rawData + // Take a copy of the local variable currentEngineState and store into Global memory + // Unlock Shared Resource rawDataMutex.lock(); rawData.EngineState = currentEngineState; rawDataMutex.unlock(); + // Conduct logical check on local varialbe currentEngineState + // if currentEngineState is HIGH, set EngineStateInd to HIGH + // else set EngineStateInd LOW if(currentEngineState) { EngineStateInd = HIGH; @@ -198,45 +244,66 @@ } - +// Task3_CalcAvSpeed updates the AverageSpeed global varialbe and operates at a frequency of 5Hz void Task3_CalcAvSpeed(void const *arg) { -// PCConn.printf("Task3\r\n"); + // Initialise local variable as 0.0 float speedTotal = 0.0; + // Lock Shared Resource - speedData + // Calculate total from array of rawSpeed values and store locally + // Unlock Shared Resource + SpeedMutex.lock(); for(int num = 0; num < 3; num++) { speedTotal = speedTotal + speedData.rawSpeed[num]; -// PCConn.printf("Total: %f\r\n",speedTotal); } + SpeedMutex.unlock(); - filteredParamsMutex.lock(); - filteredParams.AverageSpeed = (speedTotal/3); -// PCConn.printf("Av: %f\r\n",filteredParams.AverageSpeed); - filteredParamsMutex.unlock(); + // Lock Shared Resource - AverageSpeed + // Calculate average from local variable speedTotal and store result Globally into AverageSpeed + // Unlock Shared Resource + filteredDataMutex.lock(); + AverageSpeed = (speedTotal/3); + filteredDataMutex.unlock(); } - +// Task4_UpdateRCWiper takes the AverageSpeed global variable at a given time and outputs +// a representation of this through a Servo. It operates at a frequenct of 1Hz void Task4_UpdateRCWiper(void const *arg) { -// PCConn.printf("Task4\r\n"); - filteredParamsMutex.lock(); - float currentAverageSpeed = filteredParams.AverageSpeed; - filteredParamsMutex.unlock(); + // Lock Shared Resource - AverageSpeed + // Take local copy of AverageSpeed + // Unlock Shared Resource + filteredDataMutex.lock(); + float currentAverageSpeed = AverageSpeed; + filteredDataMutex.unlock(); + // Update Servo Position based upon the local copy of AverageSpeed + + // Servo must be controlled in the range of 1000us to 2000us + // Thus a base value of 1000 is included, and as currentAverageSpeed cannot exceed 100, + // This is simply scaled by 10, to give a maximum of 2000, which allows Servo to operate + // Over full operational range AvSpeedWiper.pulsewidth_us(1000+(10*currentAverageSpeed)); } - +// Task5_OverspeedLED takes the 0th Element of the rawSpeed global variable and computes +// a logical assessment to detect when the speed is greater than a preset of 70mph and +// indicate results through an LED. It operates at a frequency of 0.5Hz void Task5_OverspeedLED(void const *arg) { -// PCConn.printf("Task5\r\n"); + // Lock Shared Resource - speedData + // Take local copy of rawSpeed[0] + // // Unlock Shares Resource SpeedMutex.lock(); float currentInstSpeed = speedData.rawSpeed[speedData.counter]; SpeedMutex.unlock(); + // Using local copy of rawSpeed[0], if this is above preset threshold of 70, + // OverSpeedInd is set HIGH, else it is set LOW if(currentInstSpeed > 70.0) { OverSpeedInd = HIGH; @@ -245,18 +312,34 @@ { OverSpeedInd = LOW; } - } +// Task6_UpdateOdometer takes the AverageSpeed global variable and calculates the +// distance travelled by the Car over a known time increment (the delta of time between this task being ran) +// Once calculated, the distance is stored globally and also, in conjunction with AverageSpeed, displayed onto +// a LCD screen. It operates at a frequency of 2Hz void Task6_UpdateOdometer(void const *arg) { - filteredParamsMutex.lock(); - float currentAverageSpeed = filteredParams.AverageSpeed; - filteredParamsMutex.unlock(); + // Lock Shared Varialbe - AverageSpeed + // Take local copy of AverageSpeed + // Unlock Shared Variable + filteredDataMutex.lock(); + float currentAverageSpeed = AverageSpeed; + filteredDataMutex.unlock(); + // Compute newTotalDistance from current TotalDistance and average speed + + // distance = oldDistance +(currentAverageSpeed*timeIncrement) + // Note that timeIncrement (0.5 second) is converted from seconds to hours, + // To remain consistant with mph units + + // NOTE + // totalDistance does not need to be protected by a Mutex as this is the only task which updates + // or uses the variable. It is global in order to keep track of a rolling total totalDistance = totalDistance + (currentAverageSpeed*(0.5/3600)); + // Output totalDistance and currentAverageSpeed to LCD lcd -> cls(); lcd -> locate(0,0); lcd -> printf("Dist: %8.2f",totalDistance); @@ -265,45 +348,70 @@ } +// Task7_SendToMailQueue takes global variables; AverageSpeed, RawAccelerometer and RawBraking and +// creates a structure from them, then puts an instanc of this structure onto a Mail Queue, incrementing +// a globally defined Counter as it does such. It operates with a frequency of 0.2Hz void Task7_SendToMailQueue(void const *arg) { - filteredParamsMutex.lock(); - float currentAverageSpeed = filteredParams.AverageSpeed; - filteredParamsMutex.unlock(); + // Lock Shared Resource - AverageSpeed + // Take local copy of AverageSpeed + // Unlock Shared Resource + filteredDataMutex.lock(); + float currentAverageSpeed = AverageSpeed; + filteredDataMutex.unlock(); + // Lock Shared Resource - rawData + // Take local copy of RawAccelerometer and RawBraking + // Unlock Shared Resource rawDataMutex.lock(); float currentAccelerometer = rawData.RawAccelerometer; float currentBrake = rawData.RawBraking; rawDataMutex.unlock(); - PCDump_t *currentPCDump = Memory_Dump.alloc(); + // Allocate Memory for instance of PCDumpData structure + PCDumpData *currentPCDump = Memory_Dump.alloc(); + + // Populate instance of PCDumpData with local copies of desired variables currentPCDump -> currentAverageSpeed = currentAverageSpeed; currentPCDump -> currentAccelerometer = currentAccelerometer; currentPCDump -> currentBraking = currentBrake; + // Push instance of PCDumpData onto Mail Queue Memory_Dump.put(currentPCDump); + // Lock Shared Resource - MailQueueCounter + // Increment MailQueueCounter + // Unlock Shared Resource MailMutex.lock(); MailQueueCounter++; MailMutex.unlock(); } - +// Task8_DumpSerial takes all of the instances of PCDumpData which have been pushed onto the Mail Queue +// removes them from the Mail Queue and dumps their values over a PC serial connection. Once complete, the +// counter which stores the number of elements in the Mail Queue is reset to 0. It operates at a frequency of 0.05Hz void Task8_DumpSerial(void const *arg) { + // Lock Shared Resource - MailQueueCounter + // Take local copy of MailQueueCounter + // Unlock Shared Resource MailMutex.lock(); int currentQueueCounter = MailQueueCounter; MailMutex.unlock(); + // Prompt State of Memory Dump PCConn.printf("Memory Dump\r\n"); + // For each instance of PCDumpData found in the Mail Queue, import the structure and store locally + // Then print the contained values and free the element of the Mail Queue + // Repeat for all indexes of the Mail Queue for(int num = 0; num < currentQueueCounter; num++) { osEvent evt = Memory_Dump.get(); if(evt.status == osEventMail) { - PCDump_t *currentPCDump = (PCDump_t*)evt.value.p; + PCDumpData *currentPCDump = (PCDumpData*)evt.value.p; PCConn.printf("Av Speed: %f\r\nAcceler: %f\r\nBrake: %f\r\n\r\n", currentPCDump -> currentAverageSpeed, currentPCDump -> currentAccelerometer, @@ -312,16 +420,20 @@ } } + // Lock Shared Resource - MailQueueCounter + // Reset MailQueueCounter + // Unlock Shared Resource MailMutex.lock(); MailQueueCounter = 0; MailMutex.unlock(); } - +// Task9_ReadSideLight takes external input from SideLightIndicator and conducts a logical test. +// It operates at a frequency of 1Hz void Task9_ReadSideLight(void const *arg) { -// PCConn.printf("Task9\r\n"); + // If SideLightIndicator is HIGH, set SideLightInd to HIGH, else set to LOW if(SideLightIndicator) { SideLightInd = HIGH; @@ -333,11 +445,12 @@ } - +// Task10_ReadIndicatorLights takes external input from LeftIndicator and RightIndicator and conducts a +// logical test. It operates at a frequency of 0.5Hz void Task10_ReadIndicatorLights(void const *arg) { -// PCConn.printf("Task10\r\n"); - // Left Indicator Only + + // If LeftIndicator Only is HIGH, flash LeftLightInd at a frequency of 1Hz, 50% Duty if(LeftIndicator && !RightIndicator) { LeftLightInd.period(1.0); @@ -347,7 +460,7 @@ RightLightInd.pulsewidth(0.0); } - // Right Indicator Only + // If RightIndicator Only is HIGH, flash RightLightInd at a frequency of 1Hz, 50% Duty else if(!LeftIndicator && RightIndicator) { LeftLightInd.period(1.0); @@ -357,7 +470,8 @@ RightLightInd.pulsewidth(0.5); } - // Left and Right Indicators + // If LeftIndicator and RightIndicator are HIGH, flash LeftLightInd and RightLightInd + // at a frequency of 2Hz, 50% Duty else if(LeftIndicator && RightIndicator) { LeftLightInd.period(0.5); @@ -366,7 +480,7 @@ LeftLightInd.pulsewidth(0.25); RightLightInd.pulsewidth(0.25); } - + // Else, turn off both LeftLightInd and RightLightInd else { LeftLightInd.period(1.0); @@ -380,43 +494,46 @@ void InitSystem() { + // Set AvSpeedWiper to 50Hz frequency, for Servo AvSpeedWiper.period_ms(20); - par_port->write_bit(1,BL_BIT); // turn LCD backlight ON + // Initiate LCD by lighting Backlight + par_port->write_bit(1,BL_BIT); + // Initiate all Global variables to 0 rawData.EngineState = 0; rawData.RawAccelerometer = 0.0; rawData.RawBraking = 0.0; - speedData.counter = 0; speedData.rawSpeed[0] = 0.0; speedData.rawSpeed[1] = 0.0; speedData.rawSpeed[2] = 0.0; - - filteredParams.AverageSpeed = 0.0; - + AverageSpeed = 0.0; totalDistance = 0.0; - MailQueueCounter = 0; } - - + // ============================================================================ // Entry Point Thread // ============================================================================ +// Entry Point Thread, this shall be the initialiser for the Car System and +// Contains all of the Tasks and their associated properties, such as frequency. int main() { + // Construct Objects for LCD par_port = new MCP23017(p9, p10, 0x40); // initialise 16-bit I/O chip lcd = new WattBob_TextLCD(par_port); // initialise 2*26 char display + // Set PC Connection Baud Rate PCConn.baud(115200); + // Initialise System, including Global Variables InitSystem(); +// Construct Tasks as RtosTimer objects RtosTimer CarSim(CarSimulator,osTimerPeriodic); - RtosTimer Task1(Task1_ReadRawData,osTimerPeriodic); RtosTimer Task2(Task2_ReadEngineState,osTimerPeriodic); RtosTimer Task3(Task3_CalcAvSpeed,osTimerPeriodic); @@ -428,6 +545,7 @@ RtosTimer Task9(Task9_ReadSideLight,osTimerPeriodic); RtosTimer Task10(Task10_ReadIndicatorLights,osTimerPeriodic); + // Staert RtosTimer objects, with the required frequency CarSim.start(50); // 20Hz Task1.start(100); // 10Hz Task2.start(500); // 2Hz @@ -440,6 +558,7 @@ Task9.start(1000); // 1Hz Task10.start(2000); // 0.5Hz + // Ensure that RtosTimer runs within Infinite loop Thread::wait(osWaitForever);