OLED updating slowing down motors

Status
Not open for further replies.

vfxwolf

Well-known member
While researching my current project, I read that updating an LCD was ‘expensive’ as far as CPU cycles. It was one of my main reasons for choosing to build around a teensy - I thought its blistering speed would allow me to have a screen updating while driving motors.

Not so much.

Driving stepper motors slows to a crawl when updating the OLED in the same loop. I’m using the accelstepper library, along with the Adafruit GFX driving the SSD 1306 OLED

For time lapse implementation it may well be possible - depending on the length of the time lapse - as their may be time to update the display between steps, but for real-time it’s just not working.

Is there some technique that would allow me to update the screen while stepping as I should? Interrupts perhaps? Should I have a dedicated microprocessor to handle the screen alone, so the teensy can focus on stepping?

I know I could forego updating the screen until the motors have done what they need to do, but I’d really like to have some visual feedback happening on the screen.

Advice welcome and appreciated.
 
The latest 1.41b4 release has the included SSD 1306 driver code running at 400 KHz - that speeds it up over the older default that AFAIK was 100 KHz.
What display size (pixels) is in use? Such details and a runnable simple sample of the code showing the problem in use might help show if it is being well used.
 
defragster,

The display is 128x64, a small .96" screen... surprisingly tiny, surprisingly readable, but on the final project I'll probably go for a 2". As for the code, here's a quick sketch I modified to time a set movement of the motor, with and without updated the display during movement.

The time differences are noted in the comments.

Paul, thanks for the link to TeensyStep... I'm going to try it in this sketch as a test.



Code:
// Bounce Motor Display tester//
// Make a single stepper bounce from one limit to another REPS many times, then print the duration to complete
//
// 


#include <Adafruit_GFX.h>       // Include core graphics library for the display
#include <Adafruit_SSD1306.h>   // Include Adafruit_SSD1306 library to drive the display
#include <AccelStepper.h>
#include <Wire.h>


Adafruit_SSD1306 display;       // Create display


AccelStepper stepper(1,4,3);    // Define stepper & pins. AccelStepper stepper(AccelStepper::DRIVER, step pin, direction pin)


  int32_t elapsed   = 0;
  int32_t starttime = millis();
  int reps = 0;


void setup()
{  
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Initialize display with the I2C address of 0x3C
  Wire.setClock(1000000);                     // Teensy Steroid injection for the OLED
  display.clearDisplay();  // Clear the buffer
  display.setTextColor(WHITE);  // Set color of the text
  display.setRotation(0);  // Set orientation. Goes from 0, 1, 2 or 3
  display.setTextWrap(false);  // By default, long lines of text are set to automatically “wrap” back to the leftmost column.
  // To override this behavior (so text will run off the right side of the display - useful for
  // scrolling marquee effects), use setTextWrap(false). The normal wrapping behavior is restored
  // with setTextWrap(true).


  display.dim(0);  //Set brightness (0 is maximun and 1 is a little dim)
  // Change these to suit your stepper if you want
 /*                                                                                     16 microstep  quarterstep
         16/256  2/256  fullstep  halfstep  halfstep/246  quarterstep   16 microstep    stealthchop   stealthchop
maxspeed 58000   15000    3000     6500       7700          14800         57000           18000         15000
accel    25000   7500     1500     3000       6000          6000          25000            9000          7000
*/ 
  stepper.setMaxSpeed(10000);
  stepper.setAcceleration(5000);
  stepper.moveTo(3000);


}


void loop()
{
    
    do 
    {


      if (stepper.distanceToGo() == 0)
      {
        // If at the end of travel go to the other end      
      stepper.moveTo(-stepper.currentPosition());
      reps++;
      }
      display.clearDisplay();                       //    with these lines commented out.  Bounce Completion is 7sec.
      display.setFont();                            //
      display.setCursor(1,5);                       //    with these lines active. Bounce Completion is 333 sec. 
      display.print("current motor position");      //
      display.setCursor(10,25);                     //
      display.print(stepper.currentPosition());     //
      display.display();                            //
      stepper.run();


    }while (reps < 4);


    int32_t endtime = millis();
    elapsed = (endtime - starttime)/1000;
    
    display.clearDisplay();
    display.setFont();
    display.setCursor(1,5);
    display.print("Time to complete:");
    display.setCursor(1,15);
    display.print(elapsed);
    display.display();
    delay (30000);            // Hold execution for 30 sec. to note time
    
}
 
Does this display work reliably with 1 MHz I2C speed? Just curious?

And did you add pullup resistors, or just using whatever resistors are already on the display? I ask because this high speed usually requires low value pullup resistors, like 330 to 820 ohms.
 
Paul, before adding in the motor issue, it was working VERY reliably. No pullup resistors added. What would have INstability looked like? I didnt see any glitches...

There were a couple of instances where the Teensy wouldnt accept a new program.... I had to click the button on the board to reinitialize.. I dont know if its related.
 
Hi,

There are a number of ways to speed up your display output. Keep in mind that the 1306 has no read screen data back capability so virtually all 1306 libraries use an in memory "frame buffer" to hold a copy of the current display data which is 128 x 64 / 8 = 1024 bytes in your case.

