LEX_Threaded_Programming

Dependencies:   Heater_V2 MODSERIAL Nanopb FastPWM ADS8568_ADC

main.cpp

Committer:
justinbuckland
Date:
2019-10-23
Revision:
27:0ee855da5ba9
Parent:
26:e2e834e16367
Child:
28:b2513f36a50d

File content as of revision 27:0ee855da5ba9:

#include "mbed.h"
#include "pb.h"
#include "pb_decode.h"
#include "pb_encode.h"
#include "MODSERIAL.h"
#include "ADS8568_ADC.h"
#include "Heater.h"
#include "FastPWM.h"
#include "memspcr.pb.h"
#include <vector>
#include <iterator>

#define BUFFER_SIZE 8192

//UID lookup address and pointer
#define UID_ADDR 0x1FFF7A10
unsigned long *uid = (unsigned long *) UID_ADDR; 

//UID and drive board calibration table
#define UID_TABLE_LENGTH 4

int drive_board_serial_number[UID_TABLE_LENGTH] =
  {1,
   2,
   3,
   4};

unsigned long drive_board_uid[UID_TABLE_LENGTH][3] =
 {{0x005B0060, 0x32375101, 0x32363531},
  {0x0051003D, 0x32375114, 0x30333732},
  {0x00520060, 0x32375101, 0x32363531},
  {0x00570060, 0x32375101, 0x32363531}};

float drive_board_cal[UID_TABLE_LENGTH][2][2] = 
 {{{0.0908347476278717, 10.1921343711427}, {0.0497613470164513, 10.2109327517567}},
  {{0.0596907336847412, 10.1550084867437}, {0.0320376283698263, 10.2580153464834}},
  {{0.0119648730956925, 10.4065902688349}, {0.0256785142683800, 10.2921134395920}},
  {{0.0482969653247984, 10.0688110602909}, {0.0882102280729402, 10.1322703041679}}};
  
Heater * heater;
float r_gradient; //setpoint setting

MODSERIAL pc(PA_9, PA_10, BUFFER_SIZE); //mcu TX, RX, BUFFER_SIZE byte TX and RX buffers
ADS8568_ADC adc(PB_15, PB_14, PB_13, PB_12, PC_15, PC_0, PC_1, PC_2, PC_3);
I2C i2c(PB_7, PB_8);            //SDA, SCL
Timer timer;
DigitalIn adc_busy(PA_8);                   //Busy interrupt sig#

//Fluidic Control
DigitalOut pump(PA_2);
AnalogIn pressure_1(PA_5);

float pressure_in;
float pressure_out;

//Heater Control
FastPWM drive_1(PC_9);
FastPWM drive_2(PC_8);
FastPWM guard_1(PC_7);
FastPWM guard_2(PC_6);

//ADC channels for heater current and voltage measurements
int i_port_1 = 0;
int i_port_2 = 2;
int v_port_1 = 1;
int v_port_2 = 3;

//Heater ID: heater 1 is nearer liquid sense location, heater 2 is other location
int heater_ID;

//Illumination LED Control

//Indicator LEDs
DigitalOut hb_led(PC_13);       //Red
DigitalOut led_0(PC_4);         //Green
DigitalOut led_1(PC_5);         //Red

//Camera and LED drive
DigitalOut camTrigger(PB_2);     //Trigger camera
DigitalOut ledDrive(PB_4);       //Drive LED for fluorescence detection

//User buttons
DigitalIn user_0(PB_0);
DigitalIn user_1(PB_1);

BusOut converts(PC_0, PC_1, PC_2, PC_3);

//Threads
Thread heater_control(osPriorityHigh);
Thread logging_thread(osPriorityAboveNormal);
Thread pressure_thread(osPriorityNormal);

//Tickers
Ticker heat_tick;
Ticker pressure_tick;
Ticker log_tick;

//Flags
EventFlags flags; //Flags:
                  //        0 => update heater
                  //        1 => log state
                  //        2 => read pressure
bool triggered_flag;
bool status = true;

//Configuration data
memspcr_ExperimentConfiguration exp_config = memspcr_ExperimentConfiguration_init_zero;
int buffer_length;
size_t message_length;
uint8_t buffer[BUFFER_SIZE];


//Functions for reading and decoding the message__________________________________________________

