chad

Dependencies:   MCP23017 WattBob_TextLCD mbed-rtos mbed

main.cpp

Committer:
f_legge
Date:
2018-03-26
Revision:
20:aeb7b7044c31
Parent:
19:1fecb6fa14a2

File content as of revision 20:aeb7b7044c31:

// Frazer Legge - H001262392
// Embedded Software - Assignment 3
// 
// This program operates a simulation of a car control system.
// THe simulation is completed through the use of external controls,
// ignition, sidelights switch, idicator switches, accelerator and 
// brake pedals. 
// The simulation calculates Average speed and total distance travelled.
// The simulation outputs are onboard LEDs to show ignition, sidelights,
// indicators and overspeed indicator. The system shows the distance 
// traveled and average speed of the car on the LCD. A USB dump is also 
// done showing the average speed, accelerator and brake positions.


#include "mbed.h"
#include "rtos.h"
#include "MCP23017.h"
#include "WattBob_TextLCD.h"


// LCD Definition
#define     BACK_LIGHT_ON(INTERFACE)    INTERFACE->write_bit(1,BL_BIT)
#define     BACK_LIGHT_OFF(INTERFACE)    INTERFACE->write_bit(0,BL_BIT)

// Car Sim Max/Min speed (mph)
#define MAX_SPEED 100
#define MIN_SPEED 0

// Serial Connection
Serial pc(USBTX, USBRX,115200);

// ============================================================================
// MBED Pin Assignments
// ============================================================================
//

// System Inputs
// ----------------------------------------------------------------------------
// Analogue Input
AnalogIn Accel(p15);            // Accelerator Pedal Input
AnalogIn Brake(p16);            // Brake Pedal Input

// Digital Input
DigitalIn Ignit(p11);           // Engine On/Off
DigitalIn SideL(p12);           // Side Light On/Off
DigitalIn Indi_L(p13);          // Left indicator switch
DigitalIn Indi_R(p14);          // Right idicator switch

// System Outputs
// ----------------------------------------------------------------------------
// PWM Output
PwmOut SpeedIndicator(p21);     // Speed Indicator Servo
PwmOut Indi_L_LED(LED3);        // Left Indicator LED
PwmOut Indi_R_LED(LED4);        // Right Indicator LED

// Digital Output
DigitalOut Ignit_LED(LED1);     // Ignition LED Indicator
DigitalOut SideL_LED(LED2);     // Side Light Indicator
DigitalOut OverS_LED(p17);        // Overspeed LED (REDBOX)

// Pointers to LCD Screen
MCP23017            *par_port;  // pointer to 16-bit parallel I/O chip
WattBob_TextLCD     *lcd;       // pointer to 2*16 character LCD object

// ============================================================================
// Data Structure 
// ============================================================================
//

// Raw Data Structure
// ----------------------------------------------------------------------------

// RawData is a global memory section that contains an instance of the raw input
// data from speedControl.

typedef struct
{
    bool EngineState;
    float RawAccel;
    float RawBrake;
} RawData;

// Create MUTEX to contol access to RawData 
Mutex RawDataMutex;

// Create instance of RawData
RawData rawData;

// Speed Data Structure
// ----------------------------------------------------------------------------

// Speed data is a global memory section that contains an instance of the 
// calculated car speed.  

typedef struct
{
    float rawSpeed[3];
    int counter;
} CarSpeedData;

// Create MUTEX to control access to CarSpeedData

Mutex SpeedDataMutex;

// Create insance of CarSpeedData
CarSpeedData speedData;

// Filtered Data Structure
// ----------------------------------------------------------------------------
typedef struct
{
    float AvgSpeed; 
    float TotalDist;
} FilteredData;

// Create MUTEX to control access to FilterData
Mutex filteredDataMutex;

// Create instance of FilteredData
FilteredData filteredData;

// Mail Queue Structure
// ----------------------------------------------------------------------------

// PCDump is a global memory section that is populated in Mail_queue. The data 
// is coppied from the current state of the control system every 5 seconds.

typedef struct 
{
    float   speed;
    float   accelerator;
    float   brake;
} PCDump;

// Create 100 element Mail Queue
Mail<PCDump, 100> Memory_dump;

