Final project repo for ECE 495

Dependencies:   Adafruit_GFX_MBED Adafruit_ILI9341 BurstSPI DS1820 mbed mbed-rtos ltc2991_lib

main.cpp

Committer:
bdk9
Date:
2016-12-14
Revision:
5:c1c710391df2
Parent:
3:a1b5d7541c69

File content as of revision 5:c1c710391df2:


#include "mbed.h"
#include "rtos.h"
#include "DS1820.h"
#include "TemperatureScreen.h"
#include "VoltageScreen.h"
#include "CurrentScreen.h"
#include "Display.h"
#include "LTC2991.h" 
#include <stdarg.h>

struct SerialMessages {
    char *error_msg;
    char *next_screen;
    char *fet_state;
    char *fet_policy;
    char *relay_state;
    char *relay_policy;
    char *fan_voltage;
};

struct PowerPolicy {
    int fet_id;
    int cur_id;
    double amp_max;
    PowerPolicy *next;
};

#define NUM_DS1820      1
#define PIN_DS1820      PC_0

// DEVICES 
DS1820* thermometers[NUM_DS1820];
Adafruit_ILI9341 tft(PA_13, PA_15, PA_14);
Display disp;
RawSerial pc(USBTX, USBRX);
DigitalOut led(LED1);
DigitalOut *fetPin[6];

// THREADS
Thread display_thread(osPriorityNormal, DEFAULT_STACK_SIZE, NULL);
Thread fast_data_thread(osPriorityNormal, DEFAULT_STACK_SIZE, NULL);
Thread slow_data_thread(osPriorityNormal, DEFAULT_STACK_SIZE, NULL);
Thread serial_thread(osPriorityNormal, DEFAULT_STACK_SIZE, NULL);

// DATA VARIABLES
double temperatures[6];
double old_temperatures[6];
double voltages[4];
double currents[10];

SerialMessages messages;
PowerPolicy *head;
PowerPolicy *tail;

double max_system_current = 30.0;
DigitalOut relay_control(PA_0); //TODO update
PwmOut fan_control(PA_0); // TODO update pin (FET controlling fan)

// DRAWING VARIABLES
int margin;
int max_temp_plot = 75;
int axes_x_cur;
int axes_x_min, axes_x_max;
int axes_y_bot, axes_y_top;
int temp_colors[6];
int temp_label_rect_y[6];
char *temp_label_str[6];
int temp_label_str_x[6]; 
int temp_label_str_y[6];
int temp_val_y[6];
bool plotFlag;
bool errFlag = false;
char *errMsg;

// FUNCTION DECLARATIONS
int main();

// setup
void ds1820_init();
// thread drivers
void display_task();
void slow_data_task();
void fast_data_task();
void serial_task();
// data read functions
void read_temps();
// ISRs
void execute_command(char *cmd);
void parse_command();
// helpers
int scale_temp(double val, int min_domain, int max_domain, int min_range, int max_range);

void power_policy_init();
void add_power_policy(int fet, int cur, double amps);

int8_t ack; // 0 == ack, 1 == no ack
const uint16_t LTC2991_TIMEOUT=1000; //!< Configures the maximum timeout allowed for an LTC2991 read.

//These pins for the nucleo nucleo f401re
LTC2991 *ltc0 = new LTC2991(I2C_SDA, I2C_SCL);
LTC2991 *ltc1 = new LTC2991(PB_3, PB_10);
LTC2991 *ltc2 = new LTC2991(PB_4, PA_8);
float readSingle(LTC2991 *l, int p);
float readDiff(LTC2991 *l, int upper_pin);
void ltc_init();



