Dependencies:   KS0108 mbed

main.cpp

Committer:
kagelump
Date:
2012-06-18
Revision:
0:b669c97da2c0

File content as of revision 0:b669c97da2c0:

// Display Test
#include "KS0108.h"
#include "calsolimage.h"
#include "MyPhone_20.h"
#include "MyPhone_40.h"
#include "rtos.h"

// Declare each of the things you're going to use

// The display
KS0108 display(p23,p21,p22,p20,p24,p25,p17,p18,p19,p30,p29,p28,p27,p26);
// The button on the breadboard
DigitalIn topButton(p13);
// For printing things to the computer
Serial pc(USBTX,USBRX);

// Timers
Timer timer;
Timer testTimer;

//Methods
void readFromCAN();
void setupScreen();
void updateBattery(int newVoltage);
void updateBatteryGraphic(int newPercentage, int percentage);
void printCenteredInt(int newInt, int oldInt, int startX, int startY, int charWidth, int charHeight, int charSpacing, int maxDigits);
void printCenteredInt(int newInt, int oldInt, int leftLimit, int rightLimit, int startY, int charWidth, int charHeight, int charSpacing, int maxDigits);
void printRightJustifiedInt(int newInt, int oldInt, int startX, int startY, int charWidth, int charHeight, int charSpacing, int maxDigits);

// Constants

//Maximum voltage for battery = 151.2 V (4.2V per cell, 36 cells total)
const double MaxVoltage = 151.2;
//Minimum voltage for battery = 108 V (3V per cell, 36 cells total)
const double MinVoltage = 108;

// Mutex and screen state
Mutex screen_mutex;
CAN can(p9, p10);
int demo = 0;


//Speed: int from 0 to 999, in mph
int speed = 0, newSpeed = 0;
//Voltage: int from MinVoltage to MaxVoltage, in volts
int voltage = MinVoltage, newVoltage = MinVoltage;
//Current: int from 0 to 9999, in milliAmps
int amperage = 0, newAmperage = 0;


//Union type to make float to char conversion easier (used to read CAN messages)
typedef union _data
{
    float f[2];
    char s[8];
} floatchar;

//Union type to make int to char conversion easier (used to read CAN messages)
typedef union _data2
{
    uint16_t i[4];
    char s[8];
} intchar;


/* Input thread - Modifies state variables based on input */
void input_runner(void const *argument) {
    while (true) {
        // If top button is pressed, don't accept other input
        if (!topButton) {
            // Debounce wait
            Thread::wait(100);
            if (!topButton) {
                screen_mutex.lock();
                demo++;
                if (demo > 1) {
                    demo = 0;
                }
                screen_mutex.unlock();
            }
            // Don't accept other input while button is pressed
            while(!topButton) {
                Thread::wait(100);
            }
        }
    }
}