// Counter to track number of elements in mail
int MailCounter;

// Create MUTEX to control access to MailQueueCounter
Mutex MailMutex;
 
// ============================================================================
// Car Simulation
// ============================================================================ 
 
// The CarSim task updates the rawSpeed float at freq of 20Hz

void CarSim(void const *arg) 
{ 
    float NewSpeed;
    
    // Deposit global variables to local variables
    RawDataMutex.lock();
    bool currentEngineState = rawData.EngineState;
    float currentAccel = rawData.RawAccel;
    float currentBrake = rawData.RawBrake;
    RawDataMutex.unlock();
    
    // Calulate current speed as an fraction of the MAX_SPEED based on the 
    // percentage of accelerator and brake. 
    // Speed is set to 0 if Engine State is changed to 0;
    NewSpeed = currentAccel*MAX_SPEED*(1-currentBrake)*currentEngineState;
    
    // Check Counter. If outside array set to 0
    // Data within shared resource therefore MUTEX is used.
    SpeedDataMutex.lock();
    
    if (speedData.counter > 2)
    {
        speedData.counter = 0; 
    }
    
    // Output rawSpeed value to next index of rawSpeed. 
    // Increment counter
    speedData.rawSpeed[speedData.counter] = NewSpeed;
    speedData.counter++;
    SpeedDataMutex.unlock();
}
    
// ============================================================================
// Controller Tasks
// ============================================================================    

// Speed Control (Task 1) reads external analog inputs and updates rawData.
// Operating freq of 10Hz

void SpeedControl(void const *arg) 
{ 
    // Deposit global variables to local variables
    RawDataMutex.lock();
    rawData.RawAccel = Accel.read();
    rawData.RawBrake = Brake.read();
    RawDataMutex.unlock();
}

// Ignition(Task 2) reads the state of an "ignition switch". 
// Updates to rawData.
// Operating freq of 2Hz.

void Ignition(void const *arg) 
{
    // Get Switch state and store locally
    bool currentEngineState = Ignit.read();
    
    // Deposit Local data to global under locked state
    RawDataMutex.lock();
    rawData.EngineState = currentEngineState;
    RawDataMutex.unlock();
    
    // Logic check on Engine state
    if (currentEngineState == 1)
    { 
        Ignit_LED = 1;
    }
    else
    { 
        Ignit_LED = 0;
    }
}

// Speed_Avg (Task 3) Calculates the average speed of the car over 3 speed 
// samples.
// Operation freq of 5Hz

void Speed_Avg(void const *arg) 
{ 
    // Init local float as 0
    float speed_T = 0.0;
    
    // Deposit gloabal data to local data under locked state
    SpeedDataMutex.lock();
    for (int i = 0; i < 3; i++)
    { 
        speed_T = speed_T + speedData.rawSpeed[i];
    } 
    SpeedDataMutex.unlock();
    
    // Deposit local data to global data under locked state
    filteredDataMutex.lock();
    filteredData.AvgSpeed = (speed_T/3);
    filteredDataMutex.unlock();
}

// Speed_Indi (Task 4) shows a representation of the car speed through a servo
// Operation freq of 1Hz

void Speed_Indi(void const *arg) 
{
    // Deposit gloabal data to local data under locked state
    filteredDataMutex.lock();
    float currentAvgSpeed = filteredData.AvgSpeed;
    filteredDataMutex.unlock();
    
    // Update Servo
    // Operates between 1000us and 2000us pwm
    // ie -> 10 * 50(mph) will give middle positioning
    SpeedIndicator.pulsewidth_us(1000+(10*currentAvgSpeed));
}
 
// OverSpeed (Task 5) illuminates an LED indicator to show that the car is above
// 70mph.
// Opperation freq of 0.5Hz

void OverSpeed(void const *arg) 
{ 
    // Deposit gloabal data to local data under locked state
    SpeedDataMutex.lock();
    float currentSpeed = speedData.rawSpeed[1];
    SpeedDataMutex.unlock();
    
    // Use locally stored current speed to show if car is travelling over 70mph
    if (currentSpeed > 70.0)
    {
        OverS_LED = 1;
    }
    else
    { 
        OverS_LED = 0;
    }
} 

