assignment_2_herpe

Dependencies:   mbed WattBob_TextLCD MCP23017

main.cpp

Committer:
xherpe
Date:
2012-03-08
Revision:
0:aaddc17011a9

File content as of revision 0:aaddc17011a9:

// XAVIER HERPE                                      Assignment 2
// 5th year Robotics & Cybertronics
// Heriot-Watt University

#include "mbed.h"
#include "MCP23017.h"
#include "WattBob_TextLCD.h"
#include "SDFileSystem.h"
#include "FATFileSystem.h"

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

// Pointers to LCD screen and SD card
MCP23017            *par_port;  // pointer to 16-bit parallel I/O chip
WattBob_TextLCD     *lcd;       // pointer to 2*16 character LCD object
FILE                *fp;        // Pointer to SD card object


//=====================================================================================
//  I/O ports allocation
//=====================================================================================
DigitalIn TTL(p17); // TTL input for frequency measurement
DigitalIn switch_1(p18); // Switch 1 input
DigitalIn switch_2(p19); // Switch 2 input
DigitalIn switch_off(p11); // Switch used to close SD file and stop cyclic executive
AnalogIn analogue_in_1(p15); // POT value
AnalogIn analogue_in_2(p16); // LDR value
PwmOut servo(p21); // Servo output
DigitalOut TestPin(p20); // Pin only used to test program and measure time
SDFileSystem sd(p5, p6, p7, p8, "sd"); // The pinout on the mbed Cool Components workshop board


//=====================================================================================
// Internal objects declaration
// ====================================================================================
BusOut LEDs(LED4, LED3, LED2, LED1); // Address the four LEDs to a single bus
Timer timer; // Timer used to measure frequency in task 1
Timer DoNothing; // Timer used to measure how long the program does nothing
Ticker ticker; // Ticker used as clock for cyclic executive program


//=====================================================================================
// Constants declaration
//=====================================================================================
const int SampFreq = 100; // Sampling frequency is 10kHz (100us)


//=====================================================================================
// Variables declaration
//=====================================================================================

// Variables for cyclic executive program
long int ticks = 0; // Used to define what task to call in the cyclic executive program
int NoTask = 0; // Used to return how long the program does nothing in ms
int NoTaskCount = 0; // Variable incremented until one total cycle of 10 seconds is reached

// Variables for tasks 1 and 2
int period = 0; // Returned period of the TTL input signal
int frequency = 0; // Returned frequency of the TTL signal

// Varibles for task 4
int switch_1_val = 0; // Used to return how many times the switch is high
int switch_2_val = 0;
bool switch_1_state = 0; // Used to define whether the debounced switch is ON or OFF
bool switch_2_state = 0;

// Variables for task 5
float analogue_1_val = 0; // Used to return the filtered analogue input
float analogue_2_val = 0;

int analogue_1_int = 0; // Used to convert float to int (results in quicker display on LCD in task 6)
int analogue_2_int = 0;

// Variable for task 7
int LogCount = 0; // Used to define logging number

// Variable used for task 8
int BinCount = 0; // Used to increment a binary display on LEDs. Goes from 0 to 15 and then is reset
bool BinEnable = 0; // Used to tell task 5 to display binary pattern on LEDs every 1.5s
int IncCheck = 0; // Check increment to see if 6 cycles have elapsed to light LEDs ( 6 * 250us = 1.5s)


//=====================================================================================
// Task declaration
//=====================================================================================

void CyclEx();

void Task1(); // Measure TTL input frequency
void Task2(); // Show frequency on LCD screen
void Task3(); // Show speed on servo dial
void Task4(); // Read and debounce two digital inputs
void Task5(); // Read and filter two analogue inputs
void Task6(); // Display digital and analogue inputs on LCD screen
void Task7(); // Log speed, analogue and digital inputs on SD card
void Task8(); // Display error message on LCD screen and display binary pattern on LEDs

void WaitRisEdge(); // Subroutine to detect rising edge
void WaitFalEdge(); // Subroutine to detect falling edge

void Stop(); // Close log file and stop cyclic executive


//=====================================================================================
// Main program
//=====================================================================================

