Final project repo for ECE 495

Dependencies:   Adafruit_GFX_MBED Adafruit_ILI9341 BurstSPI DS1820 mbed mbed-rtos ltc2991_lib

Revision:
5:c1c710391df2
Parent:
3:a1b5d7541c69
--- a/main.cpp	Thu Dec 08 20:50:14 2016 +0000
+++ b/main.cpp	Wed Dec 14 07:01:33 2016 +0000
@@ -6,6 +6,25 @@
 #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
@@ -14,31 +33,45 @@
 DS1820* thermometers[NUM_DS1820];
 Adafruit_ILI9341 tft(PA_13, PA_15, PA_14);
 Display disp;
-
-// IO
 RawSerial pc(USBTX, USBRX);
 DigitalOut led(LED1);
-InterruptIn display_interrupt(PC_13);
+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[NUM_DS1820];
-double old_temperatures[NUM_DS1820];
+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[3];
-int temp_label_y_pos[3];
-int temp_str_y_pos[3];
+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();
@@ -53,20 +86,84 @@
 // data read functions
 void read_temps();
 // ISRs
-void display_swap();
+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);
-char *int2bin(int x);
+
+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 display_swap() {
-    if (display_interrupt)
-       disp.next_screen();
+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) {
-        disp.update();
+        if (errFlag) {
+            disp.error("A Fault Occured");
+            errFlag = false;
+        }
+        else {
+            disp.update();
+        }
         Thread::wait(1000);  
     }   
 }
@@ -94,7 +191,38 @@
 }
 
 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() {
@@ -117,21 +245,61 @@
     pc.printf("Found %d device(s)\r\n\n", num_devices);   
 }
 
-char *int2bin(int x) {
-    static char b[33];
-    b[0] = '\0';
-    uint32_t z;
-    for (z = 2147483648; z > 0; z >>= 1) {
-        strcat(b, ((x & z) == z) ? "1" : "0");
-    }
-    return b;
-}
 
 char buff[64];
 int loc = 0;
 
+
 void execute_command(char *cmd) {
-    pc.printf("%s\n", 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() { 
@@ -144,15 +312,53 @@
     }
 }
 
+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);
@@ -162,13 +368,22 @@
     CurrentScreen cs(2, &tft);
     Screen *s[3] = {&ts, &vs, &cs};
     disp.set_screens(s, 3);
-    display_interrupt.rise(&display_swap);
+    
+    //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);
+    //serial_thread.start(serial_task);
 }
 
 
@@ -191,12 +406,44 @@
     temp_colors[0] = CYAN; 
     temp_colors[1] = YELLOW; 
     temp_colors[2] = GREEN;
-    temp_label_y_pos[0] = margin+9; 
-    temp_label_y_pos[1] = margin+88; 
-    temp_label_y_pos[2] = margin+168;
-    temp_str_y_pos[0] = margin+40; 
-    temp_str_y_pos[1] = margin+120;
-    temp_str_y_pos[2] = margin+200;
+    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);
@@ -205,22 +452,14 @@
     _tft->setCursor(155, margin);
     _tft->print("TEMPERATURE");
     
-    _tft->fillRect(margin, margin, axes_x_min-2*margin, 30, temp_colors[0]);
-    _tft->setCursor(margin+33, temp_label_y_pos[0]);
-    _tft->setTextColor(BLACK);
-    _tft->print("CPU");
-    
-    _tft->fillRect(margin, margin+80, axes_x_min-2*margin, 30, temp_colors[1]);
-    _tft->setCursor(margin+20, temp_label_y_pos[1]);
-    _tft->setTextColor(BLACK);
-    _tft->print("MOTOR");
-    
-    _tft->fillRect(margin, margin+160, axes_x_min-2*margin, 30, temp_colors[2]);
-    _tft->setCursor(margin+10, temp_label_y_pos[2]);
-    _tft->setTextColor(BLACK);
-    _tft->print("AMBIENT");
-    _tft->setTextSize(2);
-    
+    // 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
@@ -244,14 +483,14 @@
     }
     int y_new, y_old;
     for (int i=0; i<NUM_DS1820; i++) {
-        _tft->fillRect(margin, temp_str_y_pos[i], axes_x_min-2*margin, 30, BLACK); 
+        _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, temp_str_y_pos[i]);
+        _tft->setCursor(margin+10, temp_val_y[i]);
         _tft->setTextColor(temp_colors[i]);
         _tft->print(str);
-        y_new = scale_temp(temperatures[i], 0, 150, axes_y_top, axes_y_bot);
-        y_old = scale_temp(old_temperatures[i], 0, 150, axes_y_top, axes_y_bot);
+        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; 
@@ -261,18 +500,37 @@
     }
 }
 
+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(RED);
+    _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(); 
+}  
+
 
 
 
@@ -284,4 +542,99 @@
 }
 
 void CurrentScreen::update() {
-} 
\ No newline at end of file
+    
+}
+ 
+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;
+}
\ No newline at end of file