Arduino旋转编码器和EEPROM同时使用无法写入的问题

最近搞了个Arduino研究研究,当积木搭到同时上了旋转编码器和EEPROM(FT24C256兼容AT24C256)时,踩到了一个小坑,在编码器开关触发时进行EEPROM写入操作时会出现卡死情况,特此记录下解决过程:

#include <Wire.h>
#include "PinChangeInterrupt.h"

#define CLK 2
#define DT 3
#define SW 4

#define ADDRESS_AT24C256 0x50

static word data_addr = 0x0F00; 
static uint8_t anim_delay = 40;

void setup() {
  Serial.begin(9600);
  delay(2000);
  Serial.println(F("Starting!"));
  Wire.begin();

  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(CLK), encoder_value, CHANGE);
  attachPCINT(digitalPinToPCINT(SW), button_press, CHANGE);

  Wire.beginTransmission(ADDRESS_AT24C256);
  Wire.write(highByte(data_addr));
  Wire.write(lowByte(data_addr));
  Wire.endTransmission();
  Wire.requestFrom(ADDRESS_AT24C256, sizeof(anim_delay));
  if(Wire.available() >= sizeof(anim_delay))
  {
    anim_delay = Wire.read();
    Serial.print(F("anim_delay loaded from eeprom: "));
    Serial.println(anim_delay, DEC);
  }
}

bool anim = true;

void loop() {
  if (!anim)
    return;
  delay(anim_delay);
}
unsigned long debounce_delay = 0;
void button_press() {
  int btn_val = digitalRead(SW);
  if (LOW == btn_val) {
    if (millis() - debounce_delay > 200) {
      anim = !anim;
      Serial.print(F("Animation switched to: "));
      Serial.println(anim, DEC);
      save_delay = !anim;

      Wire.beginTransmission(ADDRESS_AT24C256);
      Wire.write(highByte(data_addr));
      Wire.write(lowByte(data_addr));
      Wire.write(byte(anim_delay));
      Wire.endTransmission();  
      delay(6);
      Serial.println(F("anim_delay saved!"));
    }
    debounce_delay = millis();
  }
}
static unsigned char encode_cnt;
void encoder_value() {
  bool clk_value = digitalRead(CLK);
  bool dt_value = digitalRead(DT);
  static u8 check_cnt = 0;
  static bool last_clk;
  static u32 timee = 0;
  if (last_clk != clk_value && millis() - timee > 5) {
    last_clk = clk_value;

    if (++check_cnt == 1) {
      check_cnt = 0;
      encode_cnt = (clk_value != dt_value ? 1 : -1);
      if (encode_cnt == 1) {
        // Serial.println(F(" r++"));
        anim_delay += 5;
      } else {
        // Serial.println(F(" r--"));
        anim_delay -= 5;
      }
      if (anim_delay < 5) anim_delay = 5;
      Serial.print(F("anim_delay: "));
      Serial.println(anim_delay, DEC);
    }
    timee = millis();
  }
}

代码基本都是抄来的,用了“PinChangeInterrupt”这个库,顺时针、逆时针旋转编码器时以5为步进加减anim_delay变量的值,在开关按下时将当前值写入EEPROM,所以顺利成章的认为在button_press中用I2C通信写入EEPROM就ok了,结果跑起来一按编码器就卡死,Arduino IDE中的串口监视器看到的”Animation switched to:”这行日志也没打全,开始还以为是代码写的有问题,可是setup时的读EEPROM值却没有出现卡死的情况,顺利读出了“0”,最后盲猜了下是不能在中断处理里写写存储代码,于是将代码逻辑改成了下面这版:

#include <Wire.h>
#include "PinChangeInterrupt.h"

#define CLK 2
#define DT 3
#define SW 4

#define ADDRESS_AT24C256 0x50

static word data_addr = 0x0F00; 
static uint8_t anim_delay = 40;

void setup() {
  Serial.begin(9600);
  delay(2000);
  Serial.println(F("Starting!"));
  Wire.begin();

  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(CLK), encoder_value, CHANGE);
  attachPCINT(digitalPinToPCINT(SW), button_press, CHANGE);

  Wire.beginTransmission(ADDRESS_AT24C256);
  Wire.write(highByte(data_addr));
  Wire.write(lowByte(data_addr));
  Wire.endTransmission();
  Wire.requestFrom(ADDRESS_AT24C256, sizeof(anim_delay));
  if(Wire.available() >= sizeof(anim_delay))
  {
    anim_delay = Wire.read();
    Serial.print(F("anim_delay loaded from eeprom: "));
    Serial.println(anim_delay, DEC);
  }
}

bool anim = true;
bool save_delay = false;

void loop() {
  if (save_delay) {
    Wire.beginTransmission(ADDRESS_AT24C256);
    Wire.write(highByte(data_addr));
    Wire.write(lowByte(data_addr));
    Wire.write(byte(anim_delay));
    Wire.endTransmission();  
    save_delay = false;
    delay(6);
    Serial.println(F("anim_delay saved!"));
  }
  if (!anim)
    return;
  delay(anim_delay);
}
unsigned long debounce_delay = 0;
void button_press() {
  int btn_val = digitalRead(SW);
  if (LOW == btn_val) {
    if (millis() - debounce_delay > 200) {
      anim = !anim;
      Serial.print(F("Animation switched to: "));
      Serial.println(anim, DEC);
      save_delay = !anim; 
    }
    debounce_delay = millis();
  }
}
static unsigned char encode_cnt;
void encoder_value() {
  bool clk_value = digitalRead(CLK);
  bool dt_value = digitalRead(DT);
  static u8 check_cnt = 0;
  static bool last_clk;
  static u32 timee = 0;
  if (last_clk != clk_value && millis() - timee > 5) {
    last_clk = clk_value;

    if (++check_cnt == 1) {
      check_cnt = 0;
      encode_cnt = (clk_value != dt_value ? 1 : -1);
      if (encode_cnt == 1) {
        // Serial.println(F(" r++"));
        anim_delay += 5;
      } else {
        // Serial.println(F(" r--"));
        anim_delay -= 5;
      }
      if (anim_delay < 5) anim_delay = 5;
      Serial.print(F("anim_delay: "));
      Serial.println(anim_delay, DEC);
    }
    timee = millis();
  }
}

按键按下时只进行标记,在loop中处理写存储操作,上电一试,果然ok了。

博主友情提示:

如您在评论中需要提及如QQ号、电子邮件地址或其他隐私敏感信息,欢迎使用>>博主专用加密工具v3<<处理后发布,原文只有博主可以看到。