Dependencies:   ros_lib_kinetic

main.cpp

Committer:
WD40andTape
Date:
2018-07-31
Revision:
3:c83291bf9fd2
Parent:
2:eea12b149dba
Child:
4:303584310071

File content as of revision 3:c83291bf9fd2:

#include "mbed.h"
#include "math.h"

//#include "mbed.h"
#include "mbed_events.h"

//ADC SPI stuff
#define PREAMBLE 0x06
#define CHAN_1 0x30
#define CHAN_2 0x70
#define CHAN_3 0xB0
#define CHAN_4 0xF0

#define DATA_MASK 0x0F

//simple channel selection
#define ADC_PRESSURE 1
#define ADC_POSITION 3
//---------------------

#define N_CHANNELS 8 //number of channels to control
                        //1-3: front segment
                        //4-6: rear segment
                        //7-8: mid segment
                
//parameters to manually change                       
#define LOW_LEVEL_SPI_FREQUENCY 10000000

#define PATH_SAMPLE_TIME_S 0.05

#define MAX_LENGTH_MM 100.0
#define MAX_ACTUATOR_LENGTH 52.2
#define MAX_SPEED_MMPS 24.3457
#define PATH_TOLERANCE_MM 0.2 

double dblMaxChamberLengths_mm[N_CHANNELS] = {100.0,50.0,50.0,50.0,50.0,50.0,30.0,30.0};



int ii,jj; //counting varaibles

//-----These are the variables being shared between High-Level and Mid-Level-----------------------------------------------//

double dblPSI[3][3]; //the message from high-level containing the chamber lengths in meters in following format:
            /*
            {L(front,1),   L(front,2),    L(front,3);
             L(mid,1)  ,   L(mid,2)  ,    L(mid,3);
             L(rear,1) ,   L(rear,2) ,    L(rear,3);}
            */
    
double dblPosition_mtrs[N_CHANNELS]; //the actual chamber lengths in meters given as the change in length relative to neutral (should always be >=0)
double dblPressure_bar[N_CHANNELS]; //the pressure in a given chamber in bar (1 bar  = 100,000 Pa).
double dblTargetTime_s; //the time in seconds desired to reach the target (>0...!)

//
Semaphore semReplan(1);// this must be set every time new high-level transmissions are received to allow replanning to take place!
//--------------------------------------------------------------------------------------------------------------------------//

//boolean flag to indicate that new target information has been received
//bool blnReplan;//set this when new transmission is received over ethernet

//path variables
double dblVelocity_mmps[N_CHANNELS];//the linear path velocity (not sent to actuator)
double dblLinearPathCurrentPos_mm[N_CHANNELS]; //the current position of the linear path (not sent to actuator)
double dblSmoothPathCurrentPos_mm[N_CHANNELS]; //the current position of the smooth path (not sent to actuator)

double dblTargetActLen_mm[N_CHANNELS];
double dblPathToActuator[N_CHANNELS];//the target position for the actuator (sent to actuator)

int intDemandPos_Tx[N_CHANNELS]; //13-bit value to be sent to the actuator
int intPosSPI_Rx[N_CHANNELS]; //13 bit value received over SPI from the actuator
int intPosADC_Rx[N_CHANNELS]; //12-bit ADC reading of potentiometer on actuator

double dblPathTolerance; //how close the linear path must get to the target position before considering it a success.

double dblActuatorConversion[N_CHANNELS] = {0.24176,0.24176,0.24176,0.24176,0.24176,0.36264,0.36264};// covert from chamber lengths to actuator lengths

double dblSmoothingFactor = 0.5; // 0<x<=1, where 1 is no smoothing

char chrErrorFlag[N_CHANNELS];// 3 error bits from LL

Serial pc(USBTX, USBRX); // tx, rx for usb debugging


InterruptIn pinGate6(PE_11); //this pin HAS TO BE defined before SPI set up. No Clue Why.

SPI spi(PC_12, PC_11, PC_10); // mosi, miso, sclk