First is to up the speed of the link. You have already discovered how to increase the I2C clock from 100kHz to 400kHz to 1mHz which is a good start. Keep in mind that many of the I2C OLEDs will not run reliably at 1mHz. Switching from I2C to SPI will further increase the link speed for two reasons: SPI typically supports higher transfer clock rates (eg: 4mHz or more) and the 1306 supports full frame SPI transfers whereas 1306 I2C support is limited to 16 bytes at a time (so 1 x 1024 byte transfer for SPI versus 64 X 16 byte transfers for I2C).

Second is to limit the number of times that you call "display". Each time you call "display" the entire frame buffer (1024 bytes) is transferred to the display hardware. For an I2C interface clocked at 400kHz this takes ~30mSec. So try to only call "display" when you actually have new data such as when the stepper position in your sketch has changed.

Third is to limit the amount of data that needs to be transferred from the frame buffer to the display. This is easily done by implementing an "update rectangle" which contains the coordinates of the smallest rectangle that contains the pixels changed since the last call to "display". Then when the next "display" is called these coordinates are output to the 1306 and only the data within the "update rectangle" bounds is output. This can really speed things up unless your code repeatedly calls "clear" before updating the display (which unfortunately, your sketch is doing). "clear" will always set the "update rectangle" to the entire frame buffer requiring that the entire buffer be transferred. A more advanced technique is to alternate between two frame buffers and only update bytes which differ between the two, but that requires more RAM and more code with diminishing benefit.

Finally, all of the 1306 libraries that I've seen (including Adafruit's) are really quite inefficient. This is especially true when outputting text. If you trace down to the low level routines you will find something named "drawChar" or some such. Typically this routine will copy the character glyph pixel-by-pixel into the frame buffer. This technique has the advantage of being easy to implement and allows for simple text rotation via coordinate translation but is very slow. However, a simple x,y bit-block transfer is more than 10 times faster than a bit-by-bit transfer.

The above list is loosely ordered by complexity. The first two items are confined to your sketch. The third item requires (somewhat straight forward) changes to whichever 1306 library you are using. Changing the character output code in the library is more advanced and would be pretty much the last thing that I'd recommend to someone looking for better display performance.
 
Paul,

TeensyStep looks fantastic... the OLED is updating realtime with seemingly no delay in the motors. Unfortunately, there seems to be a bug where the library drives the motor in one direction only..
 
dgranger,

Thanks for the tips! I'm aware of the I2C vs SPI.. unfortunately, I'd already ordered I2C screens.. but I'll be switching to SPI for the final build.

The second option isnt really feasible for what I want to see on the display - I do need to update it at every step, which is whats causing the slowdown. I want to draw a progress bar, and show motor position at every step between keyframes, completion percentage, hours, minutes and seconds remaining... and an animated Mona Lisa in the upper right hand corner... the more the better ;) I know I could stop all display updates until the motor comes to a stop - but ideally I'd like to have the visual feedback throughout each move.

The third option - the "update rectangle" - is something I thought would be theoretically possible, but I'm not sure I'm up to knock-on effect it'll have on my code. Clearing the whole display I know is costly, but when I'm updating all the info I plan to.. might as well redraw the whole screen. If I was just updating a single four digit number - it'd make sense.

Thankfully, Paul S. pointed me to TeensyStep, which seems to solve all my problems. Initial testing has the motors running at full speed while the display updates in realtime - its almost magical after struggling with AccelStepper.. the problem seems to be a bug that only allows me to run the motors in one direction.... annoying be stopped on the eve of my greatest triumph ;)
 
quote_icon.png
Originally Posted by vfxwolf
What would have INstability looked like?



Most likely the display not working at all. Less likely, corrupted pixels or erratic performance.



Havent seen anything like that... yet... knock on silicon.
 
TeensyStep looks fantastic... the OLED is updating realtime with seemingly no delay in the motors. Unfortunately, there seems to be a bug where the library drives the motor in one direction only..

You are right, this is a bug in the continuous rotation functionality, I'll fix it as soon as I find some time (sigh...)
Anyway, for a quick workaround you can try to replace

Code:
if(buttons.isPressedAfter(BUTTON_BLUE,500))    // Hold Down to move
{
    stepper.setMaxSpeed(-30000);
    controller.rotateAsync(stepper);
    while (controller.isRunning() && analogRead(BUTTONS_PIN)!=1023)
    {
      //....
    }
    controller.stopAsync();
}

with

Code:
if(buttons.isPressedAfter(BUTTON_BLUE,500))    // Hold Down to move
{
    stepper.setTargetAbs(INT_MAX);      // <-- cw rotoation
    // stepper.setTargetAbs(INT_MIN);   // <-- ccw rotation---------
    controller.moveAsync(stepper);      // <-- use move instead of rotate
    while (controller.isRunning() && analogRead(BUTTONS_PIN)!=1023)
    {
      //....
    }
    controller.stopAsync();
}

Details: https://github.com/luni64/TeensyStep/issues/13
 
Paul,

Starting to experience some glitchy-ness now.. not sure why it started. Seemingly random. Display starts shifting around, pixels blocking.. the motor in the circuit makes some squeaking noises then the whole Teensy seems to freeze up. Do you recommend the resistors you spoke of, or just turn down the speed?
 
I was also going to mention, that if you were running with SPI, I have a version of the Sparkfun Teensyview (SSD1306) library that runs using DMA... Keep meaning to get back to it and maybe get changes put back into the Sparkfun master version. I don't think I made a version of the Adafruit library that works using DMA over I2C...
 
Status
Not open for further replies.
Back
Top