Digital speed controller

Dependencies:   USBDevice mbed

main.cpp

Committer:
BPPearson
Date:
2016-01-05
Revision:
0:14f74b704963

File content as of revision 0:14f74b704963:

#include "mbed.h"
#include "USBSerial.h"

DigitalOut myled(LED1);


#include    <float.h>
#include    <math.h>
#include    <stdio.h>
#include    <string.h>


#define revString            "V1.00"


//#define BAUD_RATE                     19200      /* Serial baud rate */
#define DEBOUNCE_TIME                    10      /* 10 * timer0Cntr1 cycle times */
#define OUTSIDE_COUNT_BAND_LIMIT         40      /* uchar counter limit */
#define MAX_COUNT_VALUE              150000      /* maximum difference between counts */
#define MIN_COUNT_VALUE                1000      /* minimum difference between counts */
#define OUTPUT_CALIBRATION_FACTOR      10.0 / 2048.0  /* 0..2047 binary to 0..10 V */

/* type definitions */
#define uchar    unsigned char
#define uint     unsigned int

/* boolean definitions */
#define FALSE              0
#define TRUE               1


/* ascii definitions */
#define CR               '\x0D'


// peripherals and function blocks used on LPC1768
DigitalOut txLed(LED1);
DigitalOut rxLed(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
DigitalOut rs485Enable(p8);
DigitalOut d2aEnable1(p11);
DigitalOut d2aEnable2(p12);

DigitalOut scope1(p19);
DigitalOut scope2(p20);

InterruptIn sensor1(p15);
InterruptIn sensor2(p16);
InterruptIn sensor3(p17);
InterruptIn sensor4(p18);
InterruptIn sensor5(p21);
InterruptIn sensor6(p22);
InterruptIn sensor7(p23);
InterruptIn sensor8(p24);

PwmOut pulseEvery10ms(p25);

Serial host(p9, p10);
Serial cascade(p13, p14);
Serial pc(USBTX, USBRX); // tx, rx

SPI spi(p5,p6,p7);

Ticker oneMSec;
Ticker fiveMSec;

USBSerial serial;

/* port 3 bit definitions */
#define DTOA_DATA       0x20
#define DTOA_CLOCK      0x40
#define DTOA_SYNC       0x80

/* port 6 bit definitions */
#define WATCHDOG        0x40

/* lcd page definitions */
#define RESET_PAGE         0
#define SPEED_PAGE         1
#define COUNT_PAGE         2
#define DEBUG1_PAGE        3
#define COMMS_PAGE         4
#define LAST_PAGE          4

/* status bits */
#define h8Reset           0x01

/* host comms status definitions */
#define badCircumference     1
#define badSpeedSetpoint     2
#define badRatioSetpoint     3
#define badOutputSetpoint    4
#define badIntGainSetpoint   5
#define badPropGainSetpoint  6
#define badNumOfTeeth        7
#define badMaxSpeedSetpoint  8
#define unknownPacket        9

/* control modes */
#define outputMode         0
#define ratioMode          1
#define speedMode          2
#define thirdNipMode       3

const char *modestrings[4] = {"Out:","Rat:","Spd:","3rd:"};

#define OUTPUT_WITHIN      0
#define OUTPUT_LOW         1
#define OUTPUT_HIGH        2

#define commsBufLen       12  /* Anything over 12 will need display routine changed */

#define hexToBin(x)       (x > '9') ? x - '7' : x - '0'

#define LAST_EXT_PAGE  28


const float teethOnEveryWheel[21] = { 0.0, 30.0, 30.0,
                0.0, 0.0, 120.0, 60.0, 30.0,
                20.0, 60.0, 60.0, 60.0,
                60.0, 60.0, 35.0, 35.0,
                35.0, 35.0, 35.0, 20.0,
                20.0 };

const float pulleyRatios[21] = { 1.0, 1.0, 1.0,
                1.0, 1.0,
                1.0, 1.0, 1.0,
                1.0, 1.0, 1.0, 1.0,
                1.0, 1.0, 1.0, 1.0,
                1.0, 1.0, 1.0, 1.0,
                1.0 };

const float circumOfRoll[21] = { 0.0, 1.0, 1.0,
                0.0, 0.0,
                0.75084, 1.2252, 1.2252,
                0.82090, 2.00000, 2.00000, 2.00000,
                2.00000, 2.00000, 1.09955, 1.09955,
                1.03044, 1.03044, 1.09955, 0.68420,
                0.82090 };

/* Rope threader circ changed from 0.33500 to 0.42949       */
/* Then changed to 0.68420 as it was doing 137 but saying 86 */

/* because it needed a 0.78 ratio to run at the right speed */


const float maxSpeed[21] = { 1.0, 76.5, 76.5,
                           74.0,  74.0,
                           35.0, 220.0, 220.0,
                          220.0, 220.0, 220.0, 220.0,
                          220.0, 220.0, 220.0, 220.0,
                          220.0, 220.0, 220.0, 250.0,
                          220.0 };

const float propGains[21] = { 0.01, 0.015, 0.015,
                             0.015, 0.015,
                             0.015, 0.005, 0.015,
                             0.01, 0.01, 0.01, 0.01,
                             0.01, 0.01, 0.01, 0.01,
                             0.01, 0.01, 0.01, 0.005,
                             0.01};

const float intGains[21] = { 0.01, 0.006, 0.006,
                            0.006, 0.006,
                            0.006, 0.0010, 0.006,
                            0.003, 0.003, 0.003, 0.003,
                            0.003, 0.003, 0.003, 0.003,
                            0.003, 0.003, 0.003, 0.001,
                            0.003 };

const char *driveName[21] = { "All Drives  ","Melt Pump A   ","Melt Pump B   ",
                            "Inner Extruder","Outer Extruder",
                            "First Nip     ","Second Nip    ","Third Nip     ",
                            "4th Nip East  ","1st Bole East ","2nd Bole East ","3rd Bole East ",
                            "4th Bole East ","5th Bole East ","Cooler 1 East ","Cooler 2 East ",
                            "Detreat 1 East","Detreat 2 East","Cooler 3 East ","E Rope Thread ",
                            "4th Nip West  " };

const uchar modeAtReset[21] = { outputMode, speedMode, speedMode,
                                outputMode, outputMode, speedMode, ratioMode, thirdNipMode,
                                ratioMode, ratioMode, ratioMode, ratioMode,
                                ratioMode, ratioMode, ratioMode, ratioMode,
                                ratioMode, ratioMode, ratioMode, ratioMode,
                                ratioMode };

const int startupCount[21] = { 0, 4, 4,
                               0, 0, 20, 30, 30,
                              50, 100, 100, 100,
                             100, 100, 100, 100,
                             100, 100, 100, 100,
                              50 };

const char *debugtitles[28] = { "Invalid   ",
                              "Mode      ",
                              "Address   ",
                              "Age       ",
                              "In Band   ",
                              "In Startup",
                              "Comms Stat",
                              "DtoA Stpt ",
                              "actual Cnt",
                              "cnt Stpt  ",
                              "loCnt Lmt ",
                              "hiCnt Lmt ",
                              "last BCnt ",
                              "last SCnt ",
                              "Pull Ratio",
                              "Circumf   ",
                              "Max Dv Spd",
                              "Teeth     ",
                              "Start Cnt ",
                              "Prev Ratio",
                              "P.P.M.P.S.",
                              "Spd Stpt  ",
                              "Spd Prev  ",
                              "Act Spd   ",
                              "Cnt error ",
                              "Int Sum   ",
                              "propGain  ",
                              "intGain   "};

/* variables */
uchar   commsBuf0[commsBufLen+1];
uchar   commsCntr0;
uchar   commsBuf1[commsBufLen+1];
uchar   commsCntr1;
uchar   boardAddress;
uchar   activeControllers;
uchar   hostCrRcvd;
uchar   cascadeCrRcvd;
uchar   updateLcdDue;
uchar   updateAnalogOutput[8];
uchar   lcdPage;
uchar   overflowCntr;
uchar   frtOverflowCntr;
uchar   pageChanged;
uchar   redrawLcd;
uchar   disableHostComms;
uchar   extended_lcd_page;

uchar   xmitCascadeSpeed[8];
uchar   recalcSpeedSetpoint[8];
uchar   recalcFixedPart[8];
uchar   recalcReqdCount[8];
uchar   processFrtReading[8];
uchar   withinControlBand[8];                   /* flag */
uchar   outsideCountBand[8];                  /* counter */
uchar   noPulseCntr[8];
uchar   inStartupMode[8];
uchar   startupCntr[8];
uchar   controlMode[8];
uchar   status;
uchar   output_limit_status[8];


int     hostCommsStatus;
int     timer0Cntr;
int     dtoaSetpnt[8];
int     maxDtoa[8];
int     minDtoa[8];


unsigned long   prevFrtValue[8];
unsigned long   currentFrtValue[8];
unsigned long   currentCountDiff[8];

unsigned long    actualCount[8];
unsigned long    countSetpoint[8];
unsigned long    loCountLimit[8];
unsigned long    hiCountLimit[8];
unsigned long    lastBigCount[8];
unsigned long    lastSmallCount[8];
unsigned long    ageInSecs;


float   pulleyRatio[8];
float   refFreq;
float   rollCircum[8];
float   maxSpeedOfDrive[8];
float   teethOnWheel[8];
float   ratioToPrev[8];
float   pulsesPerMetrePerSec[8];
float   speedSetpnt[8];
float   speedOfPrev[8];
float   measuredSpeed[8];

float   errorCount[8];
float   intSum[8];
float   propGain[8];
float   intGain[8];


/* Procedure declarations */
void            pin15ISR();
void            pin16ISR();
void            pin17ISR();
void            pin18ISR();
void            pin21ISR();
void            pin22ISR();
void            pin23ISR();
void            pin24ISR();
void            acknowledge_host(int idx);
float           calcActualSpeed();
void            calcFixedPart(int idx);
void            disable_host_comms();
void            enable_host_comms();
void            enable_interrupts();
void            init_free_running_counter();
void            init_locals();
void            init_timer0();
void            init_uarts();
void            lcd_put( uchar dat );
void            lcd_str( char *str );
void            processCascadeComms();
void            processHostComms();
void            process_latest_count(int idx);
void            putch0(uchar c);
void            putch1(uchar c);
void            putstr0(char *str);
void            putstr1(char *str);
void            serial_rcv_err_int0();
void            serial_rcv_err_int1();
void            serial_rcv_int0();
void            serial_rcv_int1();
void            repetitiveControlUpdate();
void            repetitiveUpdateTimer();
void            updateDtoaChip(int idx);
void            xmit_cascade_speed(int idx);



int    main()

{
    
    /* initialize serial ports and vectors */
    init_uarts();                               /* initialize the uarts  */

    /* initialise locals */
    init_locals();

    /* initialize periodic timers */
    oneMSec.attach(&repetitiveUpdateTimer, 0.001);
    fiveMSec.attach(&repetitiveControlUpdate, 0.005);
    
    /* enable free running counter */
    LPC_SC->PCONP |= 1 < 1;         //timer0 power on
    LPC_TIM0->TCR = 1;              //enable Timer2

    // configure interrupt inputs
    sensor1.mode(PullUp);
    sensor1.fall(&pin15ISR);  
    sensor2.mode(PullUp);
    sensor2.fall(&pin16ISR);  
    sensor3.mode(PullUp);
    sensor3.fall(&pin17ISR);  
    sensor4.mode(PullUp);
    sensor4.fall(&pin18ISR);  
    sensor5.mode(PullUp);
    sensor5.fall(&pin21ISR);  
    sensor6.mode(PullUp);
    sensor6.fall(&pin22ISR);  
    sensor7.mode(PullUp);
    sensor7.fall(&pin23ISR);  
    sensor8.mode(PullUp);
    sensor8.fall(&pin24ISR);  

    /* enable interrupts */
    enable_interrupts();

    // initialise D to A enables high
    d2aEnable1 = 1;
    d2aEnable2 = 1;
    
    // configure serial peripheral interface: 16 bits clocked on first rising edge at a frequency of 1MHz
    spi.format(16, 0);
    spi.frequency(1000000);

    // start pulse output to check interrupt inputs
    pulseEvery10ms.period_ms(10);
    pulseEvery10ms.write(0.1);

    /* set bit in status to reflect reset of h8 */
    status |= h8Reset;

    while (1)
    {
        char ch = 0;
        char buff[10];
        int idx = 0;
        
        while (ch != 0x0d && idx < 9)
        {
            ch = serial.getc();
            
            buff[idx++] = ch;
        }
        
        switch (buff[0])
        {
            case '1':
                serial.printf("Measured speed ");
                for (int i=0; i<activeControllers - 1; i++)
                    serial.printf("%5.1f, ", measuredSpeed[i]);

                serial.printf("%5.1f\n", measuredSpeed[activeControllers - 1]);

                serial.printf("Actual count ");
                for (int i=0; i<activeControllers - 1; i++)
                    serial.printf("%4d, ", actualCount[i]);

                serial.printf("%4d\n", actualCount[activeControllers - 1]);

                serial.printf("Count setpoint ");
                for (int i=0; i<activeControllers - 1; i++)
                    serial.printf("%4d, ", countSetpoint[i]);

                serial.printf("%4d\n", countSetpoint[activeControllers - 1]);
                break;
            default:
                break;
        }

        // wait a while to allow other things to run
        wait_ms(1);
    }
}



void pin15ISR()
{

    // read and save value of free running timer
    currentFrtValue[0] = LPC_TIM0->TC;
    
    // calculate difference in count from last interrupt pulse
    currentCountDiff[0] = currentFrtValue[0] - prevFrtValue[0];
    
    // save the current value to the previous for the next time
    prevFrtValue[0] = currentFrtValue[0];

    /* set flag for background routine to process */
    processFrtReading[0] = TRUE;

    /* clear no pulse counter */
    noPulseCntr[0] = 0;

    /* if in startup mode */
    if (inStartupMode[0])
    {
        /* increment starup counter for each tooth of the wheel */
        startupCntr[0]++;
    }
}



void pin16ISR()
{

    currentFrtValue[1] = LPC_TIM0->TC;

    // calculate difference in count from last interrupt pulse
    currentCountDiff[1] = currentFrtValue[1] - prevFrtValue[1];
    
    // save the current value to the previous for the next time
    prevFrtValue[1] = currentFrtValue[1];

    /* set flag for background routine to process */
    processFrtReading[1] = TRUE;

    /* clear no pulse counter */
    noPulseCntr[1] = 0;

    /* if in startup mode */
    if (inStartupMode[1])
    {
        /* increment starup counter for each tooth of the wheel */
        startupCntr[1]++;
    }
}



void pin17ISR()
{

    currentFrtValue[2] = LPC_TIM0->TC;

    // calculate difference in count from last interrupt pulse
    currentCountDiff[2] = currentFrtValue[2] - prevFrtValue[2];
    
    // save the current value to the previous for the next time
    prevFrtValue[2] = currentFrtValue[2];

    /* set flag for background routine to process */
    processFrtReading[2] = TRUE;

    /* clear no pulse counter */
    noPulseCntr[2] = 0;

    /* if in startup mode */
    if (inStartupMode[2])
    {
        /* increment starup counter for each tooth of the wheel */
        startupCntr[2]++;
    }
}



void pin18ISR()
{

    currentFrtValue[3] = LPC_TIM0->TC;

    // calculate difference in count from last interrupt pulse
    currentCountDiff[3] = currentFrtValue[3] - prevFrtValue[3];
    
    // save the current value to the previous for the next time
    prevFrtValue[3] = currentFrtValue[3];

    /* set flag for background routine to process */
    processFrtReading[3] = TRUE;

    /* clear no pulse counter */
    noPulseCntr[3] = 0;

    /* if in startup mode */
    if (inStartupMode[3])
    {
        /* increment starup counter for each tooth of the wheel */
        startupCntr[3]++;
    }
}



void pin21ISR()
{

    currentFrtValue[4] = LPC_TIM0->TC;

    // calculate difference in count from last interrupt pulse
    currentCountDiff[4] = currentFrtValue[4] - prevFrtValue[4];
    
    // save the current value to the previous for the next time
    prevFrtValue[4] = currentFrtValue[4];

    /* set flag for background routine to process */
    processFrtReading[4] = TRUE;

    /* clear no pulse counter */
    noPulseCntr[4] = 0;

    /* if in startup mode */
    if (inStartupMode[4])
    {
        /* increment starup counter for each tooth of the wheel */
        startupCntr[4]++;
    }
}



void pin22ISR()
{

    currentFrtValue[5] = LPC_TIM0->TC;

    // calculate difference in count from last interrupt pulse
    currentCountDiff[5] = currentFrtValue[5] - prevFrtValue[5];
    
    // save the current value to the previous for the next time
    prevFrtValue[5] = currentFrtValue[5];

    /* set flag for background routine to process */
    processFrtReading[5] = TRUE;

    /* clear no pulse counter */
    noPulseCntr[5] = 0;

    /* if in startup mode */
    if (inStartupMode[5])
    {
        /* increment starup counter for each tooth of the wheel */
        startupCntr[5]++;
    }
}



void pin23ISR()
{

    currentFrtValue[6] = LPC_TIM0->TC;

    // calculate difference in count from last interrupt pulse
    currentCountDiff[6] = currentFrtValue[6] - prevFrtValue[6];
    
    // save the current value to the previous for the next time
    prevFrtValue[6] = currentFrtValue[6];

    /* set flag for background routine to process */
    processFrtReading[6] = TRUE;

    /* clear no pulse counter */
    noPulseCntr[6] = 0;

    /* if in startup mode */
    if (inStartupMode[6])
    {
        /* increment starup counter for each tooth of the wheel */
        startupCntr[6]++;
    }
}



void pin24ISR()
{

    currentFrtValue[7] = LPC_TIM0->TC;

    // calculate difference in count from last interrupt pulse
    currentCountDiff[7] = currentFrtValue[7] - prevFrtValue[7];
    
    // save the current value to the previous for the next time
    prevFrtValue[7] = currentFrtValue[7];

    /* set flag for background routine to process */
    processFrtReading[7] = TRUE;

    /* clear no pulse counter */
    noPulseCntr[7] = 0;

    /* if in startup mode */
    if (inStartupMode[7])
    {
        /* increment starup counter for each tooth of the wheel */
        startupCntr[7]++;
    }
}



void    acknowledge_host(int idx)
{

    /* enable host comms */
    enable_host_comms();

    /* acknowledge packet */
    if (hostCommsStatus == 0)
    {
        putch0( '>' );
        putch0( '0'+ output_limit_status[idx] );
        putch0( CR );
    }
    else
    {
        putch0( '#' );
        putch0( CR );
    }

    /* set counter to disable host comms xcvr in 2 timer periods */
    /* the timer period should be optimised to get this down to msecs */
    disableHostComms = 3;
}



float   calcActualSpeed(int idx)
{

    return (pulsesPerMetrePerSec[idx] / (float)actualCount[idx]);
}



void    calcFixedPart(int idx)
{

    pulsesPerMetrePerSec[idx] = ((refFreq * rollCircum[idx] * 60 * pulleyRatio[idx]) / teethOnWheel[idx]);
}



void    disable_host_comms()
{

    /* turn off tx led */
    txLed = 0;

    /* disable 485 */
    rs485Enable = 0;
}



void    enable_host_comms()
{

    /* enable 485 */
    rs485Enable = 1;

    /* turn on tx led */
    txLed = 1;
}



void    enable_interrupts()
{
    
    sensor1.enable_irq();
   
    sensor2.enable_irq();

    sensor3.enable_irq();

    sensor4.enable_irq();

    sensor5.enable_irq();

    sensor6.enable_irq();

    sensor7.enable_irq();

    sensor8.enable_irq();
}



void    init_locals()
{

    commsBuf0[0]                    = 0;
    commsBuf0[commsBufLen]          = 0;   /* Terminate for display on LCD */
    commsCntr0                      = 0;
    commsBuf1[0]                    = 0;
    commsBuf1[commsBufLen]          = 0;   /* Terminate for display on LCD */
    commsCntr1                      = 0;
    boardAddress                    = 1;                //******** read from the config file in nvram
    activeControllers               = 4;                //******** read from the config file in nvram
    
    for (int i=0; i<activeControllers; i++)
    {
        int idx = i + boardAddress;
        controlMode[i]              = modeAtReset[idx];
        updateAnalogOutput[i]       = TRUE;
        xmitCascadeSpeed[i]         = FALSE;
        recalcSpeedSetpoint[i]      = FALSE;
        recalcFixedPart[i]          = TRUE;
        recalcReqdCount[i]          = TRUE;

        pulleyRatio[i]              = pulleyRatios[idx];
        rollCircum[i]               = circumOfRoll[idx];
        maxSpeedOfDrive[i]          = maxSpeed[idx];
        teethOnWheel[i]             = teethOnEveryWheel[idx];
        ratioToPrev[i]              = 1.0;
        pulsesPerMetrePerSec[i]     = 30.0;             /* some daft value */
        speedSetpnt[i]              = 8.0;
        speedOfPrev[i]              = 5.0;
        measuredSpeed[i]            = 1.0;              /* some daft value */
        intSum[i]                   = 0.0;
        propGain[i]                 = propGains[idx];
        intGain[i]                  = intGains[idx];
    
        /* initialize but hopefully the next calc will set it to the right value */
        dtoaSetpnt[i]               = (int)(2047.0 * speedSetpnt[idx] / maxSpeedOfDrive[idx]);
        maxDtoa[i]                  = dtoaSetpnt[i];
        minDtoa[i]                  = dtoaSetpnt[i];
    
        /* do crude bumpless transfer */
        intSum[i]                   = (float)dtoaSetpnt[i] / intGain[i];

        prevFrtValue[i]         = 0L;
        currentFrtValue[i]      = 0;
        actualCount[i]          = 0;
        countSetpoint[i]        = 3000;
        loCountLimit[i]         = 2900;
        hiCountLimit[i]         = 3100;
        lastBigCount[i]         = MAX_COUNT_VALUE;
        lastSmallCount[i]       = MIN_COUNT_VALUE;
        processFrtReading[i]    = FALSE;
        withinControlBand[i]    = FALSE;
        outsideCountBand[i]     = 0;
        noPulseCntr[i]          = 0;
        inStartupMode[i]        = FALSE;
        startupCntr[i]          = 0;
        output_limit_status[i]  = 0;
    }

    status               = 0;
    hostCrRcvd           = FALSE;
    cascadeCrRcvd        = FALSE;
    updateLcdDue         = FALSE;
    lcdPage              = RESET_PAGE;
    overflowCntr         = 0;
    frtOverflowCntr      = 0;
    pageChanged          = FALSE;
    redrawLcd            = FALSE;
    disableHostComms     = 0;
    ageInSecs            = 0;

    extended_lcd_page    = 0;

    hostCommsStatus      = 0;
    timer0Cntr           = 0;

    refFreq              = 307200.0;

}



void    init_uarts()
{

    // set host baud rate to 19200
    host.baud(19200);
    
    // attach serial port to handler
    host.attach(&serial_rcv_int0);
 
    // set cascade baud rate to 19200
    cascade.baud(19200);

    // attach serial port to handler
    cascade.attach(&serial_rcv_int1);
}



void    processCascadeComms()
{
    /* Only called when a complete packet has been received      */
    /* Packet contains speed setpoint of previous drive          */
    /* Packet structure is listed below                          */
    /*                                                           */
    /*   *1234<chk><chk><ret> where 1234 is speed setpoint * 10  */

    int      i;
    uchar    chkSum;
    uchar    pcktChkSum;
    int      speed;

    /* check packet structure is valid, 8th char is <cr> */
    if (commsBuf1[7] == CR)
    {
        /* set checksum accumulator to zero */
        chkSum = 0;

        /* checksum packet */
        for (i=0; i<5; i++)
        {
            chkSum ^= commsBuf1[i];
        }

        /* build packet checksum from both chars */
        pcktChkSum = ((hexToBin(commsBuf1[5])) << 4);
        pcktChkSum += (hexToBin(commsBuf1[6]));

        /* if checksum ok */
        if (chkSum == pcktChkSum)
        {
            /* initialize speed accumulator */
            speed = 0;

            /* extract speed from packet */
            for (i=1; i<5; i++)
                speed = (speed * 10) + (commsBuf1[i] - '0');

            /* if valid speed */
            if (speed >= 0 && speed < 3000)
            {
                /* save to speed setpoint of prev drive */
                speedOfPrev[boardAddress] = (float)speed / 10.0;

                /* set flag to recalculate if in ratio mode */
                if (controlMode[boardAddress] == ratioMode)
                {
                    recalcSpeedSetpoint[boardAddress] = TRUE;
                }
            }
        }

    }
}



void    processHostComms()
{
    /* Only called when a complete packet has been received           */
    /* All packets are same length to simplify protocol               */
    /* Valid packets are as listed below                              */
    /*                                                                */
    /*   *01O1234<chk><chk><ret>    sets analog output to 1234        */
    /*   *01S1234<chk><chk><ret>    sets speed setpoint to 123.4      */
    /*   *01R1234<chk><chk><ret>    sets ratio to prev drive to 1.234 */
    /*   *0131234<chk><chk><ret>    sets third nip output to 1234     */
    /*   *01T1234<chk><chk><ret>    sets number of teeth 1234         */
    /*   *01C1234<chk><chk><ret>    sets circumference to 1234 mm     */
    /*   *01P1234<chk><chk><ret>    sets pulley ratio to 1.234        */
    /*   *01M1234<chk><chk><ret>    sets max surface speed to 123.4   */
    /*   *01p1234<chk><chk><ret>    sets prop gain to 1.234           */
    /*   *01i1234<chk><chk><ret>    sets integral gain to 1.234       */
    /*   *01ENQ01<chk><chk><ret>    requests current speed            */
    /*   *01ENQ02<chk><chk><ret>    requests host comms status        */
    /*   *01ENQ03<chk><chk><ret>    requests current status           */
    /*   *01ENQ04<chk><chk><ret>    requests max dtoa value           */
    /*   *01ENQ05<chk><chk><ret>    requests min dtoa value           */
    /*                                                                */
    /* Sending a speed setpoint packet puts the controller into       */
    /* speed mode and so on for ratio and output packets.             */
    /* The controller responds with an > CR or # CR packet to         */
    /* the setpoint type of packets.                                  */
    /* The controller responds with a packet structure as below       */
    /* to the read packets                                            */
    /*                                                                */
    /*   *011234<chk><chk><ret>     where 123.4 is current speed      */
    /*   *010000<chk><chk><ret>     where 0000 is host comms status   */
    /*   *010000<chk><chk><ret>     where 0000 is current status      */
    /*   *011456<chk><chk><ret>     where 1456 is max dtoa value      */
    /*   *011234<chk><chk><ret>     where 1234 is min dtoa value      */

    int      i;
    int      address;
    int      idx;
    int      ival;
    int      intsp;
    uchar    chkSum;
    uchar    pcktChkSum;
    char     buf[12];

    /* check packet structure is valid, 11th char is <cr> */
    if (commsBuf0[10] == CR || commsBuf0[10] == 0x0a)
    {
        /* set checksum accumulator to zero */
        chkSum = 0;

        /* checksum packet */
        for (i=0; i<8; i++)
        {
            chkSum ^= commsBuf0[i];
        }

        /* build packet checksum from both chars */
        pcktChkSum = ((hexToBin(commsBuf0[8])) << 4);
        pcktChkSum += (hexToBin(commsBuf0[9]));

        /* if checksum ok */
        if (chkSum == pcktChkSum)
        {
            /* extract address from packet */
            address = ((commsBuf0[1] - '0') * 10) + (commsBuf0[2] - '0');

            //pc.printf("Address %d\n", address);
            
            /* check if packet was for this controller or board set to reply to all addresses */
            if ((address >= boardAddress && address < (boardAddress + activeControllers))|| boardAddress == 0 )
            {
                // index into array is address of loop - board address
                idx = address - boardAddress;
            
                //pc.printf("Idx %d\n", idx);
                
                /* set status to zero */
                hostCommsStatus = 0;

                /* process packet request */
                switch (commsBuf0[3])
                {
                    case 'C':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        /* check value is sensible */
                        if (ival > 300 && ival < 2000)
                        {
                            /* put ivalerence into metres */
                            rollCircum[idx] = (float)ival / 1000.0;

                            /* set flag to force recalc */
                            recalcFixedPart[idx] = TRUE;
                            
                            //pc.printf("circumference %5.3f\n", rollCircum[idx]);
                        }
                        else
                        {
                            hostCommsStatus = badCircumference;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case 'S':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        /* check value is sensible */
                        if (ival >= 0 && ival < 3000)
                        {
                            /* scale new speed setpoint */
                            speedSetpnt[idx] = (float)ival * 0.1;

                            /* set to speed control mode */
                            controlMode[idx] = speedMode;

                            /* set flag to force recalc */
                            recalcSpeedSetpoint[idx] = TRUE;

                            //pc.printf("speed %5.3f\n", speedSetpnt[idx]);
                        }
                        else
                        {
                            hostCommsStatus = badSpeedSetpoint;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case 'R':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        /* check value is sensible */
                        if (ival >= 100 && ival < 9999)
                        {
                            /* scale to get max ratio of 9.999 */
                            ratioToPrev[idx] = (float)ival * 0.001;

                            /* set to ratio control mode */
                            controlMode[idx] = ratioMode;

                            /* set flag to force recalc */
                            recalcSpeedSetpoint[idx] = TRUE;
                            //pc.printf("Ratio to prev %5.3f\n", ratioToPrev[idx]);
                        }
                        else
                        {
                            hostCommsStatus = badRatioSetpoint;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case 'O':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        /* check value is sensible */
                        if (ival >= 0 && ival < 2048)
                        {
                            /* save for output to d/a */
                            dtoaSetpnt[idx] = ival;

                            /* do crude bumpless transfer */
                            intSum[idx] = (float)dtoaSetpnt[idx] / intGain[idx];

                            /* set to direct output mode */
                            controlMode[idx] = outputMode;

                            /* set flag to write to d/a chip */
                            updateAnalogOutput[idx] = TRUE;
                        }
                        else
                        {
                            hostCommsStatus = badOutputSetpoint;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case '3':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        
                        /* check value is sensible */
                        if (ival >= 0 && ival < 2048)
                        {
                            /* save for output to d/a */
                            dtoaSetpnt[idx] = ival;

                            /* set to third nip mode */
                            controlMode[idx] = thirdNipMode;

                            /* set flag to write to d/a chip */
                            updateAnalogOutput[idx] = TRUE;
                            //pc.printf("3rd nip %d\n", dtoaSetpnt[idx]);
                        }
                        else
                        {
                            hostCommsStatus = badOutputSetpoint;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case 'T':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        /* check value is sensible */
                        if (ival >= 10 && ival < 180)
                        {
                            /* save into global variable teeth */
                            teethOnWheel[idx] = ival;

                            /* set flag to force recalc */
                            recalcFixedPart[idx] = TRUE;
                        }
                        else
                        {
                            hostCommsStatus = badNumOfTeeth;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case 'P':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        /* check value is sensible */
                        if (ival >= 100 && ival < 9999)
                        {
                            /* scale to get max ratio of 9.999 */
                            pulleyRatio[idx] = (float)ival * 0.001;

                            /* set flag to force recalc */
                            recalcFixedPart[idx] = TRUE;
                        }
                        else
                        {
                            hostCommsStatus = badRatioSetpoint;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case 'p':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        /* check value is sensible */
                        if (ival >= 1 && ival < 9999)
                        {
                            /* scale to get max prop gain of 9.999 */
                            propGain[idx] = (float)ival * 0.001;
                        }
                        else
                        {
                            hostCommsStatus = badPropGainSetpoint;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case 'i':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        /* check value is sensible */
                        if (ival >= 1 && ival < 9999)
                        {
                            /* scale to get max integral gain of 9.999 */
                            intGain[idx] = (float)ival * 0.001;
                        }
                        else
                        {
                            hostCommsStatus = badIntGainSetpoint;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case 'M':
                        ival = 0;
                        for (i=4; i<8; i++)
                        {
                            ival = ival * 10 + commsBuf0[i] - '0';
                        }
                        /* check value is sensible */
                        if (ival >= 100 && ival < 3000)
                        {
                            /* scale new max speed value */
                            maxSpeedOfDrive[idx] = (float)ival * 0.1;
                        }
                        else
                        {
                            hostCommsStatus = badMaxSpeedSetpoint;
                        }
                        /* reply to host with ack or nak */
                        acknowledge_host(idx);
                        break;
                    case 'E':
                        /* determine type of enquiry */
                        switch (commsBuf0[7])
                        {
                            /* if enquiry 1 return current speed */
                            case '1':

                                /* round up/down properly */
                                intsp = (int)(measuredSpeed[idx] * 10.0);
                                if (((measuredSpeed[idx] * 10.0) - intsp) >= 0.5 )
                                {
                                    intsp = intsp + 1;
                                }

                                sprintf( buf, "*%02d%04d", address,
                                                (int)(intsp));
                                /* set checksum accumulator to zero */
                                chkSum = 0;

                                /* checksum packet */
                                for (i=0; i<7; i++)
                                {
                                    chkSum ^= buf[i];
                                }

                                /* enable host comms */
                                enable_host_comms();

                                /* return packet to host */
                                putstr0( buf );

                                /* generate checksum */
                                sprintf( buf, "%02X%c", (int)chkSum, CR );

                                /* return packet to host */
                                putstr0( buf );

                                // disable host comms after 6mS allowing 10 chars to be sent at 19200 baud
                                disableHostComms = 6;
                                break;
                            /* if enquiry 2 return current host comms status */
                            case '2':
                                sprintf( buf, "*%02d%04d", address,
                                                hostCommsStatus );
                                /* set checksum accumulator to zero */
                                chkSum = 0;

                                /* checksum packet */
                                for (i=0; i<7; i++)
                                {
                                    chkSum ^= buf[i];
                                }

                                /* enable host comms */
                                enable_host_comms();

                                /* return packet to host */
                                putstr0( buf );

                                sprintf( buf, "%02X%c", chkSum, CR );

                                /* return packet to host */
                                putstr0( buf );

                                // disable host comms after 6mS allowing 10 chars to be sent at 19200 baud
                                disableHostComms = 6;
                                break;
                            /* if enquiry 3 return current status */
                            case '3':
                                sprintf( buf, "*%02d%04d", address, status );

                                /* set checksum accumulator to zero */
                                chkSum = 0;

                                /* checksum packet */
                                for (i=0; i<7; i++)
                                {
                                    chkSum ^= buf[i];
                                }

                                /* enable host comms */
                                enable_host_comms();

                                /* return packet to host */
                                putstr0( buf );

                                sprintf( buf, "%02X%c", chkSum, CR );

                                /* return packet to host */
                                putstr0( buf );

                                // disable host comms after 6mS allowing 10 chars to be sent at 19200 baud
                                disableHostComms = 6;

                                /* reset status */
                                status = 0;
                                break;
                            /* if enquiry 4 return max dtoa value */
                            case '4':
                                sprintf( buf, "*%02d%04d", address, maxDtoa[idx] );

                                /* set checksum accumulator to zero */
                                chkSum = 0;

                                /* checksum packet */
                                for (i=0; i<7; i++)
                                {
                                    chkSum ^= buf[i];
                                }

                                /* enable host comms */
                                enable_host_comms();

                                /* return packet to host */
                                putstr0( buf );

                                // add checksum 
                                sprintf( buf, "%02X%c", chkSum, CR );

                                /* return packet to host */
                                putstr0( buf );

                                // disable host comms after 6mS allowing 10 chars to be sent at 19200 baud
                                disableHostComms = 6;
                                break;
                            /* if enquiry 5 return current min dtoa value */
                            case '5':
                                sprintf( buf, "*%02d%04d", address, minDtoa[idx] );

                                /* set checksum accumulator to zero */
                                chkSum = 0;

                                /* checksum packet */
                                for (i=0; i<7; i++)
                                {
                                    chkSum ^= buf[i];
                                }

                                /* enable host comms */
                                enable_host_comms();

                                /* return packet to host */
                                putstr0( buf );

                                // add checksum */
                                sprintf( buf, "%02X%c", chkSum, CR );

                                /* return packet to host */
                                putstr0( buf );

//******* consider implementing a timer to disable the 485 driver as using the counter is not deterministic
// the serial handler in mbed buffers the characters so the above code takes uSecs but the string can take 5mSecs to send
// and 2 tics of the disableHostComms may be as little as 1.6666mS up to 3.3333mS depending on the timing between the packet being sent and the timer

                                // disable host comms after 6mS allowing 10 chars to be sent at 19200 baud
                                disableHostComms = 6;

                                /* reset values */
                                maxDtoa[idx] = dtoaSetpnt[idx];
                                minDtoa[idx] = dtoaSetpnt[idx];
                                break;
                        }
                        break;
                    default:
                        hostCommsStatus = unknownPacket;
                }
            }
        }
    }
}






void    process_latest_count(int idx)

{
    unsigned long        count;

    // get local copy
    count = currentCountDiff[idx];
    
    /* limit count to maximum value */
    if (count > MAX_COUNT_VALUE)
    {
        count = MAX_COUNT_VALUE;
    }
    else
    {
        /* limit count to minimum value */
        if (count < MIN_COUNT_VALUE)
        {
            count = MIN_COUNT_VALUE;
        }
    }

    /* if count > previous biggest count */
    if (count > lastBigCount[idx])
    {
        /* adjust lastBigCount by one eigth of count value */
        lastBigCount[idx] = lastBigCount[idx] + (count / 8);
    }
    else
    {
        /* if count < previous smallest count */
        if (count < lastSmallCount[idx])
        {
            /* adjust lastSmallCount by one eigth of count value */
            lastSmallCount[idx] = lastSmallCount[idx] - (count / 8);
        }
        else
        {
            /* bring limits back in around current count */
            lastBigCount[idx]   = count + (count / 8);
            lastSmallCount[idx]= count - (count / 8);

            /* process count for mode controller is in */
            switch (controlMode[idx])
            {
                case speedMode:
                case ratioMode:
                    /* filter the current count into the actual count value */
                    actualCount[idx] = ((actualCount[idx] / 8) * 7) + (count / 8);

                    /* check if within band */
                    if (actualCount[idx] > loCountLimit[idx] && actualCount[idx] < hiCountLimit[idx])
                    {
                        /* if greater than zero */
                        if (outsideCountBand[idx] > 0)
                        {
                            /* decrement counter */
                            outsideCountBand[idx]--;

                            if (outsideCountBand[idx] == 0)
                            {
                                /* set flag for within band */
                                withinControlBand[idx] = TRUE;
                            }
                        }
                    }
                    else
                    {
                        /* check if not reached limit */
                        if (outsideCountBand[idx] < OUTSIDE_COUNT_BAND_LIMIT)
                        {
                            /* increment out of band counter */
                            outsideCountBand[idx]++;

                            /* check if reached limit */
                            if (outsideCountBand[idx] >= OUTSIDE_COUNT_BAND_LIMIT)
                            {
                                /* set flag for not within band */
                                withinControlBand[idx] = FALSE;
                            }
                        }
                    }

                    /* if not in startup mode */
                    if (!inStartupMode[idx])
                    {
                        /* get count error (override to long to get sign back)*/
                        errorCount[idx] = (long)(actualCount[idx] - countSetpoint[idx]);

                        /* perform P, I and D */
                        intSum[idx] += errorCount[idx];
                        dtoaSetpnt[idx] = (int)((propGain[idx] * errorCount[idx]) + (intGain[idx] * intSum[idx]));

                        /* check within output limits */
                        if (dtoaSetpnt[idx] < 0)
                        {
                            dtoaSetpnt[idx] = 0;
                            output_limit_status[idx] = OUTPUT_LOW;
                            /* do crude bumpless transfer */
                            intSum[idx] = 0.0;
                        }
                        else
                        {
                            if (dtoaSetpnt[idx] > 2047)
                            {
                                dtoaSetpnt[idx] = 2047;
                                output_limit_status[idx] = OUTPUT_HIGH;
                                /* do crude bumpless transfer */
                                intSum[idx] = (float)dtoaSetpnt[idx] / intGain[idx];
                            }
                            else
                            {
                                output_limit_status[idx] = OUTPUT_WITHIN;
                            }
                        }

                        /* check new dtoa value against monitored limits */
                        if (dtoaSetpnt[idx] > maxDtoa[idx])
                        {
                            /* set new maximum */
                            maxDtoa[idx] = dtoaSetpnt[idx];
                        }
                        else
                        {
                            if (dtoaSetpnt[idx] < minDtoa[idx])
                            {
                                /* set new minimum */
                                minDtoa[idx] = dtoaSetpnt[idx];
                            }
                        }

                        /* set flag to force update */
                        updateAnalogOutput[idx] = TRUE;

                        /* calculate speed for logging */
                        measuredSpeed[idx] = calcActualSpeed(idx);
                    }
                    break;
                case thirdNipMode:
                    /* filter count into actual count */
                    actualCount[idx] = ((actualCount[idx] / 8) * 7) + (count / 8);

                    /* calculate speed */
                    measuredSpeed[idx] = calcActualSpeed(idx);

                    /* filter into speed setpoint to be cascaded to next drive */
                    speedSetpnt[idx] = (speedSetpnt[idx] * 0.875) + (measuredSpeed[idx] * 0.125);
                    /* make speedSetpoint the same as measured speed */
/*                  speedSetpnt = measuredSpeed; */

                    /* force speed to be cascaded */
                    xmitCascadeSpeed[idx] = TRUE;
                    break;
                default: ;
            }
        }
    }
}



void    putch0(uchar c)
{

    host.putc(c);
}



void    putch1(uchar c)
{

    cascade.putc(c);
}



void    putstr0(char* str )
{

    while (*str)
    {
        putch0( *str++ );
    }
}



void    putstr1( char* str )
{

    while (*str)
    {
        putch1( *str++ );
    }
}



void serial_rcv_int0()
{
    uchar   c, loop;
    
    /* if not processing a packet */
    if (hostCrRcvd == FALSE)
    {
        /* get char into local */
        c = host.getc();

        /* if first char received */
        if (commsCntr0 == 0)
        {
            /* set receive led on */
            rxLed = 1;
        }
        
        /* if space in buffer */
        if (commsCntr0 < (commsBufLen - 1))
        {
            /* put char in buffer and increment pointer */
            commsBuf0[commsCntr0++] = c;
        }

        /* if char is end of packet */
        if (c == CR || c == 0x0a)
        {
            // clear to the end of the packet
            for (loop = commsCntr0; loop<commsBufLen; loop++)
            {
                commsBuf0[loop] = 0;
            }

            /* set flag to process packet */
            hostCrRcvd = TRUE;

            /* turn receive led off */
            rxLed = 0;
        }

    }
    else
    {
        /* if the end of the packet */
        if (c == CR)
        {
            /* reset char pointer */
            commsCntr0 = 0;

            /* turn receive led off */
            rxLed = 0;
        }
    }

    /* reset receive data register full flag */
    //SSR0 &= ~RDRF;
}



void serial_rcv_int1()
{
    uchar   c, loop;

    /* if not processing a packet */
    if (cascadeCrRcvd == FALSE)
    {
        /* get char into local */
        c = cascade.getc();

        /* if space in buffer */
        if (commsCntr1 < (commsBufLen - 1))
        {
            /* put char in buffer and increment pointer */
            commsBuf1[commsCntr1++] = c;
        }

        /* if char is end of packet */
        if (c == CR)
        {
            for (loop = commsCntr1; loop<commsBufLen; loop++)
            {
                commsBuf1[loop] = 0;
            }

            /* reset char pointer */
            commsCntr1 = 0;

            /* set flag to process packet */
            cascadeCrRcvd = TRUE;
        }

    }
    else
    {

        /* if the end of the packet */
        if (c == CR)
        {

            /* reset char pointer */
            commsCntr1 = 0;
        }
    }
}



void repetitiveControlUpdate()
{
    
    //scope1 = 1;
    
    for (int i=0; i<activeControllers; i++)
    {
        /* if time to transmit cascade speed */
        if (xmitCascadeSpeed[i])
        {
            // if this is the last controller on this card
            if (i == (activeControllers - 1))
                /* send current speed setpoint by comms to next card */
                xmit_cascade_speed(i);
            else
                // save the value to internal registers to be used for all drives controlled by this card except the last one
                speedOfPrev[i + 1] = speedSetpnt[i];

            /* clear flag */
            xmitCascadeSpeed[i] = FALSE;
        }

        /* recalculate fixed part of speed calculation */
        if (recalcFixedPart[i])
        {
            /* calculate fixed part of speed calc */
            calcFixedPart(i);

            /* clear flag */
            recalcFixedPart[i] = FALSE;
        }

        /* recalculate speed setpoint */
        if (recalcSpeedSetpoint[i])
        {
            /* only recalculate if in ratio mode */
            if (controlMode[i] == ratioMode)
            {
                /* calculate new speed setpoint */
                speedSetpnt[i] = speedOfPrev[i] * ratioToPrev[i];
            }

            /* set flag to send new speed setpoint on to next drive */
            xmitCascadeSpeed[i] = TRUE;

            /* force recalc of required count */
            recalcReqdCount[i] = TRUE;

            /* clear flag */
            recalcSpeedSetpoint[i] = FALSE;
        }

        /* if speed or ratio changed */
        if (recalcReqdCount[i])
        {
            /* calculate count using current speed setpoint */
            /* check for zero speed setpoint */
            if (speedSetpnt[i] > 0.0)
            {
                /* calculate required count */
                countSetpoint[i] = (unsigned long)(pulsesPerMetrePerSec[i] / speedSetpnt[i]);
            }

            /* calculate 12% control bands */
            hiCountLimit[i] = countSetpoint[i] + (countSetpoint[i] / 8);
            loCountLimit[i] = countSetpoint[i] - (countSetpoint[i] / 8);

            /* clear flag */
            recalcReqdCount[i] = FALSE;
        }

        /* if new reading from free running timer */
        if (processFrtReading[i])
        {
            /* if in speed control or ratio mode */
            switch (controlMode[i])
            {
                case speedMode:
                case ratioMode:
                case thirdNipMode:
                    /* calculate new output */
                    process_latest_count(i);
                    break;
                case outputMode:
                    break;
                default: ;
            }

            /* clear flag */
            processFrtReading[i] = FALSE;
        }

        /* if no pulses from toothed wheel in last 3 seconds */
        if (noPulseCntr[i] > 6)
        {
            /* if in speed control or ratio mode */
            switch (controlMode[i])
            {
                case speedMode:
                case ratioMode:
                    /* set default output */
                    dtoaSetpnt[i] = (int)(2047.0 * speedSetpnt[i] / maxSpeedOfDrive[i]);

                    if ( dtoaSetpnt[i] > 2047 )
                        dtoaSetpnt[i] = 2047;

                    /* do crude bumpless transfer */
                    intSum[i] = (float)dtoaSetpnt[i] / intGain[i];

                    /* set flag to update analog output */
                    updateAnalogOutput[i] = TRUE;
                    break;
                case thirdNipMode:
                case outputMode:
                    break;
                default: ;
            }

            /* set flag to indicate starting from cold */
            inStartupMode[i] = TRUE;

            /* clear within band flag */
            withinControlBand[i] = FALSE;

            /* zero startup counter */
            startupCntr[i] = 0;

            /* clear flag */
            noPulseCntr[i] = 2;
        }


        /* if new analog output value flag set */
        if (updateAnalogOutput[i])
        {
            /* write binary value to d/a chip */
            updateDtoaChip(i);

            /* clear flag */
            updateAnalogOutput[i] = FALSE;
        }
    }
    
    /* process host comms */
    if (hostCrRcvd)
    {
        /* process packet received from host */
        processHostComms();

        /* reset flag */
        hostCrRcvd = FALSE;

        /* reset input pointer */
        commsCntr0 = 0;
    }

    /* process cascade comms */
    if (cascadeCrRcvd)
    {
        /* process the packet received through cascade port */
        processCascadeComms();

        /* clear flag */
        cascadeCrRcvd = FALSE;
    }

    for (int i=0; i<activeControllers; i++)
    {
        /* if in startup mode and startup counter reached limit (pulses from wheel) */
        if (inStartupMode[i] && startupCntr[i] > startupCount[boardAddress + i])
        {
            /* drive should have started by now so clear flag */
            inStartupMode[i] = FALSE;
        }
    }
    
    //scope1 = 0;
}



void repetitiveUpdateTimer()

{

    /* called every ms */
    /* increment counter */
    timer0Cntr++;

    /* if host comms xmit driver enabled and waiting for timeout */
    if (disableHostComms > 0)
    {
        /* decrement timer */
        disableHostComms--;

        /* if timer is zero */
        if (disableHostComms == 0)
        {
            /* disable the driver */
            disable_host_comms();
        }
    }

    /* check for 8th pulse (8 * 1.666 ms = every 13 msec) */
    if ((timer0Cntr & 0x0F) == 0x08)
    {
        for (int i=0; i<activeControllers; i++)
        {
            /* set flag to force update of analog output */
            updateAnalogOutput[i] = TRUE;
        }
    }

    /* check for 64th pulse (64 * 1.666 ms = every 0.106 sec) */
    if ((timer0Cntr & 0x7F) == 0x40)
    {
        for (int i=0; i<activeControllers; i++)
            /* set flag to resend speed setpoint */
            xmitCascadeSpeed[i] = 1;
    }

    /* check for 256th pulse (256 * 1.666 ms = every 0.426 sec) */
    if ((timer0Cntr & 0x1FF) == 0x100)
    {
        for (int i=0; i<activeControllers; i++)
        {
            /* increment no pulse counter */
            noPulseCntr[i]++;
        }
    }

    /* check for 512th pulse (512 * 1.666 ms = every 0.852 sec) */
    if ((timer0Cntr & 0x3FF) == 0x200)
    {
        /* increment age since reset */
        ageInSecs++;
    }
}



void    updateDtoaChip(int idx)
{
    int d2aCmd;
    
    // build command with the following format: aaccddddddddddss
    //  where aa = output address
    //        cc = 3 - update all outputs when value loaded into buffer register
    //        dddddddddd - 10 bit output value
    //        ss = 0 - not used but must be zero
    d2aCmd = ((idx & 3) << 14) | (3 << 12) | ((dtoaSetpnt[idx] & 0x7fe) << 1);
    
    //select correct d/a chip
    if (idx > 3)
        d2aEnable2 = 0;
    else
        d2aEnable1 = 0;
    
    // write the command to the d/a chip over the serial peripheral interface
    spi.write(d2aCmd);
    
    //deselect correct d/a chip
    if (idx > 3)
        d2aEnable2 = 1;
    else
        d2aEnable1 = 1;
}



void    xmit_cascade_speed(int idx)
{
    char      str[20];
    char      chksum;
    char     *pstr;
    int       intsp;

    intsp = (int)(speedSetpnt[idx] * 10.0);
    if (((speedSetpnt[idx] * 10.0) - intsp) >= 0.5 )
    {
        intsp = intsp + 1;
    }

    /* build up packet of current speed setpoint */
    sprintf( str, "*%04d", (int)(intsp) );

    /* init pointer to string */
    pstr = str;

    /* clear checksum accumulator */
    chksum = 0;

    /* checksum string */
    while (*pstr)
    {
        chksum ^= *pstr++;
    }

    /* send the current speed setpoint */
    putstr1( str );

    /* build checksum part of string */
    sprintf( str, "%02X\x0d", chksum );

    /* send the checksum */
    putstr1( str );
}