I have an Arduino Mega with a RAMPS 1.4 to control two stepper motors and an LCD screen. This is a snippet of code, which is a function that displays the elapsed time on the experiment and simultaneously controls the motors.
I am getting a skip every minute when the LCD updates. (exp_in_progress is an LCD update function).
void motor_cyclic() // MOTOR ID = 2
{
uint32_t time_start = millis();
uint32_t time_current = millis()-time_start;
static int d = 0;
static int h = 0;
static int m = -1;
while (time_current < exp_time)
{
int endstop_rmin = digitalRead(3); // R-min
int endstop_rmax = digitalRead(2); // R-max
int endstop_fmin = digitalRead(14); // F-min
int endstop_fmax = digitalRead(15); // F-max
if (time_current%60000 == 0)
{
m++;
if (m == 60)
{
m = 0;
h++;
if (h == 24)
{
h = 0;
d++;
}
}
exp_in_progress(newBreaths, flowRate, d, h, m); // LCD UPDATE//
}
time_current = millis()-time_start;
if (time_current % 10000 < 5000) {
digitalWrite(X_DIR_PIN, HIGH);
digitalWrite(Y_DIR_PIN, HIGH);
}
else {
digitalWrite(X_DIR_PIN, LOW);
digitalWrite(Y_DIR_PIN, LOW);
}
digitalWrite(X_STEP_PIN, HIGH);
digitalWrite(Y_STEP_PIN, HIGH);
delay(0.25);
digitalWrite(X_STEP_PIN, LOW);
digitalWrite(Y_STEP_PIN, LOW);
if (endstop_rmin or endstop_rmax or endstop_fmin or endstop_fmax == HIGH)
{
time_current = exp_time + 1000;
break;
}
if (!digitalRead(selectbutton))
{
time_current = exp_time + 1000;
break;
}
}
m=-1;
h=0;
d=0;
menu_return();
}
Do not use the clear() function. It takes a lot of time.
Update only data that changes. Use a function, in setup() perhaps, that writes the more permanent labels. Then use the setCursor command to place the cursor where the changeable data goes and print the data. If you need to clear a section do it by placing the cursor, printing to overwrite the old data and use setCursor to move the cursor back and write the data.
Change to the hd44780 library. It is a faster library and all around better than the older LiquidCrystal libraries.
The library is available in the Library Manager. Go to Library Manager (in the IDE menus, Sketch, Include Libraries, Manage Libraries) and in the Topics dropdown choose Display and in the Filter your search box enter hd44780. Select and install the hd44780 library by Bill Perry.
The class that you want to use is the hd44780_I2Cexp class. There are examples to show how to use the library. The nice thing about the hd44780 library is that it will autodetect the I2C address
You can try adding stepper motor update calls between lcd.print() statements.
See my tutorial Simple Multitasking Arduino which covers adding a loopTimer to monitor how fast your stepper code is being called and includes a detailed stepper example controlled by user commands and the adding extra calls to keep the stepper running consistently.
Here is some information on how much time it takes to update the display when using i2c.
The LiquidCrystal_I2C library can transfer a byte to the display in 1486us or about 1.5ms
The hd44780_I2Cexp i/o class can transfer a byte to the display in 550us or about 0.55ms nearly 3x the speed of LiquidCrstal_I2C
Each character you write the LCD display or each time you set the cursor it takes writing 1 byte to the display.
Doing a clear() takes 2ms
You can add up all the operations you are doing to calculate the total time that exp_in_progress() will take.
With numbers above you can calculate the time for either/both libraries.
Once you come up with the overall time for exp_in_progress(),
Can your other code tolerate that amount of latency?
If you want to minimize the time you need to pre-fill the screen with everything that is fixed and only update the changing part.
For the numbers, I would print them as fixed width fields on the display.
vs printing spaces then overwriting the spaces.
The reason being a fixed width field will update faster and will avoid the flicker from clearing the number and re-drawing it and it will keep the digits steady since each numeric decimal position will not move.
i.e. the 0nes position, the 10s position, the 100s position, will always be in the same location.
It makes reading the display easier.
Either pad the leading unused positions of the number with spaces or zeros.
The easiest way to do that is use sprintf() using the fill formatting options.
if you have the extra 2k or code space that the xxprintf() code will use, it will be the easiest way to do the output formatting.
You would simply
format the fixed width output field for the number into a buffer.
set the cursor position where the field needs to start on the lcd
print the field using the formatted buffer.
Another alternative would be to bump the i2c clock to 400kHz which will reduce the byte transfer times.
The LiquidCrystal_I2C byte transfer time will drop to 608us
The hd44780_I2Cexp byte transfer time will drop to 205us
The time for clear() does not change.
If you do decide to bump the i2c clock, keep in mind that while 400kHz seems to work for PCF8574 chips, it is beyond the rated spec. Also some i2c slaves like many RTC chips cannot run this fast.
If you didn't do anything but switch to hd44780_I2Cexp and bumped the clock to 400kHz, all the code after the clear() would speed up over 7x
in general stepper-motors need the step-pulses in a well calculated regular pattern.
Most of the time the pulse-train has a constant frequency. only of you do acceleration/decceleration the frequency changes but still in a well caclulated manner.
Doing other things like serial-printing or sending data to an LCD needs too much time.
So you shouldn't do that while creating step-pulses.
One way to do it in parallel is to create the step-pulses using a timer-interrupt.
The basic principle is to set up the timer-interrupt to call a interrupt-service-routine and use two variables to control it.
One variable holds the number of steps that shall done and the second variable is of the bool-type to control the start of creating steps.
The NoOfSteps variable is initialised with the number of the steps and then set th ebool-variable true to make the ISR start "stepping" and decrement the NoOfSteps until zero is reached. If zero is reached the ISR stops creating steps
=> don't move endlessly but just the amount of steps given before start.
As long as you don't need acceleration/decceleration and different speeds this works very well.
If you need different speeds You would have to setup the timing-constant to another value or use fractions of a maximum speed 1/2, 1/3, 1/4 1/5 etc. making the IRS create a step only every 2, 3, 4, 5 calls