// Display (Taks 6) shows the total distance traveled and the average speed on 
// the LCD.
// Operation freq of 2Hz

void Display(void const *arg) 
{ 
    // Local distance variable
    float currentTotalDist;
    
    // Deposit gloabal data to local data under locked state
    filteredDataMutex.lock();
    float currentAvgSpeed = filteredData.AvgSpeed;
    float current_TotalDist = filteredData.TotalDist;
    filteredDataMutex.unlock();
    
    // Update distance traveled
    // Polling every 0.5 seconds therefore convereted from 0.5s to hours
    // MUTEX not protected as TotalDist is only used for this function 
    currentTotalDist = current_TotalDist + (currentAvgSpeed*(0.5/3600)); 
    
    filteredDataMutex.lock();
    filteredData.TotalDist = currentTotalDist;
    filteredDataMutex.unlock();
    
    // Output on LCD
    lcd->locate(0,0);
    lcd->printf("D: %f",currentTotalDist);
    lcd->locate(1,0); 
    lcd->printf("S: %f",currentAvgSpeed);
}

// Mail_Queue (Task 7) sends gloabal variable to a MAIL structure. 
// A counter is incremented to track the variables. 
// Operation freq of 0.2Hz.

void Mail_Queue(void const *arg) 
{ 
    // Deposit gloabal data to local data under locked state
    RawDataMutex.lock();
    float currentAccel = rawData.RawAccel;
    float currentBrake = rawData.RawBrake;
    RawDataMutex.unlock();
    
    filteredDataMutex.lock();
    float currentAvgSpeed = filteredData.AvgSpeed;
    filteredDataMutex.lock();

    // Allocate and populate PCDump Struct
    PCDump *currentPCDump = Memory_dump.alloc();
    
    currentPCDump->brake = currentBrake;
    currentPCDump->accelerator = currentAccel;
    currentPCDump->speed = currentAvgSpeed; 
    
    // PCDump struct pushed to mail queue
    Memory_dump.put(currentPCDump);
    
    // Lock mail MUTEX and increment counter
    MailMutex.lock();
    MailCounter++;
    MailMutex.unlock();
} 

// Mail_Print (Task 8) sends the MAIL queue to a terminal over USB serial. 
// When finished MAIL counter is reset.
// Operation freq of 0.05Hz.

void Mail_Print(void const *arg) 
{ 
    // Deposit gloabal data to local data under locked state
    MailMutex.lock();
    int currentMCount = MailCounter;
    MailMutex.unlock();
    
    // Terminal header notifying dump
    pc.printf("Mail Dump\n\r"); 
    
    // Print each record in MAIL queue. MAIL queue is deposited locally then 
    // values are printed and elements freed in MAIL queue. 
    for (int i = 0; i < currentMCount; i++)
    {
        osEvent evnt = Memory_dump.get();
        if (evnt.status == osEventMail)
        {
            PCDump *currentPCDump = (PCDump*)evnt.value.p;
            
            pc.printf("AvgSpeed: %f\n\rAccel: %f\n\rBrake: %f\n\rCounter: %i\n\n\r",currentPCDump->speed,currentPCDump->accelerator,currentPCDump->brake,i);
            
            Memory_dump.free(currentPCDump);
        }
    }
    
    // Reset counter under locked state
    MailMutex.lock();
    MailCounter = 0;
    MailMutex.unlock();
}

// Side_Light (Task 9) checks the state of the side light and sets the LED
// indicator
// Operation freq of 1Hz

void Side_Light(void const *arg) 
{ 
    if(SideL == 1)
    { 
        SideL_LED = 1;
    } 
    else 
    {
        SideL_LED = 0;
    } 
} 

// Indicator (Task 10) checks the left and right indicator switches.
// Illuminates either left or right LED accordingly.
// If both high then LEDs flash in hazard mode.