DigitalOut cs_LL[N_CHANNELS] = {PD_15, PE_10, PD_14, PD_11, PE_7, PD_12, PF_10, PD_13};//chip select for low level controller
DigitalOut cs_ADC[N_CHANNELS] = {PG_12, PG_9, PE_1, PG_0, PD_0, PD_1, PF_0, PF_1}; //chip select for ADC

DigitalOut pinCheck(PE_5);

//InterruptIn pinGate[N_CHANNELS] ={PF_11, PG_14, PF_15, PF_12, PF_3, PF_13, PE_11, PE_13};//gate interrupt pins

//WRONG!!!!!!!!!!
//InterruptIn pinGate[N_CHANNELS] ={PG_11, PG_14, PF_15, PF_12, PF_3, PF_13, PE_11, PE_13};//gate interrupt pins
//WRONG!!!!!

//InterruptIn testPinGate(PF_11);
//these interrupt pins have to be declared AFTER SPI declaration. No Clue Why.
InterruptIn pinGate0(PF_11);
InterruptIn pinGate1(PG_14);
InterruptIn pinGate2(PF_15);
InterruptIn pinGate3(PF_12);
InterruptIn pinGate4(PF_3);
InterruptIn pinGate5(PF_13);
//InterruptIn pinGate6(PE_11); //
InterruptIn pinGate7(PE_13);

DigitalOut pinReset(PD_2); //reset pin for all controllers.


EventQueue queue(32 * EVENTS_EVENT_SIZE);

Thread t(osPriorityRealtime);

Thread threadReplan(osPriorityBelowNormal);
Thread threadPathPlan(osPriorityNormal);

Thread threadSimulateDemand(osPriorityHigh);

Mutex mutChannel[N_CHANNELS];
Mutex mutPsi;

Semaphore semPathPlan(1);//

Timer timer;//timers are nice


/*
  unsigned int result1 = 0;
  unsigned int result2 = 0;
  unsigned int result3 = 0;
  unsigned int result4 = 0;
  
  unsigned int result1a = 0;
  unsigned int result1b = 0;
*/



/*
double dblDemandPosition[N_CHANNELS];
double dblPosition[N_CHANNELS];
double dblPressure[N_CHANNELS];
*/

int ThreadID[N_CHANNELS];

bool isDataReady[N_CHANNELS]; // flag to indicate path data is ready for transmission to low level.

double ReadADCPosition(int channel)
{
    unsigned int outputA;
    unsigned int outputB;
    int output;
    double dblOutput;

    spi.format(8,0);
    spi.frequency(1000000);
    
    cs_ADC[channel] = 0;
    spi.write(PREAMBLE);
    outputA = spi.write(CHAN_3);
    outputB = spi.write(0xFF);
    cs_ADC[channel]= 1;
    
    outputA = outputA & DATA_MASK;
    outputA = outputA<<8;
    output = (outputA | outputB);
    output = 4095- output;
    dblOutput = (double) (output - 1504)/4095*100.0;
    return dblOutput;
}
    
double ReadADCPressure(int channel)
{
    unsigned int outputA;
    unsigned int outputB;
    int output;
    double dblOutput;

    spi.format(8,0);
    spi.frequency(1000000);
    
    cs_ADC[channel] = 0;
    spi.write(PREAMBLE);
    outputA = spi.write(CHAN_1);
    outputB = spi.write(0xFF);
    cs_ADC[channel] = 1;
    
    outputA = outputA & DATA_MASK;
    outputA = outputA<<8;
    output = (outputA | outputB);
    
    dblOutput = (double) (output-502)/4095*8.0;
    return dblOutput;
}

void SendReceiveData(int channel) 
{
    
    //get data from controller
    spi.format(16,2);
    spi.frequency(LOW_LEVEL_SPI_FREQUENCY);
    
    cs_LL[channel] = 0;//select relevant chip
    intPosSPI_Rx[channel] = spi.write(intDemandPos_Tx[channel]); //transmit & receive
    cs_LL[channel] = 1;//deselect chip
    isDataReady[channel] = 0;//data no longer ready, i.e. we now require new data
    

    
    //sort out received data
    chrErrorFlag[channel] = intPosSPI_Rx[channel]>>13;
    intPosSPI_Rx[channel] = intPosSPI_Rx[channel] & 0x1FFF;
    dblPosition_mtrs[channel] = (double)intPosSPI_Rx[channel]/8191*(MAX_ACTUATOR_LENGTH/dblActuatorConversion[channel])/1000;
    //printf("%d, %d\r\n",intPosSPI_Rx[0],intDemandPos_Tx[0]);
    
}

