Класс для чтения резисторной клавиатуры

Имею I2C датчик с одним адресом. Хочу подключать несколько штук - первый по быстрой железной I2C, остальные по софтверным.
Написал класс, где, условно:

if (i2c_hw) { 
  pWireHW = &Wire;
} else {
  pWireSW = new SoftwareWire();
}
...
if (i2c_hw) { 
  pWireHW->beginTransaction(...);
  // ещё 10 строк с pWireHW->...
} else {
 pWireSW->beginTransaction(...);
  // ещё 10 строк с pWireSW->...
}

Чувство прекрасного свербит и намекает - сделай какой-нить волшебный враппер, который вжух - и будет разбираться, по какому указателю метод дернуть pWireHW->любаяфункция() или pWireSW->любаяфункция(). Два родительских класса на 99% по интерфейсной части одинаковы.

А это Wire во второй строке уже создан? Его самому нельзя создавать?

Мне помнится, что экземпляр создаётся в конце Wire.h - подключил == создал.

Ну, тогда совсем тускло. Нужен чисто внешний враппер, а там вариантов немного. Если бы мы сами создавали экземпляр, мы могли бы сделать класс - наследник TwoWire, задружить его с нашим классом, а экземпляр создавать от него. Тогда можно что-то придумать, а с готовым экземпляром что сделаешь? Ему неоткуда знать про наши классы. Такое легко делается в JScript или Питоне, но не здесь.

Штош. Придётся перфекционизм придушить.

class Resistive_keyboard {
  public:
    constexpr Resistive_keyboard(const uint16_t min, const uint16_t max, const uint8_t count, const uint8_t pin)
      : _min(min / 4), _max(max / 4), _tmr(0), _counts(0), _count(count), _pin(pin)  {}
    bool get();
  private:
    const uint8_t _min;
    const uint8_t _max;
    uint8_t _tmr;
    uint8_t _counts;
    uint8_t _count;
    uint8_t _pin;
};

Resistive_keyboard Up(80, 100, 10, BUTTONS_PIN);     //500мс (10*50)
Resistive_keyboard Down(270, 300, 10, BUTTONS_PIN);  //500мс (10*50)
Resistive_keyboard Stop(350, 380, 5, BUTTONS_PIN);   //250мс (5*50)

bool Resistive_keyboard::get() {
  const uint16_t _sensor = analogRead(_pin) / 4;
  const uint8_t nowMillis = millis();
  bool _status = false;
  if (_sensor >= _min && _sensor < _max) {
    if (static_cast<uint8_t>(nowMillis - _tmr) >= 50) {
      _tmr = nowMillis;
      if (++_counts >= _count) {
        _counts = 0;
        _status = true;
      }
    }
  } else {
    _counts = 0;
    _tmr = nowMillis;
  }
  return _status;
}

Я добавила переменную _pin
Ругается на const uint16_t _sensor = analogRead(_pin) / 4;


error: invalid conversion from 'uint8_t {aka unsigned char}' to 'analog_pin_t' [-fpermissive]
   const uint16_t _sensor = analogRead(_pin) / 4;
 
\Local\Arduino15\packages\MicroCore\hardware\avr\2.3.0\cores\microcore/Arduino.h:102:11: note:   initializing argument 1 of 'int16_t analogRead(analog_pin_t)'
   int16_t analogRead(analog_pin_t pin);
           ^~~~~~~~~~
exit status 1
invalid conversion from 'uint8_t {aka unsigned char}' to 'analog_pin_t' [-fpermissive]


Потому что MicroCore требует называть аналоговые пины
A2, а не 2
A3, а не 3

И какого она у Вас типа? Вам же компилятор пишет, что она должна быть типа analog_pin_t, а у Вас uint8_t. Объявите её правильно и не присваивайте ей никаких левых значений и всё будет путём. Допустимые значения для переменных типа analog_pin_t: A0, A1, A2 и A3. Никаких других быть не должно.

А я то думаю что за analog_pin_t…Спасибо.

Т.е. миникоре решило несовместимость с дефолтовым ядром устроить?