void Indicator(void const *arg) 
{ 
    // If Left switch is high then flash left LED at 1Hz
    if (Indi_L == 1 && Indi_R == 0)
    { 
        Indi_L_LED.period(1.0);
        Indi_L_LED.pulsewidth(0.5);
        
        Indi_R_LED.period(1.0);
        Indi_R_LED.pulsewidth(0.0);
    }
    
    // If Right switch is high then flash right LED at 1Hz
    else if (Indi_L == 0 && Indi_R == 1)
    { 
        Indi_L_LED.period(1.0);
        Indi_L_LED.pulsewidth(0.0);
        
        Indi_R_LED.period(1.0);
        Indi_R_LED.pulsewidth(0.5);
    }
    
    // If Left & Right switch is high then flash both LEDs at 2Hz
    else if (Indi_L == 1 && Indi_R == 1)
    { 
        Indi_L_LED.period(0.5);
        Indi_R_LED.period(0.5);
        Indi_L_LED.pulsewidth(0.25);
        Indi_R_LED.pulsewidth(0.25);
    }
    
    // If Left & Right switch is low then no flash
    else if (Indi_L == 0 && Indi_R == 0)
    { 
        Indi_L_LED.period(1.0);
        Indi_L_LED.pulsewidth(0.0);
        
        Indi_R_LED.period(1.0);
        Indi_R_LED.pulsewidth(0.0);
    }
}

void ledFlasher(void const *arg)
{
    Ignit_LED =! Ignit_LED;
}


// ============================================================================
// Init System
// ============================================================================ 
    
void Init()
{
    // Servo freq set
    SpeedIndicator.period_ms(50);
    // Global Variables zeroed
    rawData.EngineState = 0;
    rawData.RawAccel = 0.0;
    rawData.RawBrake = 0.0;
    speedData.rawSpeed[0] = 0.0;
    speedData.rawSpeed[1] = 0.0;
    speedData.rawSpeed[2] = 0.0;
    speedData.counter = 0;
    filteredData.AvgSpeed = 0.0;
    filteredData.TotalDist = 0.0;
    MailCounter = 0;
}

// ============================================================================
// Main Thread
// ============================================================================

int main()
{ 
    par_port = new MCP23017(p9, p10, 0x40); // initialise 16-bit I/O chip
    par_port->config(0x0F00, 0x0F00, 0x0F00);

    lcd = new WattBob_TextLCD(par_port);    // initialise 2*26 char display
    par_port->write_bit(1,BL_BIT);          // turn LCD backlight ON

    lcd->cls();                             // clear display
     
    Init();                                 // Run init void
    
    Ignit_LED = 0; 
    
    
    //while(1)
    //{
    //    Ignit_LED =! Ignit_LED;
    //    wait(1);
        //Ignit_LED = 0;
        //wait(1);
        
    //}
    
    //SideL_LED = 1;
    
    RtosTimer ledFlash(ledFlasher, osTimerPeriodic);
    ledFlash.start(1000);
    // Timer Objects
    //RtosTimer Car_Sim(CarSim,osTimerPeriodic);
    //RtosTimer Task1(SpeedControl,osTimerPeriodic);
    //RtosTimer Task2(Ignition,osTimerPeriodic);
    //RtosTimer Task3(Speed_Avg,osTimerPeriodic);
    //RtosTimer Task4(Speed_Indi,osTimerPeriodic);
    //RtosTimer Task5(OverSpeed,osTimerPeriodic);
    //RtosTimer Task6(Display,osTimerPeriodic);
    //RtosTimer Task7(Mail_Queue,osTimerPeriodic);
    //RtosTimer Task8(Mail_Print,osTimerPeriodic);
    //RtosTimer Task9(Side_Light,osTimerPeriodic);
    //RtosTimer Task10(Indicator,osTimerPeriodic);
    
    // Start RTOS Timer objects

    //Car_Sim.start(50);                      // 20Hz
    //Task1.start(100);                       // 10Hz
    //Task2.start(500);                       // 2Hz
    //Task3.start(200);                       // 5Hz
    //Task4.start(1000);                      // 1Hz
    //Task5.start(2000);                      // 0.5Hz
    //Task6.start(500);                       // 2Hz
    //Task7.start(5000);                      // 0.2Hz
    //Task8.start(20000);                     // 0.05Hz
    //Task9.start(1000);                      // 1Hz
    //Task10.start(2000);                     // 2Hz

    Thread::wait(osWaitForever);            // Forever loop
       
}