Code to drive a CNC machine via a PC LPT port lookalike 25 pin 'D', experiment in 'PC/Mach3' replacement. Designed to compile and run on mbed LPC1768, Freescale KL25Z and Freescale KL46Z. Proved on LPC1768 and KL25Z, problem with serial port on KL46Z. Reads subset of 'G Codes' through usb/serial port and drives 3 stepper/servo drives for X, Y and Z, also similar Step/Dir outputs for spindle motor control. Emulates PC LPT, outputs 'charge pump', proved driving Seig KX3 CNC mill

Dependencies:   MODSERIAL mbed

main.cpp

Committer:
JonFreeman
Date:
2014-03-14
Revision:
3:7aaf0072cc22
Parent:
2:b3c668ec43ac

File content as of revision 3:7aaf0072cc22:

#include "mbed.h"
#include "rtos.h"
#include "MODSERIAL.h"
#include "cnc.h"
extern  void    i2c_handler    (void const *);
extern  void    command_line_interpreter    (void const *) ;
extern  fl_typ  feed_rate;      //  float type is 'float'
extern  signed long spindle_rpm;

const   int BAUD = 38400;
MODSERIAL  pc(USBTX, USBRX);    //  tx, rx to pc via usb lead
Ticker  NCO_gen;                // Ticker generating interrupts at "Kernel Speed", NCO updating frequency (about 40kHz)
Ticker  msec;                   //  Ticker updating global millisecs counter

bool    running         = false,
        new_run_pending = false,
        idle            = false,
        move_ended      = false;

volatile unsigned long  ticks    = 0L;  //  32 bit count of "interrupt_period_us" interrupts from time t=0
unsigned long           tickrun  = 0L,  //  32 bit effectively stores time in future when current movement to stop
                        ticks_next  = 0L,  //  32 bit effectively stores time in future when current movement to stop
                        millisecs = 0L;        //  32 bit
signed long
#if defined Fourth_Axis
        pir_a_next  = 0L,   //  Data for next move assembled here
#endif
        pir_x_next  = 0L,   //  during a move.
        pir_y_next  = 0L,   //  This way, next move can start immediately
        pir_z_next  = 0L,   //  on end of current move - minimised jerking
#if defined Fourth_Axis
        inc_a_next  = 1L,
#endif
        inc_x_next  = 1L,
        inc_y_next  = 1L,
        inc_z_next  = 1L,

        dir_bits_next   = 0L,
        pir_spin    = 0L;     //  Referenced only in command_interpreter as spindle speed setting

struct  Gparams    last_position;   //


#if defined (TARGET_KL25Z)
    const char Target[] = "KL25Z";  //  Note need PTE0 (sda) and PTE1 (scl)
    DigitalOut intled               (PTD7); //(PTE1);     //J2p19, was 20
    DigitalOut charge_pumpD25pin1   (PTD6); //(PTE0);     //J2p17, was 18    
//    InterruptIn     D25pin10_EStop    (PTE20);    //j10p1   KL25 J10 is KL46 j4
    DigitalIn     D25pin10_EStop    (PTE20);    //j10p1   KL25 J10 is KL46 j4
    DigitalIn       D25pin11_XLim     (PTE21);    //j10p3
    DigitalIn       D25pin12_YLim     (PTE22);    //j10p5
    DigitalIn       D25pin13_ZLim     (PTE23);    //j10p7
    DigitalIn       D25pin15_unkn     (PTE30);    //j10p11
#if defined I2C_Enable
    I2CSlave slave(PTE0, PTE1); //  PTE0 sda, (yellow) PTE1 scl (blue)
#endif
#if defined SPI_Enable
    SPISlave spidevice(PTD2, PTD3, PTD1, PTD0); // mosi, miso, sclk (uses p11, p12, p13 on mbed LPC1768)
#endif
    //                 J2p08,J2p10,J2p12, J2p06
    #define STEPPER_PORT    PortC
    const   int PortBitXSt   = 3,    //  Port bit num X Step    J1P05   D25pin 2
                PortBitXDi   = 4,    //  Port bit num X Dir     J1P07   D25pin 3
                PortBitYSt   = 5,    //  Port bit num Y Step    J1P09   D25pin 4
                PortBitYDi   = 6,    //  Port bit num Y Dir     J1P11   D25pin 5
                PortBitZSt   = 10,    //  Port bit num Z Step   J1P13   D25pin 6
                PortBitZDi   = 11,    //  Port bit num Z Dir    J1P15   D25pin 7
                PortBitASt   = 12,    //  Port bit num A Step   J2P01   D25pin 8
                PortBitADi   = 13,    //  Port bit num A Dir    J2P03   D25pin 9
                PortBitSSt   = 8,    //  Port bit num Spin Step   J1P14 D25pin 14
                PortBitSDi   = 9;    //  Port bit num Spin Dir    J1P16 D25pin 16