void ltc_init() {
  ack = 0;
  
  while (true)
  {
    int failures = 0;
    pc.printf("booting LTC0\n");
    ack |= ltc0->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CHANNEL_ENABLE_REG, LTC2991_ENABLE_ALL_CHANNELS); //! Enables all channels
    ack |= ltc0->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CONTROL_V1234_REG, 0x00); //! Sets registers to default starting values.
    ack |= ltc0->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CONTROL_V5678_REG, 0x00);
    ack |= ltc0->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CONTROL_PWM_Tinternal_REG, LTC2991_REPEAT_MODE); //! Configures LTC2991 for Repeated Acquisition mode
    
    if (ack != 0) {
      pc.printf("Error: No Acknowledge LTC0. Check I2C Address.\n");
      failures++;
    }
    
    pc.printf("booting LTC1\n");
    ack |= ltc1->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CHANNEL_ENABLE_REG, LTC2991_ENABLE_ALL_CHANNELS); //! Enables all channels
    ack |= ltc1->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CONTROL_V1234_REG, 0x00); //! Sets registers to default starting values.
    ack |= ltc1->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CONTROL_V5678_REG, 0x00);
    ack |= ltc1->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CONTROL_PWM_Tinternal_REG, LTC2991_REPEAT_MODE); //! Configures LTC2991 for Repeated Acquisition mode
    
    if (ack != 0) {
      pc.printf("Error: No Acknowledge LTC1. Check I2C Address.\n");
      failures++;
    }
    
    pc.printf("booting LTC2\n");
    ack |= ltc2->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CHANNEL_ENABLE_REG, LTC2991_ENABLE_ALL_CHANNELS); //! Enables all channels
    ack |= ltc2->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CONTROL_V1234_REG, 0x00); //! Sets registers to default starting values.
    ack |= ltc2->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CONTROL_V5678_REG, 0x00);
    ack |= ltc2->LTC2991_register_write(LTC2991_I2C_ADDRESS, LTC2991_CONTROL_PWM_Tinternal_REG, LTC2991_REPEAT_MODE); //! Configures LTC2991 for Repeated Acquisition mode
    
    if (ack != 0) {
      pc.printf("Error: No Acknowledge LTC2. Check I2C Address.\n");
      failures++;
    }
    
    if (failures > 0) {
      wait_ms(500);
    } else {
      break;  
    }
  }    
}


void display_task() {
    while(1) {
        if (errFlag) {
            disp.error("A Fault Occured");
            errFlag = false;
        }
        else {
            disp.update();
        }
        Thread::wait(1000);  
    }   
}

void read_temps() {
    double temp;
    thermometers[0]->convertTemperature(false, DS1820::all_devices);
    for (int i=0; i<NUM_DS1820; i++) {
        temp = (double) thermometers[i]->temperature();
        if (temp > 0 && temp < 150) {
            old_temperatures[i] = temperatures[i];
            temperatures[i] = temp;
        }
    }
}

void slow_data_task() {
    //get initial reading
    read_temps();

    while(1) {
        read_temps();
        Thread::wait(500);
    }   
}

void fast_data_task() {
    while(1) {
        // Voltages
        for (int i=0; i<4; i++) {
            voltages[i] = readSingle(ltc2, i+5);
        }
        // Currents
        currents[0] = readDiff(ltc0, 2);
        currents[1] = readDiff(ltc0, 4);
        currents[2] = readDiff(ltc0, 6);
        currents[3] = readDiff(ltc0, 8);
        currents[4] = readDiff(ltc1, 2);
        currents[5] = readDiff(ltc1, 4);
        currents[6] = readDiff(ltc1, 6);
        currents[7] = readDiff(ltc1, 8);
        currents[8] = readDiff(ltc2, 2);
        currents[9] = readDiff(ltc2, 4);
        
        //Iterate over all power policies and act accordingly
        PowerPolicy *p = head;
        while (p->next != NULL) {
            if (currents[p->cur_id] > p->amp_max) {
                //Shutdown condition reached. Turn off until restart and alert
                *(fetPin[p->fet_id]) = 0;
                errFlag = 1; //Notify
                sprintf(errMsg, "Overcurrent: %.2f on FET %d", currents[p->cur_id], p->fet_id);
                pc.printf("PWR: ERR: %s\n", errMsg);
            }
            p = p->next;
        }

        Thread::wait(10);
    }
}

void serial_task() {
    while(1) {
        pc.printf("PWR: hey");   
        Thread::wait(5000);
    }
}

// Discover DS1820 probes on pin defined by PIN_DS1820
void ds1820_init() {
    // Initialize the thermometer array to DS1820 objects
    int num_devices = 0;
    while(DS1820::unassignedProbe(PIN_DS1820)) {
        thermometers[num_devices] = new DS1820(PIN_DS1820);
        num_devices++;
        if (num_devices == NUM_DS1820)
            break;
    }
    pc.printf("Found %d device(s)\r\n\n", num_devices);   
}


char buff[64];
int loc = 0;


void execute_command(char *cmd) {
    char msg_type[3];
    strncpy(msg_type, cmd, 1);
    //pc.printf("%s\n", msg_type);
    // ERROR MESSAGE
    if (!strcmp(msg_type, messages.error_msg)) {
        errFlag = true;
        sprintf(errMsg, "%s", cmd);
    }
    // SWITCH SCREEN
    else if (!strcmp(msg_type, messages.next_screen)) {
        disp.switch_screen();
    }
    // RELAY STATE
    else if (!strcmp(msg_type, messages.relay_state)) {
        int n;
        sscanf(cmd, "R,%d\n", n);
        relay_control = n;
    }
    // RELAY POLICY UPDATE
    else if (!strcmp(msg_type, messages.relay_policy)) {
        sscanf(cmd, "G,%2.2f\n", max_system_current);
    }
    // FET STATE 
    else if (!strcmp(msg_type, messages.fet_state)) {
        int fet, n;
        sscanf(cmd, "T,%d,%d\n", fet, n);
        *(fetPin[fet]) = n;
    }
    // FET POLICY UPDATE
    else if (!strcmp(msg_type, messages.fet_policy)) {
        int fet_id;
        int cur_id;
        double amp_max;
        sscanf(cmd, "C,%d,%d,%2.2f\n", fet_id, cur_id, amp_max);
        add_power_policy(fet_id, cur_id, amp_max);
    }    
    else if (!strcmp(msg_type, messages.fan_voltage)) {
        double fan_volts;
        sscanf(cmd, "F,%2.2f\n", fan_volts);
        // TODO CONVERT TO PWM VALUE
        int pwm_val = (int) ((fan_volts / 12.0)) * 100;
        fan_control.period_us(100);
        fan_control.pulsewidth_us(pwm_val);
    }
    // BAD MESSAGE - IMPROPER FORMAT
    else {
        // pc.printf("BAD MESSAGE\n");
        // lol u dumb
    }
}

void parse_command() { 
    buff[loc] = USART2->DR;
    loc += 1;
    if (buff[loc-1] == '\n') {
        execute_command(buff);
        memset(&buff[0], 0, sizeof(buff));
        loc = 0;
    }
}

void power_policy_init() {
    head->fet_id = 0;
    head->cur_id = 0;
    head->amp_max = 30.0;
    head->next = NULL;
    tail = head;
    for (int i=1; i<6; i++) {
        add_power_policy(i, i, 30.0);
    }
}

void add_power_policy(int fet, int cur, double amps) {
    PowerPolicy *pp;
    pp->fet_id = fet;
    pp->cur_id = cur;
    pp->amp_max = amps;
    pp->next = NULL;
    tail->next = pp;
    tail = pp;   
}

void serial_message_init() {
    messages.error_msg = "E";
    messages.next_screen = "D";
    messages.fet_state = "T";
    messages.fet_policy = "C";
    messages.relay_state = "R";
    messages.relay_policy = "G";
    messages.fan_voltage = "F";
}
    
int main() {
    // Setup serial interrupts
    pc.attach(&parse_command);
    
    // Initialize power policy
    power_policy_init();
    
    // Initialize serial message types
    serial_message_init();
    
    // Setup temperature sensors
    ds1820_init();
    
    // Init LTCs
    ltc_init();
    
    // Setup display
    tft.begin();
    tft.fillScreen(BLACK);
    tft.setRotation(1);
    TemperatureScreen ts(0, &tft);
    VoltageScreen vs(1, &tft);
    CurrentScreen cs(2, &tft);
    Screen *s[3] = {&ts, &vs, &cs};
    disp.set_screens(s, 3);
    
    //TODO: UPDATE THESE PINS TO BE ACCURATE
    //Setup fet pins
    *(fetPin[0]) = DigitalOut(PA_0);
    *(fetPin[1]) = DigitalOut(PA_0); 
    *(fetPin[2]) = DigitalOut(PA_0); 
    *(fetPin[3]) = DigitalOut(PA_0); 
    *(fetPin[4]) = DigitalOut(PA_0); 
    *(fetPin[5]) = DigitalOut(PA_0); 
    
    // Setup RTOS threads
    fast_data_thread.start(fast_data_task);
    slow_data_thread.start(slow_data_task);
    wait_ms(1000);
    display_thread.start(display_task);
    //serial_thread.start(serial_task);
}







// TEMPERATURE DISPLAY
TemperatureScreen::TemperatureScreen(int id, Adafruit_ILI9341 *tft) : Screen(id, tft) { }

