Snippet - L6470 Beta Ported Device Driver

Also, you have to calculate a few values for the driver based on your motor to get the absolute best performance. The evaulation software contains a calculator that you should use to get the correct values (L6470 - Fully integrated microstepping motor driver with motion engine and SPI - STMicroelectronics) the BEMF Compensation tool.

@ kurtnelle -
Welcome back, have not seen you in a while. Or i just cant see…

if i recall correctly then you also need your motors inductance, and you need an oscilloscope to take a reading from the motor. Those values are required by the bemf tool…

Hi,
Thank you for the quick helpful information. I am still getting stall warnings on both A and B bridges that is throwing the flag. I have all settings according to the BEMF tool. Not sure what the issue is. It must not be extreme because a full turn (25600 steps) looks to be exactly a full turn of the shaft. I am also getting UVLO flag raised. Hey, it works great with both of these disabled! Any ideas how I can get them to not turn on in the first place?
Tom

One more thing, the device does not wait as expected for the software busy signal, but blows through. It does, however wait for the hardware signal.
Tom

Hello Tommy,

Are you using the SYNC pin? - just a side question. Also what are the Acceleration and Deceleration values. They should be suited for the inertia of the task. My strategy is to try lower and lower ACC and DECC values until I get no more Step loss errors (in theory anyways).

Hi,
I am using the sync pin as a hardware busy signal. I am having quite a few more problems. While trying to change the acceleration, I am trying to set and read parameters, and am having very little luck.

            /* Acceleration rate settings to 466 steps/s2, range 14.55 to 59590 steps/s2 */
            dSPIN_RegsStruct.ACC = (UInt16)((UInt32)(((100) * 67.108864) + 0.5)); //AccDec_Steps_to_Par
            /* Deceleration rate settings to 466 steps/s2, range 14.55 to 59590 steps/s2 */
            dSPIN_RegsStruct.DEC = (UInt16)((UInt32)(((100) * 67.108864) + 0.5)); //AccDec_Steps_to_Par
            /* Maximum speed settings to 488 steps/s, range 15.25 to 15610 steps/s */
            dSPIN_RegsStruct.MAX_SPEED = ((UInt16)(((488) * 0.065536) + 0.5)); // MaxSpd_Steps_to_Par(488)
            /* Minimum speed settings to 0 steps/s, range 0 to 976.3 steps/s */
            dSPIN_RegsStruct.MIN_SPEED = ((UInt16)(((0) * 4.194304) + 0.5)); // MinSpd_Steps_to_Par(0)
            /* Full step speed settings 252 steps/s, range 7.63 to 15625 steps/s */
            dSPIN_RegsStruct.FS_SPD = ((UInt16)((252) * 0.065536)); // FSSpd_Steps_to_Par(252)
            /* Hold duty cycle (torque) settings to 10%, range 0 to 99.6% */
            dSPIN_RegsStruct.KVAL_HOLD = ((byte)(((10) / 0.390625) + 0.5)); // Kval_Perc_to_Par(10)
            /* Run duty cycle (torque) settings to 10%, range 0 to 99.6% */
            dSPIN_RegsStruct.KVAL_RUN = ((byte)(((10) / 0.390625) + 0.5)); // Kval_Perc_to_Par(10)
            /* Acceleration duty cycle (torque) settings to 10%, range 0 to 99.6% */
            dSPIN_RegsStruct.KVAL_ACC = ((byte)(((10) / 0.390625) + 0.5)); // Kval_Perc_to_Par(10)
            /* Deceleration duty cycle (torque) settings to 10%, range 0 to 99.6% */
            dSPIN_RegsStruct.KVAL_DEC = ((byte)(((10) / 0.390625) + 0.5)); // Kval_Perc_to_Par(10)
            /* Intersect speed settings for BEMF compensation to 200 steps/s, range 0 to 3906 steps/s */
            dSPIN_RegsStruct.INT_SPD = ((UInt16)(((200) * 4.194304) + 0.5)); //IntSpd_Steps_to_Par(200)
            /* BEMF start slope settings for BEMF compensation to 0.038% step/s, range 0 to 0.4% s/step */
            dSPIN_RegsStruct.ST_SLP = ((byte)(((0.038) / 0.00156862745098) + 0.5)); //BEMF_Slope_Perc_to_Par(0.038)
            /* 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 = ((byte)(((0.063) / 0.00156862745098) + 0.5)); // BEMF_Slope_Perc_to_Par(0.063)
            /* 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 = ((byte)(((0.063) / 0.00156862745098) + 0.5)); // BEMF_Slope_Perc_to_Par(0.063)
            /* Thermal compensation param settings to 1, range 1 to 1.46875 */
            dSPIN_RegsStruct.K_THERM = ((byte)(((1 - 1) / 0.03125) + 0.5)); // KTherm_to_Par(1)
            /* Overcurrent threshold settings to 1500mA */
            dSPIN_RegsStruct.OCD_TH = (byte)dSPIN_OCD_TH_TypeDef.dSPIN_OCD_TH_1500mA;
            /* Stall threshold settings to 1000mA, range 31.25 to 4000mA */
            dSPIN_RegsStruct.STALL_TH = ((byte)(((1000 - 31.25) / 31.25) + 0.5)); // StallTh_to_Par(1000);
            /* 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);

            //stepperDriver.dSPIN_Set_Param(dSPIN_Registers_TypeDef.dSPIN_ACC, (UInt16)((UInt32)(((100) * 67.108864) + 0.5)));
            //stepperDriver.dSPIN_Set_Param(dSPIN_Registers_TypeDef.dSPIN_DEC, (UInt16)((UInt32)(((100) * 67.108864) + 0.5)));

            dSPIN_rx_data = stepperDriver.dSPIN_Get_Param(dSPIN_Registers_TypeDef.dSPIN_DEC);

            dSPIN_rx_data = stepperDriver.dSPIN_Get_Status();

            dSPIN_rx_data = stepperDriver.dSPIN_Get_Status();

After I attempt to set parameters, the GET_Param returns 0, the first Get_Status also returns 0, and the second one says that there was a NOTPERF_CMD error. I can read some variables, but not accel or decel to see if I made any changes. I wanted to read them because it seemed that the different acceleration and deceleration values made not difference - were not being stored. I also tried to use the dSPIN_SET_PARAM command with the same results.
Ideas?

Glad to be back actually… Thanks Errol,

(I should really add a TestCommunication() method to that driver)

Tommy, the problem could be communication. That fact that Get Pram is returning incorrectly is a problem. Post the code where you are setting up the SPI port.

See below:


 /* Initialize peripherals used by dSPIN */
            SPI.Configuration _spiConfig = new SPI.Configuration((Cpu.Pin)CS,
                !true, // Chip Select, active-low
                1000, // 1000 (nanoseconds?) setup time
                1000, // 1000 (nanoseconds?) hold time
                true, // Clock low on idle
                true, // Data valid on falling edge
                5000, // 5Mhz Clock Rate
                SPI.SPI_module.SPI1); //SPI device 1
            SPI spiPort = new SPI(_spiConfig);

To be clear on communications, directions on motion work, the motor moves. The GET_STATUS() method usually works as well, unless preceded by trying to write a parameter, or the whole RegsStruct.
Thanks,
Tom

Hello Tommy,

I’m going to write a CommunicationTest method for the driver this evening (into the next day God’s willing). What kind of stepper do you have? Do you know the BEMF calculation values for it? Could you link a PDF of the datasheet?

Hi Kurt,
The motor in question:
http://www.automationdirect.com/adc/Shopping/Catalog/Motion_Control/Stepper_Systems/Motors_-z-_Cables/STP-MTR-17040

The program gave me:
KVAL_HOLD - 3
KVAL_ACC - 18
KVAL_DEC - 18
KVAL_RUN - 18

ST_SLP - 18
INT_SPEED - 143B
FN_SLP_ACC - 2C
FN_SLP_DEC - 2C

Your help has been indispensable.
Tom

@ kurtnelle,
btw, I got daisychain mode working.

