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; }