G30 Design Project Continued

I believe that level interrupts were not supported in the GHI versions of the MF. Only edge interrupts were supported.

You want to use InterruptEdgeLow to capture the transition from high to low.

You are assigning the value of reg to 1 in this line.

reg =+ 1;

I think that you mean to assign the value of reg as follows:

reg += 1;

1 Like

Ok well I changed the code and still donā€™t know how to read this registry value.

It starts as 4161407 then ~ turns it to 4290805888 then +1 = 4290805888. That is the ABS_POS register for the L6470. The value is being reported after using Go_Until and triggering the SW intterupt. It is set to reset the register so the number of steps should be somewhere near zero.

The value should be zero or very close to it since I am resetting the home position / ABS_POS register. I am not sure why its such a large number if it should be getting reset.

I think this is my problem, not sure how to fix in the C# dSpin class:

We ran into problems reading ABS_POS on an Arduino board, too. The problem seems to be in the dSPIN_Param function in file dSPIN_support.ino.

C silently converts types shorter than int to int prior to any arithmetic operations. Ints in Arduino are 16 bits, so the left shift operations on the bytes returned by dSPIN_Xfer donā€™t work as intended with 16 bit ints.

Consider how dSPIN_Param handles the first byte returned from dSPIN_Xfer:

if (byte_len == 3) {
ret_val |= dSPIN_Xfer(ā€¦) < < 16;
}
dSPIN_Xfer returns a byte, and it is silently converted into a signed, 16 bit int, and then it is left shifted 16 bits. A 16 bit value shifted left 16 bits is 0. So the most significant byte of the ABS_POS is effectively ignored. Surprise #1.

The next byte is handled the same way, except itā€™s shifted left 8 bits, but this also causes an unexpected result. Suppose dSPIN_Xfer returns 0x80. Itā€™s first converted to an int (0x0080), then shifted left 8 bits (0x8000). But this is now a negative value in a (signed, 16 bit) int. So when itā€™s converted to an unsigned long to be ORā€™d with ret_val, itā€™s converted to 0xFFFF8000. Surprise #2.

The solution that worked for us was to explicitly cast the value returned by dSPIN_Xfer to an unsigned long. This forces the conversion of the 8 bit byte to a 32 bit value, which is long enough to handle the left shifts properly.

ret_val |= (unsigned long)dSPIN_Xfer(ā€¦) < < 16;
ā€¦
ret_val |= (unsigned long)dSPIN_Xfer(ā€¦) < < 8;
ā€¦
ret_val |= (unsigned long)dSPIN_Xfer(ā€¦);
(Itā€™s not strictly needed in the last case, but I like consistency.)

I suspect the original code was written for a system that used 32 bit ints, on which the left shifts would have worked as intended.

I hope Sparkfun will change the example sketch to save other from this subtle bit of C behavior.

Although, I am pretty sure the dSpin class uses 32bit Ints so this shouldnt be an issue, am I missing something?

I tried both modes and get the same behavior. When the interrupt pin is first created, I call clear interrupt (in case the previous one is still triggered) followed by DisabledInterrupt() at the very beginning of the program. Then I try to move through the interrupt zone without triggering it but the interrupt STILL stops my code. I enable it after the command to move is sent.

I would use an oscilloscope to see exactly what is happening with the signal from the sensor.

Disabling interrupts without understanding exactly what is happening seems dangerous to me.

Also you might want to post a short program which demonstrates the issue you are having.

I am already watching the signal. I only disable it when it should never go off. When the motor moves out of that position, I re-enable it. I am also thinking something is wrong with the Read/Write part of the driver. I will command a move of 30,000 steps which should be more than a revolution and it only moves like 1/8th of a turn like the interrupt is triggering it to stop out of nowhere.

Here is my test code:

    public static void Main()
{

    try
    {

        _rnd = new Random();
        dSPIN_RegsStruct = new dSPIN_RegsStruct_TypeDef();
        Debug.EnableGCMessages(false);
		
        OutputPort reset = new OutputPort(G30.Gpio.PC2, false);
        hallEffect = new InterruptPort(G30.Gpio.PA0, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLow);
        hallEffect.OnInterrupt += new NativeEventHandler(hallEffect_triggered);
        calButton = new InputPort(G30.Gpio.PC13, true, Port.ResistorMode.PullUp);
        saveButton = new InputPort(G30.Gpio.PA15, true, Port.ResistorMode.PullUp);

        hallEffect.DisableInterrupt();

        busyPort = new InputPort(G30.Gpio.PA7, false, Port.ResistorMode.Disable
        flagPort = new InputPort(G30.Gpio.PA8, false, Port.ResistorMode.Disabled);
		
        SPI.Configuration _spiConfig = new SPI.Configuration(G30.Gpio.PB7,
            false,
            2,
            2,
            false,
            true,
            1000,
            SPI.SPI_module.SPI1);
        // Cpu.Pin.GPIO_Pin2,false);
        SPI spiPort = new SPI(_spiConfig);

        /* Structure initialization by default values, in order to avoid blank records */

        stepperDriver = new dSpin(spiPort, busyPort, flagPort);

        dSpin.dSPIN_Regs_Struct_Reset(ref dSPIN_RegsStruct);

        stepperDriver.dSPIN_Reset_Device();
        stepperDriver.dSPIN_Get_Status();
        /* Acceleration rate settings to 466 steps/s2, range 14.55 to 59590 steps/s2 */
        dSPIN_RegsStruct.ACC = Macro.AccDecStepsTopar(100);  // 466
        /* Deceleration rate settings to 466 steps/s2, range 14.55 to 59590 steps/s2 */
        dSPIN_RegsStruct.DEC = Macro.AccDecStepsTopar(100);  // 466
        /* Maximum speed settings to 488 steps/s, range 15.25 to 15610 steps/s */
        dSPIN_RegsStruct.MAX_SPEED = Macro.MaxSpdStepsToPar(200);  // 488
        /* Minimum speed settings to 0 steps/s, range 0 to 976.3 steps/s */
        dSPIN_RegsStruct.MIN_SPEED = Macro.MinSpdStepsToPar(100);
        /* Full step speed settings 252 steps/s, range 7.63 to 15625 steps/s */
        dSPIN_RegsStruct.FS_SPD = Macro.FSSpdStepsToPar(1000);   // 252

        //The duty cycle values control power to the motor. If the motor is not running as expected
        //it may be because it it underpowered.

        /* Hold duty cycle (torque) settings to 10%, range 0 to 99.6% */
        dSPIN_RegsStruct.KVAL_HOLD = Macro.KvalPercToPar(6);
        /* Run duty cycle (torque) settings to 10%, range 0 to 99.6% */
        dSPIN_RegsStruct.KVAL_RUN = Macro.KvalPercToPar(15);
        /* Acceleration duty cycle (torque) settings to 10%, range 0 to 99.6% */
        dSPIN_RegsStruct.KVAL_ACC = Macro.KvalPercToPar(15);
        /* Deceleration duty cycle (torque) settings to 10%, range 0 to 99.6% */
        dSPIN_RegsStruct.KVAL_DEC = Macro.KvalPercToPar(15);

        //end duty cycles

        /* Intersect speed settings for BEMF compensation to 200 steps/s, range 0 to 3906 steps/s */
        dSPIN_RegsStruct.INT_SPD = Macro.IntSpdStepsToPar(100);
        /* BEMF start slope settings for BEMF compensation to 0.038% step/s, range 0 to 0.4% s/step */
        dSPIN_RegsStruct.ST_SLP = Macro.BEMFSlopPercToPar(2);
        /* BEMF final acc slope settings for BEMF compensation to 0.063% step/s, range 0 to 0.4% s/step */
        dSPIN_RegsStruct.FN_SLP_ACC = Macro.BEMFSlopPercToPar(15);
        /* BEMF final dec slope settings for BEMF compensation to 0.063% step/s, range 0 to 0.4% s/step */
        dSPIN_RegsStruct.FN_SLP_DEC = Macro.BEMFSlopPercToPar(15);
        /* Thermal compensation param settings to 1, range 1 to 1.46875 */
        dSPIN_RegsStruct.K_THERM = Macro.KThermToPar(1);

        /* Overcurrent threshold settings to 1500mA */
        dSPIN_RegsStruct.OCD_TH = (byte)dSPIN_OCD_TH_TypeDef.dSPIN_OCD_TH_2250mA; //my power supply can't handle more than 1000mA

        /* Stall threshold settings to 1000mA, range 31.25 to 4000mA */
        dSPIN_RegsStruct.STALL_TH = Macro.StallThToPar(2000);
        /* Step mode settings to 128 microsteps */
        dSPIN_RegsStruct.STEP_MODE = (byte)dSPIN_STEP_SEL_TypeDef.dSPIN_STEP_SEL_1_128;
        /* Alarm settings - all alarms enabled */
        dSPIN_RegsStruct.ALARM_EN = (byte)(dSPIN_ALARM_EN_TypeDef.dSPIN_ALARM_EN_OVERCURRENT | dSPIN_ALARM_EN_TypeDef.dSPIN_ALARM_EN_THERMAL_SHUTDOWN
            | dSPIN_ALARM_EN_TypeDef.dSPIN_ALARM_EN_THERMAL_WARNING | dSPIN_ALARM_EN_TypeDef.dSPIN_ALARM_EN_UNDER_VOLTAGE | dSPIN_ALARM_EN_TypeDef.dSPIN_ALARM_EN_STALL_DET_A
            | dSPIN_ALARM_EN_TypeDef.dSPIN_ALARM_EN_STALL_DET_B | dSPIN_ALARM_EN_TypeDef.dSPIN_ALARM_EN_SW_TURN_ON | dSPIN_ALARM_EN_TypeDef.dSPIN_ALARM_EN_WRONG_NPERF_CMD);
        /* Internal oscillator, 2MHz OSCOUT clock, supply voltage compensation disabled, *
         * overcurrent shutdown enabled, slew-rate = 290 V/us, PWM frequency = 15.6kHz   */
        dSPIN_RegsStruct.CONFIG = ((UInt16)dSPIN_CONFIG_OSC_MGMT_TypeDef.dSPIN_CONFIG_INT_16MHZ_OSCOUT_2MHZ) |
            ((UInt16)dSPIN_CONFIG_SW_MODE_TypeDef.dSPIN_CONFIG_SW_HARD_STOP) |
            ((UInt16)dSPIN_CONFIG_EN_VSCOMP_TypeDef.dSPIN_CONFIG_VS_COMP_DISABLE) |
            ((UInt16)dSPIN_CONFIG_OC_SD_TypeDef.dSPIN_CONFIG_OC_SD_ENABLE) |
            ((UInt16)dSPIN_CONFIG_POW_SR_TypeDef.dSPIN_CONFIG_SR_290V_us) |
            ((UInt16)dSPIN_CONFIG_F_PWM_INT_TypeDef.dSPIN_CONFIG_PWM_DIV_2) |
            ((UInt16)dSPIN_CONFIG_F_PWM_DEC_TypeDef.dSPIN_CONFIG_PWM_MUL_1);

        /* Program all dSPIN registers */
        stepperDriver.dSPIN_Registers_Set(ref dSPIN_RegsStruct);


        /* Send dSPIN command change hold duty cycle to 0.5% */
        stepperDriver.dSPIN_Set_Param(dSPIN_Registers_TypeDef.dSPIN_KVAL_HOLD, Macro.KvalPercToPar(0.1)); //Macro.KvalPercToPar(.50)); //Kval_Perc_to_Par(0.5)

        /* Send dSPIN command change run duty cycle to 5% */
        stepperDriver.dSPIN_Set_Param(dSPIN_Registers_TypeDef.dSPIN_KVAL_RUN, Macro.KvalPercToPar(15));// Macro.KvalPercToPar(40)); //Kval_Perc_to_Par(5);

        calMode();
        
    }
    catch (Exception ex)
    { ex.ToString(); }
}

public static void calMode()
{
	         i = 1;
 stepperDriver.dSPIN_Move(dSPIN_Direction_TypeDef.REV, 30000);

    while (stepperDriver.dSPIN_Busy_SW() == 1)
    {
        Thread.Sleep(3000);

    }

    hallEffect.EnableInterrupt();

    stepperDriver.dSPIN_Go_Until(dSPIN_Action_TypeDef.ACTION_RESET, dSPIN_Direction_TypeDef.FWD, 100);

    while (stepperDriver.dSPIN_Busy_SW() == 1)
    {
        Thread.Sleep(1);
    }

    do //Use this section to find approximate loations
    {
        if (!calButton.Read())
        {

            stepperDriver.dSPIN_Move(dSPIN_Direction_TypeDef.REV, 10000);
            while (stepperDriver.dSPIN_Busy_SW() == 1)
            {
                Thread.Sleep(1);

            }
            do  
            {
                if (!saveButton.Read())
                {
                    uint reg = stepperDriver.dSPIN_Get_Param(dSPIN_Registers_TypeDef.dSPIN_ABS_POS);
                    reg = ~reg;
                    reg += 1;     
                    calPos[i] = reg;
                    i++;
                
                }
           }while (saveButton.Read());
        }
    }  while(i<32);

I am so confused by this behavior. I have my interrupt set to IntteruptEdgeLow with it disabled. When my motor is moving from low to high the interrupt still triggers.

Also if I command it to move 60,000 steps (1/128 microsteps on a 200 steps/rev motor) it only moves about a quarter of a rev. That command is over 2 revs.

Too much minimal code for meā€¦

Ok well here is just the part that moves it then reads the register. Itā€™s all using the dSpin class.

stepperDriver.dSPIN_Move(dSPIN_Direction_TypeDef.REV, 10000);
        while (stepperDriver.dSPIN_Busy_SW() == 1)
        {
            Thread.Sleep(1);

        }
        do  
        {
            if (!saveButton.Read())
            {
                uint reg = stepperDriver.dSPIN_Get_Param(dSPIN_Registers_TypeDef.dSPIN_ABS_POS);
                reg = ~reg;
                reg += 1;     
                calPos[i] = reg;
                i++;
            
            }
       }while (saveButton.Read());
    }

I was trying to help you with interruptsā€¦ the code above has nothing to do with interrupts. I am totaling confused.

1 Like

Sorry that code was in relation to the motor barely moving and the ABS_POS register saying its moved like 4 billion steps.

I think the interrupts are actually fine - its the motor not moving the correct amount of steps that is causing the issue. Disconnecting the hall sensor / commenting out my interrupts still causes the motor to stop moving too soon.

Another, possibly related issue, is that fact that I can successfully move the motor towards the hall sensor, trigger the interrupt and set it as the new home position but then reading ABS_POS (which should now be zero at this point) gives me a ridiculous number). I then move the motor away from the interrupt and tell it to go home - then moves back to exactly where it was supposed to read zero position (at the hall sensor where I just set to new home).