int main()
{

    // LCD Screen Initialisation
    par_port = new MCP23017(p9, p10, 0x40); // initialise 16-bit I/O chip
    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

    // EXEL log file initialisation
    fp = fopen("/sd/log.xls", "w"); // pointer to log in text file called "log". (Use "a" to not delete file)
    fprintf(fp, "This file is the property of Xavier Herpe, the French\n\n");

    // DoNothing timer reset
    DoNothing.reset();
    
    // Internal ticker set to 25ms. Every 25ms, the scheduler is called and selects the task to run
    ticker.attach(&CyclEx, 0.025); // Period set to 25ms
    while(1)// Run until system shuts down
    {
         
    } 
}

// Where tasks are scheduled based on an EXEL sheet
void CyclEx()
{   
    // Stop timer when a new task starts 
    DoNothing.stop();
    
    if(ticks % 80 == 4) // Occures every 80 clock cycles (2 seconds). Starts with an offset of 4 clock cycles
    {
        Task1(); 
    }    

    else if(ticks % 200 == 8) // Occures every 200 clock cycles (5 seconds). Starts with an offset of 8 clock cycles
    {       
        Task2();      
    }
    else if(ticks % 240 == 7) // Occures every 240 clock cycles (6 seconds). Starts with an offset of 7 clock cycles
    {
        Task3();    
    }
    else if(ticks % 4 == 0) // Occures every 4 clock cycles (0.1 seconds). Starts with an offset of 0 clock cycles
    {
        Task4(); 
    }
    else if(ticks % 10 == 1) // Occures every 10 clock cycles (0.25 seconds). Starts with an offset of 1 clock cycles
    {   
        Task5(); 
    }
    else if(ticks % 40 == 3) // Occures every 40 clock cycles (1 seconds). Starts with an offset of 3 clock cycles
    {
        Task6(); 
    }
    else if(ticks % 400 == 10) // Occures every 400 clock cycles (10 seconds). Starts with an offset of 10 clock cycles
    {
        Task7(); 
    }
    else if(ticks % 160 == 6) // Occures every 160 clock cycles (4 seconds). Starts with an offset of 6 clock cycles
    {  
        Task8(); 
    }
    
    if (switch_off == 1) // Pin used to log data on SD card and stop Cyclic executive program
         {
             Stop();
         }
    ticks++;
    
    // Start timer when one task is ended 
    DoNothing.start();
    NoTaskCount++;
    
    // When one full cycle of 10 seconds is finished, return how long the program was doing nothing (lazy program)
    if (NoTaskCount == 400)
    {
        NoTask = DoNothing.read_ms();
        NoTaskCount = 0;
        DoNothing.reset();
    }
}


//=====================================================================================
// Tasks
//=====================================================================================

// Task 1: Measure TTL input frequency
void Task1()
{
    timer.reset();
    
    // If the input signal is low, wait for a rising edge to start counting
    if (TTL == 0)
    {
        WaitRisEdge(); // Call subroutine to wait for rising edge
        timer.start(); // Start timer
        while(TTL == 1) // Keep counting as long as signal is high
        {
            wait_us(SampFreq);
        }
    }
    
    // If the input signal is high, wait for a falling edge to start counting
    else if (TTL == 1)
    {
        WaitFalEdge(); // Call subroutine to wait for falling edge
        timer.start(); // Start timer
        while(TTL == 0) // Keep counting as long as signal is high
        {
            wait_us(SampFreq);
        }
    }

    timer.stop(); // Stop counting when signal changes
    period = timer.read_us()*2; // Convert the time into a period
    frequency = 1000000/period; // Convert the period into a frequency
}



// Task 2: display the measured frequency on LCD screen
void Task2()
{
    lcd->cls(); // clear display
    lcd->locate(0,0); // set cursor to location (0,0) - top left corner
    lcd->printf("%d Hz",frequency); // print the frequency calculated in task 1
}



// Task 3: show speed on servo output dial
void Task3()
{
    servo.period(0.02); // servo requires a 20ms period
    // To rotate the servo from -90 to +90 degrees, the pulse width must varies between 600us to 2300us
    // The pulse width is calculated from the speed measured in task one
    // 50Hz is equivalent to -90 degrees and 100Hz is equivalent to 90 degrees
    // 1Hz change is equal to 34us pulse width change, so pulse width = ((frequency - 50)*34) + 600
    servo.pulsewidth_us(2300-((frequency - 50)*34));
    wait_ms(1); // Leave the servo some time to reach its position
}