#endif



#if defined (TARGET_KL46Z)
    const char Target[] = "KL46Z";
    DigitalOut intled               (PTE1);    //J2p20 checked


    DigitalOut charge_pumpD25pin1   (PTE0);    //J2p18 checked
//    InterruptIn     D25pin10_EStop    (PTE20);  // j4p1  KL46 J4 is KL25 J10
    DigitalIn     D25pin10_EStop    (PTE20);  // j4p1  KL46 J4 is KL25 J10 checked
    DigitalIn       D25pin11_XLim     (PTE21);  // j4p3 checked
    DigitalIn       D25pin12_YLim     (PTE22);  // j4p5 checked
    DigitalIn       D25pin13_ZLim     (PTE23);  // j4p7 checked
    DigitalIn       D25pin15_unkn     (PTE30);  // j4p11 checked
#if defined I2C_Enable
    I2CSlave slave(p9, p10);
#endif
#if defined SPI_Enable
    SPISlave spidevice(PTA16, PTA17, PTA15, PTA14); // mosi, miso, sclk, ssel (uses p11, p12, p13, p? on mbed LPC)
#endif
    //                 J2p13, J2p15, J2p11, J2p09
                // Easy way to allocate port bits for
                // output of stepper motor Step and DIR sigs
    #define STEPPER_PORT    PortC
    const   int PortBitXSt   = 0,    //  Port bit num X Step    J1P05   D25pin 2 checked
                PortBitXDi   = 4,    //  Port bit num X Dir     J1P07   D25pin 3 checked
                PortBitYSt   = 6,    //  Port bit num Y Step    J1P09   D25pin 4 checked
                PortBitYDi   = 7,    //  Port bit num Y Dir     J1P11   D25pin 5 checked
                PortBitZSt   = 10,    //  Port bit num Z Step   J1P13   D25pin 6 checked
                PortBitZDi   = 11,    //  Port bit num Z Dir    J1P15   D25pin 7 checked
                PortBitASt   = 13,    //  Port bit num A Step   J2P01   D25pin 8 checked
                PortBitADi   = 16,    //  Port bit num A Dir    J2P03   D25pin 9 checked
                PortBitSSt   = 8,    //  Port bit num Spin Step J1P14   D25pin 14 checked
                PortBitSDi   = 9;    //  Port bit num Spin Dir  J1P16   D25pin 16 checked
#endif
#if defined (TARGET_MBED_LPC1768)
    const char Target[] = "MBED LPC1768";
    DigitalOut intled(LED2);                    //  Correct
    DigitalOut charge_pumpD25pin1      (p25);    //
//    InterruptIn D25pin10_EStop  (p26);    //P2.0
    DigitalIn D25pin10_EStop  (p26);    //P2.0
    DigitalIn   D25pin11_XLim   (p24);    //P2.2
    DigitalIn   D25pin12_YLim   (p23);    //P2.3
    DigitalIn   D25pin13_ZLim   (p19);    //P1.30
    DigitalIn   D25pin15_unkn   (p20);    //P1.31
#if defined I2C_Enable
    I2CSlave slave(p9, p10);
#endif
#if defined SPI_Enable
    SPISlave spidevice(p5, p6, p7, p8);
#endif
                // Easy way to allocate port bits
                // output of stepper motor Step and DIR sigs
    #define STEPPER_PORT    Port0
    /* Port 0 bits routed to DIP pins as follows:-
        P0.00   p09 Reserve SDA
        P0.01   p10 Reserve SCL
        P0.04   p30 CAN rd  -   USE X Step  D25pin 2
        P0.05   p29 CAN td  -   USE X Dir   D25pin 3
        P0.10   p28 SDA     -   USE Y Step  D25pin 4
        P0.11   p27 SCL     -   USE Y Dir   D25pin 5
        P0.15   p13 Tx      -   USE Z Step  D25pin 6
        P0.16   p14 Rx      -   USE Z Dir   D25pin 7
        P0.17   p12 miso    -   USE A Step  D25pin 8
        P0.18   p11 mosi    -   Use A Dir   D25pin 9
        P0.23   p15 A In    -   Use S Step  D25pin 14
        P0.24   p16 A In    -   Use S Dir   D25pin 16
        P0.25   p17 Reserve A In
        P0.26   p18 Reserve A Out
    */
    const   int PortBitXSt  = 4,    //  Port bit num X Step
                PortBitXDi  = 5,    //  Port bit num X Dir
                PortBitYSt  = 10,    //  Port bit num Y Step
                PortBitYDi  = 11,    //  Port bit num Y Dir
                PortBitZSt  = 15,    //  Port bit num Z Step
                PortBitZDi  = 16,    //  Port bit num Z Dir
                PortBitASt  = 17,    //  Port bit num A Step
                PortBitADi  = 18,    //  Port bit num A Dir
                PortBitSSt  = 23,   //  Port bit num Spin Step
                PortBitSDi  = 24;   //  Port bit num Spin Dir
#endif

const   long    //  Assemble mask bits from now known port bit positions
        XSt =   1 << PortBitXSt,    //  X axis Step signal
        XDi =   1 << PortBitXDi,    //  X axis Direction signal
        YSt =   1 << PortBitYSt,    //  Y axis Step, etc
        YDi =   1 << PortBitYDi,
        ZSt =   1 << PortBitZSt,    //  Z axis
        ZDi =   1 << PortBitZDi,
        ASt =   1 << PortBitASt,    //  A axis, not implemented in full, for e.g. rotary axis
        ADi =   1 << PortBitADi,
        SDi =   1 << PortBitSDi,     //  Spindle, also driven by Step and Dir signals up to 5kHz
        SSt =   1 << PortBitSSt,     //  for 5000 RPM

        SM_MASK = (XSt | XDi | YSt | YDi | ZSt | ZDi | ASt | ADi | SDi | SSt),
        direction_swappers = XDi | YDi | ZDi | SDi; //  include bit to swap direction

    PortOut Steppers    (STEPPER_PORT, SM_MASK);

void    target_cmd (struct singleGparam * a)   {
    pc.printf("Computer is %s\r\n", Target);
}

void    grain_clr   (struct singleGparam & g)  {
    g.flt = 0.0;
    g.ul = 0L;
    g.i = g.c = 0;
    g.changed = false;
}
void    Gparams_clr    (struct Gparams & p)   {
    grain_clr   (p.x);    grain_clr   (p.y);    grain_clr   (p.z);    grain_clr   (p.i);    grain_clr   (p.j);
    grain_clr   (p.r);    grain_clr   (p.a);    grain_clr   (p.b);    grain_clr   (p.c);    grain_clr   (p.d);
}

class digital_readout_stuff  {   //  class does not need to be named here
    private:
    char *  readout (char * txt, long p)         //  p has running subtotal of all pulses issued to stepper driver
    {
        txt[0] = '+';               //  constructs string e.g. "+123.456"
        txt[8] = 0;                 //  null terminated
        if  (p < 0)    {
            txt[0] = '-';
            p = -p;
        }
        p *= 1000;
        p /= pulses_per_mm;
        if  (p > 999999)    {
            sprintf(txt + 1, "OVRANGE");
            return  txt;
        }
        for(int k = 7; k > 0; k--)  {
            if  (k == 4)
                txt[k] = '.';
            else    {
                txt[k] = '0' + (p % 10);
                p /= 10;
            }
        }
        return  txt;    //  Returns pointer unaltered for subsequent use by e.g. cout
    }
        public:
    signed long x, y, z, a;    //  Could easily expand up to six or more dros
//    bool    dro_output;             //  To enabe / disable output to terminal
    void    init    ()  {
        x = y = z = a = 0;   //  These dro registers count pulses delivered to stepper motor driver
//        dro_output = true;
    }
    void    update  ()  {
        static  long    t = 300;    //  Prevent display immediately upon startup
        if  (millisecs < t)
            return;
//        if(!idle && dro_output)  {
        if(!idle)  {
            char    txt[12];
            pc.printf("dros X %s,", readout(txt, x));    //  dro.n has running subtotal of all pulses issued to stepper driver.n
            pc.printf(" Y %s, Z ", readout(txt, y));
            pc.printf("%s, %s\r\n", readout(txt, z), running ? "R":"idle");
            if(!running)    idle = true;    //  Purpose of idle flag is to stop dro updates JUST AFTER run completes.
            t = millisecs + 350;    //  Schedule next update after this non-blocking delay
        }
    }
}   dro_out ;   //  single instance of class digital_readout_stuff

/**
class   circbuff    {   public functions
    void    init    ()  {
    int     On_Q    ()  {
    bool    readable    ()  {return !buffempty; }
    bool    writeable   ()  {return !bufffull; }
    bool    read    (pirbufgrain & g)   {
    bool    write   (pirbufgrain & g)   {
*/
const int PIRBUFFSIZE = 40; //  pirbufgrain are 40 bytes each
class   circbuff    {
    private:
        pirbufgrain grain   [PIRBUFFSIZE + 1];
    int     OnPtr, OffPtr;
    bool    bufffull, buffempty, buffhalf;
    void    setempty    ()  {
        bufffull = false;
        buffhalf = false;
        buffempty = true;
    }
    void    grain_copy  (pirbufgrain & src, pirbufgrain & dest) {
        dest.x  = src.x; 
        dest.y  = src.y; 
        dest.z  = src.z; 
        dest.distance_code = src.distance_code; 
        dest.f_rate = src.f_rate;   //  int feed rate mm per min * 1000
    }
    public:
    void    init    ()  {
        OnPtr = OffPtr = 0;
        setempty    ();
    }
    int     On_Q    ()  {
        int k;
        if  (bufffull)    return  PIRBUFFSIZE;
        k = OnPtr - OffPtr;
        if  (k < 0) k += PIRBUFFSIZE;
        if  (k > PIRBUFFSIZE / 2)
            buffhalf = true;
        else
            buffhalf = false;
        return  k;
    }
    bool    readable    ()  {return !buffempty; }
    bool    writeable   ()  {return !bufffull; }
    bool    read    (pirbufgrain & g)   {
//        if  (buffempty) return  false;    //  TO DO Fix problem with buffempty flag
        if  (On_Q() == 0) return  false;
        bufffull = false;
        grain_copy  (grain[OffPtr++], g);
        if  (OffPtr >= PIRBUFFSIZE)
            OffPtr = 0;
        if  (OnPtr == OffPtr)
            buffempty = true;
        return  true;
    }
    bool    write   (pirbufgrain & g)   {
        if  (bufffull)  return  false;
        buffempty = false;
        grain_copy  (g, grain[OnPtr++]);
        if  (OnPtr >= PIRBUFFSIZE)
            OnPtr = 0;
        if  (OnPtr == OffPtr)
            bufffull = true;
        return  true;
    }
}   CircBuff;   //  single instance of class   circbuff
    

/**
void    move_to_XYZ   (struct pirbufgrain & ins)   {
    Takes floating point x, y, z and feed_rate as input.
    Finds distances from 'last_position' global,
    copies structure containing floating point values for x, y, z, distance_multiplier and feed_rate
    onto a circular buffer.
    If buffer full, executes 'osThreadYield()' until space is created on buffer
*/
void    move_to_XYZ   (struct pirbufgrain & ins)   {
static const   fl_typ  duration_multiplier =   60000000.0 /  interrupt_period_us;
    struct  pirbufgrain outs;
    fl_typ  distx   = ins.x - last_position.x.flt,
            disty   = ins.y - last_position.y.flt,
            distz   = ins.z - last_position.z.flt,
            distT   = sqrt ((distx * distx) + (disty * disty) + (distz * distz)), // 3D Pythag !
            temp    = n_for_onemmpermin / distT;
    if  (distT < 0.01)  {
        pc.printf("Very small move %.4f, Ignoring!\r\n", distT);
        return;     //  Return without updating last_position as it was not changed
    }
    last_position.x.flt   = ins.x;     //  Update global last_position record
    last_position.y.flt   = ins.y;
    last_position.z.flt   = ins.z;
    outs.f_rate = ins.f_rate;
    outs.distance_code  = duration_multiplier * distT;   //  Duration ticks subject to feed rate compo
    outs.x = temp * distx;
    outs.y = temp * disty;
    outs.z = temp * distz;  //  Have assembled data ready to put onto queue of move instructions
    while   (!CircBuff.writeable())
        osThreadYield();
    CircBuff.write  (outs);    //    Move details put on circular buffer
}