void read_message()
{
    if (pc.scanf("%d",&message_length) < 0){pc.printf("# Error reading message length");}
    size_t buffer_length = sizeof(buffer);
    if (message_length > buffer_length) 
    {
        pc.printf("# Message length exceeds buffer. \n Input configuration file\n");
        read_message();
        return;
    }
    pc.printf("# Message is %d chars long, buffer length is %d\n",message_length,buffer_length);
    unsigned int c;
    for (int i = 0; i < message_length; i++) 
    {
        pc.scanf("%02X",&c);
        buffer[i] = (char) c;
    }   
}         

void decode_message()
{
    // Create a stream that reads from the buffer.
    pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);

    //Now we are ready to decode the message.
    status = pb_decode(&istream, memspcr_ExperimentConfiguration_fields, &exp_config);

    // Check for errors...
    if (!status) {
        pc.printf("# Decoding failed: %s\n", PB_GET_ERROR(&istream));
    }
}

bool decode_callback(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
    vector <memspcr_ThermalStep> * dest = (vector <memspcr_ThermalStep> *)(*arg);
    memspcr_ThermalStep result = memspcr_ThermalStep_init_zero;
    status = pb_decode(stream, memspcr_ThermalStep_fields, & result);

    if (!status) {
        pc.printf("# Decode callback failed\n");
    }

    dest->push_back(result); //CHECK: Does result get copied into the vector?
    return true;
}


//Ticking functions_________________________________________________________________


void temp_trigger()
{
    //This function triggers a temperature update.
    //N.B. update cannot be called directly from a ticker as tickers and
    //reading the ADC both rely on interrupts.
    flags.set(0x1);
}


void log_trigger()
{
    flags.set(0x2);
}

void pressure_trigger()
{
    flags.set(0x4);
}


//Other functions__________________________________________________________________


void temp_control() {
    while(1){
        flags.wait_any(0x1,osWaitForever,true);
        heater->read();
        heater->update();
        wait_us(200);//Give other threads time to get selected
        } 
    }

void log_state()
{
    while(1){
        flags.wait_any(0x2,osWaitForever,true);
        //Output time, R_ref, R, error, error_integrated
        pc.printf("%10d,%10d,%10.6f,%10.6f,%10.6f,%10.6f,%10.6f,%10.6f,%10.6f,%10.6f\n", 
            heater_ID, timer.read_ms(), heater->Get_R_avg(), heater->Get_R_ref(), heater->Get_R(), heater->Get_error(), heater->Get_error_integrated(), heater->Get_D(), pressure_in, pressure_out);
        wait_us(200);//Give other threads time to get selected
   }
}

void pressure_control() {
    while(1){
        flags.wait_any(0x4,osWaitForever,true);
        pressure_in = pressure_1.read();
        if (pressure_in < exp_config.fluidics.pressure_sensor_setpoint_adc - exp_config.fluidics.pressure_sensor_hysteresis_adc) {
            led_1 = 1;
            pump = 1;
        }
        else if (pressure_in > exp_config.fluidics.pressure_sensor_setpoint_adc) {
            led_1 = 0;
            pump = 0;
        }
    }
}


void set_point_routine(std::vector<memspcr_ThermalStep> profile) {
    int curr_time;
    vector <memspcr_ThermalStep>::iterator it_prev, it = profile.begin();
    if (it->elapsed_time_ms != 0)
    {
        pc.printf("# Error: the first point in the profile should be at time 0.\n");
        return;
    }
    it++;

    for (it_prev = profile.begin(); it < profile.end(); it ++, it_prev++){
        triggered_flag = false;
        r_gradient = (it->resistance_set_point - it_prev->resistance_set_point)/(it->elapsed_time_ms - it_prev->elapsed_time_ms);
        while ((curr_time = timer.read_ms()) <= it->elapsed_time_ms){
            heater->Set_ref(it_prev->resistance_set_point + r_gradient * (curr_time - it_prev->elapsed_time_ms));   
                             
            if (!triggered_flag && (it->camera_offset_ms != 0) && (curr_time > it_prev->elapsed_time_ms + it->camera_offset_ms))
            {
                //Start camera exposure and turn on LED if camera_offset_ms is non-zero
                camTrigger = 0;
                wait_us(10);
                camTrigger = 1;
                led_0 = 1;
                ledDrive = 1;
                triggered_flag = true;
            }
            wait_us(200);
        }
        //Stop camera exposure and turn off LED at end of time segment
        camTrigger = 0;
        led_0 = 0;
        ledDrive = 0;
    }
}   


int main()
{
    int i_board = -1;  
    int i_heater;
    float drive_cal_a, drive_cal_b;
        
    pc.baud(115200);
    adc.init();
    
    pc.printf("\r\nUnique ID: %08X %08X %08X \r\n", uid[0], uid[1], uid[2]);   
    for (int i = 0; i < UID_TABLE_LENGTH; i++)
    {
      if (uid[0]==drive_board_uid[i][0] && uid[1]==drive_board_uid[i][1] && uid[2]==drive_board_uid[i][2])
        {
            i_board = i;
            i = UID_TABLE_LENGTH;
        }
    }
    
    buffer_length = sizeof(buffer)/sizeof(uint8_t);
    pc.printf("# Input [CONFIGURATION] file\n");
        
    //set up nanopb
    std::vector<memspcr_ThermalStep> profile;
    exp_config.profile.funcs.decode = decode_callback;
    exp_config.profile.arg = &profile;

    //read and decode configuration
    read_message();  
    pc.printf("# Message read\n");
    decode_message();
    pc.printf("# Message decoded\n");

    //Select heater
    if (exp_config.selected_heater == memspcr_ExperimentConfiguration_Heater_HEATER_1)
        i_heater = 0;
    else if (exp_config.selected_heater == memspcr_ExperimentConfiguration_Heater_HEATER_2)
        i_heater = 1;
    else {
        pc.printf("# Error - no heater has been selected\n");
        return 1;
    }
    heater_ID = i_heater + 1;
    
    //Set drive ADC->Resistance calibration coefficients (default: no change if board not found)
    drive_cal_a = drive_board_cal[i_board][i_heater][0];
    drive_cal_b = drive_board_cal[i_board][i_heater][1];
        
    pc.printf("# Heater: %d\n", heater_ID);
    pc.printf("# Drive board calibration: %10.6f, %10.6f\n", drive_cal_a, drive_cal_b);

    //Define heaters
    Heater * heater_1 = new Heater(i_port_1, v_port_1, drive_cal_a, drive_cal_b, & drive_1, & guard_1, & adc, adc_busy, exp_config.thermal);
    Heater * heater_2 = new Heater(i_port_2, v_port_2, drive_cal_a, drive_cal_b, & drive_2, & guard_2, & adc, adc_busy, exp_config.thermal);
    if (i_heater == 0)
        heater = heater_1;
    else
        heater = heater_2;
    
    //Start pressure control
    pc.printf("# Pressure setpoint: %10.6f hystersess: %10.6f\n",exp_config.fluidics.pressure_sensor_setpoint_adc, exp_config.fluidics.pressure_sensor_hysteresis_adc);
    pc.printf("# Waiting for signal to begin [PRESSURE] control (type p or press button 0)\n");
    while (pc.getcNb()!='p' && !user_0);
    pc.printf("# Pressure control start signal received\n");
    pressure_thread.start(& pressure_control);
    pressure_tick.attach_us(& pressure_trigger, exp_config.fluidics.pressure_control_loop_interval_ms * 1000); 

    //Start logging
    logging_thread.start(& log_state);
    log_tick.attach_us(& log_trigger,exp_config.logging_interval_ms * 1000);

    //Start temperature control
    pc.printf("# Waiting for signal to begin [THERMAL] control (type s or press button 0)\n");
    while (pc.getcNb()!='s' && !user_0);
    pc.printf("# Thermal control start signal received\n");

    heater->Set_ref(0.0);
    heater_control.start(& temp_control);
    heat_tick.attach_us(& temp_trigger,exp_config.thermal.thermal_control_loop_interval_ms * 1000);  

    pc.printf("# Starting routine on drive board: %d\n",drive_board_serial_number[i_board]);
    pc.printf("heater id, time (ms), R_avg (Ohm), R_set (Ohm), R (Ohm), Err (Ohm), Err_int (Ohm.ms), Duty cycle, P1 (ADC), P2 (ADC)\n");
    timer.start();
    set_point_routine(profile);
    
    //Turn off
    heat_tick.detach();
    log_tick.detach();
    wait(1);
    heater->turn_off();
       
    pc.printf("# Finished\n");
     
    return 0;
}