I'm new to Arduino and my question is rather theoretical. I have an Arduino Nano board (Atmega168 processor), a button, a display. I have written a button handler that does not stop code execution. My idea is: poll the button in "loop" cycle, and if it is pressed, perform k++
. When k
exceeds a specified value, perform cnt++
and print it on the display.
Code example No1 works correctly. I get 1 -> 2 -> 3 -> ...:
void loop() {
if (digitalRead(button_pd) == LOW) {
k = k + 1;
if (k >= 20) {
k = 0;
cnt = cnt + 1;
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(cnt);
}
}
Code example No2 is incorrect. I get 16 -> 27 -> 40 -> ... Seems like not cnt
is printed, but k
.
void loop() {
if (digitalRead(button_pd) == LOW) {
k = k + 1;
if (k >= 20) {
k = 0;
cnt = cnt + 1;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(cnt);
}
}
}
Here's my program:
#include <LiquidCrystal_I2C.h>
#define button_pd 6
LiquidCrystal_I2C lcd(0x27, 16, 2);
int k = 0;
int cnt = 0;
void setup() {
pinMode(button_pd, INPUT_PULLUP);
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(cnt);
}
void loop() {
// button handler
}
My question is. Why examples No1 and No2 work differently?
2 Answers 2
The timings are different.
Every operation on the LCD takes time. This:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(cnt);
involves some I2C communication, and thus takes orders of magnitude more
time than k++;
.
On the first version of your loop()
, these operations are performed on
each iteration, while the button is being pressed. On the second
version, they are performed only when k
reaches 20, thus twenty times
less often. The second version thus runs twenty times faster... too fast
for the LCD to correctly refresh and show all the values it is being
asked to display.
I recommend you do not rely on loop counting for controlling timings.
Instead, use the Arduino function specifically designed for time
management. For example, if you want to increment the counter every
200 ms, you should record the time when the button is pressed (the
falling edge of the signal), and increment cnt
200 ms later:
uint8_t previous_state; // previous state of the button
uint32_t press_timestamp; // time when the button was pressed
void loop() {
uint32_t now = millis();
uint8_t state = digitalRead(button_pd);
if (previous_state == HIGH && state == LOW) // falling edge
press_timestamp = now;
previous_state = state;
if (state == LOW && now - press_timestamp >= 200) {
cnt = cnt + 1;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(cnt);
}
}
-
Ah, understood... Thank you!ONamaeWa– ONamaeWa2023年10月27日 21:15:57 +00:00Commented Oct 27, 2023 at 21:15
-
Unfortunately, result is still incorrect. Like 0 -> 10 -> 10 -> 10 -> 21...ONamaeWa– ONamaeWa2023年10月28日 12:40:38 +00:00Commented Oct 28, 2023 at 12:40
-
I've modified your code a little. Modified version worked for me. Thank you for the idea!ONamaeWa– ONamaeWa2023年10月28日 12:52:50 +00:00Commented Oct 28, 2023 at 12:52
Thanks. Somehow code example No3 works correctly. I have used a library for timer and simplified code from the answer above.
#include <LiquidCrystal_I2C.h>
#include <millisDelay.h> // Timer
#define button_pd 6
LiquidCrystal_I2C lcd(0x27, 16, 2);
int k = 0;
int cnt = 0;
//uint8_t previous_state; // previous state of the button
millisDelay d;
void setup() {
pinMode(button_pd, INPUT_PULLUP);
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(cnt);
}
void loop() {
uint8_t state = digitalRead(button_pd);
if (state == LOW)
d.start(300);
//previous_state = state;
if (d.justFinished()) {
cnt = cnt + 1;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(cnt);
}
}