// This is where your programs starts running
int main()
{
  // Initialize variables**
  int current_demo = demo;
  topButton.mode(PullUp);
  // Display CalSol logo
  display.FullScreenBMP(pic);
  Thread::wait(200);

  timer.start();
  
  // Start the button thread
  Thread button_thread(input_runner);
  
  // Main thread
  while(1) {
    // First, check if the state has changed, if so, wipe the screen
    int changed = 0;
    screen_mutex.lock();
    if (current_demo != demo)
    {
        changed = 1;
    }
    current_demo = demo;
    screen_mutex.unlock();
    
    // Wipe the screen
    if (changed)
    {
        display.ClearScreen();
        changed = 0;
    }
    
    // Now run the actual program
    switch(current_demo)
    {
        // Demo 0: Just display the CalSol logo
        case 0:
            if(timer.read_ms() > 500)
            {
                display.FullScreenBMP(pic);
                timer.reset();
            }
            break;
        
        // Demo 1: Display a mock speedometer, with battery level
        case 1:
            //Initialize variables
            speed = newSpeed = 60;
            amperage = newAmperage = 900;
            voltage = newVoltage = MaxVoltage;
            
            setupScreen();
            testTimer.start();
            
            //Display values as they are being changed
            while (true)
            {
                /* This part tests the printing
                
                //Randomly selects values between appropriate ranges.
                speed = (int)(rand()%200);
                amperage = (int)(rand()%1000);
                voltage = (int)(rand()%100);
                
                //Changes values from 1 to 3 or 4 digits to test proper spacing and erasing with changing num of digits
                if (testTimer.read_ms()%1000 == 0)
                {
                    switch ((testTimer.read_ms()/1000)%4)
                    {
                        case 0: //1 digit
                            newSpeed = (int)(rand()%9)+1;
                            newAmperage = (int)(rand()%9)+1;
                            break;
                        case 3: //2 digits
                            newSpeed = (int)(rand()%90)+10;
                            newAmperage = (int)(rand()%90)+10;
                            break;
                        case 2: //3 digits
                            newSpeed = (int)(rand()%900)+100;
                            newAmperage = (int)(rand()%900)+100;
                            break;
                        case 1: //4 digits
                            newSpeed = (int)(rand()%90)+100;
                            newAmperage = (int)(rand()%900)+1000;
                            break;
                        default: break;
                    }
                }*/
                
                //Keeps screen from updating too much
                if (testTimer.read_ms()%1000==0)
                {
                    /* More printing testing
                    
                    //Randomly increases or decreases values within a small range
                    newSpeed += (int)((rand()%5)-2);
                    newAmperage += (int)((rand()%21)-10);
                    newVoltage += (int)((rand()%3)-1);
                    
                    //Keeps voltage between allowed values
                    if (newVoltage < MinVoltage || newVoltage > MaxVoltage)
                        newVoltage = MaxVoltage;
                    */
                        
                    readFromCAN();
                    
                    updateBattery(newVoltage);
                    
                    //Updates speed
                    display.SelectFont(MyPhone_40, BLACK, ReadData);
                    printCenteredInt(newSpeed, speed, 12, 16, 15, 23, 1, 3);
                    speed = newSpeed;
                    
                    //Updates amperage
                    display.SelectFont(MyPhone_20, BLACK, ReadData);
                    printCenteredInt(newAmperage, amperage, 76, 40+8, 8, 11, 1, 4);
                    amperage = newAmperage;
                }
            }
            break;
        default: break;
        }
    }
}

void setupScreen()
{
    //Draws grid lines to separate the screen.
    //Dimensions: (Speed) 70x64, (Battery) 57x42, (Amperage) 57x22 
    display.HLineShort(71, 42, 57, BLACK);
    display.VLineShort(71, 0, 64, BLACK);
    
    //This covers the stray black pixel...
    display.SetPixel(71-5, 42, WHITE);
  
    //Draws the outline for battery graphic. Dimensions: 40x24, 4x8
    display.RoundRectangle(82, 4, 40, 24, 4, BLACK);
    display.EmptyRectangle(78, 12, 78+4, 12+8, BLACK);
    
    //Prints MPH
    display.SelectFont(System5x7, BLACK, ReadData);
    display.GotoXY(26, 48);
    display.PrintString("MPH");
    
    //Print mAmp
    display.SelectFont(System5x7, BLACK, ReadData);
    display.GotoXY(114, 24+24);
    display.PrintString("mA");
    
    //Prints the percent sign
    display.GotoXY(108, 24+8);
    display.PrintString("%");
    
    //Draws initial shaded box for battery
    display.FullRectangle(86+(int)(((double)(100-((newVoltage - MinVoltage)/(MaxVoltage - MinVoltage)*100))/100.0)*32), 4+4, 86+32, 4+24-4, BLACK);
}

//This exists to fix the problem with the percentage printing that was occuring for unknown reasons.
//Writes an int to the screen right-justified over another int.
void printRightJustifiedPercentage(int newInt, int oldInt, int startX, int startY, int charWidth, int charHeight, int charSpacing, int maxDigits)
{
    for (int digits = maxDigits; digits > 0; digits--)
    {
        if (newInt >= (int)pow(10.0, digits-1))
        {
            //Instead of checking if digits need to be erased, all digits will be erased
            //Erases digits that will not be covered by new text.
            display.FullRectangle(startX, startY, startX + (charWidth+charSpacing) * (maxDigits - digits), startY + charHeight, WHITE);
            display.GotoXY(startX + (charWidth+charSpacing) * (maxDigits - digits), startY);
            //Break out of loop.
            digits = -1;
        }
    }
    display.PrintNumber(newInt);
}

//Writes an int to the screen center-justified over another int.
void printCenteredInt(int newInt, int oldInt, int startX, int startY, int charWidth, int charHeight, int charSpacing, int maxDigits)
{
    for (int digits = maxDigits; digits > 0; digits--)
    {
        //If current value has "digits" number of digits
        if (newInt >= (int)pow(10.0, digits-1))
        {
            //If previous value had more digits than current value
            if (oldInt >= (int)pow(10.0, digits))
            {
                //Erases all possible digits that could be on screen.
                //**First parameter should logically be startX, but it starts erasing 1 pixel too far to the right for some reason. ???
                display.FullRectangle(startX-1, startY, startX + maxDigits*(charWidth+charSpacing), startY + charHeight, WHITE);
            }
            display.GotoXY(startX + 0.5*((maxDigits - digits)*charWidth + (maxDigits - digits - 1)*charSpacing), startY);
            //Break out of loop.
            digits = -1;
        }
    }
    display.PrintNumber(newInt);
}

//Updates the battery graphic and prints corresponding percentage or voltage.
void updateBattery(int newVoltage)
{
    //These are needed for the battery graphic, even if the values won't be displayed.
    int percentage = (voltage - MinVoltage)/(MaxVoltage - MinVoltage)*100;
    int newPercentage = (newVoltage - MinVoltage)/(MaxVoltage - MinVoltage)*100;
    
    //This line fixes the printing problem with the percentage that was caused by drawing shaded rectangle. Might have something to do with the method WriteInstruction(int, int) used by both methods.
    display.SetPixel(88, 32, WHITE);
    
    // ** Problem: after a while of letting it run, sometimes a 0 will show up 1 pxl to the right of the hundreds place. But it gets erased when voltage drops enough
    //Prints percentage
    display.SelectFont(System5x7, BLACK, ReadData);
    printRightJustifiedPercentage(newPercentage, percentage, 88, 32, 5, 7, 1, 3);
    
    //Prints voltage -- currently NOT IN USE
    /*display.SelectFont(System5x7, BLACK, ReadData);
    display.GotoXY(108, 24+8);
    display.PrintString("V");
    display.SelectFont(System5x7, BLACK, ReadData);
    printRightJustifiedInt(newVoltage, voltage, 88, 32, 5, 7, 1, 3);*/
    
    updateBatteryGraphic(newPercentage, percentage);
    
    percentage = newPercentage;
    voltage = newVoltage;
}

//Changes shaded area of graphic to reflect current battery level. Dimensions 32x16
void updateBatteryGraphic(int newPercentage, int percentage)
{
    //Stores x-coordinates of shaded part of battery.
    int barX = 86+(int)(((double)(100-percentage)/100.0)*32);
    int newBarX = 86+(int)(((double)(100-newPercentage)/100.0)*32);
    
    //Draws increase in percentage.
    if (newBarX < barX)
    {
        display.FullRectangle(newBarX, 4+4, barX, 4+24-4, BLACK);
    }
    //Draws decrease.
    else
    {
        display.FullRectangle(86, 4+4, newBarX, 4+24-4, WHITE);
    }
}

//***This method has NOT BEEN TESTED.

//Gets information about speed, voltage, current, and battery life through CAN.
void readFromCAN()
{
    const int speedID = 0x403;
    const int voltageAmperageID = 0x523;
    
    //Gets message from CAN.
    CANMessage msg;
    can.read(msg);
    int id = msg.id;
    
    switch (id)
    {
        case voltageAmperageID: //contains four messages: voltage 1 and 2 in volts, current 1 and 2 in mA. each is an int (2 bytes)
            
            //Read & store amperage value.
            intchar amp_data;
            for (int i = 0; i < 2; i++){
                amp_data.s[i] = msg.data[i];
            }
            amperage = amp_data.i[0]; //***is this correct?
            
            //Read & store voltage/battery life value.
            floatchar volt_data;
            for (int i = 0; i < 2; i++){
                volt_data.s[i] = msg.data[i+4];
            }
            voltage = volt_data.f[0];   //***should this be reading voltage 1 or voltage 2? (volt1.f[0 or 1])
            break;
            
        case speedID:
            //** This packet actually contains motor velocity in rpm as well as vehicle velociy in m/s. How do I get only the second part?
            floatchar speed_data;
            for (int i = 0; i < 4; i++) {
                speed_data.s[i] = msg.data[i];
            }
            //Conversion factor: 1 m/s = 2.23693629 mph
            speed = (speed_data.f[2])*2.23693629; //** should this be a combination of f[2] and f[3]? 
            break;
        default: break;
    }
}