如何清除RP2040的ICSR寄存器第22位解决按键抖动问题
解决RP2040按键抖动导致LCD背光频繁切换的问题
我使用Arduino IDE(C语言)开发树莓派Pico项目,项目包含16x2 LCD及控制其背光的按键。按键功能正常,但每次按压时背光会闪烁,需多次随机按压才能稳定切换开关状态,推测是按键抖动导致。希望在中断返回前清除RP2040的ICSR寄存器第22位,以清空中断缓冲区中的待处理项。
当前代码
#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 }
解决方案
要清除RP2040的ICSR寄存器第22位(对应PENDSTCLR位,用于清空中断挂起状态),直接在中断函数末尾添加寄存器操作代码即可。同时建议去掉当前中断函数中detachInterrupt+delayMicroseconds+attachInterrupt的写法,这种方式效率低且可能影响其他中断响应。
修改后的backlight中断函数:
void backlight() { // 切换背光状态 if(lcdBacklightStatus) { lcd.noBacklight(); } else { lcd.backlight(); } lcdBacklightStatus = !lcdBacklightStatus; // 清除ICSR寄存器第22位,清空中断缓冲区待处理项 *(volatile uint32_t *)M0PLUS_ICSR_REG |= (1 << 22); }
补充说明
M0PLUS_ICSR_REG是RP2040提供的ICSR寄存器地址宏,来自你已包含的hardware/regs/m0plus.h头文件- 使用
volatile修饰指针是为了防止编译器优化该寄存器操作,确保代码直接对硬件寄存器进行写入 - 写入1到第22位即可清除对应的挂起中断,避免抖动产生的重复中断触发
另外,更彻底的按键防抖方案还包括:
- 硬件层面:给按键添加RC滤波电路(10k电阻+100n电容)
- 软件层面:在中断中记录触发时间,仅当两次触发间隔超过20-50ms时才执行状态切换逻辑
内容的提问来源于stack exchange,提问作者balazscseh
相关产品推荐
相关产品推荐

