Обсуждаем контроллеры компании Atmel.
Ответить

Re: Кнопки и кнопочный интерфейс

Сб янв 13, 2018 06:44:41

это был мой первый относительно крупный проект на С (если можно назвать крупным проект на тини :) ) люстра с ИК управлением.
я делал на захвате таймера 1, там есть аппаратный фильтр — здорово помогает от "иголок" - тонких выбросов с датчика иногда появляющихся от ЭМ наводок и при резком изменении освещенности. А т.к. код передаётся 1раз - то даже одна помеха и код не совпадёт, а команда не будет выполнена.
плюс контроллер может быть занят, сигнал в этом случае всёравно будет принят.
в других условиях я бы может сделал по другому, но, прерывания заняты (одно следит за сетью для включения симисторов, другое следит за сигналом энкодера ручной регулировки), энергии мало и МК восновном спит.
плюс для защиты от помех сужены диапазоны сигналов старта, 0 и 1, повтор реализован по наличию сигнала в течении определенного времени (ограничено прерыванием по совпадению Т1.)

Добавлено after 17 minutes 32 seconds:
потом был добавлен антишум энкодера (его провод (положение определяется по сопротивлению 0, 4кОм, бесконечность) пролегает рядом с питанием 220в, что тоже не айс, оказалось там помех валом... пришлось контроллер будить (чтобы отсеивать помехи и до и после прерывания), но для экономии снизил ему тактовую до 62кГц (потом правда до 125кГц поднял, а то аппаратный фильтр Т1 оказался груб на такой низкой частоте).

Добавлено after 15 minutes 8 seconds:
Т0 следит за временем воздействия на симисторы - ток им нужен немалый, а питания кот наплакал - 1мкФ балластный конденсатор от сети (на контроллер и 5 симисторов). ещё выяснилось, что для разных ламп открывать симистор надо в разный период (накаливания сразу при переходе 0, а те, что содержат выпрямитель и конденсатор внутри — немного позже т.к. ток в них появляется не сразу) сделал окно пошире и проблема ушла (потому и кондей 1мкФ, а не 0,4, как было изначально)

Добавлено after 8 hours 55 minutes:
вот ещё попалось в соседней теме про программные задержки:
Имхо, если в программе есть прерывания, то все эти суперточные задержки превращаются в тыкву. А прерывания есть в любой мало-мальски сложной программе.

Re: Кнопки и кнопочный интерфейс

Вс янв 14, 2018 02:28:00

Здравствуйте! Недавно начал осваивать программирование AVR на ассемблере, сейчас пока что не могу разобраться с корректным опросом кнопок. Т.е. непонятен именно сам алгоритм, а не реализация в виде программы. С дребезгом разобрался, опрашивая кнопку 5 раз в прерывании по таймеру с периодом 10 мс. Если все 5 раз кнопка была нажата, то устанавливаю "флаг" в регистре общего назначения и в основной программе при наличии этого флага выполняю нужное действие, после чего флаг сбрасываю. Но проблема в том, что за время нажатия кнопки (пусть будет 0,5 с) она будет опрошена очень много раз. Например, мне надо при каждом нажатии инкрементировать некую переменную. Получается, что за одно короткое нажатие эта переменная инкрементируется сразу на несколько десятков :shock: Не могу придумать, как сделать так, чтобы за одно нажатие кнопки нужное действие выполнялось только ОДИН раз, даже если кнопка нажата несколько секунд?

Re: Кнопки и кнопочный интерфейс

Вс янв 14, 2018 06:17:38

Как вариант, устанавливайте флаг о нажатой кнопке в момент ее отжатия. Момент отжатия на дребезг не нужно проверять, так как это момент проверяется уже после подавления дребезга нажатия.

Re: Кнопки и кнопочный интерфейс

Вс янв 14, 2018 06:33:42

...устанавливаю "флаг" в регистре общего назначения и в основной программе при наличии этого флага выполняю нужное действие, после чего флаг сбрасываю...
Как вариант, сбрасывайте флаг когда кнопка отпущена, т.е. флаг взведён - действие выполнено - кнопка ещё нажата - проходим мимо, иначе сначала. Выполнение действия потребует еще одного флага, но зато позволит расширить функционал. Например, автоповтор с ускорением.

Re: Кнопки и кнопочный интерфейс

Вс янв 14, 2018 07:22:57

инкремент счетчика (пусть i это её счетчик) вместо i++; напиши:
i+=(~i)? 1:0;
или
i+=(0xFF==i)? 0:1;
или
if (0xFF!=i) i++;
это всё ограничение счета (чтобы не сбрасывался при переполнении) выбирай какой больше нравится - действие абсолютно идентичное.
сбрасывай счетчик только когда кнопка не нажата (а инкремент можно даже и без условий - будет топтаться 0,1,0,1...), условие выполнения сделай просто равенство (не >=) тогда при нажатой кнопке счет пойдёт и дальше после сработки кнопки, но команда уде не будет повторяться.
ПС: на первой странице я словами расписывал алгоритм опроса кнопки, только там с различением короткого и длинного нажатия.

Добавлено after 12 minutes 23 seconds:
Код:
{// этот блок вызывается с частотой хх герц и опрашиваетикнопку
if (0xFF!=i) i++;
if (кнопка не нажата) i=0;
if (задержка антидребезга==i) действие;
}
както так (действие - установка флага, а там уже в коде его смотришь и действуешь.

Re: Кнопки и кнопочный интерфейс

Пн янв 15, 2018 00:23:01

что-то вы тут совсем ушли в дебри с кнопками)) я делаю по стандартному алгоритму)) :dont_know:
1-Опрашиваю кнопку в цикле. Ждём в цикле лог 0. Если лог 0, то факт "нажатия" кнопки уже зфиксирован. Смысл её дальше опрашивать ?
2-Далее включаем таймер... Пока работает таймер, кнопка не опрашивается (задержка на время переходных процессов).
3-По истечении таймера опрос кнопки возобновляется, только теперь опрашиваем кнопку на "отпускание" - ждём в цикле лог 1.
4-Как только получили лог 1, то факт "отпускания" кнопки уже зфиксирован. Смысл её дальше опрашивать ? ))
5-Далее опять включаем таймер... Пока работает таймер, кнопка не опрашивается (опять задержка на время переходных процессов).
6-По истечении таймера опрос кнопки опять возобновляется. Переходим к пункту 1.

