Reputation: 9
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.
Upvotes: 0
Views: 1047
Reputation: 1
There are some issues with the LCD initialization in the example code. The API for begin() is
begin(cols, rows, [dotsize]);
where dotsize is optional but used to select between 5x8 or 5x10 font size. You are lucky that what use used works:
lcd.begin(0x27, 16, 2);
This sets the columns to 39, the rows to 16, (which is not the correct geometry) and given the way the library code works, using 2 for the dot size (which is not a valid parameter) will end up trying to set the font size to 5x10. But since that font size only works on single line displays, it defaults back to 5x8 font.
Also, you really should consider using the auto configuration constructor, so you don't need to specify the i2c address. See the included documentation for details. like the API documentation and the wiki for even more documentation on the hd44780_I2Cexp i/o class.
https://github.com/duinoWitchery/hd44780/wiki
https://github.com/duinoWitchery/hd44780/wiki/ioClass:-hd44780_I2Cexp
Upvotes: 0
Reputation: 9
@stark became my hero, 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);
}
Edit:
I rewrote my entire code based on the State Machine concept. Now it works flawlessly, but i still want to know how to handle interrupt on an RP2040 chip, so thanks a lot for the good answers!
Code:
#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;
}
}
Upvotes: 0
Reputation: 87486
The RP2040 does not have a turn-key debouncing feature that you can just turn on by writing to a bit in a register somewhere.
You will have to perform multiple digital readings and use a timing function like millis()
to detect when the button has been held down or released for a long-enough time. You can use the Pushbutton library from Pololu to do that, or just read its code as a reference so you can understand how to implement your own debouncing.
You could also consider reading the button using a PIO state machine and just sending press/release events to the main CPU.
Upvotes: 0