void testSendReceiveData() 
{
    printf("x\r\n");
    //get data from controller
    spi.format(16,2);
    spi.frequency(LOW_LEVEL_SPI_FREQUENCY);
    
    cs_LL[0] = 0;//select relevant chip
    intPosSPI_Rx[0] = spi.write(intDemandPos_Tx[0]); //transmit & receive
    cs_LL[0] = 1;//deselect chip
    isDataReady[0] = 0;//data no longer ready, i.e. we now require new data
    
    //sort out received data
    chrErrorFlag[0] = intPosSPI_Rx[0]>>13;
    intPosSPI_Rx[0] = intPosSPI_Rx[0] & 0x1FFF;
    dblPosition_mtrs[0] = (double)intPosSPI_Rx[0]/8191*(MAX_ACTUATOR_LENGTH/dblActuatorConversion[0])/1000;
    
    
}

//common rise handler function 

void common_rise_handler(int channel)
{
    pinCheck = 1;
    //check if data is ready for tranmission
    if (isDataReady[channel])
    {
        
        // Add transmit task to event queue
        ThreadID[channel] = queue.call(SendReceiveData,channel);//schedule transmission
    }
}

void testRiseFunction(void)
{
    pinCheck = 1;
    //check if data is ready for tranmission
    if (isDataReady[0])
    {
        // Add transmit task to event queue
        ThreadID[0] = queue.call(testSendReceiveData);//schedule transmission
    }
}

void testFallFunction(void)
{
    pinCheck = 0;
    //cancel relevant queued event    
    queue.cancel(ThreadID[0]);
}



//common_fall handler functions
void common_fall_handler(int channel) 
{
    pinCheck = 0;
    //cancel relevant queued event    
    queue.cancel(ThreadID[channel]);
}

//stub rise functions
void rise0(void) { common_rise_handler(0); }
void rise1(void) { common_rise_handler(1); }
void rise2(void) { common_rise_handler(2); }
void rise3(void) { common_rise_handler(3); }
void rise4(void) { common_rise_handler(4); }
void rise5(void) { common_rise_handler(5); }
void rise6(void) { common_rise_handler(6); }
void rise7(void) { common_rise_handler(7); }

//stub fall functions
void fall0(void) { common_fall_handler(0); }
void fall1(void) { common_fall_handler(1); }
void fall2(void) { common_fall_handler(2); }
void fall3(void) { common_fall_handler(3); }
void fall4(void) { common_fall_handler(4); }
void fall5(void) { common_fall_handler(5); }
void fall6(void) { common_fall_handler(6); }
void fall7(void) { common_fall_handler(7); }

void startPathPlan() // Plan a new linear path after receiving new target data
{
    semPathPlan.release(); // Uses threadReplan which is below normal priority to ensure consistent transmission to LL
}

//this function will be called when a new transmission is received from high level
void ReplanPath()
{
    
    //while(1)
    //{
        //semReplan.wait();//wait until called
        //printf("replan!\r\n");
        //for (ii = 0; ii < N_CHANNELS; ii++)
//        {
//            mutChannel[ii].lock();
//        }
        
        mutPsi.lock();// lock mutex for PSI to ensure no conflict when receiving new messages while already replanning
        //!!!!!!!!!!!!!!! If received messages faster than path replanning, will get increasingly behind time --> need to throw away some instructions
        
        double dblTargetChambLen_mm[N_CHANNELS]; //the currenly assigned final target position (actuator will reach this at end of path)
        //update front segment
        dblTargetChambLen_mm[0] = dblPSI[0][0]*1000;
        dblTargetChambLen_mm[1] = dblPSI[0][1]*1000;
        dblTargetChambLen_mm[2] = dblPSI[0][2]*1000;
        //update mid segment
        dblTargetChambLen_mm[6] = dblPSI[1][0]*1000;
        dblTargetChambLen_mm[7] = dblTargetChambLen_mm[6]; //same because two pumps are used
        //update rear segment
        dblTargetChambLen_mm[3] = dblPSI[2][0]*1000;
        dblTargetChambLen_mm[4] = dblPSI[2][1]*1000;
        dblTargetChambLen_mm[5] = dblPSI[2][2]*1000;
       /* dblTargetChambLen_mm = { psi.
        
        dblTargetTime_s*/
        
        mutPsi.unlock();// unlock mutex for PSI to ensure no conflict when receiving new messages while already replanning
        
//        for (ii = 0; ii < N_CHANNELS; ii++)
//        {
//            mutChannel[ii].unlock();
//        }

        for (ii = 0; ii< N_CHANNELS; ii++)
        {
            mutChannel[ii].lock();//lock variables to avoid race condition
            
            //check to see if positions are achievable
            if(dblTargetChambLen_mm[ii]>dblMaxChamberLengths_mm[ii]) 
            {
                dblTargetChambLen_mm[ii] = dblMaxChamberLengths_mm[ii]; 
            }
            
            if(dblTargetChambLen_mm[ii]<0.0) 
            {
                dblTargetChambLen_mm[ii] = 0.0; 
            }
            
            dblTargetActLen_mm[ii] = dblTargetChambLen_mm[ii]*dblActuatorConversion[ii];
                
            //work out new velocities
            dblVelocity_mmps[ii] = (dblTargetActLen_mm[ii] - dblLinearPathCurrentPos_mm[ii]) / dblTargetTime_s;
            
            //check to see if velocities are achievable
            if(abs(dblVelocity_mmps[ii]) > MAX_SPEED_MMPS)
            {
                if(dblVelocity_mmps[ii]>0)
                {
                    dblVelocity_mmps[ii] = MAX_SPEED_MMPS;
                }
                else 
                {
                    dblVelocity_mmps[ii] = -1*MAX_SPEED_MMPS;
                }
            }
            
            mutChannel[ii].unlock(); //unlock mutex.
        }
    //}
    
    
}

// lock mutex
//  get variables
// unlock mutex
// do calculations
// lock mutex
//  set variables
// unlock mutex 

void CalculateSmoothPath()
{
    double dblMeasuredSampleTime;
    while(1)
    {
        semPathPlan.wait();
        //if(blnReplan)
//        {
//            blnReplan = 0;//remove flag
//            ReplanPath(dblPSI, dblTargetTime_s);
//        }
        
        // If run time is more than 50 us from expected, calculate from measured time step
        if (fabs(PATH_SAMPLE_TIME_S*1000000 - timer.read_us()) > 50)
        {
            dblMeasuredSampleTime = timer.read();
        }
        else
        {
            dblMeasuredSampleTime = PATH_SAMPLE_TIME_S;
        }
        timer.reset();    
            
        for(ii = 0; ii < N_CHANNELS; ii++)
        {
            //calculate next step in linear path
            mutChannel[ii].lock();//lock relevant mutex
            dblLinearPathCurrentPos_mm[ii] = dblLinearPathCurrentPos_mm[ii] + dblVelocity_mmps[ii]*dblMeasuredSampleTime; // PATH_SAMPLE_TIME_S SHOULD BE MEASURED
            
            //check tolerance
            if (fabs(dblLinearPathCurrentPos_mm[ii] - dblTargetActLen_mm[ii]) <= dblPathTolerance)
            {
                dblVelocity_mmps[ii] = 0; //stop linear path generation when linear path is within tolerance of target position.
            }
            mutChannel[ii].unlock();//unlock relevant mutex
            
            //calculate next step in smooth path
            dblSmoothPathCurrentPos_mm[ii] = dblSmoothingFactor*dblLinearPathCurrentPos_mm[ii] + (1.0-dblSmoothingFactor)*dblSmoothPathCurrentPos_mm[ii];
            
            //convert to actuator distances
            dblPathToActuator[ii] = dblSmoothPathCurrentPos_mm[ii];
            
            intDemandPos_Tx[ii] = (int) ((dblPathToActuator[ii]/MAX_ACTUATOR_LENGTH)*8191);//convert to a 13-bit number;
            
            intDemandPos_Tx[ii] = intDemandPos_Tx[ii] & 0x1FFF; //ensure number is 13-bit 
            
            dblPressure_bar[ii] = ReadADCPressure(ii);
            dblPosition_mtrs[ii] = ReadADCPosition(ii);
          
            isDataReady[ii] = 1;//signal that data ready

        }
        printf("%f, %d\r\n",dblPathToActuator[0], intDemandPos_Tx[0]); 
        //printf("%f,%f, %f, %f\r\n", dblTargetActLen_mm[0], dblSmoothPathCurrentPos_mm[0],dblPressure_bar[0], dblPosition_mtrs[0]);
    }
}

