Да простят меня читатели, но это реплика на реплику. А началось всё с интересной статьи, где автор познакомил нас со своей разработкой на базе ATMega8. Понятно, камень этот избыточен для той задачи, которая была на нем реализована, на что и обратил внимание оппонент.

Но теперь уже у меня засвербило… гм, в ладонях, и я не мог не попытаться вставить свои пять копеек. Я понимаю, настоящая моя заметка для комментария великовата, а на полноценную статью не тянет, но все же попробую.

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

Схема на реле, предложенная уважаемым автором, вызвала у меня некое подобие зубной боли. Возможно потому, что в юности довелось иметь дело с декадно-шаговыми и координатными АТС, после чего я на всю жизнь возненавидел реле. Да, релейные схемы — это просто, да, работать они будут даже в условиях ядерного взрыва, но…

Первое но — надежность


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

Второе но — стоимость


Сколько стоит одно реле? Поискав на «Али», ничего приличного дешевле одного американского доллара не нашел. Ладно, пусть я не умею искать, возьмем цену 50 центов. Четыре реле (ведь в оригинальной схеме четыре канала) — уже два бакса. Получается только одни реле будут стоить больше, чем вся комплектуха варианта на микроконтроллере, включая сам микроконтроллер. А если в той схеме еще повыбрасывать лишние детали…

Третье но — расширяемость


А вот скажите мне: что мы будем делать с этой схемой, если понадобится ввести в изделие дополнительную функциональность? Нет, я не прошу играть военные марши, а хотя бы и пискнуть зуммером после истечения времени, которое дается игроку на ответ?

Воооот…

Ладно, это была присказка. А теперь к делу. Смотрите схему. Процитирую: «спаять было быстрее чем рисовать».



Одна условно-бесплатная микросхема, три резистора, три конденсатора. Пьезопищалка. И все, практически. Питать это хозяйство можно от трех элементов AA. Без всяких стабилизаторов. В покое потребляет меньше микроампера (power down mode) — можно отказаться от выключателя — еще один ненавистный контакт.

Контроллер AVR AT90S1200 не потому, что он чем-то хорош, просто у меня после одного из древних проектов их осталось с полведра, а так как он давно снят с производства — сейчас уже никуда его не приткнешь. Только в какую-нибудь самоделку. Поэтому и условно-бесплатный. Но, как верно заметил автор первой статьи, здесь подойдет любой.

Теперь про программу. Так как в этом контроллере ОЗУ нет (от слова вообще), а стек аппаратный и всего лишь трёхуровневый, ни о каких языках высокого уровня речь не идет. Только Ассемблер, только хардкор…

Посадить кнопки на прерывания тоже не получится. Внешнее прерывание только одно, а знатоков за столом шесть. Что ж, будем опрашивать в цикле. Причем опрашивать все одновременно, дабы не было преимущества ни у одного игрока. Время цикла опроса при частоте тактового генератора 1МГц — 6мкс, то есть мы вылавливаем разницу между нажатиями в 6мкс. Если два игрока нажмут кнопку одновременно, ну вдруг, мало ли — будут засвечены два светодиода. Все честно. Хотя, думаю, вероятность такого события будет очень низка. Но все же, если точность определения лидера в 6мкс недостаточна — можно подключить кварц, прошить фьюз и запустить контроллер на частоте 12МГц. В этом случае частота опроса повысится в 12 раз. Ваш К.О.

Дребезг контактов ни на что не влияет, так как мы реагируем на первый же замеченный спад уровня. После этого следует обработка нажатия, и что уже там творится на кнопке — нас не волнует.



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

Собственно код
;----------------------------------------------------------------------------
;¦ Title        : Своя игра. Пример реализации
;¦ Version      : 0.0
;¦
;¦ Last updated : 16.01.16
;¦ Target       : AT90S1200  1MHz (внутренний RC-генератор)
;+---------------------------------------------------------------------------
.include "1200def.inc"

;¦ Определение регистровых переменных
;+---------------------------------------------------------------------------
.def    acc     = r20           ; временная переменная
.def    temp1   = r21           ; просто локальная переменная
.def    temp2   = r22           ; просто локальная переменная
.def    temp3   = r23           ; просто локальная переменная
.def    temp4   = r24           ; просто локальная переменная
.def    temp5   = r25           ; просто локальная переменная
.def    temp6   = r26           ; просто локальная переменная
.def    temp7   = r27           ; просто локальная переменная

;¦ Определение сигналов в портах
;+---------------------------------------------------------------------------
.equ     ini_b      =  0b00111111  ; начальные значения PORTB
.equ     dir_b      =  0b11111111  ; маска направлений 1-out 0-in
;                        ¦¦¦¦¦¦¦¦
.equ     led01      = 0 ;¦¦¦¦¦¦¦+-12(out)
.equ     led02      = 1 ;¦¦¦¦¦¦+--13(out)
.equ     led03      = 2 ;¦¦¦¦¦+---14(out)
.equ     led04      = 3 ;¦¦¦¦+----15(out)
.equ     led05      = 4 ;¦¦¦+-----16(out)
.equ     led06      = 5 ;¦¦+------17(out) (MOSI)
.equ     buzzer1    = 6 ;¦+-------18(out) (MISO)
.equ     buzzer2    = 7 ;+--------19(out) (SCK)

.equ     ini_d      =  0b00111111 ; начальные значения PORTD
.equ     dir_d      =  0b01000000 ; маска направлений 1-out 0-in
;                         ¦¦¦¦¦¦¦
.equ     key01      = 0 ; ¦¦¦¦¦¦+--2 (in)
.equ     key02      = 1 ; ¦¦¦¦¦+---3 (in)
.equ     key03      = 2 ; ¦¦¦¦+----6 (in) (INT0)
.equ     key04      = 3 ; ¦¦¦+-----7 (in)
.equ     key05      = 4 ; ¦¦+------8 (in)
.equ     key06      = 5 ; ¦+-------9 (in)
.equ     pin11      = 6 ; +--------11(out) не исп.

;¦ Макросы
;¦
;+---------------------------------------------------------------------------
.include "mymacros.inc"

.macro  init_ports
    ldi     acc,dir_b
    out     DDRB,acc            ; настроили порт B на ввод/вывод
    ldi     acc,ini_b
    out     PORTB,acc           ; вывели в порт

    ldi     acc,dir_d
    out     DDRD,acc            ; настроили порт D на ввод/вывод
    ldi     acc,ini_d
    out     PORTD,acc           ; вывели в порт
.endmacro

.macro  init_wd
    wdr                         ; reset watchdog timer
    ldi     acc,0b00001111      ; watchdog разрешен, коэф. деления - 2048
    out     WDTCR,acc
.endmacro

.macro  init_irq            ; портит acc
    ldi     acc,(1<<se)+(1<<sm)+(0<<isc01)+(0<<isc00)
;                ¦       ¦       ¦          ¦
;                ¦       ¦       +----------+-- прерывания: "00" - по низкому уровню
;                ¦       +--------------------- 0-idle mode 1-power down
;                +----------------------------- 1-sleep разрешен
    out     MCUCR,acc
;   ldi     acc,(1<<int0)
;   out     GIMSK,acc           ; разрешили внешние прерывания от INT0
    clr     acc
    out     GIMSK,acc           ; запретили внешние прерывания от INT0
.endmacro

.macro  comparator_off          ; портит acc
    ldi     acc,(1<<ACD)
    out     ACSR,acc            ; выключили компаратор для экономии энергии
.endmacro

.macro      speaker_up
    sbi     PORTB,buzzer1
    cbi     PORTB,buzzer2
.endmacro

.macro      speaker_dn
    sbi     PORTB,buzzer2
    cbi     PORTB,buzzer1
.endmacro
;----------------------------------------------------------------------------
;¦
;¦ Точка входа
;¦
;+---------------------------------------------------------------------------
.CSEG
.org 0
    rjmp    start           ; на начало программы

;----------------------------------------------------------------------------
;¦
;¦ Подпрограммы
;¦
;+---------------------------------------------------------------------------

;¦ Задержки
;¦
;¦ Портит temp5,6,7
;+---------------------------------------------------------------------------
.equ        const10ms = 2500-2

.macro  wait_ms                 ; задержка от 10 до 2550мс, кратная десяти
    ldi     temp5,@0/10
    rcall   delay10N
.endmacro

.macro  wait_s                  ; задержка от 1 до 255с
    ldi     temp4,@0
lb1:
    ldi     temp5,100
    rcall   delay10N
    dec     temp4
    brne    PC-3
.endmacro

delay10N:
;   wdr                         ; 1
    ldi temp6,low(const10ms)    ; 1
    ldi temp7,high(const10ms)   ; 1
d10ms1:                         ; один цикл - 4 такта, 4мкс
    subi    temp6,1             ; 1
    sbci    temp7,0             ; 1
    brne    d10ms1              ; 2
    djnz    temp5,delay10N      ; 
    ret

;¦ Звуковой сигнал 2.5кГц (два полупериода по 200мкс)
;¦
;¦ Портит temp6,7
;+---------------------------------------------------------------------------
.equ    const200us = 49

.macro  beep
    rcall   snd
.endmacro

snd:
    ldi     temp6,0             ; 256 периодов меандра
snd1:
    speaker_up
    rcall   delay200us
    speaker_dn
    rcall   delay200us
    djnz    temp6,snd1
    ret

delay200us:                     ; 3 (вызов)
    ldi     temp7,const200us    ; 1
del200:
    nop                         ; 1
    djnz    temp7,del200        ; 1+2
    ret                         ; 4-1

;----------------------------------------------------------------------------
;¦
;¦ Начало программы
;¦
;+---------------------------------------------------------------------------
start:
    init_ports
    init_irq                ; только чтобы разрешить sleep
    comparator_off

waitloop:
;   wdr
    in      acc,PIND
    ori     acc,0b11000000  ; забиваем два старших разряда. Они не используются
    cpi     acc,0xff
    breq    waitloop        ; ждем пока хоть одна кнопка не будет нажата

    out     PORTB,acc       ; зажгли светодиод, соответствующий нажатой кнопке
    beep                    ; звуковой сигнал

; даем время на ответ

    wait_s  10              ; ждем N секунд

; время истекло - тройной звуковой сигнал

    beep
    wait_ms 100             ; ждем N миллисекунд
    beep
    wait_ms 100
    beep
    ldi     acc,0xff
    out     PORTB,acc       ; погасили все светодиоды
    sleep
    rjmp    0               ; этот джамп не нужен. Из слипа выходим только по сбросу

.exit   ;полный конец обеда -------------------------------------------------


Спасибо за внимание. Если что не так — пишите в личку, исправлю.