void TemperatureScreen::init() {
    margin = 10;
    
    axes_x_min = 120; 
    axes_x_max = _tft->width() - margin;
    axes_y_bot = _tft->height() - margin;
    axes_y_top = 40;
    
    temp_colors[0] = CYAN; 
    temp_colors[1] = YELLOW; 
    temp_colors[2] = GREEN;
    temp_colors[3] = RED;
    temp_colors[4] = BLUE;
    temp_colors[5] = MAGENTA;
    
    temp_label_str[0] = "CPU";
    temp_label_str[1] = "AMBIENT";
    temp_label_str[2] = "MOTOR 1";
    temp_label_str[3] = "MOTOR 2";
    temp_label_str[4] = "MOTOR 3";
    temp_label_str[5] = "MOTOR 4";
    
    temp_label_rect_y[0] = margin;
    temp_label_rect_y[1] = margin+38;
    temp_label_rect_y[2] = margin+78;
    temp_label_rect_y[3] = margin+118;
    temp_label_rect_y[4] = margin+158;
    temp_label_rect_y[5] = margin+198;
    
    temp_label_str_x[0] = margin+30;
    temp_label_str_x[1] = margin+10;
    temp_label_str_x[2] = margin+10;
    temp_label_str_x[3] = margin+10;
    temp_label_str_x[4] = margin+10;
    temp_label_str_x[5] = margin+10;
    
    temp_label_str_y[0] = margin+1; 
    temp_label_str_y[1] = margin+41; 
    temp_label_str_y[2] = margin+81;
    temp_label_str_y[3] = margin+121;
    temp_label_str_y[4] = margin+161;
    temp_label_str_y[5] = margin+201;
    
    temp_val_y[0] = margin+21;
    temp_val_y[1] = margin+61; 
    temp_val_y[2] = margin+101;
    temp_val_y[3] = margin+141;
    temp_val_y[4] = margin+181;
    temp_val_y[5] = margin+221;
    
    // draw background
    _tft->fillScreen(BLACK);
    _tft->setTextSize(2);
    _tft->setTextColor(WHITE);
    _tft->setCursor(155, margin);
    _tft->print("TEMPERATURE");
    
    // draw temperature labels
    for (int i=0; i<6; i++) {
        _tft->fillRect(margin, temp_label_rect_y[i], axes_x_min-2*margin, 18, temp_colors[i]);
        _tft->setCursor(temp_label_str_x[i], temp_label_str_y[i]);
        _tft->setTextColor(BLACK);
        _tft->print(temp_label_str[i]);
    }

    // x-axis
    _tft->drawFastHLine(axes_x_min, axes_y_bot, axes_x_max-axes_x_min, WHITE);
    // y-axis
    _tft->drawFastVLine(axes_x_min, axes_y_top, axes_y_bot-axes_y_top, WHITE);
    
    axes_x_min += 1;
    axes_x_max -= 1;
    axes_y_bot -= 1; 
    axes_y_top += 1;
    axes_x_cur = axes_x_min;
}

int scale_temp(double val, int min_domain, int max_domain, int min_range, int max_range) {
    return (int) max_range - ((val - (min_domain)) / (max_domain - min_domain)) * (max_range - min_range);
}

void TemperatureScreen::update() {
    if (plotFlag) {
        _tft->fillRect(axes_x_min, axes_y_top, _tft->width()-axes_x_min, axes_y_bot-axes_y_top, BLACK);
        plotFlag = false;
    }
    int y_new, y_old;
    for (int i=0; i<NUM_DS1820; i++) {
        _tft->fillRect(margin, temp_val_y[i], axes_x_min-2*margin, 15, BLACK); 
        char str[10];
        sprintf(str, "%.2f C", temperatures[i]);
        _tft->setCursor(margin+10, temp_val_y[i]);
        _tft->setTextColor(temp_colors[i]);
        _tft->print(str);
        y_new = scale_temp(temperatures[i], 0, max_temp_plot, axes_y_top, axes_y_bot);
        y_old = scale_temp(old_temperatures[i], 0, max_temp_plot, axes_y_top, axes_y_bot);
        _tft->drawLine(axes_x_cur, y_old, axes_x_cur+3, y_new, temp_colors[i]);
    }
    axes_x_cur += 3; 
    if (axes_x_cur >= axes_x_max) {
        plotFlag = true;
        axes_x_cur = axes_x_min;
    }
}

void TemperatureScreen::error(char *msg) {
    _tft->fillScreen(RED);
    _tft->setCursor(_tft->width() / 2 - 50, _tft->height() / 2);
    _tft->setTextColor(BLACK);
    _tft->print(msg);
    wait_ms(3000);
    init(); 
}



// VOLTAGE DISPLAY
VoltageScreen::VoltageScreen(int id, Adafruit_ILI9341 *tft) : Screen(id, tft) { }

void VoltageScreen::init() {
    _tft->fillScreen(BLACK);
    
}