void SimulateDemand() // For testing purposes
{
    int kk = 0;
    while(1)
    {
        mutChannel[0].lock();
        if(kk == 0)
        {
            dblPSI[0][0] = (double) 10.0;
            dblTargetTime_s = 1.5;
        }
        else
        { 
            dblPSI[0][0] = (double) 0.0; 
            dblTargetTime_s = 2.0;
        }
        
        kk = 1 - kk;
        
        
        //semReplan.release();
        
        mutChannel[0].unlock();
        ReplanPath();
    
        Thread::wait(7000);
    }
}



Ticker PathCalculationTicker;

int main() 
{
    for (ii = 0; ii <250; ii++)
    {
        pinCheck = 1;
        wait(0.001);
        pinCheck = 0;
        wait(0.001);
    }
    
    //initialise relevant variables
    for(ii = 0; ii<N_CHANNELS; ii++)
    {
        
        //all chip selects in off state
        cs_LL[ii] = 1;
        cs_ADC[ii] = 1;
        
        //data ready flags set to not ready
        isDataReady[ii] = 0;
    }
    
    //calculate some control variables
    dblPathTolerance = 0.1;//MAX_SPEED_MMPS * PATH_SAMPLE_TIME_S * 1.05; //path tolerance in mm. 
    
    pinReset = 1; //initialise reset pin to not reset the controllers.
    wait(0.1);
    pinReset=0; //reset controllers to be safe
    wait(0.1);
    pinReset = 1;//ready to go
    
    //say something nice to the user.
    pc.baud(9600);
    printf("Hi, there! I'll be your mid-level controller for today.\r\n");
    
    // Start the event queue
    t.start(callback(&queue, &EventQueue::dispatch_forever));
    
    //set up the interrupts

    //set up rise interrupts MIGHT NOT NEED TO BE POINTERS
    //
    //pinGate[0].rise(&testRiseFunction);
    //testPinGate.rise(&testRiseFunction);//<working
    //testPinGate.rise(&rise0);
    
    pinGate0.rise(&rise0);
    /*
    pinGate1.rise(&rise1);
    pinGate2.rise(&rise2);
    pinGate3.rise(&rise3);
    pinGate4.rise(&rise4);
    pinGate5.rise(&rise5);
    pinGate6.rise(&rise6);
    pinGate7.rise(&rise7);
    */
    
    //set up fall interrupts MIGHT NOT NEED TO BE POINTERS
    //
    //pinGate[0].fall(&testFallFunction);
    //testPinGate.fall(&testFallFunction); <working
    //testPinGate.fall(&fall0);
    
    pinGate0.fall(&fall0);
    /*
    pinGate1.fall(&fall1);
    pinGate2.fall(&fall2);
    pinGate3.fall(&fall3);
    pinGate4.fall(&fall4);
    pinGate5.fall(&fall5);
    pinGate6.fall(&fall6);
    pinGate7.fall(&fall7);
    */
    
    timer.start();
    
    threadSimulateDemand.start(SimulateDemand);
    threadPathPlan.start(CalculateSmoothPath); //start planning thread
    
    PathCalculationTicker.attach(&startPathPlan, PATH_SAMPLE_TIME_S); //set up planning thread to recur at fixed intervals
    
    threadReplan.start(ReplanPath);//start Replanning thread 
     Thread::wait(1);  
    
      
    while(1) {
        Thread::wait(osWaitForever); 
    }
}