Test code for proving multi-NCO implementation on Altera FPGA using DEO Nano development board

Dependencies:   MODSERIAL mbed-rtos mbed

main.cpp

Committer:
JonFreeman
Date:
2014-04-24
Revision:
0:7f5b51873953

File content as of revision 0:7f5b51873953:

#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  msec;                   //  Ticker updating global millisecs counter

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

unsigned long   millisecs = 0L;        //  32 bit

#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    
    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
    SPI spi(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
    SPI spi(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
    SPI spi(p5, p6, p7);
//#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);

int freq_to_n   (int freq)  {
    const double factor = (1 << 25) / 390625.0;
//    unsigned long long ll = ((1 << 31) * freq) / 50000000; 
    double ll = factor * (double)freq;
    return  (long) ll;
}
//  freq = (50E6 * 'n' / 2**BUS_WIDTH)
//  'n' = freq * 2**BUS_WIDTH / 50000000
//  'n' = freq * 2**31 / 25000000
//  'n' = freq * 2**30 / 12500000
//  'n' = freq * 2**29 / 6250000
//  'n' = freq * 2**25 / 390625

int pirs[8], dros[8];

void    FPGA_bit    (int whichbit, int hiorlo)    {
    int port = Steppers;
    if  (hiorlo)    port |= whichbit;
    else            port &= ~whichbit;
    Steppers = port;
}

void    FPGA_setup  ()  {
    int port = Steppers;
    port |= sclr | clken;
    Steppers = port;
    port &= ~sclr;
    port &= ~clken;
    port &= ~ld_osr;
    port &= ~ld_pir;
    Steppers = port;
    for (int i = 0; i < 8; i++)
        pirs[i] = dros[i] = 0;
}

/*
--    About Use of 16 bit Command Word
--    bit 0   -   '1' causes zero reset of phase_inc_reg
--    bit 1   -   '1' causes zero reset of dro_udcounter
--    bit 2   -   '1' causes load of phase_inc_reg from input shift reg
--    bit 3   -   '1' causes load of dro_udcounter from input shift reg 
--    bit 4   -   '1' causes dro_udcounter --> shift reg ready to read dro value
--    bit 5   -   '1' causes phase_inc_reg --> shift reg ready to read pir value
--    bit 6   -   '1' causes reset everything to 0
--
--    bit 15  -   '1' causes reset of command_word to all 0 after one clock

*/
#define zero_pir            0x8001
#define zero_dro            0x8002
#define load_pir_from_sr    0x8004
#define load_dro_from_sr    0x8008
#define load_dro_into_sr    0x8010
#define load_pir_into_sr    0x8020

void    FPGA_cmd    (int command_word)  {
    int port = Steppers;
    int spirx[8], command_copy = command_word;
//    pc.printf("At FPGA_cmd, sending %d\r\n", command_word);
    port |= sclr;       //  cmdhi_datalo set to 1
    Steppers = port;    //  
    spirx[0] = spi.write(command_copy >> 8);
    spirx[1] = spi.write(command_copy);
    port &= ~sclr;       //  cmdhi_datalo set to 0
    Steppers = port;    //  
//    pc.printf("Read spi %x %x\r\n", spirx[0], spirx[1]);
}

void    setcmd_cmd  (struct singleGparam * a)  {
    FPGA_cmd    (a[1].i);
}

//#define load_pir_from_sr    0x8004
//#define load_dro_into_sr    0x8010

//    DigitalIn   D25pin15_unkn   (p20);    //P1.31 use this to read osr
int FPGA_rdandwr    (int tosend)  { //  send 32 bits to in_sr, read 32 bits from out_sr
    int torecv = 0, port = Steppers, tmp;
//    tosend = freq_to_n(tosend);
    for (int j = 3; j >= 0; j--) {
        torecv <<= 8;
        tmp = tosend >> (j << 3);
        torecv |= spi.write(tmp);
    }
    return  torecv;
}

const int   numof_ncos = 4;

void    setdro_cmd  (struct singleGparam * a)  {
    int recd[numof_ncos + 1];
    pc.printf("At setdro with values ");
    FPGA_cmd(load_dro_into_sr);
    for (int k = 0; k < numof_ncos; k++)    {
        recd[k] = FPGA_rdandwr    (a[k + 1].i);
        pc.printf("%d, ", a[k + 1].i);
    }
    FPGA_cmd(load_dro_from_sr);
    pc.printf("end\r\n");
}

void    setpir_cmd  (struct singleGparam * a)  {
    int recd[numof_ncos + 1];
    pc.printf("At setpir with values ");
    FPGA_cmd(load_dro_into_sr);
    for (int k = 0; k < numof_ncos; k++)    {
        recd[k] = FPGA_rdandwr    (a[k + 1].i);
        pc.printf("%d, ", a[k + 1].i);
    }
    FPGA_cmd(load_pir_from_sr);
    pc.printf("end\r\n");
}

void    getdro_cmd  (struct singleGparam * a)  {
    int read_dro[numof_ncos + 1];
    pc.printf("At rddro, retrieved values ");
    FPGA_cmd(load_dro_into_sr);
    for (int k = 0; k < numof_ncos; k++)    {
        read_dro[k] = FPGA_rdandwr (0);
        pc.printf("%d, ", read_dro[k]);
    }
    pc.printf(" end\r\n");
}

void    getpir_cmd  (struct singleGparam * a)  {
    int read_pir[numof_ncos + 1];
    pc.printf("At rdpir, retrieved values ");
    FPGA_cmd(load_pir_into_sr);
    for (int k = 0; k < numof_ncos; k++)    {
        read_pir[k] = FPGA_rdandwr (0);
        pc.printf("%d, ", read_pir[k]);
    }
    pc.printf(" end\r\n");
}

void    clrpir_cmd  (struct singleGparam * a)  {
    FPGA_cmd(zero_pir);
    getpir_cmd(a);    
}

void    clrdro_cmd  (struct singleGparam * a)  {
    FPGA_cmd(zero_dro);
    getdro_cmd(a);    
}


char const * target_str_addr  ()  {
    return  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

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 ()  {
*/
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);
}

int main() {
    long    ins, ins_old, ins_changed = 0;
    pc.baud(BAUD); //  comms to 'PuTTY' serial terminal via mbed usb
    spi.format  (8,0);          //  use 8 bit format for compatibility with Freescale KLxxZ
    spi.frequency(12000000);    //  12MHz, fast enough
    dro_out.init    ();
    FPGA_setup();
    pc.printf("\r\n*\n*\nFound Computer %s\r\n", Target);

    msec.attach_us(&millisec_update_ISR, 1001);

    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()