Зарегистрироваться

Делаем из кнопок и светодиодов минипанельку управления. Кнопки 6X6X10MM 4PIN без фиксации.

  • Цена: $0.99 за 50 штук

Как бы не было модно управлять устройствами с помощью современных интерфейсов (телефон, пульт, компьютер и т.п.), все-таки старые добрые кнопки зачастую удобнее, так как расположены в одном месте, как правило, рядом с тем устройством на которое требуется оказать воздействие. Стоят они недорого, поэтому если есть возможность, лучше обеспечить свою поделку (или доработанную чужую) местным интерфейсом управления. Именно про такой интерфейс и пойдет речь в обзоре. Под катом нехитрая поделка, arduino, ну и, собственно, немного про данную конкретную кнопку.

Посылка приехала за 3 недели, трек не отслеживался. Кнопки в пупырчатом пакете, проложены картонкой, ножки не погнулись. В пакетике их было 48 штук (спор открывать не стал :)). Собственно, предмет обзора:



Приведу чертеж такой кнопки:

H — в данном случае, 10 мм, но существуют варианты как длинней так и короче, в зависимости от потребностей.
На фото данного продавца представлен целый набор цветов и размеров:

На следующей картинке представлен весь спектр выпускаемых китайской промышленностью кнопок такого типа, отличаются они только высотой штырька:

В общем, выбор неплохой. Есть также угловые версии, типа таких:

Размеры кнопки совпадают с чертежом:


Кнопка нажимается мягко, я их использовал уже в своих поделках и нареканий к их работе не имею.
Продавец пишет про медь, поверим магнитом:

Следует сказать, что контакты равнодушны к магниту, а вот верхняя фиксирующая пластина магнитится, собственно, все хорошо с этим.

Зачастую, в качестве самой кнопки используют иную деталь, которая нажимает непосредственно на данную кнопку, это позволяет создавать красивые кнопки устройств. Помимо этого, на кнопки можно купить разноцветные колпачки, например такие:

В общем, разнообразие ограничивается только фантазией.

Если вы заказываете платы в Китае, и ваш производитель плат допускает панелизацию, то вполне можно вставить в свою плату маленький кусочек с полезным функционалом. У меня была такая ситуация, и я решил не выкидывать оплаченный текстолит, а использовать его для простых целей: разместил на нем 3 кнопки с подтягивающими резисторами и 2 светодиода с ограничительными резисторами. Резисторы использовал smd1206 (подтягивающие кнопки на 10КОм, токоограничительные для светодиодов на 680 Ом), а светодиоды стандартные 5 мм. рисовать схему, думаю, в данном случае ненужно. Получилась нехитрая платка:


Скачать файлик с платой в формате Sprint Layout можно по данной ссылке.
Конфигурация, в моем случае, определялась свободным кусочком платы и возможным применением. Вы же можете расположить так как удобно вам. С одной стороны платы я решил расположить кнопки и светодиоды, с другой все остальное. Плата имеет отверстия по краям, пригодные для крепления ее к корпусу. Припаиваем детали:



Совсем не обязательно светодиоды припаивать вплотную к плате, можно подобрать длину выводов подходящую к вашему корпусу. Плата в полной комплектации подключается семью проводами: 3 на кнопки, 2 на светодиоды и питание (для подтяжки кнопок) с землей.
В качестве тестового устройства используем Arduino Nano, подключив ее проводками:

Чтобы было не скучно, напишем простую программку (но имеющую амбиции на правильную работу с кнопками). Наша программа обрабатывает все три кнопки и переключает оба светодиода. Первая кнопка включает и выключает красный светодиод, вторая зеленый, а третья инвертирует состояния обоих светодиодов:

// функция вычисления количества элементов в массиве любого типа
template<typename T, size_t n> inline size_t arraySize(const T (&arr)[n]) {
  return n;
}

uint8_t led1_pin = 5; // красный
uint8_t led2_pin = 6; // зеленый
// Текущее состояние светодиода 1
bool led1_on = false;
// Текущее состояние светодиода 2
bool led2_on = false;

// опишем кнопку
typedef struct {
  uint8_t pin; // пин
  bool state; // текущее состояние (1 по умолчанию, так как подтяжка к питанию) 
  bool changed; // необработанный факт смены
  unsigned long start_change; // время начала смены состояния
} BtType;
// массив из 3-х кнопок
BtType BT[] = {
  {2, true, false, 0}, // 1-я кнопка
  {3, true, false, 0}, // 2-я кнопка
  {4, true, false, 0}, // 3-я кнопка
};
// количество кнопок
uint8_t NumberBT = 0;
// Защита от дребезга, мс
unsigned long DEBOUNCE_TIME = 50;

// Текущее время
unsigned long CurrentTime = 0;

void setup() {
  // Светодиоды - это выходы
  pinMode(led1_pin, OUTPUT);
  pinMode(led2_pin, OUTPUT);
  // Вначале не светим
  digitalWrite(led1_pin, LOW);
  digitalWrite(led2_pin, LOW);

  // текущее количество кнопок
  NumberBT = arraySize(BT);

  // Кнопки - это входы
  for (uint8_t i = 0; i < NumberBT; i++) {
    pinMode(BT[i].pin, INPUT);
  }
}

// контроль кнопки
void button_control(uint8_t i) {
  // если состояние отличается от текущего
  if (BT[i].state != digitalRead(BT[i].pin)) {
    // если не начат отсчет времени защиты от дребезга - начинаем
    if (BT[i].start_change == 0) BT[i].start_change = CurrentTime;
    // если это не дребезг, меняем состояние
    if (CurrentTime - BT[i].start_change > DEBOUNCE_TIME) {
      BT[i].state = !BT[i].state;
      BT[i].start_change = 0;
      BT[i].changed = true;
    }
    // это все-таки дребезг :)
  } else {
    BT[i].start_change = 0;
  }
}

// основной цикл
void loop() {
  // Устанавливаем текущее время
  CurrentTime = millis();
  // Проверяем все кнопки
  for (uint8_t i = 0; i < NumberBT; i++) {
    button_control(i);
    // если текущая кнопка изменила состояние на нажатое
    if (BT[i].changed && BT[i].state == false) {
      // первая кнопка меняет красный светодиод
      if (i == 0) {
        led1_on = ! led1_on;
        digitalWrite(led1_pin, led1_on);
      // вторая кнопка меняет зеленый светодиод
      } else if (i == 1) {
        led2_on = ! led2_on;
        digitalWrite(led2_pin, led2_on);
      // третья кнопка инвертирует оба
      } else if (i == 2) {
        led2_on = ! led2_on;
        digitalWrite(led2_pin, led2_on);
        led1_on = ! led1_on;
        digitalWrite(led1_pin, led1_on);
      }
    }
    // отжатие кнопки мы не обрабатываем
    BT[i].changed = false;
  }
}

Я постарался и прокомментировал фактически все строчки в программе, так что ее можно использовать как заготовку для своих поделок. Естественно, там где включается светодиод можно делать и иные действия, например управлять реле из этого обзора.
Видео, иллюстрирующее работу нашей панельки с данным кодом:

С такой платкой удобно отлаживать свои поделки (тут и индикация и подтянутые кнопки(датчики)). В случае применения в реальном устройстве совсем не обязательно припаивать все детали, например, можно оставить два светодиода и одну кнопку.

Кнопки оказались рабочими и полезными, к покупке рекомендую.
Спасибо всем, кто дочитал этот обзор до конца, надеюсь кому-то данная информация окажется полезной.
Планирую купить +27 Добавить в избранное +46 +78
+
avatar
+14
+
avatar
  • yurok
  • 23 января 2018, 03:18
+3
напишите проще и понятнее, а также легко поддерживаемое
без временных задержек с защитой от дребезга :)
+
avatar
+4
Сильно проще не получится при любом раскладе. Самое простое будет немасштабируемым. Ибо одна кнопка без массива повлечёт за собой копипасту, которую будет мучительно сложно поддерживать.
Веселее будет когда потребуется отработка одновременных нажатий. Это не когда зажали шифт и получили заглавную букву, а когда кнопка А это больше, Б это меньше и вместе они энтер. Длинное же нажатие двух эскейп, а удержание по одной автоповтор в нужную сторону.
Вариантов решения — куча, все правильные по выхлопу, но с разной реализацией и нюансами.

Так что нормально у вас всё, не принмайте стёб близко к сердцу.
+
avatar
  • yurok
  • 23 января 2018, 04:15
