Change from step/dir to direct GPIO program control

Moderators: TomKerekes, dynomotion

Post Reply
SJHardy
Posts: 46
Joined: Thu Oct 03, 2019 12:36 am

Change from step/dir to direct GPIO program control

Post by SJHardy » Mon Mar 13, 2023 5:55 pm

Hi Tom,

Currently on 4.33q kflop controller firmware.

I have a machine where chan[4] is normally used for step+direction (which works fine with I/O bits 36, 37) but it's possible to swap the motor out for a laser which needs to use the step output as a GPIO to switch the laser on and off.

The trouble is, I have not been able to figure out how to switch between those two modes. In fact, I have not been able to use those GPIOs at all, even if I never set up the channel or enable it. Note that I have the FPGA option set which sets active high for step pulses.

Assuming step/dir has been in use on chan[4], what steps to I need to take to disable the step/dir and set a state where I can use SetBit(36)/ClearBit(36) to manually toggle the output? I could also use the dir output (37) if necessary.

Regards,
SJH

User avatar
TomKerekes
Posts: 2676
Joined: Mon Dec 04, 2017 1:49 am

Re: Change from step/dir to direct GPIO program control

Post by TomKerekes » Mon Mar 13, 2023 6:55 pm

Hi Steve,

If the Step/Dir Generator is enabled it overrides the GPIO mode.

There is a 32-bit register that controls the Step Frequency for the generators, the mode, and whether it is enabled or not. The Enable is bit 31.

Note the +8 on the channel number to restore TTL mode for the output.

Also writing the 4 bytes should not be interrupted or the result might be corrupted. So you might want a WaitNextTimeSlice before the writes.

Code: Select all

#include "KMotionDef.h"

main()
{
	int v=0;
	int chan=4;
	int Enable=0;
	
	v = (v & 0x007fffff) | ((chan+8)<<24) | (Enable<<31);
	
	FPGA(STEP_RATE_ADD)   = v;
	FPGA(STEP_RATE_ADD+1) = v>>8;
	FPGA(STEP_RATE_ADD+2) = v>>16;
	FPGA(STEP_RATE_ADD+3) = v>>24;
}
Regards,

Tom Kerekes
Dynomotion, Inc.

SJHardy
Posts: 46
Joined: Thu Oct 03, 2019 12:36 am

Re: Change from step/dir to direct GPIO program control

Post by SJHardy » Mon Mar 13, 2023 8:32 pm

Thanks Tom, that works like a charm. I'm a bit puzzled by the initial 3 bytes of zeros, though. Does that mean "no change to anything else"?

One followup question: the code below is run in a loop. While the laser PWM is enabled, it tracks the accumulated amount of XY motion, and the amount of time the laser has been on. It then turns it on or off according to the desired amount of on-time per unit distance. It is basically working, although it seems to come up a bit short regarding the distance moved.

One requirement is that the laser is off for rapid motion, and only comes on for feeds. The initial setting of the 'run' variable is showing how this is determined.

If I use G-code G91 G1 X50 to move 50mm, the lpwm_accum_d field ends up with something like 49.247mm which, although close, is not as good as I would expect. Can you see anything wrong in the following?

Regards.
SJH

Code: Select all

    #if LASER_PWM
    if (supe->lpwm_enabled) {
        int run = !CS0_DoingRapid && CS0_TimeExecuted < CS0_TimeDownloaded; // Only run for normal feeds
        
        if (run && supe->lpwm_enabled == 1) {
            // Initial start
            supe->lpwm_enabled = 2;
            supe->lpwm_last_samp = Time_sec();
            supe->lpwm_accum_t = 0.;
            supe->lpwm_accum_d = 0.;
            supe->lpwm_samp_pos[0] = chan[0].Dest;
            supe->lpwm_samp_pos[1] = chan[1].Dest;
            supe->lpwm_on = 0;
            printf("supe: lpwm start\n");
        }
        else if (!run && supe->lpwm_enabled == 2) {
            // Stopping after run
            supe->lpwm_enabled = 1;
            set_output(STEPB, 0);
            printf("supe: lpwm stop\n");
        }
        else if (run) {
            // PWM run state
            double T = Time_sec();
            double dt = T - supe->lpwm_last_samp;
            double dx = chan[0].Dest - supe->lpwm_samp_pos[0];
            double dy = chan[1].Dest - supe->lpwm_samp_pos[1];
            double d = sqrt(dx*dx + dy*dy);
            supe->lpwm_last_samp = T;
            supe->lpwm_samp_pos[0] = chan[0].Dest;
            supe->lpwm_samp_pos[1] = chan[1].Dest;
            if (supe->lpwm_on)
                supe->lpwm_accum_t += dt;
            supe->lpwm_accum_d += d;
            supe->lpwm_on = supe->lpwm_accum_d * supe->lpwm_q > supe->lpwm_accum_t;
            set_output(STEPB, supe->lpwm_on);
        }
    }
    #endif

User avatar
TomKerekes
Posts: 2676
Joined: Mon Dec 04, 2017 1:49 am

Re: Change from step/dir to direct GPIO program control

Post by TomKerekes » Mon Mar 13, 2023 9:09 pm

Hi Steve,
I'm a bit puzzled by the initial 3 bytes of zeros, though. Does that mean "no change to anything else"?
The first 3 bytes are the Step Frequency. The 4 bytes form a 32-bit word that is written to the Generators after the 4th byte is written. Actually I suppose if the generator is being disabled then writing garbage as the frequency wouldn't matter, so only writing the 4th byte should work and that would avoid any interrupt issue.
If I use G-code G91 G1 X50 to move 50mm, the lpwm_accum_d field ends up with something like 49.247mm which, although close, is not as good as I would expect.
The only thing I can see is this code:

Code: Select all

            double dx = chan[0].Dest - supe->lpwm_samp_pos[0];
            double dy = chan[1].Dest - supe->lpwm_samp_pos[1];
            double d = sqrt(dx*dx + dy*dy);
            supe->lpwm_last_samp = T;
            supe->lpwm_samp_pos[0] = chan[0].Dest;
            supe->lpwm_samp_pos[1] = chan[1].Dest;
May lose some motion if the code is interrupted and the Dest changes between the time is used and when it is saved. How often is this code executed? The double precision sqrt is a somewhat expensive call so that might increase the chances of this. I think a better approach would be to sample it first and then use that for both the calculation and the save.
Regards,

Tom Kerekes
Dynomotion, Inc.

SJHardy
Posts: 46
Joined: Thu Oct 03, 2019 12:36 am

Re: Change from step/dir to direct GPIO program control

Post by SJHardy » Mon Mar 13, 2023 10:56 pm

The code is called in my supervisor loop, so it should return here every couple of 90us intervals - confirmed this on the scope since the minimum pulse widths are a few hundred microseconds. Maybe there are some "flyers" which I don't pick up, which would make sense if sqrt is slow. Probably for this application I could change it to use single precision for the summations. Good point about the inconsistency of Dest - normally I would do that, but I was pretty sure there would be no interrupts.

...a few minutes later...

Tried that, and it works! So now it accumulates distance increments using float instead of double, uses sqrtf(), and single access to Dest. Comes out exact now.

FYI this is a pretty nifty app. The nice thing about doing it this way is that when laser cutting, if it needs to slow down around a corner, the power is automatically reduced so that the corners don't get "burned". Originally, I was just using the step signal as a PWM, but that has the disadvantage that every G1,2,3 has to be edited to output the correct 5th axis movement. The other disadvantage was that the PWM is then quite high frequency and the laser driver tends to filter that into a quasi analog signal - this causes the laser gain to drop so that it becomes an ordinary LED, and I was finding that below about 10% duty cycle it would completely stop cutting. With this "manual" bit toggle, the relatively slow toggle can get very low duty cycle for slow moves.

Thanks for the prompt help!

User avatar
TomKerekes
Posts: 2676
Joined: Mon Dec 04, 2017 1:49 am

Re: Change from step/dir to direct GPIO program control

Post by TomKerekes » Mon Mar 13, 2023 11:38 pm

Nice!
Regards,

Tom Kerekes
Dynomotion, Inc.

Post Reply