In my driver I created a byte[] buffer for each motor where I place the commands that must be sent to it, then this code loops through all the bytes in the buffers and sends it to the drivers.


        public void ExecuteAllMotors()
        {
            byte[] Command = new byte[NumberOfMotors];
            int BufferSize = 0;

            //Get biggest buffer
            foreach (StepperMotor m in Motors)
            {
                if (m.Buffer.Length > BufferSize)
                    BufferSize = m.Buffer.Length;

            }

            //Loop through all the buffers, taking one byte from each buffer during each loop
            for (int CurrentByte = 0; CurrentByte < BufferSize; CurrentByte++)
            {
                for (int i = NumberOfMotors - 1; i >= 0; i--)
                {
                    if (Motors[i].Buffer.Length >= CurrentByte)
                    {
                        Command[NumberOfMotors - i - 1] = Motors[i].Buffer[CurrentByte];
                    }
                    else
                    {//This modor does not have data to be sent, so send NOP
                        Command[NumberOfMotors - i - 1] = StepperMotor.CmdNOP;
                    }
                }
                SpiPort.Write(Command);

            }
            //reset motor buffers
            foreach (StepperMotor m in Motors)
            {
                m.Buffer = new byte[0];
            }
        }

@ Errol, lol. that was supposed to be one of the harder things to do with this chip. I’m glad that somone got it working!. I have to build up 3 of the L6470 circuit boards (somehow) so I’ll definitely have use for that code.

@ tommy, I’m still trying to setup to write that method for ya.

Hey Error when you send a GetParms call to your chip do you get back the correct value or NOP?

Error? :slight_smile: :slight_smile:

I get data back.

I have not looked at the code you posted in depth so I apologise if this is obvious.

It must be several SPI write and reads.

One Write to send the GetParam command and the address all in one byte.

Then one SPI read for every byte.

You can’t read multiple bytes in one go, nor use the WriteRead function.

Kurt,
This is precisely the problem I am running into. In your read and write parameter methods, it seems that almost all of the number of bytes to read and write are not correct. For instance:

case dSPIN_Registers_TypeDef.dSPIN_ACC:
                    break;

Needs to be:

                     case dSPIN_Registers_TypeDef.dSPIN_ACC:
                    temp = dSPIN_Write_Byte((byte)(0x00));
                    temp = temp << 8;
                    rx |= temp;

                    temp = dSPIN_Write_Byte((byte)(0x00));
                    rx |= temp;
                    break;;

I am now sending and receiving the proper commands.
Tom

As another note - I have started using the L6472 driver instead. It is much easier to use. You can simply set the torque current in mA instead of fooling around with all of the EMF settings. I just received a few of them in a POWER_SO package that should be even better for heat dissipation.
Tom

Also, I have found the hardware busy check to work, but not the software check. The software check halts the motion early, and produces warnings (overcurrent?) that do not happen in the hardware check. That I find strange. Any ideas?

Sorry Errol. I keep typing Error for some reason :slight_smile: So then, multiple bytes I/O are not possible. That makes things a little more interesting. We were worried that the SPI port was toggling CS on every byte sent or toggling once the entire byte stream was sent. This makes a world of difference.

Sound like I have to double check a lot of those values there tommy, good find I’ll update the driver. Send a pic of the L6472!

Yeah, I’ve requested samples from my local ST representative, in the same package as the L6470, so I can use my same Gadgeteer Module PCB to test it. The L6472 does only have 16 microsteps compared to the L6470’s 128, but is it WAY easier to config…

Yes, NETMF toggles the CS line on every stream, that is why every stream, in this case, must be for only one byte. If you have two chips in the chain then every stream must contain 2 bytes. Three chip = three bytes, etc…

For the chained wiring, I have found that one can programatically determine the number of chips in the chain. Thus I have written my driver will work just as well with one chip, as with 40 chips, and it creates a Motor object for each chip what gives you access to the chip parameters and commands. I did this because the Gadgeteer module i’m building will come in different configs, with one, two, three or four chips, and I’m considiring making the modules chainable too, so you can have as many drivers as you need…

So I have my POWER SO boards, and have built one up. I am running into an issue, however. While the chip responds fine to all commands issued, all of the replies that I get back from the module are 1s (255, 65535…). This is not the correct response, and I was wondering if anyone had any ideas about why this might be. I am using a L6472, and my regular chip responds perfectly to the same commands and setup.
Thanks,
Tom

Dry joint maybe?