/**
*   Interrupt Service Routines
void    millisec_update_ISR ()  { self explanatory

void    Numerically_Controlled_Oscillators_ISR ()  {   
    services Ticker 'NCO_gen' generated interrupts ***ISR***
    Does all of the stepper motor driving.
    At end of movement, fetches and starts next move to run from circular buffer
    If nothing buffered, stops x, y and z, leaves spindle unaltered
*/
void    millisec_update_ISR ()  {
    millisecs++;
}

#define STEP_IDLE_HI    //  Choose IDLE_HI or LO to suit any power save function of stepper motor drive units
//#define STEP_IDLE_LO
void    Numerically_Controlled_Oscillators_ISR ()  {   // services Ticker 'NCO_gen' generated interrupts ***ISR***
    static  const   long    step_mask   = ASt | XSt | YSt | ZSt,    //  Added 6th Feb 14 Mask Does NOT include spindle bits
                            dir_mask    = ADi | XDi | YDi | ZDi;    //  Added 6th Feb 14 Mask Does NOT include spindle bits
    static  signed  long     //  27 Feb 14 changed from unsigned
#if defined Fourth_Axis
        acc_a   = 0L,        pir_a   = 0L,
#endif
        acc_x       = 0L,   //  acc Accumuloators
        pir_x       = 0L,   //  pir Phase Increment Registers
        acc_y       = 0L,        pir_y   = 0L,
        acc_z       = 0L,        pir_z   = 0L,
        acc_spin    = 0L,  //  separate acc for spindle rotation NCO
        inc_x       = 1L,   //  inc_x, y, z for updating DRO registers
        inc_y       = 1L,        inc_z   = 1L,
        dir_bits    = 0L,  //  direction flags for up to four axes
        oldSteps    = 0L;  //  
    long tmp, newSteps = 0L;

    intled = 1;     //  LED on for duration of interrupt service - point for scope probing
    ticks++;        //  count of interrupts serviced, vital to time end of movement
    charge_pumpD25pin1 = ticks & 0x01;  //  Can use 0x01 or 0x02 here to alter charge pump freq
    tmp = Steppers ^ direction_swappers;
#if defined STEP_IDLE_LO
    tmp &= ~step_mask;   //  Step bits prepared for idle lo
#endif
#if defined STEP_IDLE_HI
    tmp |= step_mask;   //  Step bits prepared for idle hi
#endif
    acc_spin += pir_spin;   //  Spindle NCO
    if  (acc_spin < 0)  tmp |= SSt;
    else                tmp &= ~SSt;
    if  (!running)      Steppers = tmp ^ direction_swappers;    //  Axes not moving, spindle may be turning or not
    else    {   //  running == true, Further manipulation of tmp follows, prior to rewriting to 'Steppers' IO Port
//        newSteps = 0L;   //  Added 6th Feb 14
#if defined Fourth_Axis
        acc_a   += pir_a;
        if  (acc_a < 0)    newSteps |= ASt;//  Added 6th Feb 14
#endif
        acc_x   += pir_x;     //  Update phase of signals in accumulators
        if  (acc_x < 0)    newSteps |= XSt;//  Added 6th Feb 14
        acc_y   += pir_y;
        if  (acc_y < 0)    newSteps |= YSt;//  Added 6th Feb 14
        acc_z   += pir_z;
        if  (acc_z < 0)    newSteps |= ZSt;//  Added 6th Feb 14
        //  newSteps has copy of all 4 'acc' MSBs shifted into port bit positions
        oldSteps    ^= newSteps;  //  Any bit of stbits set to initiate a Step pulse
        tmp         ^= oldSteps;
        Steppers = tmp ^ direction_swappers; //  Output signals to stepper motor drivers, next update dros from 'clocked' bits CLOCK IDLES HIGH
        if(oldSteps & XSt)  dro_out.x += inc_x;       //  got clk edge for axis X
        if(oldSteps & YSt)  dro_out.y += inc_y;       //  got clk edge for axis Y
        if(oldSteps & ZSt)  dro_out.z += inc_z;       //  got clk edge for axis Z
        oldSteps    = newSteps;   //  Added 6th Feb 14
        if  (tickrun <= ticks & !new_run_pending)   {   //  End of a machine movement detected, start next move here if possible
                running = false;
                move_ended = true;
                pir_x = 0L; //  stop all stepper motors
                pir_y = 0L;
                pir_z = 0L;
#if defined Fourth_Axis
                pir_a = 0L;
#endif
    //          ticks = 0L; //  Simply to avoid having to think about overflow problems
        }       //  end of if   (tickrun <= ticks)  {
    }           //  end of else is   (running)   {
    if  (!running & new_run_pending)  { //  Start axis movement
        dir_bits= dir_bits_next;
#if defined Fourth_Axis
        pir_a   = pir_a_next;
#endif
        pir_x   = pir_x_next;
        pir_y   = pir_y_next;
        pir_z   = pir_z_next;
        inc_x   = inc_x_next;
        inc_y   = inc_y_next;
        inc_z   = inc_z_next;
        tmp     = Steppers ^ direction_swappers;  //  read output lines
        tmp     &= ~dir_mask;
        tmp     |= dir_bits;
        Steppers = tmp ^ direction_swappers;
        tickrun = ticks + ticks_next;
        running = true;     //  Start the new run
        new_run_pending = false;    //  Clear the flag which initiated this update
        idle = false;
    }       //  end of     else    {   //  Not running. Grab next data here when or if available
    intled = 0;         //  LED off
}           //  end of interrupt handler

