How to write to a specific register on RP2040 to debounce

Hi!

I'm using the Arduino IDE and langauge (C) to program a Raspberry Pi Pico. I have a project that uses a 16x2 LCD and a button to control it's backlight. The button and everything other works correctly, my problem is that every time i press the switch, the backlight flickers, and need to press it random times to stay on or off i suggest due to bouncing.
I want to clear the 22 bit in the ICSR register of the RP2040 to clear any pending stuff in the interrupt buffer before return from the interrupt.
https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#tab-registerlist_m0plus
(page 87, or 86 on the bottom of the page)

My code so far:

#include <Adafruit_BMP280.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
#include "hardware/regs/m0plus.h"
//#include <pico/stdlib.h>
//#include <hardware/pwm.h>

Adafruit_BMP280 bmp; //I2C

hd44780_I2Cexp lcd(0x27, 16, 2);

const uint16_t AirValue = 1023;
const uint16_t WaterValue = 660; //measured fully submerged in water
const uint16_t DarkValue = 0;
const uint16_t LightValue = 1023;
uint16_t soilMoisture;
uint16_t soilMoisturePercent;
uint16_t lightIntensity;
uint16_t lightIntensityPercent;
volatile bool lcdBacklightStatus = false;

byte pressureChar[] = { 0b01000, 0b11110, 0b11100, 0b01000, 0b00011, 0b01111, 0b00000, 0b11110
};

byte moistureChar[] = { 0b00000, 0b00100, 0b01110, 0b11111, 0b11111, 0b11111, 0b01110, 0b00000
};

byte lightIntensityChar[] = { 0b00000, 0b01110, 0b10001, 0b11011, 0b10101, 0b01110, 0b01110, 0b00100
};

byte temperatureChar[] = { 0b01000, 0b10111, 0b10100, 0b11111, 0b11100, 0b11100, 0b11100, 0b01000
};

byte separatorWall[] = { 0b10101, 0b01010, 0b10101, 0b01010, 0b10101, 0b01010, 0b10101, 0b01010
};

byte degreeChar[] = { 0b00111, 0b00101, 0b00111, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000
};

void backlight();

void setup() {
  lcd.begin(0x27, 16, 2);

  lcd.createChar(0, pressureChar);
  lcd.createChar(1, moistureChar);
  lcd.createChar(2, lightIntensityChar);
  lcd.createChar(3, temperatureChar);
  lcd.createChar(4, separatorWall);
  lcd.createChar(5, degreeChar);

  lcd.home();

  pinMode(26, INPUT); //soilMoisture
  pinMode(27, INPUT); //lightIntensity
  pinMode(17, INPUT); //button

  attachInterrupt(digitalPinToInterrupt(17), backlight, RISING);
  
  //Serial.begin(9600);

  bmp.begin(0x76);
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     /* Operating Mode. */
                  Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */
                  Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */
                  Adafruit_BMP280::FILTER_X16,      /* Filtering. */
                  Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */

  lcd.clear();
  lcd.backlight(); //lcd.noBacklight();

  lcd.setCursor(1,0);
  lcd.print("Plant Station");
  lcd.setCursor(6,1);
  lcd.print("V0.8");

  delay(2000);

  lcd.noBacklight();
  lcd.clear();

  lcd.setCursor(0,0);
  lcd.write(byte(3));
  lcd.setCursor(10,0);
  lcd.write(byte(2));
  lcd.setCursor(0,1);
  lcd.write(byte(0));
  lcd.setCursor(10,1);
  lcd.write(byte(1));
  lcd.setCursor(7,0);
  lcd.write(byte(5));
  lcd.setCursor(8,0);
  lcd.print("C");
  lcd.setCursor(15,0);
  lcd.print("%");
  lcd.setCursor(6,1);
  lcd.print("hPa");
  lcd.setCursor(15,1);
  lcd.print("%");
}

void loop() {
  soilMoisture = analogRead(26);
  soilMoisturePercent = map(soilMoisture, AirValue, WaterValue, 0, 100);

  lightIntensity = analogRead(27);
  lightIntensityPercent = map(lightIntensity, DarkValue, LightValue, 0, 100);

  if(soilMoisturePercent >= 100) soilMoisturePercent = 100;
    else if(soilMoisturePercent <= 0) soilMoisturePercent = 0; 

  if(lightIntensityPercent >= 100) lightIntensityPercent = 100;
    else if(lightIntensityPercent <= 0) lightIntensityPercent = 0;

  //Serial.print("Moisture: ");
  //Serial.println(soilMoisture);
  //Serial.print("Percentage: ");
  //Serial.println(soilMoisturePercent);
  //Serial.print("LightIntensity: ");
  //Serial.println(lightIntensity);
  //Serial.print(soilMoisture);
  //Serial.println(soilMoisturePercent);

  //BANNED CURSOR POSITIONS: 0,0  9,0  0,1  9,1  6,0  14,0  7,0  15,0  5,1  15,1

  lcd.setCursor(3,0);
  lcd.print(bmp.readTemperature(),1);
  lcd.setCursor(2,1);
  lcd.print(((bmp.readPressure()/100)),0);
  if(soilMoisturePercent < 10) {
    lcd.setCursor(13,1);
    lcd.print(" ");
    lcd.print(soilMoisturePercent);
  } else if(soilMoisturePercent < 100) {
    lcd.setCursor(13,1);
    lcd.print(soilMoisturePercent);
  } else {
    lcd.setCursor(12,1);
    lcd.print(soilMoisturePercent);
  }
  if(lightIntensityPercent < 10) {
    lcd.setCursor(13,0);
    lcd.print(" ");
    lcd.print(lightIntensityPercent);
  } else if(lightIntensityPercent < 100) {
    lcd.setCursor(13,0);
    lcd.print(lightIntensityPercent);
  } else {
    lcd.setCursor(12,0);
    lcd.print(lightIntensityPercent);
  }

  delay(60000);
}

void backlight() {
  //noInterrupts();
  detachInterrupt(digitalPinToInterrupt(17));
  if(lcdBacklightStatus == true) {
    lcd.noBacklight();
  } else if(lcdBacklightStatus == false) {
    lcd.backlight();
  }
  delayMicroseconds(500000);
  //interrupts();
  attachInterrupt(digitalPinToInterrupt(17), backlight, RISING);
  lcdBacklightStatus = !lcdBacklightStatus;
  //want to clear ICSR register here i guess
}

I may be not clear enough, sorry if that's the case, English isn't my main langauge.

I removed the entire Interrupt section and the 60seconds long delay at the end of the main loop. Then added the following code, and now when the button is pressed for 1second, the lcd backlight toggles, and the sensors are still only checked every 60seconds.
Still don't know how to handle registers to debounce, so the post remains open.

  for(int i = 0; i < 600; i++) {
    if(digitalRead(17) == 1) trueCount++;
    if(trueCount == 10) {
      lcdBacklightStatus = !lcdBacklightStatus;
      trueCount = 0;
    }
    if(lcdBacklightStatus == false) {
    lcd.noBacklight();
    } else if(lcdBacklightStatus == true) {
    lcd.backlight();
    }
    delay(100);
  }

You do not need to clear interrupt register to debounce.
And you definitely do not need the interrupt itself to read button once of minute.
Use the polling.

I need to read the button constantly, so then i don't have to hold it for a whole minute to surely register the press. That's why i went with interrupts.
What is polling?

polling is when you just read the state of the button in your loop. Button pushes are pretty long time events in this world so you normally do not need an interrupt to capture when it changes state.

1 Like

That's why you "need" interrupt handling of a push button, and also why you can't use polling.

And a for loop splitting up that 60000 but still containing a 100 ms delay is only a little better.

Time to learn how to eliminate any use of the delay() function and let your loop run free. Then you would have no trouble using prosaic paradigmatic programming patterns that handle pushbuttons by polling.

Start with simple stuff - google

 Arduino blink without delay

or

 Arduino two things at once

and poke around a bit.

a7

2 Likes

Once you get the hang of using millis instead of delays you might try this simple "debounce" code.

if (digitalRead(17) ==1){
   delay(50);   I know I said no delays but this is a very short delay
   if (digitalRead(17) ==1){
        ----your button code here ----
   }
}
1 Like

I rewrite my entire code based on this new concept, and now it works flawlessly. Still want to know how to handle interrupts on a RP2040 chip, but i'm at least very happy with the result.

#include <Adafruit_BMP280.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

Adafruit_BMP280 bmp;
hd44780_I2Cexp lcd(0x27, 16, 2);