Комментарии (14)


  1. Zzzuhell
    18.01.2016 14:51

    разные кнопки с разным временем и характером дребезга могут дать кому-то преимущество в микросекунду-другую...

    Этот баг пофиксить еще проще. Меняться кнопками регулярно и все тут :)


  1. Ocelot
    18.01.2016 15:45

    Если уж так беспокоит дребезг, используйте кнопки на размыкание. Заодно пресекаются возмущения в духе «я раньше нажал, а в пульте где-то контакт отходит, с первого раза не сработало».


    1. Alexeyslav
      18.01.2016 17:09
      +3

      На размыкание точно такой же дребезг как и на замыкание. Условия тут одинаковые.


      1. Ocelot
        18.01.2016 17:26

        Разве? Мне всегда казалось, что причина дребезга в том, что контакты при соприкосновении несколько раз отскакивают друг от друга, и цепь кратковременно разрывается. А если мы их размкаем, какая сила заставит их замкнуться обратно?


        1. Alexeyslav
          18.01.2016 18:10
          +3

          Там те же процессы происходят. При размыкании подпружиненный контакт начинает вибрировать(не только в горизонтальной, но и в вертикальной плоскости, начинает изгибаться), и с самого толкателя приходит вибрация, потом еще действие самого тока… на маленьких расстояниях даже от 1В пробивает зазор напряжением.


  1. agamemnen
    18.01.2016 19:47

    А фото устройства где?


    1. EarnestUA
      18.01.2016 20:01

      Устройства нет, есть макет на котором была проверена программа. Как один из вариантов реализации данной функциональности.Это ведь не описание законченной разработки, а реплика в тему.
      Надо фото макетки?


  1. gleb_l
    18.01.2016 22:59
    +1

    программа дает 10 секунд на ответ сразу после детекции нажатой кнопки. В реальной игре вроде бы ведущий должен подтвердить право ответа, указав на игрока, первым заявившего желание ответить — и время хорошо бы отмерять именно от этого приглашения. Если это так — то командная кнопка ведущего — это не reset, а обычная программно-считываемая кнопка. Длинное нажатие на нее дает сброс программного D-триггера захвата кнопок, а, например, короткое после того, как желание ответить захвачено — начинает отсчет 10-секундного интервала, предваряя его коротким звуковым сигналом.

    В таком варианте хоть будет, что писать на ассемблере )


    1. EarnestUA
      18.01.2016 23:31

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


  1. impetus
    19.01.2016 14:04
    +1

    Да пусть расцветает сто цветов — на мой вгляд вот эта схема:
    простейший вариант
    имеет наименьший «порог вхождения» для людей, паяльника в жизни не державших. Только, конечно, Вы правы — на «бумажных» конденсаторах.
    Детали — любые, лишь бы одинаковые — вот совсем любые, от герконов до пускателей, каналов — от двух до хоть сотен — сложность с числом каналов растёт линейно, без ступенек и перегибов.
    Но даже если Вы умеете отличить базу транзистора от затвора, и знаеете как помигать светодиодом на 155-й серии — ой не факт что у вас получится сваять «одократный состязательный переключатель каналов без приоритета» быстрее и проще.
    И — очень часто, особо у кого это не работа а хобби — бывают поделки, основная судьба которых — не развитие-усложение со временем, а «поиграться, забросить, через пару недель/лет разобрать на детали». Кстати «ардуина» в таком применении очень, очень хороша.


    1. gleb_l
      19.01.2016 16:38

      В релейном варианте участники неравноправны — если участник справа начнет нажимать кнопку, и в это время то же самое сделает участник слева — то левый перехватит правого, даже если переключающий контакт правого достигнет НО вывода первым. То есть чем левее — тем выше приоритет в спорном интервале времени (пролет контактов кнопки + время заряда конденсатора через внутреннее сопротивление источника до напряжения срабатывания реле). Самый левый может вообще поднажать кнопку слегка (так, чтобы оба контакта были разомкнуты) — и держать до тех пор, пока не придумает ответ, а затем дожать полностью — и станет победителем!

      Если не хочется МК — лучше уж делать на синхронной {релейной, транзисторной, ИМС} логике, а не на последовательной


      1. impetus
        19.01.2016 17:14

        Ну поробуйте нажать кнопку на мышке так, чтобы НЗ контакт микрика разомкнулся, а НР не законтачил. Спорный интервал «с одной стороной» там — время РАЗмыкания НЗ контакта кнопки — величина принципиально неформализуемая, ибо именно это размыкание и является самим физическим фактом «нажатия», а с «другой стороной» — время РАЗмыкания НЗ контакта реле — что-то порядка ОДНОЙ милисекунды у сигнальных реле axicom.
        Я не против того что неопределённость имеет место быть, я лишь о том что в рельной жизни живыми людьми такая одновременность практичеки нереализуема, а в случае чуда — при домашей игре на малобюджетной поделке — непринципиальна. Кстати в исходной схеме на аТмеге — «окно одновременности» — 5 мс и внутри него тоже выбор по заранее назначенному приоритету.

        Вообще не понимаю спора — я думал что лишь «заполняю ячейку таблички вариантов» _если_кому_вдруг_понадобится_ (тем более, что вобще изначально я переключал аудиовходы демостенда наушников, а там реле всё равно и так есть, а вот помехи контроллерные схемы давали, зависали при длительной работе (а может от статики с рук) и плохо масштабировались из трёх в десятки каналов).


        1. gleb_l
          19.01.2016 17:36

          Ну поробуйте нажать кнопку на мышке так, чтобы НЗ контакт микрика разомкнулся, а НР не законтачил
          — я и не утверждал, что на конкретной модели микровыключателя это будет сделать просто. Я всего лишь заметил, что существуют условия, при которых схема ведет себя неравноприоритетно. Ширина временного зазора, в течение которого можно воспользоваться этим недостатком свойством схемы, уже зависит от конкретной реализации.

          В аналогии с программным миром можно сказать, что не бывает «почти» потокобезопасного кода — он либо потокобезопасен, либо нет. Этот вариант — thread unsafe.

          Вообще не понимаю спора
          я, извините, с Вами и не спорил. Просто заметил, что данный вариант имеет одну особенность, которая в принципе способна влиять на качество исполняемой им функции, и привел пример, при котором это влияние проявляется.


  1. impetus
    19.01.2016 14:19

    P.S. хотя вам за простоту и масштабируемость, конечно, респект.