/*
*   End of Interrupt Service Routine
*/
bool    spindle_running ()  {
    if  (pir_spin == 0) return  false;
    return  true;
}
void    spindle_control (signed long ss)  {
    long t, p;
    pir_spin = ss * spindle_factor;
    t = ticks;
    while   (t == ticks)    {}  //  wait until just after next interrupt
    p = Steppers ^ direction_swappers;
    if  (pir_spin & 0x80000000) p |= SDi;
    else                        p &= ~SDi;
    Steppers = p ^ direction_swappers;
    pc.printf("Done setting spindle speed %d, pir_spin %d, delay = %d\r\n", ss, pir_spin, ticks - t);
}

/**
void    pir_updater_task (void const * name)   {
    A task handed to the RTOS Round Robin
    ISR controls certain flags.
    This task responds after 'move_ended' flag asserted by NCO ISR
    If CircBuff not empty, set of floating point data for next point is fetched,
    remaining calcs performed and translated into signed longs, results placed in
    set of '_next' locations to be picked up by future NCO ISR
*/
void    pir_updater_task (void const * name)   {
    static  long    x, y, z;//, count = 0;
    struct pirbufgrain outs;
    while   (true)  {
//        while   (!move_ended  || !CircBuff.readable())   { ** TO DO ** Solve problem with empty flag
        while   (!move_ended  || CircBuff.On_Q() == 0)   {
            osThreadYield();
        }   //  Proceed beyond here only when move_ended AND CircBuff.readable() both TRUE
        CircBuff.read(outs);
        x = (long)(outs.f_rate * outs.x);  //  These take much CPU time !!
        y = (long)(outs.f_rate * outs.y);
        z = (long)(outs.f_rate * outs.z);
        ticks_next = (unsigned long)(outs.distance_code / outs.f_rate);
        pir_x_next = x;
        pir_y_next = y;
        pir_z_next = z;
        dir_bits_next = 0;
        //if  (pir_a & 0x80000000)    dir_bits_next |= ADi;//  Added 6th Feb 14 read sign bits
        inc_x_next = inc_y_next = inc_z_next = 1L;
        if  (x < 0) {
            dir_bits_next   |= XDi;
            inc_x_next      = -1L;
        }
        if  (y < 0) {
            dir_bits_next   |= YDi;
            inc_y_next      = -1L;
        }
        if  (z < 0) {
            dir_bits_next   |= ZDi;
            inc_z_next      = -1L;
        }
        move_ended = idle = false;
        new_run_pending = true; //  cleared and 'running' flag set in interrupt handler
    }   //  end of while    (true)  {
}


void    flags_report_cmd (struct singleGparam * a)   {  //  Diagnostic helper function
    pc.printf("Flags Report\r\nRunning %s\r\n", running ? "T":"F");
    pc.printf("idle %s\r\n", idle ? "T":"F");
    pc.printf("new_run_pending %s\r\n", new_run_pending ? "T":"F");
    pc.printf("move_ended %s\r\n", move_ended ? "T":"F");
    pc.printf("CircBuff.readable() %s\r\n", CircBuff.readable() ? "T":"F");
    pc.printf("On CircBuff %d\r\n", CircBuff.On_Q());
}