Всего две задержки по таймеру: при "нажатии" и при "отпускании". Впринципе работает нормально. Задержка по таймеру... ~0,1 обычно достаточна.))

Ну иожет есть и более крутые алгоритмы)) Можем ещё конечно оцифровать сигнал с кнопки... и провести спектральный анализ.. )) :)))

Re: Кнопки и кнопочный интерфейс

Пн янв 15, 2018 07:24:12

roman.com, это примерно так?
Спойлер
Код:
#define button PINB.3
.................................
void butt (void)//процедура опроса кнопки
{
while (0==button) {};//кнопка нажата
действия при нажатии кнопки;
delay_ms(100); 
while (1==button) {};//кнопка отпущена
действия при отпускании кнопки;
}

Re: Кнопки и кнопочный интерфейс

Пн янв 15, 2018 13:48:21

Недавно начал осваивать программирование AVR на ассемблере, сейчас пока что не могу разобраться с корректным опросом кнопок.

Опишу ещё вариант опроса кнопок:
Для каждой кнопки имеем переменную (допустим «Butt_x»)… так же имеем временную переменную «temp» и константу количественного опроса для антидребезга «Dre_N» (значение которой определяем в процессе отладки, но не менее 2).
Опрос пинов кнопок производится в прерывании по переполнению таймер-счётчика…
Алгоритм макроса следующий:
1) Значение переменной проверяемой кнопки (допустим «Butt_1») копируется в переменную «temp».
2) Производится проверка состояния пина проверяемой кнопки:
Нет) Если на входе «1» значит кнопка не нажата – переходим к пункту 8.
Да) Если на входе «0» значит кнопка нажата – переходим к пункту 3.
3) Значение переменной «temp» равно значению константы «Dre_N»:
Нет) Если значение «temp» не равно значению «Dre_N» то к значению переменной «temp» прибавляем один (+1) и переходим к пункту 4.
Да) Если значение «temp» равно значению «Dre_N» то переходим к пункту 12.
4) Значение переменной «temp» неравно значению константы «Dre_N» :
Да) Если значение «temp» равно значению «Dre_N» то переходим к пункту 5.
Нет) Если значение «temp» не равно значению «Dre_N» то переходим к пункту 12.
5) Значение переменной «temp» копируем в переменную опрашиваемой кнопки («Butt_1»).
6) Проверяем значение переменных кнопок («Butt_x»), если какое либо значение «Butt_x» равно «Dre_N», то поднимаем флаг соответствующей кнопки.
7) Переходим к пункту 13
8.) Проверяем значение переменной «temp»:
Да) Если значение переменной равно «0», то переходим к пункту 12.
Нет) Если значение переменной не равно «0», то переходим к пункту 9.
9) Значение переменной «temp» неравно значению константы «Dre_N» :
Да) Если значение «temp» не равно значению «Dre_N» то переходим к пункту 11.
Нет) Если значение «temp» равно значению «Dre_N» то переходим к пункту 10.
10) Сбрасываем флаги всех кнопок (кнопки не нажаты).
11) Очищаем (обнуляем) переменную «temp».
12) Значение переменной «temp» копируем в переменную проверяемой кнопки (допустим «Butt_х») и выходим из макроса для проверки следующей кнопки.
13) Выходим из прерывания.

