Hello,
I am looking for a way to print on an LCD-I2C, so that my sketch do not spend too much time to print on my LCD-I2C and to have time to check some GPIO input every few micro seconds.
My constrains are the followings:
I am using a RaspberryPi-Pico (used to replace an Arduino nano, for speed reason)
the answer should be as close as possible to a few us only.
(On Raspberry site, I found things in Python, but unfortunalty much too slow for my project.)
Do you have some idea how to proceed ? a specific library ?
thank you
The Arduino on Raspberry Pi Pico is on top of Mbed. I don't know if the Mbed I2C code disables the interrupts, but the Arduino code waits... and waits... and waits... until the I2C session has completely finished.
The well known Arduino Uno or Nano may be faster than a Raspberry Pi Pico.
For a Arduino Nano, the input signals should be connected to interrupt pins, and by using a software I2C library, you avoid to use the Wire library with its interrupt that might get in the way.
I don't know how Arduino supports interrupts on the Raspberry Pi Pico, I'm still learning to use the Raspberry Pi Pico.
I have tested your sketch, but with one screen only. It works, but I am not sure to understand its interest ?
I can however see that the library LiquidCrystal_I2C provides roughly the same time for one loop : 1ms for the first line only => equivalent to my sketch/LCD_I2C.
I have also tested my sketch on Arduino Nano : times are very similar to what was recorded for Raspberrypi-Pico.
As far as my project is concerned: I need to capture the signals from 3 Hall sensors, which finally will provide me indications on the speeds on a machine. The average lag time is 20ms for each of the Hall sensors.
Moreover, I am using 2 rotary encoders for input data.
Finally, I compute the 3 speeds and the 2 encoders in order to drive the machine.
This is working well. At least when I am not using LCD.
However, when I want to include the LCD instead of the serial print, the systems does not work anymore, due to the huge time dedicated to this library. Refer to the sketch hereafter.
I would have liked to use Interrupts, but I don't know where to use it efficiently ...
I also imagine to use the multithread property of the RP. I still need to discover this field on my side
Finally, I would really avoid to use two µcontroleurs for my project. !
Thank you for your proposals !
// Pour RaspberryPi-Pico
#include "Wire.h"
#include "LiquidCrystal_I2C.h" // pins affected : G4=SDA ; G5=SCL ; V+ ; GND
LiquidCrystal_I2C LDC(0x27, 20, 4);
#include "RotaryEncoder.h"
RotaryEncoder encoder(14, 15); // (A, B)
static int pos = 0;
int newPos;
const int BP = 7; // BP sur GP7
bool BPstate = LOW;
long tps;
void setup() {
pinMode(BP, INPUT_PULLUP);
Serial.begin(115200);
LDC.init();
LDC.backlight();
}
void loop() {
tps = millis();
encoder.tick();
newPos = encoder.getPosition();
BPstate = digitalRead(BP);
// print in Serial
if (pos != newPos) {
Serial.println(newPos);
pos = newPos;
}
// print in LDC
LDC.setCursor(2, 0);
char buffer[20];
sprintf( buffer, "newPos= %03d ", newPos);
LDC.print( buffer);
LDC.setCursor(4, 1);
sprintf( buffer, "BPstate = %02d ", BPstate);
LDC.print( buffer);
LDC.setCursor(6, 2);
sprintf( buffer, "DT(ms)= %04i ", millis() - tps);
LDC.print( buffer);
// one loop with LDC ~ 45ms sur RP-Pico !
}
Normal keys can be done by polling in almost all projects.
A rotary encoder that someone presses is slow, but sometimes interrupts are used. See : https://www.pjrc.com/teensy/td_libs_Encoder.html
A rpm sensor for a motor is something for interrupts.
The Arduino Uno has 2 real interrupts, and PCINT interrupts on the rest of the pins. There are libraries for rpm, but those can often only use those 2 real interrupts.
There are Arduino boards with real interrupts on every pin.
I'm not an expert for Arduino on the Raspberry Pi Pico, I just happen to like it a lot.
Try to avoid multiple boards. Those projects might never finish.
ok I will use interrupts for the rotary encoder. thank you.
However, if the concern between the rotary encoder and the LCD can be solved thanks to interrupts, that does not solve the fact that the LCD library takes to much time to proceed !
Basically, my initial question is related to huge time to print on LCD. Has this concern already being discussed / treated ? where/how ?
for those interested in the workaround, I have used the 2 cores of my RP2040 in //. Result is really statisfying to me ! see sketch hereafter.
I will use that solution for my project, even if I am still a bit frustrated to not find another solution to speed up (significantly) the LCD print !
Question 1 : I am updating a LCD display every time in the loop() and that takes too much time. I need a fast answer. Answer 1 : Don't do that.
Is that your question and does that solve your problem ?
The liquid crystals in a LCD display are slow. Updating the LCD display about 4 times per second might be good enough.
Use a millis-timer, see the Blink Without Delay. That is standard for a Arduino Uno.
On a Pico, you can use the Arduino Schedular or use the multitasking rtos of the Mbed directly. Just add a task and use delay().
Question 2 : The I2C bus is so slow. Answer 2 : Don't use the I2C bus or make it faster.
Is the slow I2C bus the problem ?
A LCD display without I2C can be faster. A 400kHz I2C bus is faster than a 100kHz I2C bus. A software I2C library does not use interrupts that might interfere with other interrupts. Using multiple tasks on a Pico can prevent most problems. You don't need multiple cores, since the Mbed on the Pico is already a preemtive multitasking system.
While I was typing this, you posted the answer above, but I will post this anyway. Can you try to explain the problem once more ?
I have a question for you:
A 5V LCD display has a 5V I2C bus and your Raspberry Pi Pico has a 3.3V I2C bus. How do you solve that problem ?
Well, my I2C-2004A is connected to 3.3V only from Pico, at it works To be honest, I just initially read the LCD 2004A specification for which the range is 3V to 5.5V.
thanks Paul_B for the recommendation. Right now, I don't know how to proceed ... apparently I2C bus can manage the 3.3V. Is it border line ?
well, since I am really not confortable with the all the microcontroleur stuff (this is clearly not my day-to-day activity !! - I am actually in the mechanical/aerodynamical field - not really the same world ), I would have liked to use the LCD the same way (or so) I was using the Serial port.
I appears that it's not possible ...
My objective is really this one : to be able to compute multiple external data and send information to the machine, every ~1ms. And to be able to pilot the behavior of the machine with 2 rotary encoders and visualisation via LCD.
thanks.
Really? What do you mean by "connected to 3.3V only"?
Is the "2004" display - and its backpack - not powered by 5 V?
Which specification is this? All the "2004" displays I have seen operate specifically at 5 V.
What do you mean by "manage"?
Unless I am very mistaken, the I²C backpack includes two 4k7 pull-up resistors to 5 V. Because I²C is an open-collector bus, this means that when not pulled down, the I²C lines are pulled up to 5 V and that is connected to the 3.3 V processor. This will inject 230 µA into the protection diodes on that processor. It is a somewhat debatable point, but many people consider this to be a "Bad Thing".
Well, they are very different things - a 20 by 4 display which you have to program to put the characters in the places you want compared with a serial port which connects to something with a complete terminal emulation including control codes such as carriage return, line feed, backspace etc. as well as line wrap and scrolling.
you are certainly right on all counts!
However, I bought the LDC 2004A, with I2C attached on the back. The four pins of the I2C are just connected to the Pico, therefore the VCC from I2C connected to 3.3V from Pico. And that works ...
However, if this configuration were to lead to a in-service issue (not tested yet in real condition), then I could supply that 5V to that I2C.
As far as the LCD vs Serial port is concerned, I understand now better the situation.
In term of multiple tasks synchronized activities, do you think my sketch above optimized (to get fast answer, I mean) ?
Thank you
Correction : I confirm that the complete LCD is just connected to my Pico ;
however I connected it to VBUS, which is 5V, and not 3.3V. My mistake, sorry for that !
My intent is not to update the LCD every ms, but to print/send information on LCD without loosing time to process the other tasks of the sketch which require a fast running.
If those are true requirements, then you can stop looking as there is no way to get there regardless of library and speed of processor.
As @anon73444976 said, do some math to see the theoretical timing.
And keep in mind that it takes much more than a single byte transfered over the i2c bus to the device to get a single byte transferred to the LCD.
You will have a minimum of 4 i2c bytes per LCD byte plus i2c address byte plus i2c start/stop overhead.
So you can count on 6 byte transfer times to calculate a maximum theoretical transfer time per byte.
And then many libraries like LiquidCrystal_I2C are transfering even more bytes per byte transfer so the total is closer to about double that.
For some realworld numbers:
If you use the hd44780 library (the fastest library for PCF8574 based i2c backpacks), it takes about 500us to transfer a byte to the LCD using a 100kHz bus speed. It is 1500 us using the the LiquidCrystal_I2C library.
The Arduino Print class is blocking so if you send a string of characters, your sketch will be blocked until all of them have been transferred to the LCD.
Some thoughts on this that might or might not help.
Some(?) / most(?) / all(?) I2C libraries use blocking code to service the I2C hardware. I2C is slow compared to most processors, even at 400kHz. You call an I2C function and it does not return until it's finished doing what it does, which for a display can take ages and ages. I do not know if there are any I2C libraries that just deal with 1 byte then return leaving the I2C hardware to do its thing, then do another byte when the hardware is free again. If you really want to solve the problem with I2C behaving like this you have to write your own driver for the hardware, which is possible but you need to study the data sheet for the processor very carefully to find out how to drive the I2C hardware.
You could just use a separate micro-controller to drive the display and send the data to it via serial. Serial does not block as long as you don't over fill the buffer. If you do it this way you send the data to the serial Tx on your main controller and leave it to get on with the job. The micro-controller driving the display can then take as long as it likes to update the display.
Another thought is to be very careful when you send data by I2C, for example you mention:
What is the minimum time? If you make sure that after getting an input from a Hall sensor only then do you send the I2C data you are allowing the maximum possible time for the I2C function to do its thing, if it is quicker than the changes from the Hall sensors it will have completed before the next change from one of them.