/*
#define ESTOP   0x100
#define XLIM    1
#define YLIM    2
#define ZLIM    4
#define UNKN    8
*/
class   inputsreaderstuff   {
    private:
    long    ins_now;//,    ins_old,    ins_changed;
    public:
        void    init    ()  {   ins_now = 0L;}//ins_old = ins_changed = 0L;   }
        long    read    ()  {
            ins_now = 0;
            if  (D25pin10_EStop)    ins_now |= ESTOP;
            if  (D25pin11_XLim)     ins_now |= XLIM;
            if  (D25pin12_YLim)     ins_now |= YLIM;
            if  (D25pin13_ZLim)     ins_now |= ZLIM;
            if  (D25pin15_unkn)     ins_now |= UNKN;
//            ins_changed = ins_now ^ ins_old;
//            ins_old = ins_now;
            return  ins_now;
        }
//        long    changed ()  {   return  ins_changed;    }
    }   Inputs_From_Machine;

void    report_inputs   ()  {
    long    i = Inputs_From_Machine.read();
    pc.printf("Inputs: EStop %d, XLim %d, YLim %d, ", i & ESTOP ? 1:0, i & XLIM ? 1:0, i & YLIM ? 1:0);
    pc.printf("ZLim %d, unkn %d\r\n", i & ZLIM ? 1:0, i & UNKN ? 1:0);
}

//void    Emergency_Stop_Interrupt  ()  {
//    pc.printf("Emergency Stop Activated !!\r\n");
//    spindle_control (0);  //  Stop spindle rotation
//}

/**
int main() {

*/
int main() {
    long    ins, ins_old, ins_changed = 0;
    pc.baud(BAUD); //  comms to 'PuTTY' serial terminal via mbed usb
//    D25pin11_XLim.mode  (PullDown);   External resistors now fitted
//    D25pin12_YLim.mode  (PullDown);
//    D25pin13_ZLim.mode  (PullDown);
//    D25pin15_unkn.mode  (PullDown);
//    D25pin10_EStop.mode (PullDown);
//    D25pin10_EStop.rise (& Emergency_Stop_Interrupt);
//    D25pin10_EStop.fall (& Emergency_Stop_Interrupt);

    Gparams_clr     (last_position);
    dro_out.init    ();
    CircBuff.init   ();
    Inputs_From_Machine.init    ();
#if defined SPI_Enable
    spidevice.format(8, 0);    //  8 bits mode 0,  // p11 mosi, p12 miso, p13 sclk ** ONLY 8 BIT **
    spidevice.frequency(12000000);  //  12MHz bit rate
#endif
    pc.printf("\r\n*\n*\nFound Computer %s\r\n", Target);
    pc.printf("Welcome to the CNC tester\r\nStep pulses required to move 1.0mm = %9.0f\r\n", pulses_per_mm);
    pc.printf("PIR 'n' for 1mm per min = %9.0f\r\ntop speed = %d mm per min\r\n\n", n_for_onemmpermin, feed_rate_max);


//    NVIC_SetPriority(TIMER3_IRQn, 255); // set mbed tickers to lower priority than other things ONLY COMPILES FOR LPC1768


    NCO_gen.attach_us(&Numerically_Controlled_Oscillators_ISR, interrupt_period_us);// Have setup timed interrupts, let other code deal
    msec.attach_us(&millisec_update_ISR, 1001);

    Thread tsr2   (pir_updater_task, (void *)"read from CircBuff and move");
    Thread comlin (command_line_interpreter,    (void *)"cli"); //  Read any instructions arriving via serial port and act upon them
#if defined I2C_Enable
    Thread i2cstuff (i2c_handler, (void *)"i2c thing");
#endif
    ins = ins_old = Inputs_From_Machine.read    ();
    move_ended = true;  //  Needed to kickstart system
    
    while(1) {  //  Round Robin loop
        dro_out.update  ();             //  Update DRO readings if, and as often as needed
        ins = Inputs_From_Machine.read    ();
        ins_changed = ins ^ ins_old;
        ins_old = ins;
        if  (ins_changed)
            pc.printf("Inputs Have Changed 0x%x, read 0x%x\r\n", ins_changed, ins);
        osThreadYield();                //
    }   //  end of Round Robin loop
}       //  end of int main()