So I canā€™t figure out 1) why ABS_POS is incorrect and 2) Why the motion is not going as expected - what are reasonable setting for the acceleration rate, min and max speed, ect? setting the Acc rate above 300 steps/s^2 makes the motor move a tiny bit but cuts it way short. Dropping below 300 allows for the full motion but extremely slow even with 40% duty cycles.

There are a lot of factors which influence if the motor is able to step reliably. Do you have a method of sending bytes to the driver directly? Something like SPI.WriteRead and predefined bytes to send? I want to know that youā€™re accessing the register correctly. The ABS_POS register is 3 bytes wide MSB first. So the first byte in should be pushed to the right 16 times, the second byte 8 times and the last left alone. Or them all together and you get the register value. Can you try this directly?

Figured it out! I was calculating the gear reduction for a worm gear wrongā€¦its actually reducing by 100 not by 8.

Does anyone have experience with the Go_Until() command? The motion generated by that command is EXTREMELY slow compared to using Move(). Is Go_Until based off the deceleration register or something?

GoUntil accepts a speed. Use the same speed that you use for the Move command. The move command uses the MAX_SPEED register value.

This is driving me crazy. The speed settings donā€™t seem to affect Go_Until. The only thing that does is increasing my Krun value but the value that improves Go_Until is too much for Move() to work I donā€™t understand it.

With the same speed for Max_Speed and Move(), I get completely different behavior. Move runs fine, minus a bit of slow acceleration whereas Move() creeps harder than the staff at Neverland Ranch.