// Task 4: Read two digital inputs (debounced)
void Task4()
{
    switch_1_val = 0;
    switch_2_val = 0;

    // Read each switch three consecutive times with 100us between readings
    for(int i=0; i<3; i++)
    {
        if (switch_1 == 1) // Increment variable if switch 1 is pressed
        {
            switch_1_val++;
        }

        if (switch_2 == 1) // Increment variable if switch 2 is pressed
        {
            switch_2_val++;
        }

        wait_us(SampFreq);
    }
    // Check how many times switch 1 has been high
    // if it has been high more than twice, then switch 1 state = 1
    if (switch_1_val > 1)
    {
        switch_1_state = 1;
    }
    else
    {
        switch_1_state = 0;
    }

    // Check how many times switch 1 has been high
    // if it has been high more than twice, then switch 2 state = 1
    if (switch_2_val > 1)
    {
        switch_2_state = 1;
    }

    else
    {
        switch_2_state = 0;
    }
}



// Task 5: Read two analogue inputs (filtered)
void Task5()
{
    analogue_1_val = 0; // Reset variables
    analogue_2_val = 0;

    // Takes four readings of each analogue input. Readings occure every 0.1ms
    // Because the analogue.read() function returns a value from 0 to 1,
    // we need to multiply the readings by 3.3 to cover 0V to 3.3V
    for(int i=0; i<4;i++)
    {
        analogue_1_val = analogue_1_val + (analogue_in_1*3.3);
        analogue_2_val = analogue_2_val + (analogue_in_2*3.3);
        wait_us(SampFreq);
    }

    analogue_1_val = (analogue_1_val / 4);
    analogue_2_val = (analogue_2_val / 4);
    
    analogue_1_int = analogue_1_val * 10; // Convert floating point into an integer to reduce display delay
    analogue_2_int = analogue_2_val * 10;
    
    // This section of task 5 is used to take over part of task 8.
    // Since the LEDs pattern has to be incremented every 1.5s, the pattern is
    // incremented every 6 cycles, which correspond to 1.5s.
    if(BinEnable == 1)
    {
        IncCheck++; 
        
        if(IncCheck == 6) // Corresponds to 1.5s. Increment binary pattern
        {
            LEDs = BinCount;
            BinCount++;
            IncCheck = 0;

            if (BinCount > 15) // Used to reset variable once maximum 4-bit binary value is reached
            {
                BinCount = 0;
            }  
        }  
    }
}



// Task 6: Display analogue and digital values on LCD screen
void Task6()
{
   // lcd->cls(); // clear display (takes too long)
    lcd->locate(0,0); // set cursor to location (0,0) - top left corner
    lcd->printf("%d %d%d%d",analogue_1_int,analogue_2_int,switch_1_state,switch_2_state);
}



// Task 7: Log values on SD card
void Task7()
{    
     LogCount++; //Used to print the logging number in file. Starts from 1
     fprintf(fp, "Log:  %d,   Speed: %dHz,   Switch_1: %d,   Switch_2: %d,   POT: %.2fVolts,   LDR: %.2fVolts\n",LogCount,frequency,switch_1_state,switch_2_state,analogue_1_val,analogue_2_val);
}



// Task 8: Show error message and light LEDs
void Task8()
{  
    // If switch_1 = 1 and POT value > 3V, display error message
    if(switch_1_state == 1 && analogue_1_val > 3)
    {
        //lcd->cls(); // clear display
        lcd->locate(0,0); // set cursor to location (0,0) - top left corner
        lcd->printf(".ERREUR");
    }
    
    // If switch 2 is high, return a command to task 5 to do the incrementing pattern every 1.5 seconds
    if(switch_2_state == 1)
    {
        BinEnable = 1;
    }
    
    // If switch 2 is low, stop sending a command to task 5 and light off LEDs
    else
    {
        LEDs = 0;
        BinEnable = 0;
        BinCount = 0;
    }
}



// Stop function to stop cyclic executive and close log file
void Stop()
{
    ticker.detach();
    fprintf(fp, "\n The program did nothing for %d ms, which corresponds to %d percent of the time  \n",NoTask, NoTask/100);    
    fprintf(fp, "\n PROGRAM STOPPED");
    fclose(fp);
    
}



//=====================================================================================
// Subroutines
//=====================================================================================

// Wait for rising edge
void WaitRisEdge()
{
    // As soon as it gets high, the subroutine will end and the timer will start
    while(TTL == 0)
    {        
            wait_us(SampFreq);        
    }
}


// Wait for falling edge
void WaitFalEdge()
{
    // As soon as it gets low, the subroutine will end and the timer will start
    while(TTL == 1)
    {
        wait_us(SampFreq);
    }
}