Типа того. Зато, какая-никакая защита он недопустимых номеров пинов. У меня, в моей домашней библиотеке для АЦП, номер пина тоже задан своим типом, определённым через enum. Не то подсунешь - ругается, не позволят ошибаться.

Ещё вопросик

class Resistive_keyboard {
  public:
    constexpr Resistive_keyboard(const uint16_t min, const uint16_t max, const uint8_t count) 
    		: _min(min / 4), _max(max / 4), _tmr(0), _counts(0), _count(count) {}
    bool get();
  private:
    const uint8_t _min;
    const uint8_t _max;
    uint8_t _tmr;
    uint8_t _counts;
    uint8_t _count;
};

Для чего в constexpr Resistive_keyboard писать _counts(0)? и соответственно _tmr(0)

В consexpr-конструкторе обязательно должны быть проинициализированы все свойства класса, кроме статических. И, кстати, инициализировать их здесь нужно строго в порядке объявления, переставите - компилятор обругается.

Поняла, нужно запомнить. А const - это не будет являться статической переменной?

const uint16_t min, const uint16_t max

Тоже можно const uint8_t min, ведь мы делим на 4, а максимальное число 1023

Ещё пару вопросов)))
Может проще, зная какое число будет при нажатии определённой кнопки, например 82, просто делать границу 82-5 и 82+5, соответственно если эти цифры не попадут в пределы других кнопок.

А как делать клавиатуру и отслеживать состояние нескольких нажатых кнопок, используя один вход МК, только используя кнопки с одним положением, нормально разомкнутые.

Нет, const - указание, что Вы не собираетесь её изменять и не более того. Если по ошибке попытаетесь менять, компилятор ругнётся (подстрахует Вас).

Ну, да.

Ну, 5 - наверное, маловато. Там же паспортная точность АЦП - 2lbs, т.е. 4. Я взял бы хотя бы 8 или больше. Вообще, когда я сам делал (в той теме, что Вам давал ссылку), а там по простому поступал. Вот, допустим, для одной кнопки должно быть X, для второй Y, так я просто делал границу между кнопками посерёдке (X+Y)/2. Там же всё равно ничего больше быть не может. И так со всеми кнопками. Для той у которой значение самое маленькое - снизу делал от 16 и всё надёжно работало.

Ну, для кнопок SPDT там несколько одновременно нажатых само получается, там же всё по битам. А вот для SPST (самые обычные кнопки) - это большое искусство так подобрать резисторы, чтобы уверенно отличать одиночные и “по нескольку сразу” нажатия. Сам я такого не делал, а сколько видел примеров в сети, ни одного не было правильного, во каждом можно было найти ошибку. Так что, не скажу. Что-то было в теме про кнопки на старом форуме. Точно было. Но, кажется, я там ошибку находил как раз для нескольких нажатий.

А если по вашему примеру, но вот так:

И откуда Вы здесь сигнал читать собрались?

аналоговые кнопки очень капризны, при питании от батарей у них одни значения, при отладке по USB - совершенно другие. Нужно очень аккуратно делать схему и программировать её. Когда я в очередной раз за@пся с этим, перешел на использование PCF8574 специально для кнопок, если их меньше 9.

2 лайка

Тут

Там принцип прост. Сопротивления подбираются такие,чтоб напряжение каждой последующей кнопки были в 2 раза больше предидущей. Так называемый R-2R делитель(могу в названии ошибится). По сути принцип как у ЦАП.
Допустим, при 4.096В опорного напряжения, если Кнопка1 даёт 256мВ, а Кнопка2 1024мВ,то результатом АЦП будет 0b001010000.
Вся задача сводится к проверке нужных битов.
Кнопки-“биты” через ЦАП R-2R в аналог,а
АЦП обратно в биты)
Разумеется,лучше выбирать для кнопок более старшие разряды,меньше будет погрешностей и ложных срабатываний.

Неправда Ваша.
При правильно составленной схеме измерения на аналоговом делителе дают результаты, которые зависят только от делителя и не зависят от питающего напряжения.

1 лайк