+1
я смайлик поставил — это говорит об улыбке

а одновременное нажатие вот при таком коде несложно обработать, а портянка она и не короче будет )
+
avatar
  • spc
  • 23 января 2018, 09:28
+5
В плане :):



И да, пробовал на энкодере.
+
avatar
+1
это не спортивно.
+
avatar
  • yurok
  • 23 января 2018, 09:51
0
100n — маловато
а 100к — многовато :)

ну и… задача была написать а не нарисовать )
+
avatar
+1
отсюда вывод
чтобы не быть простодругом — надо быть проще
+
avatar
+3
uint8_t? Вы действительно используете подобные типы данных в своих проектах? Для меня ардуино — это поделки выходного дня, когда можно быстро сделать что то прикольное, и тратить время на набирание лишних символов, ровно как и на экономить память, я не вижу смысла. Да и вроде uint8_t это просто byte…

По существу: 3 кнопки и 2 диода = 7 проводов и 5 вводов/выводов. Есть возможность подумать надо сокращением портов/проводов.

А в целом — всё очень наглядно и понятно, Вам +
+
avatar
  • kirich
  • 23 января 2018, 04:54
0
По существу: 3 кнопки и 2 диода = 7 проводов и 5 вводов/выводов. Есть возможность подумать надо сокращением портов/проводов.
Хоть я и не программист, но для трех кнопок и двух светодиодов достаточно 3 порта микроконтроллера и 4 провода.

Кнопки на АЦП, светодиоды встречно параллельно на два порта контроллера, но так как есть общий провод, то последнее лишнее и ничего не экономит, только усложняет.

Вот только с одновременным нажатием могут быть сложности :)
+
avatar
  • yurok
  • 23 января 2018, 05:11
+3
и еще ряд проблем…
связанных с занятием аналоговых пинов — а есть например только цифровые…
и светодиоды можно зажигать одновременно и кнопки нажимать )
а еще светодиодами мигать можно одновременно — а можно с разной частотой
очень в узких ситуациях это можно делать — обычно это делают в условиях крайней нехватки пинов…

я же сделал универсальную платку — которой удобно отладить простые вещи,

добавте еще разные ацп и питание, будет уж вовсе не универсально…
+
avatar
  • sav13
  • 23 января 2018, 05:35
+2
Если нужно экономить пины — то PCF8574 с I2C прямо на плате
Но это для готовых устройств, а для прототипирования нормально
Только при таких затратах изготовления делать уж сразу 4 входа/4 выхода. Не меньше
И подключать те которые используются
+
avatar
  • yurok
  • 23 января 2018, 06:18
+3
у меня места на плате оставалось под такое — так что бесплатно платка вышла, ее бы не вставил — обрезали бы да и все
+
avatar
+1
сейчас как раз обдумываю реализацию кнопок на мультиварке — их там 7 штук, по числу программ. Думаю обойтись белыми светодиодами и семью цифровыми пинами.
+
avatar
  • klop
  • 23 января 2018, 08:29
0
Хоть я и не программист, но для трех кнопок и двух светодиодов достаточно 3 порта микроконтроллера и 4 провода.
Для 3 кнопок (и более) достаточно 1 порта (и соответствующего числа резисторов), при условии, что порт аналоговый. Программная реализация АЦП.
+
avatar
  • sav13
  • 23 января 2018, 08:44
+1
Кому достаточно, кому недостаточно
У аналоговых кнопок нет одновременного нажатия
Плюс затруднительна обработка по прерываниям ( и такое бывает)
Плюс геммор с настройками констант, если я хочу другие порты аналоговые использовать например с другим источником опорного
Ну и элементарно кода больше, так что аналоговые кнопки нужны при сильной экономии портов ввода/вывода. Хотя и в этом случае есть универсальное решение — расширитель портов с I2C
+
avatar
  • klop
  • 23 января 2018, 09:13
0
Разумеется, для разных задач будут разные решения. Но тут обсуждается сабж.
+
avatar
  • yurok
  • 23 января 2018, 09:21
+1
Так в сабже я как раз и сделал подходящее для таких задач решение избегающее целого ряда специфических особенностей
+
avatar
  • klop
  • 23 января 2018, 10:19
+1
Я ж не против вашего решения (кроме ужасного кода*, конечно), просто зашел разговор о количестве необходимых портов и я напомнил одно из решений. А ваши обзоры я всегда читаю с удовольствием, т.к. часто нахожу для себя полезное.

* if (BT[i].changed && BT[i].state == false) — подобные вещи, извиняюсь, коробят…
if (!(a && b)) { } или if (a && b); else { }
+
avatar
  • yurok
  • 23 января 2018, 10:26
+1
иногда наглядность рулит
+
avatar
  • Marble
  • 23 января 2018, 20:46
+1
а как вам такой индусский код?
bool func(bool someParam) {
    if (someParam.ToString().Length == 4)
        return true;
    else
        return false;
+
avatar
  • AndyBig
  • 24 января 2018, 03:53
0
Шедевр. Это из реальности?
+
avatar
  • Marble
  • 24 января 2018, 04:49
0
Да, было где-то подсмотрено много лет назад. Но количество рук, через которые это дошло ко мне, утверждать не берусь.
+
avatar
  • AndyBig
  • 24 января 2018, 05:07
0
Я бы даже специально не смог до такого додуматься :)

Хотя могу предложить этим индусам вариант определения максимального из двух чисел:

int max(int a, int b)
{
  int c = 0;
  while (c < a && c < b)
    c++;
  if (c == a)
    return a;
  if (c == b)
    return b;
  if (c != a && c != b)
    return c;
}
+
avatar
  • yurok
  • 24 января 2018, 05:13
0
только минимального )
+
avatar
  • AndyBig
  • 24 января 2018, 05:26
0
У индусов — максимальное :)))
+
avatar
  • AndyBig
  • 23 января 2018, 09:58
+7
У аналоговых кнопок нет одновременного нажатия
При правильно подобранных резисторах — есть :)
+
avatar
  • Kail
  • 23 января 2018, 10:44
+1
А надо подбирать? Я думал схемы R-2R хватает.
+
avatar
  • AndyBig
  • 23 января 2018, 10:58
+1
Это и есть «подобрать» :)
+
avatar
0
для управления телефоном через наушники бывает иначе
+
avatar
  • AndyBig
  • 24 января 2018, 03:54
0
В рулевых кнопках в машинах тоже часто используются другие соотношения :)
+
avatar
  • yurok
  • 23 января 2018, 05:17
+3
uint8_t — более переносимо (мало ли)

надо сокращением тут думать не нужно — с точки зрения отладки — а основная цель именно такая- гораздо удобнее

спасибо
+
avatar
  • sir0ta
  • 23 января 2018, 06:58
+3
Вам бы свои пустые и ни к чемные понты оставили при себе.
Красиво писать надо везде. И в работе, и в поделке выходного дня. Это правило хорошего тона. А такие экономы потом в дедлайн такую пургу пишут и софт с аптаймом в неделю жрет 500 метров оперативы. А потом ты убиваешь неделю на хоть какую-то оптимизацию и дальше 200 метров софтина и через месяц не набирает.
+
avatar
0
«меньше пафоса, господа!» © Ф.Раневская
+
avatar
  • Marble
  • 23 января 2018, 20:42
0
Красиво писать надо везде. И в работе, и в поделке выходного дня.
Я тоже такого подхода придерживаюсь.
+
avatar
0
уборка мусора — обязанность компилятора;)
+
avatar
  • CuMr
  • 24 января 2018, 03:51
0
в смысле — жаба-машыны?
в смысле среды исполнения.
+
avatar
  • sir0ta
  • 24 января 2018, 04:11
0
Сарказм?
+
avatar
  • zhevak
  • 23 января 2018, 07:18
+16
uint8_t? Вы действительно используете подобные типы данных в своих проектах?
Я постоянно использую подобные обозначения типов в своих проектах. А в чём, собственно, проблема?

Когда приходится скакать с одного проекта на другой… Причём, разные проекты реализованы на разных микроконтроллерах — 8-ми разрядных (AVR), 16-ти разрядных (MSP430), 32-разрядных (STM32). Заказчики разные, требования у всех разные. Ещё хуже, когда в одном могучем супер-проекте используется разное оборудование на МК разной разрядности, и между этим оборудованием нужно перегонять массивы данных. Вот тут-то по неволе будешь использовать универсальные обозначения типов. Благо, компилятор GCC примерно одинаковый для всех МК и в том числе для компов.