const uint8_t moistureSensorPin = 26;
const uint8_t lightSensorPin = 27;
const uint8_t buttonPin = 17;

const uint16_t moistureSensorInterval = 60000;
const uint16_t lightSensorInterval = 60000;
const uint16_t temperatureSensorInterval = 60000;
const uint16_t pressureSensorInterval = 60000;
const uint16_t buttonInterval = 500;
const uint16_t lcdUpdateInterval = 60000;

const uint32_t backlightDuration = 300000;

const uint16_t AirValue = 1023;
const uint16_t WaterValue = 660;
const uint16_t DarkValue = 0;
const uint16_t LightValue = 1023;


uint16_t soilMoisture;
uint16_t soilMoisturePercent;
uint16_t lightIntensity;
uint16_t lightIntensityPercent;
float temperature;
uint32_t pressure;

byte soilMoist = LOW;
byte soilHalfMoist = LOW;
byte soilDry = LOW;
byte buttonState = LOW;
byte lcdBacklightStatus = LOW;
byte buttonPreviouslyPressed = LOW;


uint32_t currentMillis = 0;
uint32_t previousMoistureSensorMillis = 60000;
uint32_t previousLightSensorMillis = 60000;
uint32_t previousTemperatureMillis = 60000;
uint32_t previousPressureMillis = 60000;
uint32_t previousButtonMillis = 0;
uint32_t previousLCDMillis = 0;

byte pressureChar[] = { 0b01000, 0b11110, 0b11100, 0b01000, 0b00011, 0b01111, 0b00000, 0b11110
};

byte moistureChar[] = { 0b00000, 0b00100, 0b01110, 0b11111, 0b11111, 0b11111, 0b01110, 0b00000
};

byte halfMoistureChar[] = { 0b00000, 0b00100, 0b01010, 0b10001, 0b11111, 0b11111, 0b01110, 0b00000
};

byte emptyMoistureChar[] = { 0b00000, 0b00100, 0b01010, 0b10001, 0b10001, 0b10001, 0b01110, 0b00000
};

byte lightIntensityChar[] = { 0b00000, 0b01110, 0b10001, 0b11011, 0b10101, 0b01110, 0b01110, 0b00100
};

byte temperatureChar[] = { 0b01000, 0b10111, 0b10100, 0b11111, 0b11100, 0b11100, 0b11100, 0b01000
};

byte separatorWall[] = { 0b10101, 0b01010, 0b10101, 0b01010, 0b10101, 0b01010, 0b10101, 0b01010
};

byte degreeChar[] = { 0b00111, 0b00101, 0b00111, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000
};

byte painChar[] = { 0b00000, 0b00000, 0b11011, 0b11011, 0b00000, 0b01110, 0b10001, 0b00000
};

void setup() {
  pinMode(moistureSensorPin, INPUT);
  pinMode(lightSensorPin, INPUT);
  pinMode(buttonPin, INPUT);

  
  bmp.begin(0x76);
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     /* Operating Mode. */
                  Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */
                  Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */
                  Adafruit_BMP280::FILTER_X16,      /* Filtering. */
                  Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
  
  
  lcd.begin(0x27, 16, 2);

  lcd.createChar(0, pressureChar);
  lcd.createChar(1, painChar);
  lcd.createChar(2, lightIntensityChar);
  lcd.createChar(3, temperatureChar);
  lcd.createChar(4, moistureChar);
  lcd.createChar(5, halfMoistureChar);
  lcd.createChar(6, emptyMoistureChar);
  lcd.createChar(7, degreeChar);

  lcd.home();
  lcd.clear();
  lcd.backlight(); //lcd.noBacklight();

  lcd.setCursor(1,0);
  lcd.print("Plant Station");
  lcd.setCursor(6,1);
  lcd.print("V1.0");

  delay(2000);

  lcd.noBacklight();
  lcd.clear();

  lcd.setCursor(0,0);
  lcd.write(byte(3));
  lcd.setCursor(10,0);
  lcd.write(byte(2));
  lcd.setCursor(0,1);
  lcd.write(byte(0));
  lcd.setCursor(10,1);
  lcd.write(byte(4));
  lcd.setCursor(7,0);
  lcd.write(byte(7));
  lcd.setCursor(8,0);
  lcd.print("C");
  lcd.setCursor(15,0);
  lcd.print("%");
  lcd.setCursor(6,1);
  lcd.print("hPa");
  lcd.setCursor(15,1);
  lcd.print("%");
}