Надеюсь понятно описал… если нет, то могу и блок-схему нарисовать. Есть рабочий код на Algorithm Builder, но так как его на этом форуме понимают единицы, то приходится описывать словами.

ЗЫ, По просьбам: А вот то, что я примерно описывал (код на Algorithm Builder от автора GetChiper) http://www.forum.getchip.net/viewtopic.php?f=18&t=572

Re: Кнопки и кнопочный интерфейс

Пн янв 15, 2018 16:44:35

Вот еще один метод, достаточно нескучный.
Анализируя состояние переменной можно сразу сказать, нажата или отпущена кнопка. Плюс при небольшой доработке можно достаточно просто сделать анализ длинных нажатий.
Минус конечно это двухбайтовый размер переменной-аккумулятора, которых нужно по числу кнопок (если кнопок больше одной, лучше наверное массив сделать).

Спойлер
Код:
/* Key Accumulator */
static uint16_t KeyAcc = 0; // Глобальная переменная для хранения последовательности состояний кнопки (аккумулятор)

/* Key State */
static uint8_t Key = 0; // Глобальная переменная состояния кнопки (после подавления дребезга)

/*
* Функция опроса состояния кнопки
*/
void ReadKey() {
   KeyAcc = KeyAcc << 1; // Сдвигаем аккумулятор влево на 1 бит
   KeyAcc |= !(KEYPORT & KEYPIN); // читаем состояние ножки KEYPIN на порту KEYPORT и добавляем новое значение к аккумулятору
   if(KeyAcc == 0xFFFF) Key = 1; // если 16 раз подряд на ножке лог. 0, то кнопка нажата
   if(KeyAcc == 0) Key = 0; // если 16 раз подряд на ножке лог. 1, то кнопка отпущена
}


//Функцию ReadKey() необходимо вызывать либо из главного цикла, либо по прерыванию таймера. В основном цикле обрабатывать флаг Key.

//Код:
if(Key) { // Кнопка нажата
              //делаем что-то;
}

Re: Кнопки и кнопочный интерфейс

Пн янв 15, 2018 17:24:31

а прибавлять - убавлять не вариант? тогда и байта за глаза хватит

Добавлено after 6 minutes:
я тоже с такого начинал, когда боролся с наводками на энкодер: если при нажатой кнопке счетчик =0; (он туда шёл от константы антидребезга) то кнопка была отпущена достаточно долго, задираем счетчик и считаем (вверх до фф пока кнопка не опустится) понятно, что константы должны быть меньше половины диапазона (127) иначе счетчики пересекутся.

Re: Кнопки и кнопочный интерфейс

Пн янв 15, 2018 20:20:21

Спасибо всем за советы!
В принципе, я уже разобрался, но опрос трёх кнопок занял почти 70 строк кода (основной цикл + подпрограмма по прерыванию от таймера) и целых 7 регистров (по два счётчика на каждую кнопку и регистр "флагов" нажатий). Зато длительность антидребезга и задержку перед повторным опросом можно задавать для каждой кнопки индивидуально.

Re: Кнопки и кнопочный интерфейс

Вт янв 16, 2018 07:56:19

Ну вы, блин, даете...
Гефестион писал(а):...

1 - Кнопки - человекоинтерфейс. То есть медленный. Спешить никуда не нужно. Время реакции десятки миллисекунд.
2 - Подключение кнопок к внешнему прерыванию оправдано только в одном случае: энергосбережение.
3 - Конечный автомат.
Состояние 0. KBD_INIT:
Инициализация ввода-вывода, переменных. Установка состояния 1.
Состояние 1. KBD_NONE:
Если кнопка не нажата, то выход. Если нажата, запоминаем состояние кнопок, если их несколько, установка таймера антидребезга. Установка состояния 2. Выход.
Состояние 2. KBD_PRESSED:
Если время таймера не вышло, то выход. Если вышло и: если кнопка нажата и состояние остальных кнопок равно предыдущему, установка состояния удержания или отпускания кнопок. Выход. Если не нажата или состояние кнопок не равно предыдущему, установка состояния 1. Выход.
Состояние 3. KBD_HOLD или KBD_WAIT_UNPRESSED.
KBD_WAIT_UNPRESSED:
Если кнопка не отпущена, то выход. Если отпущена, установка таймера антидребезга, установка состояния 4. Выход.
Состояние 4. KBD_UNPRESSED.
KBD_UNPRESSED:
Если время не вышло, то выход. Если вышло, и кнопка отпущена, установка состояния 1. Выход. Если не отпущена, установка состояния 3. Выход.

Статья.

Re: Кнопки и кнопочный интерфейс

Вт янв 16, 2018 09:46:02

Demiurg, читайте внимательнее:
Гефестион писал(а):по прерыванию от таймера

Re: Кнопки и кнопочный интерфейс

Вт янв 16, 2018 10:25:40

У меня сейчас получился такой алгоритм.

Основной цикл:

Первая кнопка:
- Если в нулевом бите регистра ButtonFlags 0, значит кнопка не нажата - переходим к опросу следующей.
- Если в этом бите 1, то выполняем нужное действие, сбрасываем этот бит в ноль - действие выполнено.
- В регистр Button1Delay записываем 100 - это будет счётчик для задержки повторного опроса кнопки на 1 секунду.
- Переходим к опросу второй кнопки.

Для второй и третей кнопок аналогично.

Прерывание по совпадению малого таймера с перидом 10 мс:

- Сохраняем SREG в стек

Первая кнопка:
- Вычитаем 1 из регистра Button1Delay (если кнопка была нажата, то в регистре число 100)
- Если не 0, переходим к опросу следующей кнопки.
- Когда досчитаем до нуля, переходим собственно к опросу кнопки, а в Button1Delay пишем 1.
- Если первая кнопка не нажата (бит 7 PortB = 1), переходим к опросу следующей кнопки.
- Если первая кнопка нажата (бит 7 PortB = 0), то инкремент регистра Button1Counter.
- Если Button1Counter не равен 5, то переходим к опросу следующей кнопки (это антидребезг - 5 отсчётов).
- Когда Button1Counter = 5, сбрасываем его в 0 и устанавливаем нулевой бит в регистре ButtonFlags в 1 - это означает, что кнопка нажата.

Для второй и третей кнопок аналогично.

- Достаём SREG из стека и выходим из прерывания в основной цикл.

Re: Кнопки и кнопочный интерфейс

Вт янв 16, 2018 10:48:21

Ivanoff-iv писал(а):по прерыванию от таймера

Никогда не использую прерывания для опроса кнопок. Прерывания должны быть максимально короткими. Опрос кнопок у меня всегда в основном цикле.

Гефестион писал(а):...

Автоматное программирование, программные таймеры.
Почитайте статью по ссылке. Преимущество такого подхода в том, что все кнопки, сколько бы их не было, рассматриваются как одна.

Re: Кнопки и кнопочный интерфейс

Вт янв 16, 2018 11:38:55

Автомат использует один таймер (KEYB_TIMER).
таймер, то, всеравно задействован (таймерное прерывание) т.е. накладывает некоторые ограничения на таймера применение другим кодом, тут уж делать как удобней, если есть динамическая индикация - то просто грех к ней не присоседиться (и по линиям опроса и по прерываниям).
а так - да, по потреблению оперативы этот код намного дешевле выходит (потому что не помнит задержку каждой кнопки, т.е. если 1 была уже в автоповторе, добавил 2 и весь таймер сбросился - для обеих пошла задержка антидребезга) (тут уж по нужде смотри, в смысле - критично или нет такое поведение)
из минусов - в таком включении (из осн. цикла) 1) при любом длительном вычислении (цикле, прерывании, ожидании события) клава мрёт и возможности нажать "Ctrl+Alt+Del" уже не будет, и при большом количестве тяжестей в коде в первую очередь будет страдать клавиатурный ввод 2) не выйдет усыпить контроллер (точнее усыпить, то выйдет, но вместе с клавиатурой).
ПС: тут выбор - как приоритеты расставишь: нужна плавная, четкая клава - присоседь её к чему нибудь стабильному, нет - затолкай в дальний угол, как нелюбимую падчерицу, но не ропщи потом на её глюки.
да, опятьже 4 ноги можно сэкономить если с экраном синхронно клаву проверять...

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

Re: Кнопки и кнопочный интерфейс

Вт янв 16, 2018 11:52:06

Все зависит от подхода в программировании. По вашим ответам я вижу, что вы скорее на ПК программировали. Не равняйте подходы в программировании на ПК и микроконтроллерах. Есть такой подход: никаких долгих циклов. Дробление всех модулей на подзадачи. Флагами, условиями, состояниями конечных автоматов. Вход, проверка условий, выполнение части кода, выход. Аппаратный таймер настраивается на системный тик. Скажем, 1 мс. Итерация основного цикла должна выполняться за системный тик с запасом. Этот таймер служит нам основой программных таймеров. Которых может быть десятки. В моих проектах даже динамическая индикация выполняется в основном цикле. Не говоря уж об опросе кнопок.

Re: Кнопки и кнопочный интерфейс

Вт янв 16, 2018 12:12:43

Demiurg писал(а):Есть такой подход: никаких долгих циклов.
есть и другой: самое простое решение оказывается самым правильным. если длинный цикл проще сделать, и при этом "всё пучком" - почему нет? категоричность неубедительна.

Re: Кнопки и кнопочный интерфейс

Вт янв 16, 2018 13:01:26

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

Добавлено after 8 minutes 40 seconds:
и ещё учесть те места, куда ты обязан успеть... а куда только должен...

Re: Кнопки и кнопочный интерфейс

Вт янв 16, 2018 14:37:32

Demiurg писал(а):Есть такой подход: никаких долгих циклов.
есть и другой: самое простое решение оказывается самым правильным. если длинный цикл проще сделать, и при этом "всё пучком" - почему нет? категоричность неубедительна.

Как обычно пишут пишут программы начинающие и с устоявшимся подходом? Биглуп. Большой длинный основной цикл. Кто-то приверженец RTOS. Тот вообще не заморачивается. Если вытесняющая RTOS, задачи закольцованы. RTOS переключает задачи. То же касается программистов от ПК. И ардуинщиков.
И когда начинающему нужно сделать что-то более сложное, чем мигалку светодиодами, многие впадают в ступор. А как сделать опрос кнопок, мигать светодиодами, что-то вывести на экран, и чем-то поуправлять. Еще и временные параметры выдерживать. И все это делать "одновременно". Ответ прост. Распараллеливать процесы. Дробить, чтобы чтобы каждый процесс за итерацию основного цикла выполнял кусочек кода. Плюсы такого подхода оценивайте сами.
Ответить