void VoltageScreen::update() {
    
}

void VoltageScreen::error(char *msg) {
    _tft->fillScreen(RED);
    _tft->setCursor(_tft->width() / 2 - 50, _tft->height() / 2);
    _tft->print(msg);
    wait_ms(3000);
    init(); 
}  




// CURRENT DISPLAY
CurrentScreen::CurrentScreen(int id, Adafruit_ILI9341 *tft) : Screen(id, tft) { }

void CurrentScreen::init() {
    _tft->fillScreen(BLUE);
}

void CurrentScreen::update() {
    
}
 
void CurrentScreen::error(char *msg) {
    _tft->fillScreen(RED);
    _tft->setCursor(_tft->width() / 2 - 50, _tft->height() / 2);
    _tft->print(msg);
    wait_ms(3000);
    init();  
}


// LTC READING
// read single pins 1 - 8
float readSingle(LTC2991 *l, int p) {
  int v_control_reg, v_msb;
  switch (p) {
    case 1: 
        v_control_reg = LTC2991_CONTROL_V1234_REG;
        v_msb = LTC2991_V1_MSB_REG;
        break;
    case 2: 
        v_control_reg = LTC2991_CONTROL_V1234_REG;
        v_msb = LTC2991_V2_MSB_REG;
        break;
    case 3: 
        v_control_reg = LTC2991_CONTROL_V1234_REG;
        v_msb = LTC2991_V3_MSB_REG;
        break;
    case 4: 
        v_control_reg = LTC2991_CONTROL_V1234_REG;
        v_msb = LTC2991_V4_MSB_REG;
        break;
    case 5: 
        v_control_reg = LTC2991_CONTROL_V5678_REG;
        v_msb = LTC2991_V5_MSB_REG;
        break;
    case 6: 
        v_control_reg = LTC2991_CONTROL_V5678_REG;
        v_msb = LTC2991_V6_MSB_REG;
        break;
    case 7: 
        v_control_reg = LTC2991_CONTROL_V5678_REG;
        v_msb = LTC2991_V7_MSB_REG;
        break;
    case 8: 
        v_control_reg = LTC2991_CONTROL_V5678_REG;
        v_msb = LTC2991_V8_MSB_REG;
        break;
  }
  
  int8_t data_valid;
  int16_t code;
  float voltage;
  ack = 0;
  ack |= l->LTC2991_register_set_clear_bits(LTC2991_I2C_ADDRESS, v_control_reg, 0x00, LTC2991_V1_V2_DIFFERENTIAL_ENABLE | LTC2991_V1_V2_TEMP_ENABLE);
  ack |= l->LTC2991_adc_read_new_data(LTC2991_I2C_ADDRESS, v_msb, &code, &data_valid, LTC2991_TIMEOUT);
  voltage = l->LTC2991_code_to_single_ended_voltage(code, LTC2991_SINGLE_ENDED_lsb);
  if (ack != 0) {
      pc.printf("Error: No Acknowledge\n");
  }
  return voltage;
}

float readDiff(LTC2991 *l, int upper_pin) {
  int v_control_reg, v_msb;
  switch (upper_pin) {
    case 2: 
        v_control_reg = LTC2991_CONTROL_V1234_REG;
        v_msb = LTC2991_V2_MSB_REG;
        break;
    case 4: 
        v_control_reg = LTC2991_CONTROL_V1234_REG;
        v_msb = LTC2991_V4_MSB_REG;
        break;
    case 6: 
        v_control_reg = LTC2991_CONTROL_V5678_REG;
        v_msb = LTC2991_V6_MSB_REG;
        break;
    case 8: 
        v_control_reg = LTC2991_CONTROL_V5678_REG;
        v_msb = LTC2991_V8_MSB_REG;
        break;
  }
  int8_t data_valid;
  int16_t code;
  float voltage;
  ack = 0;
  ack |= l->LTC2991_register_set_clear_bits(LTC2991_I2C_ADDRESS, v_control_reg, LTC2991_V1_V2_DIFFERENTIAL_ENABLE, LTC2991_V1_V2_TEMP_ENABLE);
  ack |= l->LTC2991_adc_read_new_data(LTC2991_I2C_ADDRESS, v_msb, &code, &data_valid, LTC2991_TIMEOUT);
  voltage = l->LTC2991_code_to_differential_voltage(code, LTC2991_DIFFERENTIAL_lsb);
  if (ack != 0) {
      pc.printf("Error: No Acknowledge.\n");
  } 
  return voltage;
}