void loop() {
  currentMillis = millis();

  readMoistureSensor();
  readLightSensor();
  readTemperature();
  readPressure();
  readButtonState();
  updateBacklight();
  updateLCD();
}

void readMoistureSensor() {
    if(currentMillis - previousMoistureSensorMillis >= moistureSensorInterval) {
        soilMoisture = analogRead(26);
        soilMoisturePercent = map(soilMoisture, AirValue, WaterValue, 0, 100);

        if(soilMoisturePercent >= 100) soilMoisturePercent = 100;
            else if(soilMoisturePercent <= 0) soilMoisturePercent = 0;

        previousMoistureSensorMillis += moistureSensorInterval;
    }
}

void readLightSensor() {
    if(currentMillis - previousLightSensorMillis >= lightSensorInterval) {
        lightIntensity = analogRead(27);
        lightIntensityPercent = map(lightIntensity, DarkValue, LightValue, 0, 100);

        if(lightIntensityPercent >= 100) lightIntensityPercent = 100;
            else if(lightIntensityPercent <= 0) lightIntensityPercent = 0;

        previousLightSensorMillis += lightSensorInterval;
    }
}

void readTemperature() {
    if(currentMillis - previousTemperatureMillis >= temperatureSensorInterval) {
        temperature = bmp.readTemperature();

        previousTemperatureMillis += temperatureSensorInterval;
    }
}

void readPressure() {
    if(currentMillis - previousPressureMillis >= pressureSensorInterval) {
        pressure = (bmp.readPressure()/100);

        previousPressureMillis += pressureSensorInterval;
    }
}

void readButtonState() {
    if(currentMillis - previousButtonMillis >= buttonInterval)  {
        buttonState = digitalRead(buttonPin);
    
        if(buttonState == HIGH) {
            lcdBacklightStatus = !lcdBacklightStatus;
        }

        previousButtonMillis += buttonInterval;
    }
}

void updateBacklight() {
    if(lcdBacklightStatus == HIGH) {
        lcd.backlight();
    } else {
        lcd.noBacklight();
    }
}

void updateLCD() {
    if(currentMillis - previousLCDMillis >= lcdUpdateInterval) {
        if(soilMoisturePercent >= 50) {
            lcd.setCursor(10,1);
            lcd.write(byte(4));
        } else if(soilMoisturePercent >= 25) {
            lcd.setCursor(10,1);
            lcd.write(byte(5));
        } else {
            lcd.setCursor(10,1);
            lcd.write(byte(6));
        }

        if(pressure > 1007) {
            lcd.setCursor(0,1);
            lcd.write(byte(0));
        } else {
            lcd.setCursor(0,1);
            lcd.write(byte(1));
        }

        lcd.setCursor(3,0);
        lcd.print(temperature,1);
        lcd.setCursor(2,1);
        lcd.print(pressure);
        if(soilMoisturePercent < 10) {
            lcd.setCursor(13,1);
            lcd.print(" ");
            lcd.print(soilMoisturePercent);
        } else if(soilMoisturePercent < 100) {
            lcd.setCursor(13,1);
            lcd.print(soilMoisturePercent);
        } else {
            lcd.setCursor(12,1);
            lcd.print(soilMoisturePercent);
        }
        if(lightIntensityPercent < 10) {
            lcd.setCursor(13,0);
            lcd.print(" ");
            lcd.print(lightIntensityPercent);
        } else if(lightIntensityPercent < 100) {
            lcd.setCursor(13,0);
            lcd.print(lightIntensityPercent);
        } else {
            lcd.setCursor(12,0);
            lcd.print(lightIntensityPercent);
        }

        previousLCDMillis += lcdUpdateInterval;
    }
}

Nice. That has to be near record time for getting with the program, so to speak, without much trouble as is sometimes seen, or resistance.

When I'm at the big rig I'll take a closer look, but I see no significant use of delay (one call in setup() so sue me!) and only one call to millis() is almost certain to mean you have nailed it.

I will attempt to measure the frequency with which your loop() is, um, looping.

And of course if it works, there's that. :expressionless:

a7

1 Like

Thanks a lot, it mens a lot to me :grin:
I welcome any other constructive criticism.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.