Так что я считаю, что использование обозначений типов типа (простите за каламбур!) uint8_t — скорее благо, чем зло.

И ещё, что бы не было никому обидно. Когда я использую «общие» данные (то есть такие данные, которые создаются на одном типе МК, передаются по линиям связи с помощью других МК, а обрабатываются, скажем, на ПК), то я прибегаю к обозначениям типа, у которых в названии участвует их размер. С другой стороны, когда мне, допустим, нужно обработать какой-нибудь массив, к элементам которого я получаю доступ по индексу, то для типа переменной для индекса я использую стандартное int. В этом случае сам компилятор выбирает «родной» для вычислителя тип. (Разумеется, бывают исключения!)

Во всяком случае, у меня нет никаких проблем с выбором типов. Всё однозначно и всё понятно.

К стати! Размер байта не всегда 8 бит! (Просто погуглите ради интереса!) Я такой старый, что я помню времена, когда байт был равен 5 битам (у меня до сих пор в рабочей тетрадке вклеен тетрадный листочек с кодировкой символов этими 5 битами. Ужас! Где мы были тогда, и где сейчас находимся!). Потом как-то быстро проскочили времена 6-битных байтов, и настала пора 7-битных. А потом пошли персональные компы разных мастей и байт окончательно оформился в умах новоявленных программистов как только 8-рязрадная величина. Но это уж издержки возрастного ценза.

В общем, не судите строго!
+
avatar
  • skif31
  • 23 января 2018, 18:45
+2
Теперь и в километре не 1000 метров, а 1024. Другие времена, другие нравы…
+
avatar
  • zhevak
  • 24 января 2018, 08:05
0
нет! В километре по прежнему 1000 метров.
+
avatar
  • Marble
  • 23 января 2018, 20:58
+1
К стати! Размер байта не всегда 8 бит!
То же касается и других типов, типа short/int/long. Поэтому и ввели те самые xintx_t.

Вы абсолютно правы в своих размышлениях, но при этом пишете так, как будто не совсем уверены в своей правоте :)
+
avatar
+1
русский термин «слово» был достаточно удачен

погуглите
всё рядом и всё правда!
+
avatar
+3
Тем не менее, если к клавиатуре привык, то нет особой разницы в паре лишних символов. Тем более, что нет типа byte в стандартных Сях и плюсах. Есть char. Но с ним есть нюансы, где-то он по умолчанию знаковый, где-то беззнаковый. Более точное соответствие это uint8_t == unsigned char. И этот uint8_t худо-бедно более стандартен.
Это в итоге влияет на переносимость кода, когда «суть» процесса отлаживается к примеру вне Ардуино или наоборот будучи отлаженой на Ардуино уезжает в более взрослую среду. Да и не только Ардуино это касается.
+
avatar
  • zhevak
  • 24 января 2018, 08:33
0
Более того размер char (то есть количество бит в нём) зависит от кодировки.

Те, кто работает в Линуксе, знают, что по умолчанию там кодировка utf-8. А это значит, что часть символов будет 8-битовыми, а часть 16-битовыми. Это тот ещё головняк!

С кодировкой utf-8 сложно работать на низком уровне, где уделяется внимание аспекту сколько байт памяти нужно резервировать по ту или иную строковую переменную. Для сравнения, в ДОС (коддировка CP866) и в Виндовсе (кодировка CP1251) было всегда, что один символ равен одному байту (8 бит). И те эмбеддеры, которые писали программы для микроконтроллеров всегда знали сколько выделяется памяти под строки. Было очень удобно. По этой причине мне нравится Виндовс.

Но в Линуксе нельзя написать (в Си) строку «Привет» и быть уверенным, что она займёт 6 байт. Реально она займет 12 байт. Символы кириллицы 16-битные. Но в то же время, размер этой строки будет равен ровно 6 символов (6 char). С непривычки может крышу снести!

А вот слово «Hello», что в байтах, что в символах всегда будет равно 5.

Особый пиетет возникает, когда нужно писать прогу для МК, который общается с символьным ЖКИ типа WH1604 (контроллер HD44780).

Возникает законный вопрос — может быть стоит избегать разрабатывать в Линуксе проги для МК, раз тут так всё сложно? Ответ — нет! Линукс легко «переваривает» файлы как с кодировкой utf-8, так и с кодировкой CP1251.

Делается всё очень просто. Те файлы, в которых не используется строковых переменных с кириллицей, вы можете писать в родной кодировке (utf-8). А те файлы, в которых используется кириллица, вы тупо создаёте в кодировке CP1251 или C866, и просто работаете как обычно. Со временем, вы можете даже забыть какой файл у вас в какой кодировке. Всё будет открываться и компилироваться абсолютно одинаково! Вот за это я уважаю Линукс! Уважаю даже больше, чем Виндовс. В Линуксе нет сложностей, но возможностей в нём значительно больше, чем в Виндовсе. Те, кто на площадях плачется о том, что Линукс корявый, его просто не знают!
+
avatar
  • LynXzp
  • 23 января 2018, 12:27
0
uint8_t?
А вот напишете Вы char, в ардуинах на атмегах у вас будет int8_t, а в Due uint8_t

Не, ну если вы конечно имели в виду что вместо uint8_t нужно писать uint_fast8_t или uint_least8_t, то прошу прощения. </>
+
avatar
  • Corvair
  • 23 января 2018, 05:04
+1
Ещё бывают такие же кнопки со встроенной индикацией.
+
avatar
  • yurok
  • 23 января 2018, 05:15
+1
стоят дороже и зависимая индикация… тут независимые процессы
+
avatar
+1
Лайк за ёжика!
+
avatar
  • arbatjc
  • 23 января 2018, 07:07
+5
Минимум за скетч с комментами лайк и благодарность.
Только-только начинаю и просто копипаст не интересно, потому и благодарю. Ибо не все тут «родились с ардуинами в руках» ( в одной уно/ в другой нано) и конечно приходится постигать.
А в целом, спасибо, ибо выпаивать кнопки из старых проектов, не самое полезное занятие (это я про себя). И лучше иметь запас по видам и типам.
+
avatar
+1
Тема для DIY.
+
avatar
  • AndyBig
  • 23 января 2018, 10:12
+2
Я для кнопок использую похожую обработку, но в структуре только два байта:
typedef struct
{
KBD_STATES state;
Int8U time;
} KEYBOARD;

Возможные состояния кнопки:
typedef enum
{
KB_FREE,
KB_PROCESSED,
KB_PREPRESSED,
KB_SPRESSED,
KB_LPRESSED,
KB_SRELEASED,
KB_LRELEASED,
} KBD_STATES;

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

ЗЫ: если выкинуть из структуры номер кнопки, то это сэкономит память, а отразится только на инициализации, в которой это, в общем-то, и не нужно :)
+
avatar
  • yurok
  • 23 января 2018, 10:28
+1
так в структуре нет номера, или речь про пин? )
понятно, что в байт можно много пакануть — но наглядности коду это явно не прибавит )
невнимательно посмотрел, тут нет упаковки — можно и так сделать — время перехода из состояния в состояние оставлять — но тут в примере их два всего
+
avatar
  • AndyBig
  • 23 января 2018, 10:42
0
так в структуре нет номера, или речь про пин? )
Да, про пин :)
А зачем оставлять время перехода в другое состояние? Это не нужно, просто текущее состояние на данный момент — нажата, отпущена, долго нажата, отпущена после долгого нажатия, обработана :)
+
avatar
  • yurok
  • 23 января 2018, 10:45
0
так вы их как опрашиваете? по какому признаку
так чтобы понять что долго нажата, нужно момент первой смены состояния запомнить, либо задержками — что не есть гуд
+
avatar
  • AndyBig
  • 23 января 2018, 10:55
+1
Теперь я понял о чем Вы. Нет, я не запоминаю время при смене состояния, я начинаю отсчет, инкрементируя переменную time в структуре кнопки при каждом вызове процедуры опроса. Для этого хватает 8-битной переменной, в отличии от запоминания времени :)
Наверное, проще будет дать процесс опроса кнопки :)

switch (keyb_keys[0].state)
{
case KB_PROCESSED:
if (!KEY1_READ())
{
keyb_keys[0].state = KB_FREE;
}
break;
case KB_FREE:
case KB_SRELEASED:
case KB_LRELEASED:
if (KEY1_READ())
{
keyb_keys[0].state = KB_PREPRESSED;
keyb_keys[0].time = 0;
}
break;
case KB_PREPRESSED:
if (KEY1_READ())
{
if (keyb_keys[0].time > 6)
{
keyb_keys[0].state = KB_SPRESSED;
}
else
keyb_keys[0].time++;
}
else
{
keyb_keys[0].state = KB_PROCESSED;
keyb_keys[0].time = 0;
}
break;
case KB_SPRESSED:
if (KEY1_READ())
{
if (keyb_keys[0].time > 150)
{
keyb_keys[0].state = KB_LPRESSED;
}
else
keyb_keys[0].time++;
}
else
{
keyb_keys[0].state = KB_SRELEASED;
keyb_keys[0].time = 0;
}
break;
case KB_LPRESSED:
if (!KEY1_READ())
{
keyb_keys[0].state = KB_LRELEASED;
keyb_keys[0].time = 0;
}
else
keyb_keys[0].time++;
break;
}
+
avatar
  • yurok
  • 23 января 2018, 12:15
0
понял ваш процесс, нормик

немного иная логика, мне проще привязаться ко времени чем к количеству опросов, так я могу регулировать процессы в более широких диапазонах.

KEY1_READ() — это макрос у вас? в котором захардкожен номер пина который читается? :) может лучше его хранить в массиве?
+
avatar
  • AndyBig
  • 23 января 2018, 12:31
0
Да, это макрос с чтением из порта :) У меня в этом примере сделано именно так потому что в этом проекте используется только одна кнопка (энкодер читается аппаратными средствами одного из таймеров) и состояние ее пина может читаться из других модулей. Когда использую несколько кнопок, то делаю инлайновую функцию, возвращающую состояние пина по порядковому номеру и провожу опрос состояний в цикле :)
Опрос и у меня привязан ко времени, так как вызывается из прерывания таймера :) В этом случае я могу гарантировать корректную обработку кнопок даже если главный цикл где-то застрял в длительной процедуре, освободится — отработает кнопку по уже готовому состоянию :)
+
avatar
  • Aostspb
  • 23 января 2018, 14:12
0
Учат-учат программистов комментировать свой код, да так и не доучивают… :) Тяжело читать «с листа» совсем уж без комментариев.
+
avatar
  • AndyBig
  • 23 января 2018, 14:48
0
Согласен, я в этом деле весьма грешен, комментирую по минимуму и в основном лишь бы мне самому было понятно :) Да и тут еще без нормального форматирования читать тяжело.
+
avatar
  • CuMr
  • 23 января 2018, 18:06
+2
А что здесь комментировать? Код ведь короткий, простой и прямолинейный.
Про комментирование наоборот учат избегать лишнего комментирования в стиле
time++; // инкрементируем время
И похоже что вам именно таких комментариев не хватает.
+
avatar
  • Aostspb
  • 23 января 2018, 18:15
-1
А что здесь комментировать?
Вот за такое — надо увольнять без отработки.
+
avatar
  • sir0ta
  • 24 января 2018, 04:26
0
Если вам в том коде надо ставить комменты, то вы или домашний программер или вообще к этому делу кроме как диванного эксперта отношения не имеете.
Коммент пишется для описания функции и внутри только в особосложном участке с подвыпердом. Ну и конечно если был поставлен костыль. Еще комментят изменения в авторском коде. Но это уже для последующих обновлений скажем так.
+
avatar
  • Aostspb
  • 24 января 2018, 10:53
0
Если вам в том коде надо ставить комменты, то вы или домашний программер или вообще к этому делу кроме как диванного эксперта отношения не имеете.
Просто я существенно больше одного года этим занимаюсь, и по себе знаю, что код, написанный лет 10 назад, не всегда понятен. Особенно, если он не единственный за эти 10 лет. :)
+
avatar
  • AndyBig
  • 23 января 2018, 18:29
+1
Когда не привык читать чужой код, то даже такой прямолинейный набор if-else может вызвать затруднения, особенно когда условия нигде не описаны :)
Но я работаю не в команде, заказчику этот код мне тоже отдавать не нужно, так что особых угрызений совести я не испытываю :)
+
avatar
  • sir0ta
  • 24 января 2018, 04:22
0
Слишком простой код. Где-то описать состояния и общее понимание. А комментить каждую строчку… Такому программистов не учат. Уверяю.
+
avatar
  • sir0ta
  • 24 января 2018, 04:21
0
На ardiuno.ru на сколько я помню есть «класс титановый велосипед для тактовой кнопки»
Временами его юзаю. Удобно.
У вас же есть косяк малька. Case… Дело в том что это медленная структура. Замените ее на if. И будет быстрее.
+
avatar
  • AndyBig
  • 24 января 2018, 04:45
0
case — медленная конструкция? Вы меня удивили :)
Не говоря уже о читаемости кода:

	switch (a)
	{
		case 2:
			i += 8;
			break;
		case 3:
			i += 9;
			break;
		case 5:
			i += 10;
			break;
		case 8:
			i += 11;
			break;
		case 12:
			i += 12;
			break;
		case 13:
			i += 13;
			break;
	}


или

	if (a == 2)
	{
		i += 8;
	}
	else
	{
		if (a == 3)
		{
			i += 9;
		}
		else
		{
			if (a == 5)
			{
				i += 10;
			}
			else
			{
				if (a == 8)
				{
					i += 11;
				}
				else
				{
					if (a == 12)
					{
						i += 12;
					}
					else
					{
						if (a == 13)
						{
							i += 13;
						}
					}
				}
			}
		}
	}
+
avatar
  • yurok
  • 24 января 2018, 04:56
+1

if (a == 2) i += 8;
else if (a == 3) i += 9;
else if (a == 5) i += 10;
else if (a == 8) i += 11;
else if (a == 12) i += 12;
else if (a == 13) i += 13;

+
avatar
  • AndyBig
  • 24 января 2018, 04:57
0
А если в каждом if не по одному оператору? Так в одну строку и писать их вместе со скобками? :)
Вообще я обычно посылаю лучи ненависти за такое оформление кода, когда в одну строку стремятся впихнуть весь цикл или условие :)
+
avatar
  • yurok
  • 24 января 2018, 05:04
0
в некоторых случаях оправдано — но по факту код не больше — и так

if (a == 2) {
  i += 8;
} else if (a == 3) {
  i += 9;
} else if (a == 5) {
  i += 10;
} else if (a == 8) {
  i += 11;
} else if (a == 12) {
  i += 12;
} else if (a == 13) {
  i += 13;
}
+
avatar
  • AndyBig
  • 24 января 2018, 05:09
0
Нет, без нормального форматирования подобный код читается плохо, не наглядно. И чем больше if-else тем хуже читать их :)
+
avatar
  • AndyBig
  • 24 января 2018, 05:02
0
Не поленился, скомпилировал с высокой оптимизацией два варианта:

	switch (a)
	{
		case 1:
			i += 8;
			break;
		case 2:
			i += 9;
			break;
		case 3:
			i += 10;
			break;
		case 4:
			i += 11;
			break;
		case 5:
			i += 12;
			break;
		case 6:
			i += 13;
			break;
		default:
			i += 14;
	}

и

	if (a == 1)
	{
		i += 8;
	}
	else
	{
		if (a == 2)
		{
			i += 9;
		}
		else
		{
			if (a == 3)
			{
				i += 10;
			}
			else
			{
				if (a == 4)
				{
					i += 11;
				}
				else
				{
					if (a == 5)
					{
						i += 12;
					}
					else
					{
						if (a == 6)
						{
							i += 13;
						}
						else
						{
							i += 14;
						}
					}
				}
			}
		}
	}


Результат в ассемблере для switch:

main:
        LDR.N    R0,??main_1
        LDRB     R1,[R0, #+0]
        SUBS     R1,R1,#+1
        CMP      R1,#+5
        BHI.N    ??main_2
        TBB      [PC, R1]
        DATA
??main_0:
        DC8      0x3,0x7,0xB,0xF
        DC8      0x13,0x17
        THUMB
??main_3:
        LDRH     R1,[R0, #+2]
        ADDS     R1,R1,#+8
        STRH     R1,[R0, #+2]
        B.N      ??main_4
??main_5:
        LDRH     R1,[R0, #+2]
        ADDS     R1,R1,#+9
        STRH     R1,[R0, #+2]
        B.N      ??main_4
??main_6:
        LDRH     R1,[R0, #+2]
        ADDS     R1,R1,#+10
        STRH     R1,[R0, #+2]
        B.N      ??main_4
??main_7:
        LDRH     R1,[R0, #+2]
        ADDS     R1,R1,#+11
        STRH     R1,[R0, #+2]
        B.N      ??main_4
??main_8:
        LDRH     R1,[R0, #+2]
        ADDS     R1,R1,#+12
        STRH     R1,[R0, #+2]
        B.N      ??main_4
??main_9:
        LDRH     R1,[R0, #+2]
        ADDS     R1,R1,#+13
        STRH     R1,[R0, #+2]
        B.N      ??main_4
??main_2:
        LDRH     R1,[R0, #+2]
        ADDS     R1,R1,#+14
        STRH     R1,[R0, #+2]
??main_4:
        MOVS     R0,#+0
        BX       LR               ;; return


Для if:

main:
        LDR.N    R0,??main_0
        LDRH     R1,[R0, #+2]
        LDRB     R2,[R0, #+0]
        CMP      R2,#+1
        BNE.N    ??main_1
        ADDS     R1,R1,#+8
        STRH     R1,[R0, #+2]
        B.N      ??main_2
??main_1:
        CMP      R2,#+2
        BNE.N    ??main_3
        ADDS     R1,R1,#+9
        STRH     R1,[R0, #+2]
        B.N      ??main_2
??main_3:
        CMP      R2,#+3
        BNE.N    ??main_4
        ADDS     R1,R1,#+10
        STRH     R1,[R0, #+2]
        B.N      ??main_2
??main_4:
        CMP      R2,#+4
        BNE.N    ??main_5
        ADDS     R1,R1,#+11
        STRH     R1,[R0, #+2]
        B.N      ??main_2
??main_5:
        CMP      R2,#+5
        BNE.N    ??main_6
        ADDS     R1,R1,#+12
        STRH     R1,[R0, #+2]
        B.N      ??main_2
??main_6:
        CMP      R2,#+6
        BNE.N    ??main_7
        ADDS     R1,R1,#+13
        STRH     R1,[R0, #+2]
        B.N      ??main_2
??main_7:
        ADDS     R1,R1,#+14
        STRH     R1,[R0, #+2]
??main_2:
        MOVS     R0,#+0
        BX       LR               ;; return


Мой склероз меня не обманул — switch переводится в аналог goto и сразу прыгает в нужную ветку, а if в каждой ветке производит сравнение с условием :)
+
avatar
  • sir0ta
  • 24 января 2018, 05:58
0
www.sql.ru/forum/1235489/chto-bystree-if-else-ili-switch
С наскока. Когда-то читал статью (было лет 10 назад), там тонны примеров и компиляторов с замерами времени. И как итог совет не увлекаться, лучше вообще не трогать свичи.
А ваш пример:
if (1<a & а<7)
i=14
else
i+=a;
Как это будет выглядить в асме?
Частный случай так сказать.
+
avatar
  • AndyBig
  • 24 января 2018, 06:05
0
if (1<a & а<7)
i=14
else
i+=a;
Как это будет выглядить в асме?

main:
        LDR.N    R0,??main_0
        LDRB     R1,[R0, #+0]
        SUBS     R2,R1,#+2
        CMP      R2,#+5
        BCS.N    ??main_1
        MOVS     R1,#+14
        STRH     R1,[R0, #+2]
        B.N      ??main_2
??main_1:
        LDRH     R2,[R0, #+2]
        ADDS     R1,R1,R2
        STRH     R1,[R0, #+2]
??main_2:
        MOVS     R0,#+0
        BX       LR               ;; return
+
avatar
  • sir0ta
  • 24 января 2018, 06:41
0
Короче же? Оптимизированее. Вот и пример победы ифа над кейсом ;-)
+
avatar
  • AndyBig
  • 24 января 2018, 07:04
0
Короче чем что?
+
avatar
  • sir0ta
  • 24 января 2018, 07:34
0
Чем свитч )
+
avatar
  • AndyBig
  • 24 января 2018, 08:25
0
А причем тут свич в Вашем примере? :)
+
avatar
  • sir0ta
  • 24 января 2018, 08:59
0
А при том что показывая плюс чего-то на одном частном примере вы забываете что есть другой пример с такой же задачей и таким же результатом, но в куда более быстрой форме. Я вам его и показал.
И то что вы расписали в кучу ифов или сейсов я уместил грубо говоря в 3 строчки кода.
+
avatar
  • LynXzp
  • 23 января 2018, 12:44
0
У меня процедура опроса внутри решает, послать ли что клавиша нажата или не послать и ждать длинного нажатия, сочетания или дребезга.
Опрашивающая функция только обрабатывает в зависимости от номера enum, и знать ничего не знает что бывают разные нажатия, или что кнопок две, а не пять, у нее 5 событий и гори все пропадом.
+
avatar
  • AndyBig
  • 23 января 2018, 12:47
0
Такой вариант тоже имеет право на жизнь, но удобен далеко не во всех случаях :)
+
avatar
  • LynXzp
  • 23 января 2018, 12:55
0
Единственный известный неудобный мне случай когда у программы очень сложное управление, например переход в режим калибровки или что-то такого где управление совсем иное, но и это не составляет каких-то серьезных проблем.

Если Вам известный какой-то другой случай — пожалуйста, хотелось бы услышать (без иронии).
+
avatar
  • AndyBig
  • 23 января 2018, 13:14
0
Например, многоуровневые меню, пункты которых при выборе могут вести в другое меню, или менять какой-то параметр, или запускать выполнение какой-то процедуры, причем какой именно — зависит от некоторых условий. Тут callback функции не очень подходят :) Можно, конечно, и извратиться, например менять по необходимости адрес вызываемой из обработчика кнопок функции, или все значимые пакетов сделать глобальными, или ещё как-то :) Но это просто неудобно :)
+
avatar
  • LynXzp
  • 23 января 2018, 13:46
+1
Кто-то из нас недопонимает другого. У меня ровно так сейчас — много меню в программе (50+), каждое меню — структура — какой отображать текст, какую переменную, и указатель на функцию.

Не callback функции, а еще проще — возврат только номера кнопки или сочетания. Обработчик прост:
switch(key)
{  case 5 : if (menu[N].ptr!=NULL) menu[N].prt(); break;
   case 6 : nextMenu(/*N++ с доп условиями*/); updateDisplay(); break;
   case 7 : N=0; updateDisplay(); break;
   case 8 : /*функция примерно такого содержания*/ menu[N].var++; updateDisplay();
   case 9 : reboot();   }

Единственное что в специальных меню (калибровки) приходиться писать свой switch(key)

Правда тут у меня получается глобальное меню, может у Вас вместо этого глобальная структура состояния кнопки, но не совсем представляю себе это возможным.
+
avatar
  • AndyBig
  • 23 января 2018, 14:46
+1
каждое меню — структура — какой отображать текст, какую переменную, и указатель на функцию.
Угу, у меня похоже. Структура меню:
typedef struct
{
	Int8U 		name_font;
	Int8U 		val_font;
	Int16U		back_color[2];		// normal, selected
	Int16U 		name_color[3];		// normal, selected, disabled
	Int16U 		val_color[4];		// normal, selected, disabled, editing
	Int8U		l_indent;
	Int8U		r_indent;
	Int16S		x;
	Int16S		y;
	Int16S		w;
	Int16S		h;
	Int8U		items_count;
	Int8U 		selected_item;
	Int16U		flags;
	MENU_LINES_ITEM	*items;
	void		*prevmenu;
	void		*nextmenu;
	void		*parentmenu;
	Int32S		oldnval;
	char		oldcval[16];
	menu_func	menudrawfunc;
} MENU_LINES_STRUCT;


Структура пункта меню:
typedef struct
{
	const char 	*text;
	char		cval[16];
	Int32S		nval;
	Int16S		x;
	Int16S		y;
	Int16S		w;
	Int16S		h;
	Int16U		flags;
	void*		childmenu;
	menuitem_func	activatefunc;
	menuitem_func	drawfunc;
	menuitem_func	changevaluefunc;
} MENU_LINES_ITEM;


И все было бы просто, если бы пункты выполняли только переходы между меню, но в каких-то случаях активация пункта должна вызывать редактирование параметра, причем в зависимости от условий может редактироваться тот или иной параметр. Некоторые пункты должны быть сами редактируемыми (названия). То есть отработка многих пунктов зависит от текущих условий и чтобы процедура обработки кнопок знала как именно поступить в данный момент при нажатии той или иной кнопки — она должна обладать полной информацией по текущему статусу программы. А ей это совершенно не нужно, у нее своя маленькая и узкозаточенная задача :)
В принципе, обработка кнопок и у меня происходит примерно по тому же сценарию, но в главном цикле, где есть вся полнота информации :)
+
avatar
  • LynXzp
  • 23 января 2018, 16:22
+1
Ух ты, очень похоже. (У меня экран 1604 поэтому чем-то проще) Редактирование параметра у меня одна кнопка, и не важно что это за пункт — число или запретить/разрешить, функция инкремента переменной увеличивает значение, если вышло за рамки допустимого для «типа» — обнуляет. А функция вывода на экран знает как выводить ту или иную переменную, в зависимости от «типа» (числом или текстом и каким).

Я старался сделать чтобы значение кнопок не менялось, так и пользователю проще тоже, удалось такими оставить почти все: «следующее меню», «следующая позиция», "++ / изменить", «выход» и одну сделал универсальной — указатель на вызов функции «записать/применить/выполнить/Enter». Ну а остальное тоже слегка меняется. Для принятия решения что делать по той или иной кнопке у меня достаточно данных в структуре меню, но все же некоторые «особенные» меню я вынес в отдельные подпрограммы чтобы не загромождать универсальные обработчики.

Спасибо, было интересно взглянуть. Из основных различий: указателей на функции у меня только один, все значения константны (указатели, текст и неизменяемые параметры меню) и удалось все запихнуть в EEPROM (AVR).

P.S. Еще в одной небольшой программе был у меня двумерный массив указателей на обработчики.
Первый индекс — кнопка, второй — текущее состояние. И того и того было по 6, и почти все обработчики разные. Решение оказалось хорошим. Единственное что без графического комментария ничего не понятно.
+
avatar
  • AndyBig
  • 23 января 2018, 17:48
+1
Редактирование параметра у меня одна кнопка, и не важно что это за пункт
У меня вообще только один энкодер с кнопкой :)
функция инкремента переменной увеличивает значение, если вышло за рамки допустимого для «типа» — обнуляет
У меня не обнуляет, а просто перестает увеличивать или уменьшать по достижении пределов:
void	SomeValueChange(void *menu, Int8U item, void *param)
{
	MENU_LINES_STRUCT *mn = (MENU_LINES_STRUCT*)menu;
	Int32S *val = &(mn->items[mn->selected_item].nval);
	Int32S oldval = *val;
	Int32U encspd = KEYB_GetEncSpeed();
	// time
	if (mn->selected_item == 0)
	{
		if (encspd > 150)
			*val += KEYB_GetEncCount();
		else
			if (encspd > 100)
				*val += KEYB_GetEncCount() * 2;
			else
				if (encspd > 60)
					*val += KEYB_GetEncCount() * 10;
				else
					*val += KEYB_GetEncCount() * 100;
		if (*val < 1)
			*val = 1;
		if (*val > 5000)
			*val = 5000;
	}
	// power
	else
	{
		if (mn->selected_item == 1)
		{
			if (encspd > 100)
				*val += KEYB_GetEncCount()*5;
			else
				*val += KEYB_GetEncCount() * 10;
			if (*val < 5)
				*val = 5;
			if (*val > 100)
				*val = 100;
		}
	}
	if (oldval == *val)
		return;
	sprintf(mn->items[mn->selected_item].cval, "%d", *val);
	mn->items[mn->selected_item].drawfunc(menu, item, param);
}

(encspd — скорость вращения энкодера, чем быстрее крутишь тем сильнее меняется значение).

А функция вывода на экран знает как выводить ту или иную переменную, в зависимости от «типа» (числом или текстом и каким).
У меня всегда выводится текст, так что если значение числовое, то функции, изменяющие его, заботятся о переводе его в текст. Ну и во многих местах выводится не одно значение, а два-три через разделители, а кое-где меняются и сами названия пунктов, причем меняются самим пользователем :)
одну сделал универсальной — указатель на вызов функции «записать/применить/выполнить/Enter»
Сохранение текущих настроек у меня происходит либо принудительно пользователем через меню в один из 12 (кажется) пресетов, либо автоматом по истечении 30 секунд после последних изменений — эти настройки сохраняются не в пресеты, а в дефолтный набор, который потом загружается при включении. Выход из настроек — или соответствующим пунктом меню или длительным нажатием кнопки, при этом все изменения отменяются.
некоторые «особенные» меню я вынес в отдельные подпрограммы чтобы не загромождать универсальные обработчики.
Аналогично :) У меня есть стандартные функции для навигации по меню любого уровня вложенности или горизонтального расширения и отрисовки, в принципе сейчас добавить новое полностью рабочее меню — дело 5 минут, которые уйдут на задание координат пунктам :) Но некоторые меню и некоторые пункты отрисовываются особым образом, для них я в указатель drawfunc подставляю отдельные функции.
Из основных различий: указателей на функции у меня только один, все значения константны (указатели, текст и неизменяемые параметры меню) и удалось все запихнуть в EEPROM (AVR).
У меня все константы запихнуты в code memory, EPROM в ARM вещь редкая, к сожалению :) Все настройки и параметры так же сохраняются внутри флэши в двух последних страницах (поочередно, для увеличения ресурса). Практически все тексты у меня вынесены в отдельный файл:
const char* STRING_Otmena = "Отмена";
const char* STRING_Vyhod = "Выход";
const char* STRING_Sohranit = "Сохранить";
const char* STRING_SOHRANIT = "СОХРАНИТЬ";
const char* STRING_Zagruzit = "Загрузить";
const char* STRING_ZAGRUZIT = "ЗАГРУЗИТЬ";
...

Не смейтесь над именами — я в них пишу буквальную транскрипцию для максимально точного понимания что это за строка :)))
+
avatar
  • LynXzp
  • 23 января 2018, 18:35
0
У меня не обнуляет, а просто перестает увеличивать или уменьшать по достижении пределов:
Да энкодер хорошо, мне пришлось пользоваться только кнопкой сдвига курсора и ++. Кнопки псевдосенсорные, за пленкой для большей изоляции, там в цехах столько пыли что забивается все. Сделал сначала рост до максимума, останов, и обнуление. Неудобно конечно клацать. Но много разных пределов, если вышло за предел — установить максимум, если максимум и хотят больше — обнулить:
switch (pgm_read_byte(&menu[menu_cur].maximum))
		{	case maximum_32:			if(32		 <lcd_tmp_var)lcd_tmp_var=32;return;
			case maximum_8:			if(8		 <lcd_tmp_var)lcd_tmp_var=8;return;
			case maximum_20000:		if(20000	 <lcd_tmp_var)lcd_tmp_var=20000;return;
			case maximum_sl:			if(2000000000<lcd_tmp_var)lcd_tmp_var=2000000000;return;
			case maximum_255_or_disable:if(255 		 <lcd_tmp_var)lcd_tmp_var=255;return;
			case maximum_500:			if(500 	 <lcd_tmp_var)lcd_tmp_var=500;return;
			case maximum_1440:		if(1440	 <lcd_tmp_var)lcd_tmp_var=1440;return;
		}
// Ах сколько магических констант в коде. Ну ничего, проект старый... и мой самый долгоиграющий, еще не все плохие свои методики от туда удалил.
Сохранение текущих настроек
Т.к. у меня сохраняется по подтверждению, то я добавил возможность визуального просмотра некоторых измененных параметров до применения. Например коэффициент округления, пока его меняешь можно ниже наблюдать как скачут показания, выбрал нужный, нажал «применить».
Не смейтесь над именами — я в них пишу буквальную транскрипцию для максимально точного понимания что это за строка :)))
Хорошая идея. У меня константы такие:
#define uart_receiver_buf_size_8 8
+
avatar
  • AndyBig
  • 23 января 2018, 17:56
+1
Собсно, вот как работают мои меню в текущий момент с помощью энкодера и одной кнопки:

Качество ужасное, снято на скорую руку :)

На главном экране на самом деле аж три разных меню: 1 — первые две строки, 2 — оставшиеся три строки, 3 — нижняя полоса с пиктограммами. Тип меню везде один и тот же, просто первое и второе отличаются цветом значений, а третье имеет нестандартную отрисовку. Для такого «бесшовного» соединения разных меню у меня в структуре меню есть указатели *prevmenu и *nextmenu, через них курсор переходит от одного меню к другому так, как если бы это было одно меню :)
Систему работы с шрифтами тоже писал сам. Существующие не устроили по качеству и разнообразию. Тут у меня есть возможность работать как с 1-битными шрифтами для экономии флэши, так и с теми, в которых каждый пиксель кодируется 2-битным значением прозрачности. При отрисовке смешивается с фоном в соответствии с прозрачностью и шрифты выглядят гораздо более плавными и сглаженными. Выглядит отлично, но жрет место во флеши…
Нижние пиктограммы, кстати, тоже являются символами отдельного шрифта :)
+
avatar
  • LynXzp
  • 23 января 2018, 18:44
+1
Переименовывание параметров это класс. Мне как-то хотелось вбить конструктор протоколов передачи данных примерно так же, но одумался.

Иконки у меня тоже символами, потому что на 1604 никак по-другому, но неплохо выходит, даже анимация.

С таким LCD тоже делал, на AVR, память конечно выбирал много, если не оптимизировать изображения.
+
avatar
  • AndyBig
  • 23 января 2018, 19:27
0
У меня шрифты сожрали много — полный большой (им пишутся все пункты и значения внутри настроек), большой цифровой (значения в пунктах на главном экране), мелкий (в бутлоадере и в некоторых служебных сообщениях основной программы), заставка (это тоже один символ в односимвольном шрифте), нижние иконки… В шрифтах ничего лишнего, каждый символ кодируется под свою ширину-высоту, лишними могут остаться не больше 7 бит. Но больше 9 КБ заняли :)
Это вот результат в дебаге без всякой оптимизации:

    Module                    ro code  ro data  rw data  rw data
                                                          (abs)
    ------                    -------  -------  -------  -------
D:\Electronics\Projects\welding\v0.1\PRG_flash\Debug\Obj: [1]
    allmenu.o                   4 380             2 868
    buzzer.o                      648                68
    flash.o                     1 088                         16
    font_1b62h_splash.o                  1 547       16
    ili9225.o                   4 772        2       22
    keyb.o                        792       19       12
    main.o                      8 124      497       66
    menus.o                     3 400
    startup_stm32f0xx.o           286        2
    stm32f0xx_gpio.o              372
    stm32f0xx_it.o                 28
    stm32f0xx_misc.o              120
    stm32f0xx_rcc.o               220
    stm32f0xx_spi.o               284
    stm32f0xx_tim.o             1 076
    strings_def.o                          490      144
    system_stm32f0xx.o            372
    tft_font_1b12h.o                     1 995       16
    tft_font_1b18h.o                     4 003       16
    tft_font_1b22h_digits.o                980       16
    tft_font_1b22h_symbols.o               741       16
    timer.o                       640               140
    ------------------------------------------------------------
    Total:                     26 602   10 276    3 400       16

37 КБ флэши… Плюс еще 16 КБ в начальных адресах занимает бутлоадер. А всего у контроллера 64 КБ :)

Только сейчас дошло — можно же разместить мелкий шрифт у бутлоадера по фиксированному адресу и в основной прошивке пользоваться им :) Сэкономлю целых 2 КБ флеши :)

Иконки у меня тоже символами, потому что на 1604 никак по-другому, но неплохо выходит, даже анимация.
Я так сделал просто для удобства — процедура отрисовки текста у меня отработана (с автопереносами, выравниванием по левому-правому краям, в заданных координатных окнах, есть даже возможность тегами внутри текста менять цвет/фон/подчеркивание/зачеркивание) и не нужно возиться с отдельными изображениями каждой иконки :)
+
avatar
  • AndyBig
  • 23 января 2018, 19:33
0
Че это у меня сообщения на модерацию улетают? Вроде запрещенных слов не употребляю :)
+
avatar
  • yurok
  • 23 января 2018, 19:53
0
вижу дубль моего творчества двухгодичной давности по сварке :)
главное — цена )
+
avatar
  • AndyBig
  • 23 января 2018, 20:42
0
Именно она :)
По цене мой вариант будет однозначно дороже, и из-за токового датчика, и микроконтроллер другой, подороже, хоть и незначительно, и дисплей, и энкодер более дорогой на 30 имп/оборот (хотя его как раз можно безболезненно заменить на более дешевый 20 имп/оборот) :) Да и плат будет две — управление и силовая/высоковольтная, ею в последние пару недель как раз занимаюсь:
+
avatar
  • yurok
  • 24 января 2018, 02:13
-1
вижу знакомые детали ) то есть вы решили клон моей штуки сделать со своими особенностями? )
+
avatar
  • AndyBig
  • 24 января 2018, 03:40
0
Ну как клон… Из Вашей конструкции я почерпнул единственный момент — модуль AC-DC, до этого я планировал использовать миниатюрную платку БП на 5в/700 мА :))
Все остальное на силовой плате — сборная солянка из десятков просмотренных схем, каждая из которых прогонялась раз по 10 с изменениями в симуляторе, по результатам что-то менялось, что-то выкидывалось, что-то добавлялось… Например, я перебрал штук шесть вариантов детектора нуля, пара из которых с нуля нарисована мною, а остальные четыре — из инета, но с изменениями по экспериментам в симуляторе :)
Управляющая плата вообще полностью собственная с нуля :)
+
avatar
  • yurok
  • 24 января 2018, 05:16
0
от этого оно не перестанет быть клоном )
+
avatar
  • AndyBig
  • 24 января 2018, 05:18
0
Но тогда уже клоном гораздо более ранних споттеров, чем Ваш :) Уже лет 15, наверное, как их делают на симисторах и микроконтроллерах :)
Правда, подавляющее большинство обычно не вникает глубоко в теорию :)
+
avatar
  • AndyBig
  • 24 января 2018, 05:29
0
Кстати, вариант детектора перехода через ноль подобный Вашему я отмел сразу — лишний нагреватель, работающий все время, в девайсе не нужен :)
+
avatar
  • yurok
  • 24 января 2018, 05:37
0
я вижу вариант с конденсатором — принцип тот же
+
avatar
  • AndyBig
  • 24 января 2018, 05:55
0
Тот же только принцип опторазвязки, а само детектирование происходит по совсем другому принципу. На светодиоде оптопары формируется очень короткий импульс длительностью меньше половины миллисекунды, за счет этого среднее потребление несравненно меньше варианта с просто гасящим резистором и диодом :)

Дарю Вам результаты не одного дня поисков, исследований, проб — две хорошие схемы детекторов :))
В последнее время я колебался между двумя вот такими схемами:

это моя:

По параметрам — превосходная, работает в диапазоне как минимум 150-250 вольт без существенного изменения времени импульса относительно нуля, помехозащищенность хорошая, потребляет в среднем около 20 милливатт. Я ее пробовал уже и в железе, все снятые вживую осциллограммы почти не отличаются от симуляции.

это доработанная из инета:

(тут ошибка на схеме — C5 должен быть 10n, это помехозащитный конденсатор)
Помехозащищенность тоже неплохая, тоже работает как минимум от 150 до 250 вольт, но при этом от напряжения зависит ток светодиода (хоть и не очень сильно), время импульса относительно перехода через ноль во всем диапазоне тоже гуляет не больше 0.1 миллисекунды, но жрет около 180 милливатт.

Остановился на втором варианте, хоть он по параметрам и слегка уступает первому, но требует меньше деталей. А я что-то вдруг решил экономить место на плате :)))
+
avatar
  • yurok
  • 24 января 2018, 06:12
0
Спасибо, но моя вполне справляется
+
avatar
  • AndyBig
  • 24 января 2018, 06:30
0
Да справляется, никто не спорит, только в тепло выкидывает целый ватт и все помехи ловит :)
Но дело Ваше, конечно :)
+
avatar
  • yurok
  • 24 января 2018, 06:39
0
там менее полуватта — если посмотрите, не вижу проблем в этом

вот тут народ в выключатели резисторы со светодиодами ставит и советует на 2 Ватта лучше — и ничего
+
avatar
  • AndyBig
  • 24 января 2018, 07:02
0
Уже посмотрел, вон нижний зелененький график — это рассеиваемая на гасящем резисторе мощность, почти 1000 мВт :) Заодно посмотрел и на реакцию на помехи, красненьким посередине — это выход оптопары :)
(кликабельно)
+
avatar
  • yurok
  • 24 января 2018, 07:28
0
а это факт )

а помехи там вовсе не критичны — не тот случай, времена там иные и задачи — я думал над этим — но делайте то что делаете )

погрешность в миллисекунду — никак не скажется на результате, важно попасть в заданный интервал, это если учесть особенности трансформаторов таких, если нужна точность выше то используют постоянный ток, но для большинства бытовых задач хватает и переменного, дозируя его временем в заданном интервале — поэтому фокусы с регулировками дополнительными не дают плодов
+
avatar
  • AndyBig
  • 24 января 2018, 08:24
0
а это факт )
Вы уж извините, но я больше верю нормальному симулятору, чем китайскому показометру, тем более на пределах его погрешности :)
погрешность в миллисекунду — никак не скажется на результате
Погрешность в миллисекунду в одну сторону может раза в полтора увеличить мощность импульса, а в другую — отнимет до 20% от имеющегося рабочего времени :)
поэтому фокусы с регулировками дополнительными не дают плодов
Хм. Я сам еще не добирался до экспериментов с железом, но вообще в серьезной технической литературе, посвященной контактной сварке, пишут другое :)
+
avatar
  • yurok
  • 24 января 2018, 08:36
0
Я же и говорю, действуйте
+
avatar
  • yurok
  • 24 января 2018, 10:07
0
очень странно мощность кстати у вас рассеивается в модели )
при переменном токе и пропускаемой одной полуволне — у вас на графике ровная прямая )

думаю тут лучше смотреть реальную картину а не усредненные значения ввиртуального ваттметра RMS
+
avatar
  • Aostspb
  • 24 января 2018, 10:58
0
сборная солянка из десятков просмотренных схем
DD1 будет нормально работать без экранирования в условиях помех от сварки?
+
avatar
  • sir0ta
  • 24 января 2018, 05:13
0
вижу знакомые детали ) то есть вы решили клон моей штуки сделать
Т.е. вы единственный первый кто сделал точечную сварку и применил подобную схемотехнику? Звучит глупо.
+
avatar
  • sir0ta
  • 24 января 2018, 05:09
0
Очень не дурно. С учетом текущего положения вашего кода выходит совсем мелочь домалевать для работы с сенсором.
И вопросец первый, ок и отмена я так понимаю пункты меню и каждый раз они описываются сами по себе? Я как-то подобное гондобил, но функциональные кнопки и диалоговые окна были описаны один раз и хранили входное значение, возвращаемое и от куда все это тело приехало и куда дальше двигаться. С одной стороны было так удобно, А потом… А потом я забыл переименовать в хлам переписанную библу по работе с экраном и обновил ее… Короче код вроде и есть, но выглядит на экране и работает все боком… Сейчас планирую все повторить, но библу экрана я уже скопировал и переименовал )))
+
avatar
  • AndyBig
  • 24 января 2018, 05:26
0
С учетом текущего положения вашего кода выходит совсем мелочь домалевать для работы с сенсором.
Дисплей без сенсора, так что это мне не нужно. Но да, был бы сенсор — было бы очень просто перейти на работу через него, для этого все уже есть :)
ок и отмена я так понимаю пункты меню и каждый раз они описываются сами по себе?
Да, все, что выделяется курсором (кроме случая редактирования) — это стандартные пункты меню, состоящие из пары «имя + значение».
И нет, для «Сохранить» и «Отмена» во всех параметрах работают две одни и те же процедуры :)
При сохранении из отредактированного пункта меню берется новое значение и вносится в массив текущих параметров, заодно обновляются и остальные параметры, зависящие от измененного. При отмене в отредактированный пункт вписывается старое значение из структуры текущих параметров и все продолжает работать как будто ничего и не менялось :)
+
avatar
  • sir0ta
  • 24 января 2018, 04:52
0
А что не так у автора? Все те же состояния кнопки. А дальше как по кайфу. Ибо у кнопки есть состояние нажата и нажата длинно, была нажата коротко. И у всех должно ыть состояние обработано. А вы уже по этим состояниям и гуляете.
Но не отдать нажата, когда кнопка нажата… Это называется «кнопка отпущена».
Как пример кнопка "+". Нажал, считал первый раз нажато, в обработчике сделао +1, не скинул состояние (чтобы второй раз не было +1 по нажат), считал длинное нажатие, погнал +5 и дальше как по кайфу.
Как по мне удобно и многофункционально из коробки.
Добавим сюда вообще переопределение пинов кнопки и вообще сказка. Была кнопка скажем далее, стала кнопка "+". Со своими потребностями.
И откажитесь вы уже от свича. If. Доказано что он быстрее.
+
avatar
  • AndyBig
  • 24 января 2018, 05:16
0
А что не так у автора?
Да нормально все у автора для простейшей отработки нажатий (а какую-то бОльшую цель он перед собой и не ставил, как я понимаю), просто можно немного оптимизировать :) Ну и не все состояния будут отрабатываться корректно когда, например, главный цикл где-то задержался и пользователь успел нажать и отпустить кнопку. С длительными нажатиями там тоже не все так просто, по текущему коду они работать нормально не будут :)
И откажитесь вы уже от свича. If. Доказано что он быстрее.
Не может if быть быстрее switch, в лучшем случае они будут равны по быстродействию, но обычно свич работает быстрее потому что не перебирает все условия, а идет сразу в нужное :)
+
avatar
  • sir0ta
  • 24 января 2018, 06:02
0
Ну и не все состояния будут отрабатываться корректно когда, например, главный цикл где-то задержался и пользователь успел нажать и отпустить кнопку.
А тут все алгоритмы без перывания будут работать не верно. От этого ни куда не деться.
По свичам почитайте практику.
+
avatar
  • AndyBig
  • 24 января 2018, 06:09
0
Читал, давно и не раз, чего и Вам могу посоветовать :) Свич действует через таблицы переходов, условия — перебором. Если условие выполнилось в первом же if — он отработает чуть быстрее свича (за счет нескольких команд подготовки к переходу у свича), но если условие выполняется лишь во втором или далее if-ах, свич отработает быстрее, потому что ифы будут перебирать все условия, начиная с первого, а свич прыгнет сразу в нужную ветку.
+
avatar
  • sir0ta
  • 24 января 2018, 06:49
0
Ваше право. Я для себя выводы сделал давно. Любой интсрумент уместен если он уместен )))
+
avatar
  • AndyBig
  • 24 января 2018, 07:04
0
Вот это точно :)
+
avatar
  • SERG27
  • 23 января 2018, 10:33
0
Чтобы было не скучно, напишем простую программку
за работу плюс! но всегда хотелось спросить как программеры в сортир ходят. по алгоритму? :)))
+
avatar
  • yurok
  • 23 января 2018, 10:37
0
спасибо
по полу ходят — как и все )
+
avatar
  • LynXzp
  • 23 января 2018, 12:52
+3
Когда программист ложиться спать то ставит рядом два стакана — один с водой, а второй пустой. Первый — если захочется пить. Второй — если нет.
+
avatar
  • SERG27
  • 23 января 2018, 15:25
0
Второй — если нет
я думал- отлить. :)
+
avatar
  • LynXzp
  • 23 января 2018, 16:23
+1
Ну сразу видно — не мыслите Вы шаблонами программиста-функциональщика. :)
+
avatar
  • SERG27
  • 23 января 2018, 21:48
0
Ну сразу видно — не мыслите Вы шаблонами программиста-функциональщика. :)
мое программирование закончилось на Z80 ^))
+
avatar
  • AndyBig
  • 23 января 2018, 18:00
0
Для этого нужен отдельный стакан с контролем переполнения и обработкой исключений :)
+
avatar
  • sir0ta
  • 24 января 2018, 05:15
0
Тогда и первые два не лишины недостатков.
+
avatar
  • AndyBig
  • 24 января 2018, 05:30
+1
Увы, идеал недостижим :)
+
avatar
  • c350l
  • 23 января 2018, 13:24
0
С такой платкой удобно отлаживать свои поделки
« Наши отношения с другими народами налаживаются и будут налаживаться! Отставить. Ложить неправильно – правильно класть. Накладываются и будут накладываться! »
Генерал Бурдун. «День выборов»
:)
+
avatar
  • Aostspb
  • 23 января 2018, 14:17
0
Ложить неправильно
Ложить — Лажать. :)
+
avatar
0
Подскажите не попадались ли кому кнопочки такого размера или меньше с фиксацией?
+
avatar
  • Aostspb
  • 24 января 2018, 11:02
0
Посмотрите по фразе «on off micro switch».