Как бы не было модно управлять устройствами с помощью современных интерфейсов (телефон, пульт, компьютер и т.п.), все-таки старые добрые кнопки зачастую удобнее, так как расположены в одном месте, как правило, рядом с тем устройством на которое требуется оказать воздействие. Стоят они недорого, поэтому если есть возможность, лучше обеспечить свою поделку (или доработанную чужую) местным интерфейсом управления. Именно про такой интерфейс и пойдет речь в обзоре. Под катом нехитрая поделка, 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;
}
}
Я постарался и прокомментировал фактически все строчки в программе, так что ее можно использовать как заготовку для своих поделок. Естественно, там где включается светодиод можно делать и иные действия, например управлять реле из этого обзора.
Видео, иллюстрирующее работу нашей панельки с данным кодом:
С такой платкой удобно отлаживать свои поделки (тут и индикация и подтянутые кнопки(датчики)). В случае применения в реальном устройстве совсем не обязательно припаивать все детали, например, можно оставить два светодиода и одну кнопку.
Кнопки оказались рабочими и полезными, к покупке рекомендую.
Спасибо всем, кто дочитал этот обзор до конца, надеюсь кому-то данная информация окажется полезной.
Планирую купить+30Добавить в избранноеОбзор понравился+49
+89
Сильно проще не получится при любом раскладе. Самое простое будет немасштабируемым. Ибо одна кнопка без массива повлечёт за собой копипасту, которую будет мучительно сложно поддерживать.
Веселее будет когда потребуется отработка одновременных нажатий. Это не когда зажали шифт и получили заглавную букву, а когда кнопка А это больше, Б это меньше и вместе они энтер. Длинное же нажатие двух эскейп, а удержание по одной автоповтор в нужную сторону.
Вариантов решения — куча, все правильные по выхлопу, но с разной реализацией и нюансами.
Так что нормально у вас всё, не принмайте стёб близко к сердцу.
uint8_t? Вы действительно используете подобные типы данных в своих проектах? Для меня ардуино — это поделки выходного дня, когда можно быстро сделать что то прикольное, и тратить время на набирание лишних символов, ровно как и на экономить память, я не вижу смысла. Да и вроде uint8_t это просто byte…
По существу: 3 кнопки и 2 диода = 7 проводов и 5 вводов/выводов. Есть возможность подумать надо сокращением портов/проводов.
По существу: 3 кнопки и 2 диода = 7 проводов и 5 вводов/выводов. Есть возможность подумать надо сокращением портов/проводов.
Хоть я и не программист, но для трех кнопок и двух светодиодов достаточно 3 порта микроконтроллера и 4 провода.
Кнопки на АЦП, светодиоды встречно параллельно на два порта контроллера, но так как есть общий провод, то последнее лишнее и ничего не экономит, только усложняет.
Вот только с одновременным нажатием могут быть сложности :)
и еще ряд проблем…
связанных с занятием аналоговых пинов — а есть например только цифровые…
и светодиоды можно зажигать одновременно и кнопки нажимать )
а еще светодиодами мигать можно одновременно — а можно с разной частотой
очень в узких ситуациях это можно делать — обычно это делают в условиях крайней нехватки пинов…
я же сделал универсальную платку — которой удобно отладить простые вещи,
добавте еще разные ацп и питание, будет уж вовсе не универсально…
Если нужно экономить пины — то PCF8574 с I2C прямо на плате
Но это для готовых устройств, а для прототипирования нормально
Только при таких затратах изготовления делать уж сразу 4 входа/4 выхода. Не меньше
И подключать те которые используются
сейчас как раз обдумываю реализацию кнопок на мультиварке — их там 7 штук, по числу программ. Думаю обойтись белыми светодиодами и семью цифровыми пинами.
Кому достаточно, кому недостаточно
У аналоговых кнопок нет одновременного нажатия
Плюс затруднительна обработка по прерываниям ( и такое бывает)
Плюс геммор с настройками констант, если я хочу другие порты аналоговые использовать например с другим источником опорного
Ну и элементарно кода больше, так что аналоговые кнопки нужны при сильной экономии портов ввода/вывода. Хотя и в этом случае есть универсальное решение — расширитель портов с I2C
Я ж не против вашего решения (кроме ужасного кода*, конечно), просто зашел разговор о количестве необходимых портов и я напомнил одно из решений. А ваши обзоры я всегда читаю с удовольствием, т.к. часто нахожу для себя полезное.
* if (BT[i].changed && BT[i].state == false) — подобные вещи, извиняюсь, коробят…
if (!(a && b)) { } или if (a && b); else { }
Вам бы свои пустые и ни к чемные понты оставили при себе.
Красиво писать надо везде. И в работе, и в поделке выходного дня. Это правило хорошего тона. А такие экономы потом в дедлайн такую пургу пишут и софт с аптаймом в неделю жрет 500 метров оперативы. А потом ты убиваешь неделю на хоть какую-то оптимизацию и дальше 200 метров софтина и через месяц не набирает.
uint8_t? Вы действительно используете подобные типы данных в своих проектах?
Я постоянно использую подобные обозначения типов в своих проектах. А в чём, собственно, проблема?
Когда приходится скакать с одного проекта на другой… Причём, разные проекты реализованы на разных микроконтроллерах — 8-ми разрядных (AVR), 16-ти разрядных (MSP430), 32-разрядных (STM32). Заказчики разные, требования у всех разные. Ещё хуже, когда в одном могучем супер-проекте используется разное оборудование на МК разной разрядности, и между этим оборудованием нужно перегонять массивы данных. Вот тут-то по неволе будешь использовать универсальные обозначения типов. Благо, компилятор GCC примерно одинаковый для всех МК и в том числе для компов.
Так что я считаю, что использование обозначений типов типа (простите за каламбур!) uint8_t — скорее благо, чем зло.
И ещё, что бы не было никому обидно. Когда я использую «общие» данные (то есть такие данные, которые создаются на одном типе МК, передаются по линиям связи с помощью других МК, а обрабатываются, скажем, на ПК), то я прибегаю к обозначениям типа, у которых в названии участвует их размер. С другой стороны, когда мне, допустим, нужно обработать какой-нибудь массив, к элементам которого я получаю доступ по индексу, то для типа переменной для индекса я использую стандартное int. В этом случае сам компилятор выбирает «родной» для вычислителя тип. (Разумеется, бывают исключения!)
Во всяком случае, у меня нет никаких проблем с выбором типов. Всё однозначно и всё понятно.
К стати! Размер байта не всегда 8 бит! (Просто погуглите ради интереса!) Я такой старый, что я помню времена, когда байт был равен 5 битам (у меня до сих пор в рабочей тетрадке вклеен тетрадный листочек с кодировкой символов этими 5 битами. Ужас! Где мы были тогда, и где сейчас находимся!). Потом как-то быстро проскочили времена 6-битных байтов, и настала пора 7-битных. А потом пошли персональные компы разных мастей и байт окончательно оформился в умах новоявленных программистов как только 8-рязрадная величина. Но это уж издержки возрастного ценза.
Тем не менее, если к клавиатуре привык, то нет особой разницы в паре лишних символов. Тем более, что нет типа byte в стандартных Сях и плюсах. Есть char. Но с ним есть нюансы, где-то он по умолчанию знаковый, где-то беззнаковый. Более точное соответствие это uint8_t == unsigned char. И этот uint8_t худо-бедно более стандартен.
Это в итоге влияет на переносимость кода, когда «суть» процесса отлаживается к примеру вне Ардуино или наоборот будучи отлаженой на Ардуино уезжает в более взрослую среду. Да и не только Ардуино это касается.
Более того размер 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, и просто работаете как обычно. Со временем, вы можете даже забыть какой файл у вас в какой кодировке. Всё будет открываться и компилироваться абсолютно одинаково! Вот за это я уважаю Линукс! Уважаю даже больше, чем Виндовс. В Линуксе нет сложностей, но возможностей в нём значительно больше, чем в Виндовсе. Те, кто на площадях плачется о том, что Линукс корявый, его просто не знают!
Возможна Вы правы. Даже наверно так — скорее всего Вы правы!
Вот только я ведь говорил не про то, что чего-то может не заработать, если не правильно сделать. Я акцентировал внимание на том, что описанные выше проблемы принципиально решаются. Причем решаются весьма элегантно и далее (по ходу работ над проектом) уже более не отвлекают разработчика.
Да решаются, конечно, и раньше решались, хотя и чуть более сложно — вынесением всех текстов другой кодировки в отдельный файл, который правился редактором, поддерживавшим эту кодировку :)
С другой стороны, и с IDE, поддерживающими разные кодировки, могут возникать проблемы, когда, к примеру, на дисплей нужно вывести текст в одной кодировке, а в UART/USB отправить этот же текст в другой кодировке :)
Минимум за скетч с комментами лайк и благодарность.
Только-только начинаю и просто копипаст не интересно, потому и благодарю. Ибо не все тут «родились с ардуинами в руках» ( в одной уно/ в другой нано) и конечно приходится постигать.
А в целом, спасибо, ибо выпаивать кнопки из старых проектов, не самое полезное занятие (это я про себя). И лучше иметь запас по видам и типам.
При этом можно отлавливать как нажатия, короткие и длинные, так и отпускания после них, то есть отлавливаются 4 события и после обработки тоже ставится признак обработанной. В длинном нажатии еще и ведется подсчет условного времени нажатия в тиках опроса кнопок :)
Но сама процедура опроса, конечно, занимает больше кода и вызывается таймером :)
ЗЫ: если выкинуть из структуры номер кнопки, то это сэкономит память, а отразится только на инициализации, в которой это, в общем-то, и не нужно :)
так в структуре нет номера, или речь про пин? ) понятно, что в байт можно много пакануть — но наглядности коду это явно не прибавит )
невнимательно посмотрел, тут нет упаковки — можно и так сделать — время перехода из состояния в состояние оставлять — но тут в примере их два всего
Да, про пин :)
А зачем оставлять время перехода в другое состояние? Это не нужно, просто текущее состояние на данный момент — нажата, отпущена, долго нажата, отпущена после долгого нажатия, обработана :)
так вы их как опрашиваете? по какому признаку
так чтобы понять что долго нажата, нужно момент первой смены состояния запомнить, либо задержками — что не есть гуд
Теперь я понял о чем Вы. Нет, я не запоминаю время при смене состояния, я начинаю отсчет, инкрементируя переменную 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;
}
Да, это макрос с чтением из порта :) У меня в этом примере сделано именно так потому что в этом проекте используется только одна кнопка (энкодер читается аппаратными средствами одного из таймеров) и состояние ее пина может читаться из других модулей. Когда использую несколько кнопок, то делаю инлайновую функцию, возвращающую состояние пина по порядковому номеру и провожу опрос состояний в цикле :)
Опрос и у меня привязан ко времени, так как вызывается из прерывания таймера :) В этом случае я могу гарантировать корректную обработку кнопок даже если главный цикл где-то застрял в длительной процедуре, освободится — отработает кнопку по уже готовому состоянию :)
Согласен, я в этом деле весьма грешен, комментирую по минимуму и в основном лишь бы мне самому было понятно :) Да и тут еще без нормального форматирования читать тяжело.
А что здесь комментировать? Код ведь короткий, простой и прямолинейный.
Про комментирование наоборот учат избегать лишнего комментирования в стиле
time++; // инкрементируем время
И похоже что вам именно таких комментариев не хватает.
Если вам в том коде надо ставить комменты, то вы или домашний программер или вообще к этому делу кроме как диванного эксперта отношения не имеете.
Коммент пишется для описания функции и внутри только в особосложном участке с подвыпердом. Ну и конечно если был поставлен костыль. Еще комментят изменения в авторском коде. Но это уже для последующих обновлений скажем так.
Если вам в том коде надо ставить комменты, то вы или домашний программер или вообще к этому делу кроме как диванного эксперта отношения не имеете.
Просто я существенно больше одного года этим занимаюсь, и по себе знаю, что код, написанный лет 10 назад, не всегда понятен. Особенно, если он не единственный за эти 10 лет. :)
что код, написанный лет 10 назад, не всегда понятен
Я вот совсем недавно окунался в код написанный 7 лет назад. Функция на 300 строк. В начале функции идет описание, мол делается то и то, вводные параметры такие, выдает то и то. Дальше по коду в 2-х местах еще комменты. Один объясняет на кой лад я конвертирую файл в одну code page, а потом в исходную, а во втором описание чего надо сделать в исходных данных если будет валиться определенный exception (приложение писалось для себя, по сему проще было в алерте написать что в коде есть описание, а в коде потому что кое-кто подтырил exe и пытался рубнуть бабла). И там много всяких бухгалтерских магий. Но ни чего. В описании функции ясно что и зачем. Ну и внутри через 5-7 минут все стало ясно и понятно.
А каждую строчку комментировать:
//вычитаем из облагамой суммы не облагаемую
Не. Не катит. Просто именам переменных даны осмысленные названия и все быстро и ясно становится понятно с какими данными мы сейчас оперируем.
Так что просто я же говорю код надо изначально писать красиво (а не как некоторые i:=u+k-j, тогда конечно голову сломаешь что это за перенные). Потом и вопросов не будет лишних.
А там что? Там кусок чего-то. ясный как солнечный день. И вам уже сказали что если вам и такое надо комментировать, то читать чужой код вам ни когда не стоит (а может и свой).
Когда не привык читать чужой код, то даже такой прямолинейный набор if-else может вызвать затруднения, особенно когда условия нигде не описаны :)
Но я работаю не в команде, заказчику этот код мне тоже отдавать не нужно, так что особых угрызений совести я не испытываю :)
На ardiuno.ru на сколько я помню есть «класс титановый велосипед для тактовой кнопки»
Временами его юзаю. Удобно.
У вас же есть косяк малька. Case… Дело в том что это медленная структура. Замените ее на if. И будет быстрее.
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;
}
}
}
}
}
}
А если в каждом if не по одному оператору? Так в одну строку и писать их вместе со скобками? :)
Вообще я обычно посылаю лучи ненависти за такое оформление кода, когда в одну строку стремятся впихнуть весь цикл или условие :)
в некоторых случаях оправдано — но по факту код не больше — и так
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;
}
Не поленился, скомпилировал с высокой оптимизацией два варианта:
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;
}
}
}
}
}
}
www.sql.ru/forum/1235489/chto-bystree-if-else-ili-switch
С наскока. Когда-то читал статью (было лет 10 назад), там тонны примеров и компиляторов с замерами времени. И как итог совет не увлекаться, лучше вообще не трогать свичи.
А ваш пример:
if (1<a & а<7)
i=14
else
i+=a;
Как это будет выглядить в асме?
Частный случай так сказать.
А при том что показывая плюс чего-то на одном частном примере вы забываете что есть другой пример с такой же задачей и таким же результатом, но в куда более быстрой форме. Я вам его и показал.
И то что вы расписали в кучу ифов или сейсов я уместил грубо говоря в 3 строчки кода.
А, вон откуда Ваш пример :)
Тогда вынужден Вас разочаровать — во-первых Ваш пример совсем не аналогичен по результату моему примеру, а во-вторых это как раз у Вас частный случай оптимизации конкретного примера (причем с ошибкой), в жизни очень не часто получается так оптимизировать :)
Чем же он не аналогичен?
Для
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 (0<a && а<7)
i+=14
else
{
i+=a;
i+=7;
}
Конечно данный пример справедлив только для целочисленных a. Это единственное не указанное условие.
совсем не аналогичен по результату моему примеру
первому так же аналогичен, только опять же я с «И» пролетел и a целочисленная.
как раз у Вас частный случай оптимизации конкретного примера
Просто LynXzp ниже легко добрался до момента когда компилятор превратил switch из переходов в бинарное дерево. Но опять же это тоже частый случай.
Спросил он и подставил уже переделанный вариант :)))) Вы же сами увидели чем, раз переделали практически весь алгоритм — и условие и обе операции по истинности/ложности условия, зачем спрашивать? :)
первому так же аналогичен, только опять же я с «И» пролетел и a целочисленная
Ну сравните сами сколько раз Вы пролетели в первом варианте:
if (1<a & а<7)
i=14
else
i+=a;
if (0<a && а<7)
i+=14
else
{
i+=a;
i+=7;
}
Вообще совершенно разные алгоритмы по факту :) И оба не аналогичны исходному алгоритму с ифами — перепутаны действия по истинности и ложности условия.
компилятор превратил switch из переходов в бинарное дерево. Но опять же это тоже частый случай.
Это общий случай. Второй общий случай свича — таблица переходов, работающая еще быстрее.
У меня процедура опроса внутри решает, послать ли что клавиша нажата или не послать и ждать длинного нажатия, сочетания или дребезга.
Опрашивающая функция только обрабатывает в зависимости от номера enum, и знать ничего не знает что бывают разные нажатия, или что кнопок две, а не пять, у нее 5 событий и гори все пропадом.
Единственный известный неудобный мне случай когда у программы очень сложное управление, например переход в режим калибровки или что-то такого где управление совсем иное, но и это не составляет каких-то серьезных проблем.
Если Вам известный какой-то другой случай — пожалуйста, хотелось бы услышать (без иронии).
Например, многоуровневые меню, пункты которых при выборе могут вести в другое меню, или менять какой-то параметр, или запускать выполнение какой-то процедуры, причем какой именно — зависит от некоторых условий. Тут callback функции не очень подходят :) Можно, конечно, и извратиться, например менять по необходимости адрес вызываемой из обработчика кнопок функции, или все значимые пакетов сделать глобальными, или ещё как-то :) Но это просто неудобно :)
Кто-то из нас недопонимает другого. У меня ровно так сейчас — много меню в программе (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)
Правда тут у меня получается глобальное меню, может у Вас вместо этого глобальная структура состояния кнопки, но не совсем представляю себе это возможным.
И все было бы просто, если бы пункты выполняли только переходы между меню, но в каких-то случаях активация пункта должна вызывать редактирование параметра, причем в зависимости от условий может редактироваться тот или иной параметр. Некоторые пункты должны быть сами редактируемыми (названия). То есть отработка многих пунктов зависит от текущих условий и чтобы процедура обработки кнопок знала как именно поступить в данный момент при нажатии той или иной кнопки — она должна обладать полной информацией по текущему статусу программы. А ей это совершенно не нужно, у нее своя маленькая и узкозаточенная задача :)
В принципе, обработка кнопок и у меня происходит примерно по тому же сценарию, но в главном цикле, где есть вся полнота информации :)
Ух ты, очень похоже. (У меня экран 1604 поэтому чем-то проще) Редактирование параметра у меня одна кнопка, и не важно что это за пункт — число или запретить/разрешить, функция инкремента переменной увеличивает значение, если вышло за рамки допустимого для «типа» — обнуляет. А функция вывода на экран знает как выводить ту или иную переменную, в зависимости от «типа» (числом или текстом и каким).
Я старался сделать чтобы значение кнопок не менялось, так и пользователю проще тоже, удалось такими оставить почти все: «следующее меню», «следующая позиция», "++ / изменить", «выход» и одну сделал универсальной — указатель на вызов функции «записать/применить/выполнить/Enter». Ну а остальное тоже слегка меняется. Для принятия решения что делать по той или иной кнопке у меня достаточно данных в структуре меню, но все же некоторые «особенные» меню я вынес в отдельные подпрограммы чтобы не загромождать универсальные обработчики.
Спасибо, было интересно взглянуть. Из основных различий: указателей на функции у меня только один, все значения константны (указатели, текст и неизменяемые параметры меню) и удалось все запихнуть в EEPROM (AVR).
P.S. Еще в одной небольшой программе был у меня двумерный массив указателей на обработчики.
Первый индекс — кнопка, второй — текущее состояние. И того и того было по 6, и почти все обработчики разные. Решение оказалось хорошим. Единственное что без графического комментария ничего не понятно.
Редактирование параметра у меня одна кнопка, и не важно что это за пункт
У меня вообще только один энкодер с кнопкой :)
функция инкремента переменной увеличивает значение, если вышло за рамки допустимого для «типа» — обнуляет
У меня не обнуляет, а просто перестает увеличивать или уменьшать по достижении пределов:
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 вещь редкая, к сожалению :) Все настройки и параметры так же сохраняются внутри флэши в двух последних страницах (поочередно, для увеличения ресурса). Практически все тексты у меня вынесены в отдельный файл:
У меня не обнуляет, а просто перестает увеличивать или уменьшать по достижении пределов:
Да энкодер хорошо, мне пришлось пользоваться только кнопкой сдвига курсора и ++. Кнопки псевдосенсорные, за пленкой для большей изоляции, там в цехах столько пыли что забивается все. Сделал сначала рост до максимума, останов, и обнуление. Неудобно конечно клацать. Но много разных пределов, если вышло за предел — установить максимум, если максимум и хотят больше — обнулить:
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;
}
// Ах сколько магических констант в коде. Ну ничего, проект старый... и мой самый долгоиграющий, еще не все плохие свои методики от туда удалил.
Сохранение текущих настроек
Т.к. у меня сохраняется по подтверждению, то я добавил возможность визуального просмотра некоторых измененных параметров до применения. Например коэффициент округления, пока его меняешь можно ниже наблюдать как скачут показания, выбрал нужный, нажал «применить».
Не смейтесь над именами — я в них пишу буквальную транскрипцию для максимально точного понимания что это за строка :)))
Собсно, вот как работают мои меню в текущий момент с помощью энкодера и одной кнопки:
Качество ужасное, снято на скорую руку :)
На главном экране на самом деле аж три разных меню: 1 — первые две строки, 2 — оставшиеся три строки, 3 — нижняя полоса с пиктограммами. Тип меню везде один и тот же, просто первое и второе отличаются цветом значений, а третье имеет нестандартную отрисовку. Для такого «бесшовного» соединения разных меню у меня в структуре меню есть указатели *prevmenu и *nextmenu, через них курсор переходит от одного меню к другому так, как если бы это было одно меню :)
Систему работы с шрифтами тоже писал сам. Существующие не устроили по качеству и разнообразию. Тут у меня есть возможность работать как с 1-битными шрифтами для экономии флэши, так и с теми, в которых каждый пиксель кодируется 2-битным значением прозрачности. При отрисовке смешивается с фоном в соответствии с прозрачностью и шрифты выглядят гораздо более плавными и сглаженными. Выглядит отлично, но жрет место во флеши…
Нижние пиктограммы, кстати, тоже являются символами отдельного шрифта :)
У меня шрифты сожрали много — полный большой (им пишутся все пункты и значения внутри настроек), большой цифровой (значения в пунктах на главном экране), мелкий (в бутлоадере и в некоторых служебных сообщениях основной программы), заставка (это тоже один символ в односимвольном шрифте), нижние иконки… В шрифтах ничего лишнего, каждый символ кодируется под свою ширину-высоту, лишними могут остаться не больше 7 бит. Но больше 9 КБ заняли :)
Это вот результат в дебаге без всякой оптимизации:
37 КБ флэши… Плюс еще 16 КБ в начальных адресах занимает бутлоадер. А всего у контроллера 64 КБ :)
Только сейчас дошло — можно же разместить мелкий шрифт у бутлоадера по фиксированному адресу и в основной прошивке пользоваться им :) Сэкономлю целых 2 КБ флеши :)
Иконки у меня тоже символами, потому что на 1604 никак по-другому, но неплохо выходит, даже анимация.
Я так сделал просто для удобства — процедура отрисовки текста у меня отработана (с автопереносами, выравниванием по левому-правому краям, в заданных координатных окнах, есть даже возможность тегами внутри текста менять цвет/фон/подчеркивание/зачеркивание) и не нужно возиться с отдельными изображениями каждой иконки :)
Именно она :)
По цене мой вариант будет однозначно дороже, и из-за токового датчика, и микроконтроллер другой, подороже, хоть и незначительно, и дисплей, и энкодер более дорогой на 30 имп/оборот (хотя его как раз можно безболезненно заменить на более дешевый 20 имп/оборот) :) Да и плат будет две — управление и силовая/высоковольтная, ею в последние пару недель как раз занимаюсь:
Ну как клон… Из Вашей конструкции я почерпнул единственный момент — модуль AC-DC, до этого я планировал использовать миниатюрную платку БП на 5в/700 мА :))
Все остальное на силовой плате — сборная солянка из десятков просмотренных схем, каждая из которых прогонялась раз по 10 с изменениями в симуляторе, по результатам что-то менялось, что-то выкидывалось, что-то добавлялось… Например, я перебрал штук шесть вариантов детектора нуля, пара из которых с нуля нарисована мною, а остальные четыре — из инета, но с изменениями по экспериментам в симуляторе :)
Управляющая плата вообще полностью собственная с нуля :)
Но тогда уже клоном гораздо более ранних споттеров, чем Ваш :) Уже лет 15, наверное, как их делают на симисторах и микроконтроллерах :)
Правда, подавляющее большинство обычно не вникает глубоко в теорию :)
Тот же только принцип опторазвязки, а само детектирование происходит по совсем другому принципу. На светодиоде оптопары формируется очень короткий импульс длительностью меньше половины миллисекунды, за счет этого среднее потребление несравненно меньше варианта с просто гасящим резистором и диодом :)
Дарю Вам результаты не одного дня поисков, исследований, проб — две хорошие схемы детекторов :))
В последнее время я колебался между двумя вот такими схемами:
это моя:
По параметрам — превосходная, работает в диапазоне как минимум 150-250 вольт без существенного изменения времени импульса относительно нуля, помехозащищенность хорошая, потребляет в среднем около 20 милливатт. Я ее пробовал уже и в железе, все снятые вживую осциллограммы почти не отличаются от симуляции.
это доработанная из инета:
(тут ошибка на схеме — C5 должен быть 10n, это помехозащитный конденсатор)
Помехозащищенность тоже неплохая, тоже работает как минимум от 150 до 250 вольт, но при этом от напряжения зависит ток светодиода (хоть и не очень сильно), время импульса относительно перехода через ноль во всем диапазоне тоже гуляет не больше 0.1 миллисекунды, но жрет около 180 милливатт.
Остановился на втором варианте, хоть он по параметрам и слегка уступает первому, но требует меньше деталей. А я что-то вдруг решил экономить место на плате :)))
Уже посмотрел, вон нижний зелененький график — это рассеиваемая на гасящем резисторе мощность, почти 1000 мВт :) Заодно посмотрел и на реакцию на помехи, красненьким посередине — это выход оптопары :)
(кликабельно)
а помехи там вовсе не критичны — не тот случай, времена там иные и задачи — я думал над этим — но делайте то что делаете )
погрешность в миллисекунду — никак не скажется на результате, важно попасть в заданный интервал, это если учесть особенности трансформаторов таких, если нужна точность выше то используют постоянный ток, но для большинства бытовых задач хватает и переменного, дозируя его временем в заданном интервале — поэтому фокусы с регулировками дополнительными не дают плодов
Вы уж извините, но я больше верю нормальному симулятору, чем китайскому показометру, тем более на пределах его погрешности :)
погрешность в миллисекунду — никак не скажется на результате
Погрешность в миллисекунду в одну сторону может раза в полтора увеличить мощность импульса, а в другую — отнимет до 20% от имеющегося рабочего времени :)
поэтому фокусы с регулировками дополнительными не дают плодов
Хм. Я сам еще не добирался до экспериментов с железом, но вообще в серьезной технической литературе, посвященной контактной сварке, пишут другое :)
Очень не дурно. С учетом текущего положения вашего кода выходит совсем мелочь домалевать для работы с сенсором.
И вопросец первый, ок и отмена я так понимаю пункты меню и каждый раз они описываются сами по себе? Я как-то подобное гондобил, но функциональные кнопки и диалоговые окна были описаны один раз и хранили входное значение, возвращаемое и от куда все это тело приехало и куда дальше двигаться. С одной стороны было так удобно, А потом… А потом я забыл переименовать в хлам переписанную библу по работе с экраном и обновил ее… Короче код вроде и есть, но выглядит на экране и работает все боком… Сейчас планирую все повторить, но библу экрана я уже скопировал и переименовал )))
С учетом текущего положения вашего кода выходит совсем мелочь домалевать для работы с сенсором.
Дисплей без сенсора, так что это мне не нужно. Но да, был бы сенсор — было бы очень просто перейти на работу через него, для этого все уже есть :)
ок и отмена я так понимаю пункты меню и каждый раз они описываются сами по себе?
Да, все, что выделяется курсором (кроме случая редактирования) — это стандартные пункты меню, состоящие из пары «имя + значение».
И нет, для «Сохранить» и «Отмена» во всех параметрах работают две одни и те же процедуры :)
При сохранении из отредактированного пункта меню берется новое значение и вносится в массив текущих параметров, заодно обновляются и остальные параметры, зависящие от измененного. При отмене в отредактированный пункт вписывается старое значение из структуры текущих параметров и все продолжает работать как будто ничего и не менялось :)
А что не так у автора? Все те же состояния кнопки. А дальше как по кайфу. Ибо у кнопки есть состояние нажата и нажата длинно, была нажата коротко. И у всех должно ыть состояние обработано. А вы уже по этим состояниям и гуляете.
Но не отдать нажата, когда кнопка нажата… Это называется «кнопка отпущена».
Как пример кнопка "+". Нажал, считал первый раз нажато, в обработчике сделао +1, не скинул состояние (чтобы второй раз не было +1 по нажат), считал длинное нажатие, погнал +5 и дальше как по кайфу.
Как по мне удобно и многофункционально из коробки.
Добавим сюда вообще переопределение пинов кнопки и вообще сказка. Была кнопка скажем далее, стала кнопка "+". Со своими потребностями.
И откажитесь вы уже от свича. If. Доказано что он быстрее.
Да нормально все у автора для простейшей отработки нажатий (а какую-то бОльшую цель он перед собой и не ставил, как я понимаю), просто можно немного оптимизировать :) Ну и не все состояния будут отрабатываться корректно когда, например, главный цикл где-то задержался и пользователь успел нажать и отпустить кнопку. С длительными нажатиями там тоже не все так просто, по текущему коду они работать нормально не будут :)
И откажитесь вы уже от свича. If. Доказано что он быстрее.
Не может if быть быстрее switch, в лучшем случае они будут равны по быстродействию, но обычно свич работает быстрее потому что не перебирает все условия, а идет сразу в нужное :)
Читал, давно и не раз, чего и Вам могу посоветовать :) Свич действует через таблицы переходов, условия — перебором. Если условие выполнилось в первом же if — он отработает чуть быстрее свича (за счет нескольких команд подготовки к переходу у свича), но если условие выполняется лишь во втором или далее if-ах, свич отработает быстрее, потому что ифы будут перебирать все условия, начиная с первого, а свич прыгнет сразу в нужную ветку.
Я так понял этот ответ на мое сообщение, но не совсем понятно на какое.
sir0ta: А что не так у автора? Все те же состояния кнопки.
И да и нет. Я предлагаю не выносить состояния за пределы функции обработки кнопок, только события. Нужно короткое нажатие — пожалуйста событие №1, нужно длинное — пожалуйста событие №2, нужно сочетание — пожалуйста событие №3. Но функции работы логики и знать не знают что там происходит с реальными кнопками. Для них все равно — кнопок 20 или одна с разными способами нажатия, у них 20 событий и все.
sir0ta:И откажитесь вы уже от свича. If. Доказано что он быстрее.
Впервые о таком слышу. Давно читал что switch может разворачиваться в бинарное дерево примерно так:
И если заранее знать что c вероятностью 99% A==1 то можно уменьшить количество сравнений. Но обработка кнопок выполняется даже не 1000 раз в секунду и таких знаний нет, так что не вижу способа оптимизации.
Но тут пишет
AndyBig: свич прыгнет сразу в нужную ветку.
Не представляю как, разве что прыгнет по смещению проверяемой переменной, если диапазон позволяет, а от туда выполнит код — да шикарная оптимизация.
Но я решил проверить как будет в реальности:
gcc -S -O2 $file.c -o file.o
gcc -S -Os $file.c -o file.o
Я даже не понял как switch был оптимизирован, даже отрицательные числа появились. Но я конечно поспособствовал возможности такой оптимизации — многие числа шли подряд. А if увы сильно проигрывает в скорости и в размере.
Так что спасибо, раньше я только догадывался что switch лучше, но теперь я уверен что switch гораздо лучше и по всем параметрам. Это конечно х86, но не суть, оптимизации есть и они шикарны.
Для них все равно — кнопок 20 или одна с разными способами нажатия, у них 20 событий и все.
ммм… не едут лыжи… Что значит события? Получается вы функции приписываете какое-то событие. Затем в каком-то месте типа проходим по сопоставлениям и если событие произошло то запускаем функцию? Даже не представлю как по другому.
Давно читал что switch может разворачиваться в бинарное дерево примерно так:
Тут все зависит по итогу от компилятора. Во что он это дело превратит по итогу.
Я даже не понял как switch был оптимизирован, даже отрицательные числа появились
а break ни кто не забыл в кейсах?
Не будем спорить, юзайте в удовольствие )
ммм… не едут лыжи… Что значит события? Получается вы функции приписываете какое-то событие. Затем в каком-то месте типа проходим по сопоставлениям и если событие произошло то запускаем функцию? Даже не представлю как по другому.
// псевдокод
int getkey(){
if (button1_short) return event1;
if (button2_short) return event2;
if (button2_long) return event3;
if (button1 && button2) return event4;}
или
if (pin1_short) return button1;
if (pin2_short) return button2;
if (pin2_long) return button3;
if (pin1 && pin2) return button4;
Я назвал события чтобы не путать с кодом клавиши, хотя у меня они именуются клавиши — просто номер «виртуальной клавиши» или «номер события» на которое нужно среагировать. Они не зависят вообще никак от реальной клавиатуры. Функции работы с логикой знать не знают какая реальная клавиатура и сколько нужно держать кнопку.
а break ни кто не забыл в кейсах?
Точно, из-за отсутствия break программа выродилась и соотв. оказалась короткой. Исправил ситуацию, но это никак не помогло if-ам (справа):
Здесь ярко видно то что говорил AndyBig — плюс ему в карму.
Но я решил все-таки проверить и более сложный случай для switch (диапазон значений переменной изменил):
И вот оно наше бинарное дерево! Еху-ху! Что подтверждает мою догадку об возможностях и ограничениях оптимизации swtich.
Не будем спорить, юзайте в удовольствие )
Не… я люблю объективную истину познать насколько это возможно. Объективная истина в шкриншотах. Мой субъективный об этом вывод: это как С против Ассемблера — если писать просто то C и switch быстрее, если упарываться и разворачивать бинарные деревья вручную с учетом что программист знает такое что компилятор знать не может, то if и assembler быстрее (при условии что не ошибешься).
Все верно, при большом разрыве проверяемых значений свич строит сравнение по бинарному дереву (и это в большинстве случаев оказывается быстрее простого перебора if-ами), при последовательных проверяемых значениях (или небольших разрывах между ними) — строит таблицу смещений на обработчики кейсов, загружает адрес этой таблицы, прибавляет к нему проверяемое значение и прыгает по полученному адресу :) А в случаях перебора состояний из енумов (статусы кнопок, state-машины и т.п.) как раз и перебираются последовательные значения :) sir0ta как раз на такой случай и покатил бочку :)
« Наши отношения с другими народами налаживаются и будут налаживаться! Отставить. Ложить неправильно – правильно класть. Накладываются и будут накладываться! »
Генерал Бурдун. «День выборов»
:)
без временных задержек с защитой от дребезга :)
Веселее будет когда потребуется отработка одновременных нажатий. Это не когда зажали шифт и получили заглавную букву, а когда кнопка А это больше, Б это меньше и вместе они энтер. Длинное же нажатие двух эскейп, а удержание по одной автоповтор в нужную сторону.
Вариантов решения — куча, все правильные по выхлопу, но с разной реализацией и нюансами.
Так что нормально у вас всё, не принмайте стёб близко к сердцу.
а одновременное нажатие вот при таком коде несложно обработать, а портянка она и не короче будет )
И да, пробовал на энкодере.
а 100к — многовато :)
ну и… задача была написать а не нарисовать )
чтобы не быть простодругом — надо быть проще
По существу: 3 кнопки и 2 диода = 7 проводов и 5 вводов/выводов. Есть возможность подумать надо сокращением портов/проводов.
А в целом — всё очень наглядно и понятно, Вам +
Кнопки на АЦП, светодиоды встречно параллельно на два порта контроллера, но так как есть общий провод, то последнее лишнее и ничего не экономит, только усложняет.
Вот только с одновременным нажатием могут быть сложности :)
связанных с занятием аналоговых пинов — а есть например только цифровые…
и светодиоды можно зажигать одновременно и кнопки нажимать )
а еще светодиодами мигать можно одновременно — а можно с разной частотой
очень в узких ситуациях это можно делать — обычно это делают в условиях крайней нехватки пинов…
я же сделал универсальную платку — которой удобно отладить простые вещи,
добавте еще разные ацп и питание, будет уж вовсе не универсально…
Но это для готовых устройств, а для прототипирования нормально
Только при таких затратах изготовления делать уж сразу 4 входа/4 выхода. Не меньше
И подключать те которые используются
У аналоговых кнопок нет одновременного нажатия
Плюс затруднительна обработка по прерываниям ( и такое бывает)
Плюс геммор с настройками констант, если я хочу другие порты аналоговые использовать например с другим источником опорного
Ну и элементарно кода больше, так что аналоговые кнопки нужны при сильной экономии портов ввода/вывода. Хотя и в этом случае есть универсальное решение — расширитель портов с I2C
* if (BT[i].changed && BT[i].state == false) — подобные вещи, извиняюсь, коробят…
if (!(a && b)) { } или if (a && b); else { }
Хотя могу предложить этим индусам вариант определения максимального из двух чисел:
надо сокращением тут думать не нужно — с точки зрения отладки — а основная цель именно такая- гораздо удобнее
спасибо
Красиво писать надо везде. И в работе, и в поделке выходного дня. Это правило хорошего тона. А такие экономы потом в дедлайн такую пургу пишут и софт с аптаймом в неделю жрет 500 метров оперативы. А потом ты убиваешь неделю на хоть какую-то оптимизацию и дальше 200 метров софтина и через месяц не набирает.
в смысле среды исполнения.
Когда приходится скакать с одного проекта на другой… Причём, разные проекты реализованы на разных микроконтроллерах — 8-ми разрядных (AVR), 16-ти разрядных (MSP430), 32-разрядных (STM32). Заказчики разные, требования у всех разные. Ещё хуже, когда в одном могучем супер-проекте используется разное оборудование на МК разной разрядности, и между этим оборудованием нужно перегонять массивы данных. Вот тут-то по неволе будешь использовать универсальные обозначения типов. Благо, компилятор GCC примерно одинаковый для всех МК и в том числе для компов.
Так что я считаю, что использование обозначений типов типа (простите за каламбур!) uint8_t — скорее благо, чем зло.
И ещё, что бы не было никому обидно. Когда я использую «общие» данные (то есть такие данные, которые создаются на одном типе МК, передаются по линиям связи с помощью других МК, а обрабатываются, скажем, на ПК), то я прибегаю к обозначениям типа, у которых в названии участвует их размер. С другой стороны, когда мне, допустим, нужно обработать какой-нибудь массив, к элементам которого я получаю доступ по индексу, то для типа переменной для индекса я использую стандартное int. В этом случае сам компилятор выбирает «родной» для вычислителя тип. (Разумеется, бывают исключения!)
Во всяком случае, у меня нет никаких проблем с выбором типов. Всё однозначно и всё понятно.
К стати! Размер байта не всегда 8 бит! (Просто погуглите ради интереса!) Я такой старый, что я помню времена, когда байт был равен 5 битам (у меня до сих пор в рабочей тетрадке вклеен тетрадный листочек с кодировкой символов этими 5 битами. Ужас! Где мы были тогда, и где сейчас находимся!). Потом как-то быстро проскочили времена 6-битных байтов, и настала пора 7-битных. А потом пошли персональные компы разных мастей и байт окончательно оформился в умах новоявленных программистов как только 8-рязрадная величина. Но это уж издержки возрастного ценза.
В общем, не судите строго!
Вы абсолютно правы в своих размышлениях, но при этом пишете так, как будто не совсем уверены в своей правоте :)
всё рядом и всё правда!
Это в итоге влияет на переносимость кода, когда «суть» процесса отлаживается к примеру вне Ардуино или наоборот будучи отлаженой на Ардуино уезжает в более взрослую среду. Да и не только Ардуино это касается.
Те, кто работает в Линуксе, знают, что по умолчанию там кодировка 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, и просто работаете как обычно. Со временем, вы можете даже забыть какой файл у вас в какой кодировке. Всё будет открываться и компилироваться абсолютно одинаково! Вот за это я уважаю Линукс! Уважаю даже больше, чем Виндовс. В Линуксе нет сложностей, но возможностей в нём значительно больше, чем в Виндовсе. Те, кто на площадях плачется о том, что Линукс корявый, его просто не знают!
Вот только я ведь говорил не про то, что чего-то может не заработать, если не правильно сделать. Я акцентировал внимание на том, что описанные выше проблемы принципиально решаются. Причем решаются весьма элегантно и далее (по ходу работ над проектом) уже более не отвлекают разработчика.
С другой стороны, и с IDE, поддерживающими разные кодировки, могут возникать проблемы, когда, к примеру, на дисплей нужно вывести текст в одной кодировке, а в UART/USB отправить этот же текст в другой кодировке :)
Не, ну если вы конечно имели в виду что вместо uint8_t нужно писать uint_fast8_t или uint_least8_t, то прошу прощения. </>
Только-только начинаю и просто копипаст не интересно, потому и благодарю. Ибо не все тут «родились с ардуинами в руках» ( в одной уно/ в другой нано) и конечно приходится постигать.
А в целом, спасибо, ибо выпаивать кнопки из старых проектов, не самое полезное занятие (это я про себя). И лучше иметь запас по видам и типам.
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 события и после обработки тоже ставится признак обработанной. В длинном нажатии еще и ведется подсчет условного времени нажатия в тиках опроса кнопок :)
Но сама процедура опроса, конечно, занимает больше кода и вызывается таймером :)
ЗЫ: если выкинуть из структуры номер кнопки, то это сэкономит память, а отразится только на инициализации, в которой это, в общем-то, и не нужно :)
понятно, что в байт можно много пакануть — но наглядности коду это явно не прибавит )невнимательно посмотрел, тут нет упаковки — можно и так сделать — время перехода из состояния в состояние оставлять — но тут в примере их два всего
А зачем оставлять время перехода в другое состояние? Это не нужно, просто текущее состояние на данный момент — нажата, отпущена, долго нажата, отпущена после долгого нажатия, обработана :)
так чтобы понять что долго нажата, нужно момент первой смены состояния запомнить, либо задержками — что не есть гуд
Наверное, проще будет дать процесс опроса кнопки :)
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;
}
немного иная логика, мне проще привязаться ко времени чем к количеству опросов, так я могу регулировать процессы в более широких диапазонах.
KEY1_READ() — это макрос у вас? в котором захардкожен номер пина который читается? :) может лучше его хранить в массиве?
Опрос и у меня привязан ко времени, так как вызывается из прерывания таймера :) В этом случае я могу гарантировать корректную обработку кнопок даже если главный цикл где-то застрял в длительной процедуре, освободится — отработает кнопку по уже готовому состоянию :)
Про комментирование наоборот учат избегать лишнего комментирования в стиле
time++; // инкрементируем время
И похоже что вам именно таких комментариев не хватает.
Коммент пишется для описания функции и внутри только в особосложном участке с подвыпердом. Ну и конечно если был поставлен костыль. Еще комментят изменения в авторском коде. Но это уже для последующих обновлений скажем так.
А каждую строчку комментировать:
//вычитаем из облагамой суммы не облагаемую
Не. Не катит. Просто именам переменных даны осмысленные названия и все быстро и ясно становится понятно с какими данными мы сейчас оперируем.
Так что просто я же говорю код надо изначально писать красиво (а не как некоторые i:=u+k-j, тогда конечно голову сломаешь что это за перенные). Потом и вопросов не будет лишних.
Но я работаю не в команде, заказчику этот код мне тоже отдавать не нужно, так что особых угрызений совести я не испытываю :)
Просто оппонент, кажется, еще штанишки с подтяжками не сменил… ;)
Временами его юзаю. Удобно.
У вас же есть косяк малька. Case… Дело в том что это медленная структура. Замените ее на if. И будет быстрее.
Не говоря уже о читаемости кода:
или
Вообще я обычно посылаю лучи ненависти за такое оформление кода, когда в одну строку стремятся впихнуть весь цикл или условие :)
и
Результат в ассемблере для switch:
Для if:
Мой склероз меня не обманул — switch переводится в аналог goto и сразу прыгает в нужную ветку, а if в каждой ветке производит сравнение с условием :)
С наскока. Когда-то читал статью (было лет 10 назад), там тонны примеров и компиляторов с замерами времени. И как итог совет не увлекаться, лучше вообще не трогать свичи.
А ваш пример:
if (1<a & а<7)
i=14
else
i+=a;
Как это будет выглядить в асме?
Частный случай так сказать.
И то что вы расписали в кучу ифов или сейсов я уместил грубо говоря в 3 строчки кода.
Тогда вынужден Вас разочаровать — во-первых Ваш пример совсем не аналогичен по результату моему примеру, а во-вторых это как раз у Вас частный случай оптимизации конкретного примера (причем с ошибкой), в жизни очень не часто получается так оптимизировать :)
Для
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 (0<a && а<7)
i+=14
else
{
i+=a;
i+=7;
}
Конечно данный пример справедлив только для целочисленных a. Это единственное не указанное условие.
первому так же аналогичен, только опять же я с «И» пролетел и a целочисленная.
Просто LynXzp ниже легко добрался до момента когда компилятор превратил switch из переходов в бинарное дерево. Но опять же это тоже частый случай.
Ну сравните сами сколько раз Вы пролетели в первом варианте:
Вообще совершенно разные алгоритмы по факту :) И оба не аналогичны исходному алгоритму с ифами — перепутаны действия по истинности и ложности условия.
Это общий случай. Второй общий случай свича — таблица переходов, работающая еще быстрее.
Опрашивающая функция только обрабатывает в зависимости от
номераenum, и знать ничего не знает что бывают разные нажатия, или что кнопок две, а не пять, у нее 5 событий и гори все пропадом.Если Вам известный какой-то другой случай — пожалуйста, хотелось бы услышать (без иронии).
Не callback функции, а еще проще — возврат только номера кнопки или сочетания. Обработчик прост:
Единственное что в специальных меню (калибровки) приходиться писать свой switch(key)
Правда тут у меня получается глобальное меню, может у Вас вместо этого глобальная структура состояния кнопки, но не совсем представляю себе это возможным.
Структура пункта меню:
И все было бы просто, если бы пункты выполняли только переходы между меню, но в каких-то случаях активация пункта должна вызывать редактирование параметра, причем в зависимости от условий может редактироваться тот или иной параметр. Некоторые пункты должны быть сами редактируемыми (названия). То есть отработка многих пунктов зависит от текущих условий и чтобы процедура обработки кнопок знала как именно поступить в данный момент при нажатии той или иной кнопки — она должна обладать полной информацией по текущему статусу программы. А ей это совершенно не нужно, у нее своя маленькая и узкозаточенная задача :)
В принципе, обработка кнопок и у меня происходит примерно по тому же сценарию, но в главном цикле, где есть вся полнота информации :)
Я старался сделать чтобы значение кнопок не менялось, так и пользователю проще тоже, удалось такими оставить почти все: «следующее меню», «следующая позиция», "++ / изменить", «выход» и одну сделал универсальной — указатель на вызов функции «записать/применить/выполнить/Enter». Ну а остальное тоже слегка меняется. Для принятия решения что делать по той или иной кнопке у меня достаточно данных в структуре меню, но все же некоторые «особенные» меню я вынес в отдельные подпрограммы чтобы не загромождать универсальные обработчики.
Спасибо, было интересно взглянуть. Из основных различий: указателей на функции у меня только один, все значения константны (указатели, текст и неизменяемые параметры меню) и удалось все запихнуть в EEPROM (AVR).
P.S. Еще в одной небольшой программе был у меня двумерный массив указателей на обработчики.
Первый индекс — кнопка, второй — текущее состояние. И того и того было по 6, и почти все обработчики разные. Решение оказалось хорошим. Единственное что без графического комментария ничего не понятно.
У меня не обнуляет, а просто перестает увеличивать или уменьшать по достижении пределов:
(encspd — скорость вращения энкодера, чем быстрее крутишь тем сильнее меняется значение).
У меня всегда выводится текст, так что если значение числовое, то функции, изменяющие его, заботятся о переводе его в текст. Ну и во многих местах выводится не одно значение, а два-три через разделители, а кое-где меняются и сами названия пунктов, причем меняются самим пользователем :)
Сохранение текущих настроек у меня происходит либо принудительно пользователем через меню в один из 12 (кажется) пресетов, либо автоматом по истечении 30 секунд после последних изменений — эти настройки сохраняются не в пресеты, а в дефолтный набор, который потом загружается при включении. Выход из настроек — или соответствующим пунктом меню или длительным нажатием кнопки, при этом все изменения отменяются.
Аналогично :) У меня есть стандартные функции для навигации по меню любого уровня вложенности или горизонтального расширения и отрисовки, в принципе сейчас добавить новое полностью рабочее меню — дело 5 минут, которые уйдут на задание координат пунктам :) Но некоторые меню и некоторые пункты отрисовываются особым образом, для них я в указатель drawfunc подставляю отдельные функции.
У меня все константы запихнуты в code memory, EPROM в ARM вещь редкая, к сожалению :) Все настройки и параметры так же сохраняются внутри флэши в двух последних страницах (поочередно, для увеличения ресурса). Практически все тексты у меня вынесены в отдельный файл:
Не смейтесь над именами — я в них пишу буквальную транскрипцию для максимально точного понимания что это за строка :)))
Т.к. у меня сохраняется по подтверждению, то я добавил возможность визуального просмотра некоторых измененных параметров до применения. Например коэффициент округления, пока его меняешь можно ниже наблюдать как скачут показания, выбрал нужный, нажал «применить».
Хорошая идея. У меня константы такие:
Качество ужасное, снято на скорую руку :)
На главном экране на самом деле аж три разных меню: 1 — первые две строки, 2 — оставшиеся три строки, 3 — нижняя полоса с пиктограммами. Тип меню везде один и тот же, просто первое и второе отличаются цветом значений, а третье имеет нестандартную отрисовку. Для такого «бесшовного» соединения разных меню у меня в структуре меню есть указатели *prevmenu и *nextmenu, через них курсор переходит от одного меню к другому так, как если бы это было одно меню :)
Систему работы с шрифтами тоже писал сам. Существующие не устроили по качеству и разнообразию. Тут у меня есть возможность работать как с 1-битными шрифтами для экономии флэши, так и с теми, в которых каждый пиксель кодируется 2-битным значением прозрачности. При отрисовке смешивается с фоном в соответствии с прозрачностью и шрифты выглядят гораздо более плавными и сглаженными. Выглядит отлично, но жрет место во флеши…
Нижние пиктограммы, кстати, тоже являются символами отдельного шрифта :)
Иконки у меня тоже символами, потому что на 1604 никак по-другому, но неплохо выходит, даже анимация.
С таким LCD тоже делал, на AVR, память конечно выбирал много, если не оптимизировать изображения.
Это вот результат в дебаге без всякой оптимизации:
37 КБ флэши… Плюс еще 16 КБ в начальных адресах занимает бутлоадер. А всего у контроллера 64 КБ :)
Только сейчас дошло — можно же разместить мелкий шрифт у бутлоадера по фиксированному адресу и в основной прошивке пользоваться им :) Сэкономлю целых 2 КБ флеши :)
Я так сделал просто для удобства — процедура отрисовки текста у меня отработана (с автопереносами, выравниванием по левому-правому краям, в заданных координатных окнах, есть даже возможность тегами внутри текста менять цвет/фон/подчеркивание/зачеркивание) и не нужно возиться с отдельными изображениями каждой иконки :)
главное — цена )
По цене мой вариант будет однозначно дороже, и из-за токового датчика, и микроконтроллер другой, подороже, хоть и незначительно, и дисплей, и энкодер более дорогой на 30 имп/оборот (хотя его как раз можно безболезненно заменить на более дешевый 20 имп/оборот) :) Да и плат будет две — управление и силовая/высоковольтная, ею в последние пару недель как раз занимаюсь:
Все остальное на силовой плате — сборная солянка из десятков просмотренных схем, каждая из которых прогонялась раз по 10 с изменениями в симуляторе, по результатам что-то менялось, что-то выкидывалось, что-то добавлялось… Например, я перебрал штук шесть вариантов детектора нуля, пара из которых с нуля нарисована мною, а остальные четыре — из инета, но с изменениями по экспериментам в симуляторе :)
Управляющая плата вообще полностью собственная с нуля :)
Правда, подавляющее большинство обычно не вникает глубоко в теорию :)
Дарю Вам результаты не одного дня поисков, исследований, проб — две хорошие схемы детекторов :))
В последнее время я колебался между двумя вот такими схемами:
это моя:
По параметрам — превосходная, работает в диапазоне как минимум 150-250 вольт без существенного изменения времени импульса относительно нуля, помехозащищенность хорошая, потребляет в среднем около 20 милливатт. Я ее пробовал уже и в железе, все снятые вживую осциллограммы почти не отличаются от симуляции.
это доработанная из инета:
(тут ошибка на схеме — C5 должен быть 10n, это помехозащитный конденсатор)
Помехозащищенность тоже неплохая, тоже работает как минимум от 150 до 250 вольт, но при этом от напряжения зависит ток светодиода (хоть и не очень сильно), время импульса относительно перехода через ноль во всем диапазоне тоже гуляет не больше 0.1 миллисекунды, но жрет около 180 милливатт.
Остановился на втором варианте, хоть он по параметрам и слегка уступает первому, но требует меньше деталей. А я что-то вдруг решил экономить место на плате :)))
Но дело Ваше, конечно :)
вот тут народ в выключатели резисторы со светодиодами ставит и советует на 2 Ватта лучше — и ничего
(кликабельно)
а помехи там вовсе не критичны — не тот случай, времена там иные и задачи — я думал над этим — но делайте то что делаете )
погрешность в миллисекунду — никак не скажется на результате, важно попасть в заданный интервал, это если учесть особенности трансформаторов таких, если нужна точность выше то используют постоянный ток, но для большинства бытовых задач хватает и переменного, дозируя его временем в заданном интервале — поэтому фокусы с регулировками дополнительными не дают плодов
Погрешность в миллисекунду в одну сторону может раза в полтора увеличить мощность импульса, а в другую — отнимет до 20% от имеющегося рабочего времени :)
Хм. Я сам еще не добирался до экспериментов с железом, но вообще в серьезной технической литературе, посвященной контактной сварке, пишут другое :)
при переменном токе и пропускаемой одной полуволне — у вас на графике ровная прямая )
думаю тут лучше смотреть реальную картину а не усредненные значения ввиртуального ваттметра RMS
Rms по расчетам менее 0.5 вт
И вопросец первый, ок и отмена я так понимаю пункты меню и каждый раз они описываются сами по себе? Я как-то подобное гондобил, но функциональные кнопки и диалоговые окна были описаны один раз и хранили входное значение, возвращаемое и от куда все это тело приехало и куда дальше двигаться. С одной стороны было так удобно, А потом… А потом я забыл переименовать в хлам переписанную библу по работе с экраном и обновил ее… Короче код вроде и есть, но выглядит на экране и работает все боком… Сейчас планирую все повторить, но библу экрана я уже скопировал и переименовал )))
Да, все, что выделяется курсором (кроме случая редактирования) — это стандартные пункты меню, состоящие из пары «имя + значение».
И нет, для «Сохранить» и «Отмена» во всех параметрах работают две одни и те же процедуры :)
При сохранении из отредактированного пункта меню берется новое значение и вносится в массив текущих параметров, заодно обновляются и остальные параметры, зависящие от измененного. При отмене в отредактированный пункт вписывается старое значение из структуры текущих параметров и все продолжает работать как будто ничего и не менялось :)
Но не отдать нажата, когда кнопка нажата… Это называется «кнопка отпущена».
Как пример кнопка "+". Нажал, считал первый раз нажато, в обработчике сделао +1, не скинул состояние (чтобы второй раз не было +1 по нажат), считал длинное нажатие, погнал +5 и дальше как по кайфу.
Как по мне удобно и многофункционально из коробки.
Добавим сюда вообще переопределение пинов кнопки и вообще сказка. Была кнопка скажем далее, стала кнопка "+". Со своими потребностями.
И откажитесь вы уже от свича. If. Доказано что он быстрее.
Не может if быть быстрее switch, в лучшем случае они будут равны по быстродействию, но обычно свич работает быстрее потому что не перебирает все условия, а идет сразу в нужное :)
По свичам почитайте практику.
И да и нет. Я предлагаю не выносить состояния за пределы функции обработки кнопок, только события. Нужно короткое нажатие — пожалуйста событие №1, нужно длинное — пожалуйста событие №2, нужно сочетание — пожалуйста событие №3. Но функции работы логики и знать не знают что там происходит с реальными кнопками. Для них все равно — кнопок 20 или одна с разными способами нажатия, у них 20 событий и все.
Впервые о таком слышу. Давно читал что switch может разворачиваться в бинарное дерево примерно так:
И если заранее знать что c вероятностью 99% A==1 то можно уменьшить количество сравнений. Но обработка кнопок выполняется даже не 1000 раз в секунду и таких знаний нет, так что не вижу способа оптимизации.
Но тут пишет Не представляю как, разве что прыгнет по смещению проверяемой переменной, если диапазон позволяет, а от туда выполнит код — да шикарная оптимизация.
Но я решил проверить как будет в реальности:
gcc -S -O2 $file.c -o file.o
gcc -S -Os $file.c -o file.o
Я даже не понял как switch был оптимизирован, даже отрицательные числа появились. Но я конечно поспособствовал возможности такой оптимизации — многие числа шли подряд. А if увы сильно проигрывает в скорости и в размере.
Так что спасибо, раньше я только догадывался что switch лучше, но теперь я уверен что switch гораздо лучше и по всем параметрам. Это конечно х86, но не суть, оптимизации есть и они шикарны.
Тут все зависит по итогу от компилятора. Во что он это дело превратит по итогу.
а break ни кто не забыл в кейсах?
Не будем спорить, юзайте в удовольствие )
Я назвал события чтобы не путать с кодом клавиши, хотя у меня они именуются клавиши — просто номер «виртуальной клавиши» или «номер события» на которое нужно среагировать. Они не зависят вообще никак от реальной клавиатуры. Функции работы с логикой знать не знают какая реальная клавиатура и сколько нужно держать кнопку.
Точно, из-за отсутствия break программа выродилась и соотв. оказалась короткой. Исправил ситуацию, но это никак не помогло if-ам (справа):
Здесь ярко видно то что говорил AndyBig — плюс ему в карму.
Но я решил все-таки проверить и более сложный случай для switch (диапазон значений переменной изменил):
И вот оно наше бинарное дерево! Еху-ху! Что подтверждает мою догадку об возможностях и ограничениях оптимизации swtich.
Не… я люблю объективную истину познать насколько это возможно. Объективная истина в шкриншотах. Мой субъективный об этом вывод: это как С против Ассемблера — если писать просто то C и switch быстрее, если упарываться и разворачивать бинарные деревья вручную с учетом что программист знает такое что компилятор знать не может, то if и assembler быстрее (при условии что не ошибешься).
по полу ходят — как и все )
Генерал Бурдун. «День выборов»
:)