Без реле, но и без Ардуины или Драка в песочнице

от автора

Да простят меня читатели, но это реплика на реплику. А началось всё с интересной статьи, где автор познакомил нас со своей разработкой на базе 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   ;полный конец обеда ------------------------------------------------- 

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

ссылка на оригинал статьи http://geektimes.ru/post/269332/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *