Forum Rule: Always post complete source code & details to reproduce any issue!
Page 2 of 2 FirstFirst 1 2
Results 26 to 47 of 47

Thread: ILI9341 and encoders in an Electronic Lead Screw Application

  1. #26
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,832
    The Error_Callback is a left over from development and it not working anymore. I'll remove the leftover code in the next update. The error callback was invoked whenever the internal state machine detects an A/B pattern which is not allowed. E.g. 1/1 following a 0/0. Such patterns will simply be ignored and shouldn't crash the processor.

    Does your Encoder library allow abs(delta)>1?
    Currently delta can only be +/- 1 or 0. Later versions might support acceleration, then it can be larger.

    Have a look at your callbacks. In case you do something taking too long you might run into problems at high count rates. At 200kHz you only have 5Ás between the encoder edges / interrupts. Again, it might be much easier if you show us your code.

  2. #27
    Here is a copy of what I have so far...stepper_n_encoder_v2_05172022_1.zip

  3. #28
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,832
    Thanks, I'll have a closer look later. So, I'll disable the touchdisplay stuff, attach EncSim to pin 0/1 and see if it crashes at about 200kHz right?

  4. #29
    Think it will crash before then, but yes. Also, I see that EncSim is outputting a couple of random counts in the beginning on start up. Not always, but occasionally. Might be the outputs changing state, but it is interpreted by the other Teensy as a count.

  5. #30
    Got a crash at Count = 99591/100000 counts. freq = 100000. My instance of spindleEncoder did not print out the count of 99592, but did for 99591. It printed out "-", then restarted and printed the setup information. No SPI or display writes. Only Serial print. Speaking of which, in Arduino, should I select the Teensy ports or the Serial ports? Seems if I use Serial ports, I don't lose as many counts...

  6. #31
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,832
    That was easy ;-)
    You print the current position in the encoder callback. At high encoder speeds the printing is not yet done when it tries to invoke the callback again. This will basically lock the system.

    I moved the printing to loop which fixes it. Here the recording of a 3'000'000 step move with 500kHz count rate. Works perfectly, no counts lost.

    (left->EncSim output, right -> output of your code)
    Click image for larger version. 

Name:	Screenshot 2022-05-17 192813.jpg 
Views:	6 
Size:	70.7 KB 
ID:	28402

    Here the changed code:
    Code:
    #include "stepper_n_encoder_v2.h"
    
    void doincrement(bool adir);
    
    //// imperial 32 TPI
    // int N = 293;
    // int D = 2000;
    
    //// imperial 8 TPI
    // int N = 293;
    // int D = 500;
    
    //// imperial 6 TPI
    // int N = 781;
    // int D = 1000;
    
    // Note: 17 May 2022, N & D were calculated off line.  The value of microsteps is embedded in the ratio.  To
    // add one's own feeds or threads, we need to calculate using the required # of microsteps!  This functionality
    // is not in the code base yet.
    
    #ifdef imperial
    uint16_t myidx               = 1;
    float TPI                    = impthread[myidx].tpi;
    int N                        = impthread[myidx].N;
    int D                        = impthread[myidx].D;
    int ustep                    = impthread[myidx].ustep; // added for future capability
    float tenpitch_error_percent = impthread[myidx].error;
    #endif
    
    OneShotTimer t1(PIT); // for stepper pulse
    OneShotTimer t2(PIT); // for measuring speed.
    
    // XPT2046_Touchscreen ts(TOUCH_CS);
    // ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCK, TFT_MISO);
    
    void onEncoderChanged(int value, int delta)
    {
        // volatile int mydelta = -delta;
        if (delta > 0)
        {
            if (delta > 1)
            {
                Serial.printf("fault! delta = %i\n", delta);
                errorcount += 1;
            }
            else
            {
                // delta = 1
                myacc = myacc + delta * N;
                if (myacc >= D) // works for positive accumulation only
                {
                    myacc = myacc - D;
                    mydir = CCW;
                    doincrement(mydir);
                }
            }
        }
        if (delta < 0)
        {
            if (delta < -1)
            {
                Serial.printf("fault! delta = %i\n", delta);
                errorcount += 1;
            }
            else
            {
                // delta = -1
                myacc = myacc + delta * N;
                if (myacc <= D)
                {
                    myacc = myacc + D;
                    mydir = CW;
                    doincrement(mydir);
                }
            }
        }
        val1 = value;
        //Serial.printf("Count = %i\n", value);
    }
    
    // void onEncoderError() don't know how it is used, nor what to look for!
    
    void pulseme() // turns off stepper pulse after time out
    {
        digitalWriteFast(PUL, LOW);
        // digitalWriteFast(LED_BUILTIN, LOW);
    }
    
    void doincrement(bool adir) // sets direction and turns on stepper pulse
    {
        digitalWriteFast(DIR, adir); // set direction
        delayNanoseconds(100);       // can improve stepper speed by doing this, but is dangerous...
        digitalWriteFast(PUL, HIGH);
        t1.trigger(steplen); // switch off after steplen us (2us)
    }
    
    void myms() // xxms timer callback for estimating spindle speed and step rate
    {
        val2 = (uint32_t)SpindleEnc.getValue(); // read encoder for current position
        // we know the time was 20 ms
    
        cps = 50.0f * ((int)val2 - valstart) / (float)encoder_resolution;
        ;                                                                // rev per second  50 = 1/0.02
        sps     = cps * (float)encoder_resolution * (float)N / (float)D; // steps/second
        myrpm   = -cps * 60.0f;
        timeout = true;
    }
    
    void readconsole()
    {
        if (Serial.available())
        {
            int incomingByte = Serial.read(); // read the incoming byte
            char c           = (char)incomingByte;
            // Parse the command.
            if ((c != '\n') & (c != '\r'))
            {
                Serial.println();
                Serial.print(F("key pressed: "));
                if (c == ' ')
                    Serial.println(F("[space]"));
                else
                    Serial.println(c);
                if (c == 'r') // reset the counter
                {
                    SpindleEnc.setValue(0); // wait til we get the right syntax!
                    Serial.printf("Reset spindle counter to 0\n");
                } // add more commands as you see fit
            }
        }
    }
    
    void setup()
    {
        while (!Serial && millis() < 4000)
            ;
    
        if (CrashReport)
            Serial.println(CrashReport);
    
        for (int i = 5; i < 8; i++) pinMode(i, OUTPUT); // init these pins to outputs
    
        t1.begin(pulseme);          // set up t1 callback function
        t2.begin(myms);             // set up t2 callback function
        digitalWriteFast(DIR, CW);  // sets direction of rotation  LOW =CW, HIGH=CCW
        digitalWriteFast(ENA, LOW); // active low signal, enables the stepper controller
    
        // Use luni's SW encoder
        SpindleEnc.begin(0, 1, CountMode::full); // encoder on pins 0,1 count all e3dges of the quadrature signal
        SpindleEnc.attachCallback(onEncoderChanged);
    
        myacc   = 0; // Bresenham accumulator
        oldrpm  = 0.0f;
        timeout = true; // so we start measuring speed right away
    
        // if (0){ // prevent all tft writess for debug!!!
        //   if (!initMyDisplay(3))
        //   {
        //     Serial.println("Display failed to initialize");
        //     while(1);
        //   }
        //   if (!mainscreen())
        //   {
        //     Serial.println("Failed to send first message to display");
        //     while(1);
        //   }
        // }
        errorcount   = 0;
        displaycount = 0;
        first        = true;
    
        // Eventually all this will be selected via the touch panel
        Serial.printf("TPI = %3.1f, usteps/step = %i\n", TPI, ustep);
        Serial.printf("N = %i, D = %i\n", N, D);
        Serial.printf("Ten pitch error as per cent of pitch = %4.2f\n", tenpitch_error_percent);
    }
    
    void loop()
    {
        if (timeout) // if no timeout, skip this
        {
            timeout = false;  // determine how many counts we get in 20ms, this is the base for a speed estimate
            t2.trigger(20ms); // set timer to go off in 20ms
            valstart = val1;  // save current position
        }
        if ((myrpm == 0.0f) && (first))
        {
            Serial.printf("%7.1f%\tRPM\n", -myrpm); // there's a zero bug of some sort
            // displayRPM(-myrpm);
            first = false;
        }
        if (myrpm != 0)
        {
            first = true;
            Serial.printf("pos: %d, RPM: %7.1f\n", SpindleEnc.getValue(), myrpm);
            // displayRPM(myrpm);
        }
        delay(200);
        // readconsole();
    }

  7. #32
    Think I found part of the problem! Was calling Serial print way too often. I updated the code to only print out the RPM once every 100ms, it seems it was trying to output every time it went around the loop. That plus the count being printed at 100KHz put me over the edge. Strange that calling serial print too often can cause a processor reset, I wouldn't think that was possible!

    I can now run at freq = 200KHz, and update the display. But I cannot output the count by calling Serial.printf at 200KHz, even at 230400 baud. Eventually the serial buffers overrun and the somehow, the program is reset - still not sure if TyCommander is doing the reset or the Teensy. Disabling the print statement I used in the onEncoderChanged callback, fixed the basic problem.

  8. #33
    Yes, you found the problem as well! Thank you very much for looking. It is very appreciated.

    Still have a problem with resetting the counter value to zero for testing. I get some strange output, but that is just an ordinary bug... I have lots more to do for this project...

    Edit: fixed that bug. Just needed to reinitialize a couple of values. Now I can reset the count, if I need to in my programs, which is handy.
    Last edited by clinker8; 05-17-2022 at 06:37 PM. Reason: Update: Fixed my bug.

  9. #34
    @luni, Making some good progress. Have two display screens and a way to get from one to the other. Working on selecting the pitch value and loading the associated values for N and D to the main program.

    As I am getting closer to a real setup, some interesting questions come up. Obviously, one doesn't want the system to run until it is initialized And obviously, if the configuration changes, ie. the operator selects a new thread pitch, we don't want to change values on the fly. Instead we want to re-initialize some values, and re-enable things in an orderly fashion. Where we proceed through a state machine. I think I have most of that figured out.

    For my code right now, everything is gated on the spindle encoder. For the first time through, I can wait to attach the callback onEncoderChanged. Without the callback, there is no lead screw movement.

    What would be a good strategy for a change in setup, ie, pitch change? Is there a way to detach the callback? Or should I just gate the stepper, until I reinitialize N, D and probably the encoder count?

  10. #35
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,832
    Are you sure that you want to change the pitch while the spindle rotates? Thinking of the noise the breaking thread tool makes gives me goose bumps :-))

    Seriously;
    • In your controlling application I'd require that the spindle is stopped before allowing any change of the pitch.
    • If you really want to change the leadscrew pitch while the spindle rotates you could simply change the bresenham parameters on the fly. While this might not be good for threading it might come in handy for changing the feed in longitudinal turning.
    • If you really need to stop the leadscrew while the spindle moves you can simply attach an empty function to the encoder. I.e., void dummy(int, int){}
    • Alternatively, I could add a detachCallback to the library on the weekend :-)

  11. #36
    No, I would never change thread pitch while cutting threads with the spindle is turning! My state machine (if I write it correctly) would prevent that from happening. Although it might be interesting to see what such a screw would look like!

    However, I could foresee threading to a stop someday. I do have a DRO on my lathe. As you know, they are just linear quadrature encoders. That would give me carriage position. If I set the carriage stop, to the position of the thread relief, then the spindle could continue to rotate and the leadscrew would stop rotating when the carriage reached the thread relief. If doing this, I could record the position of the spindle and the leadscrew count and the DRO. I think one could use this to resynchronize the thread for another pass. That might be fun to try once I get the basic unit to run.

    A more immediate use, that doesn't require crashing or threading, is a programmable feed rate for the lead screw, when one is simply turning for finish quality. In this case, it would be ok to change the feed rate. Feed rate is just a pitch that is very very small. I do think changing the Bresenham parameters would work for that case.

    A detachCallback would be very nice (I think).

  12. #37
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,832
    A detachCallback would be very nice (I think)
    I just had a look at the library code. You can simply attach a nullptr to remove the callback.
    I.e.:

    Code:
    SpindleEnc.attachCallback(onEncoderChanged); // attaches onEncoderChanged to SpindleEnc
    SpindleEnc.attachCallback(nullptr);          // removes the callback

  13. #38
    @luni, very sorry, not to have acknowledged. Thank you for looking into this. I have updated my code accordingly. Now spending a bit of time on the UI along with the system level state machine. There's probably four times as much code there, than in the motion control, which I understand is typical.

    Have ordered a 4 Nm NEMA-24 closed loop stepper motor and driver. The fun part will be figuring out how to mount and package the motor and encoder on the lathe. Fortunately, I have both a milling machine and lathe, so I will be able to machine the parts, once I figure out what's needed.

  14. #39
    Alas, no significant progress. My laptop has some sort of problem where it just freezes and turns off the power. This typically happens just after flashing a Teensy. Don't know if it is HW, or SW. Been attempting to port nearly everything over to my RPI4-8GB 64bit OS on SSD. It's tolerable, but still quite slow. Been a slow slog getting enough things running ok, email, browsers, files, etc. to carry on. Can't find any HW, or SW problem, nothing shows up in the logs that would point me to a clue. Just rsync'd everything and I am going to do a new install, wiping everything. Really dislike doing this, as it takes a long time to recover, but I've run out of options. After that, it is return to manufacturer for further analysis.

    But nearly everything is on the RPI4 now, including Teensyduino and Ty Commander. @luni, thanks for letting me know about TyCommander. Had to use a 64bit OS on the RPI so I could get a modern version of FreeCAD on it. All my mechanical design is using FreeCAD, which is pretty important when working on an Electronic Lead Screw.

    Even though the laptop died, I did hook up my NEMA-24 motor to the Teensy and it ran it with no problems. Glad I used EncoderSim for the initial testing.

  15. #40
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,832
    Wow, running Freecad on the RPI! Didn't know that the RPI is that fast by now... I use onshape for mech design. It runs in the browser (no installation required) and is quite state of the art. As long as you can live with a public design it is free.

    Looking forward to that video showing the first thread cutting success :-)

  16. #41
    The RPI4B isn't that fast... But it is tolerable, especially compared to doing it all again by hand!
    FreeCAD is free. You can have your own designs and they can be proprietary, if you want. Not going to say it isn't clunky at times, but it does work. If you use the spreadsheet mode it is easy to parameterize your design, which is a great help. I have used that mode quite frequently.

    I'm quite a ways from first threading!

    Ty Commander or something else is causing hard Linux crashes after Teensy flashing. Trying to debug this, and it has been really tough to diagnose. Nothing insightful in system logs. Don't know what the root cause is yet, except is always happens after programming the Teensy and always turns my Linux computer off...

  17. #42
    Watching this with interest as I've just added a rotary encoder to the spindle of my lathe with the intention of adding an ELS.

  18. #43
    Seems to be TyCommander that is doing the dirty deed. Why, I don't know. If I use the Teensyloader, it is fine. So for now, that's what I will use.

    On a side note, I had ordered a TI Launchpad board, in February, (for Clough42's ELS), which claimed a July 7 delivery date. Just got a notice that the date has been pushed to November 7. Think I am going to cancel that order.

    Yeah, good thing I decided to make my own ELS with a Teensy. For what it is worth, the hard part is always the user interface. The basic ELS stuff is pretty easy.

  19. #44
    Senior Member BriComp's Avatar
    Join Date
    Apr 2014
    Location
    Cheltenham, UK
    Posts
    754
    Yeah, good thing I decided to make my own ELS with a Teensy. For what it is worth, the hard part is always the user interface. The basic ELS stuff is pretty easy.
    Have a look at this. It is an example of how the HMI design can be much easier.

  20. #45
    Quote Originally Posted by BriComp View Post
    Have a look at this. It is an example of how the HMI design can be much easier.
    Interesting idea on using Powerpoint to make up the initial storyboards. I just did that part with a couple of pencil sketches. Hadn't thought about making a bit map from it. That bit was clever.

    At the moment, I need to do this on the cheap side and wasn't considering such large screens. Would be nice to have one of those nice screens, though! Problem with the UI design for me is, that I need to know exactly what I need to do - and I haven't invented or thought of it yet! I know the basics, but haven't diagrammed it all out yet. Probably should be the next step?

  21. #46
    Some more progress on menus and things. All of the threading section is complete. Most of feeds are done. Have lockouts on the stepper driver until explicitly initialized and started. Still need to clean up the state machine to make it easier to navigate between states.

    Somewhat toying with adding my DRO (digital read out for position) outputs to the Teensy. I need a level translator, since these are ancient 5V devices, but that's not hard.

    @luni Can I presume that a quadrature linear encoder can be read by Encoder Tool? I will have to do some scope work on the outputs. Are there any digital input pin restrictions? I have two DRO's, the carriage position and the cross-slide. They are magnetic pickups. I am hoping the TTL level signals are available at the interface connector. Will find out tonight.

  22. #47
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,832
    Quote Originally Posted by clinker8 View Post
    @luni Can I presume that a quadrature linear encoder can be read by Encoder Tool? I will have to do some scope work on the outputs. Are there any digital input pin restrictions? I have two DRO's, the carriage position and the cross-slide. They are magnetic pickups. I am hoping the TTL level signals are available at the interface connector. Will find out tonight.
    Of course you can. No restrictions on pins.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •