Занимательная микроэлектроника — страница 105 из 117

out TCCR0,temp  ;запускаем Timer0 div 1:256


Прерывание Timer 1 будет очень простое (листинг 19.6).

Листинг 19.6

TIM1:

    inc count3

reti

Теперь рассмотрим самое главное прерывание, Timer 0 (листинг 19.7).

Листинг 19.7

TIM0:  ;таймер 122,07 Гц

inc count_sek

          cpi count_sek,122  ;получаем 0.999424 с

  breq corr_1  ;если секунда прошла, то на коррекцию счета


   cpi count_sek,1

          brne corr_1  ;в самом первом цикле запускаем Timer 1:

 ldi temp,0b00000111  ;внешний сигнал Т1 (выв. 9) по фронту

          out TCCR1B,temp  ;запускаем Timer1

corr_1:  ;1 сек + коррекция

clr temp

          out TCCR0,temp  ;останавливаем Timer0

          ;задержка на -600 мкс для коррекции интервала

 ldi ZH,high(1200)  ;8 МГц, цикл 4 такта

ldi ZL,low(1200)

loop:

          sbiw ZL,1

             brne loop

;переключение контр. выв 11 PinD6 период 1 с

sbis PinD,6

             rjmp set_1

             cbi PortD,6

             rjmp Set_0

Set_1:

             sbi PortD,6

Set_0:

             clr temp

             out TCCR1B,temp  ;останавливаем Timer1

;читаем данные

   in temp,TCNT1L

<пишем младший байт в память>

  in temp,TCNT1H

<пишем второй байт в память>

<пишем старший байт count3 в память>

;очищаем все регистры

clr temp out TCNT1H,temp

             out TCNT1L,temp  ;очищаем Timer1

out TCNT0,temp  ;очищаем Timer0

    clr count3

             clr count_sek  ;очищаем счетчик прерываний

;запускаем таймер 0 опять

  ldi temp,0Ь00000100;

             out TCCR0,temp  ;запускаем Timer0 div 1:256

reti


Обратите внимание, что читать данные из регистров таймера нужно в указанном порядке: сначала старший, потом младший, а записывать в обратном порядке. Это сделано специально: при чтении из старшего регистра TCNT1H данные из младшего TCNT1L одновременно переписываются в специальный регистр временного хранения, и ситуации, когда в промежутке между командами чтения младший разряд может измениться (при запущенном таймере), не возникает. То же самое, только в обратном порядке, происходит и при записи. Запись данных я не расшифровывал, потому что это может быть и запись в SRAM с последующим выводом на индикацию, и запись во внешнюю энергонезависимую память для последующего чтения из компьютера (или одновременно и то и другое). Простейший частотомер можно сделать, если организовать автоматическую передачу данных через UART в компьютер, который и занимается отображением и записью информации.

Из-за инструкции sbiw, которая занимает два такта (а не один, как инструкция dec, которую мы использовали в процедуре delay для интерфейса I2С, см. главу 16), здесь один цикл задержки равен четырем тактам, или 0,5 мкс при тактовой частоте 8 МГц. Меняя число циклов задержки, можно подстроить длительность секундного интервала в интервале от 576 мкс в сторону уменьшения (задержка равна нулю) до целых 131 мс в сторону увеличения. 576 мкс может показаться слишком маленьким значением, но этого достаточно для подстройки стандартного кварца, в крайнем случае, можно отобрать экземпляр из нескольких. Калибровка осуществляется измерением длительности импульса на контрольном вводе 11 МК с помощью точного частотомера (профессионального лабораторного прибора, а не любительского, который имеет недостаточную точность).

У AT90S2313 недостаточно выводов, чтобы напрямую обеспечить динамическую индикацию для нашей цели (7 десятичных разрядов), поэтому можно либо только использовать данные непосредственно, либо управлять разрядами через внешние дешифраторы (скажем, 561ИД5 позволяет управлять семисегментным индикатором четырехразрядным двоичным кодом с выводов PD0—PD3, а управление переключением семи и даже восьми разрядов можно тогда осуществить через полностью свободный порт В). Можно, конечно, выбрать другой контроллер. Динамическая индикация практически никак не помешает счету, так как таймер считает абсолютно независимо от остальных схем.

Частотомер получится довольно низкочастотный (если только не использовать внешние счетчики-делители с ущербом для разрешающей способности измерений). Но самой сложной проблемой для построения настоящего частотомера будет формирование из исходного сигнала произвольной формы последовательности «чистых» импульсов нужной формы и амплитуды.

Без подробного обсуждения привожу одну из возможных схем такого формирователя импульсов (рис. 19.6), работающего на частотах до 4 МГц. 554САЗ заменяется на 521САЗ (импортный аналог — LM311). Конденсатор 100 пФ служит для фильтрации высокочастотных помех и его емкость подбирается при регулировке. Предпочтителен более современный (и несколько более быстродействующий) LM6511, совпадающий с указанными по выводам. Переключатель полярности сигнала в принципе не требуется (его можно реализовать программно простым переключением режимов Timer 1), но таким образом можно сэкономить вывод МК, который пришлось бы подсоединять к переключателю.



Рис. 19.6.Формирователь входных импульсов для частотомера 0–4 МГц


Объединение систем на МК

Как я уже упоминал в главе 16, последовательный порт USART может работать в режиме мультипроцессорного обмена. Однако реализован он довольно сложно и малопригоден для организации такого часто встречающегося варианта, когда есть главный компьютер и несколько равноправных систем на МК, с которым хочется организовать двустороннюю связь через единый COM-порт. Сейчас мы разберем одну из возможностей организации такого обмена.

Обмен такого рода не может обойтись без присвоения индивидуального адреса устройству, т. к. их надо как-то различать. Все подобные протоколы (I2С хотя бы) различаются лишь способом доставки и форматом этого адреса. В нашем же случае придется еще придумать, как обеспечить «прозрачное» переключение каналов обмена во избежание конфликтов (в USART это достигается односторонностью обмена— по линии от главного МК к ведомым передается только 9-битовый адрес, а обратно— только 8-битовые данные, нам же нужен двусторонний обмен).

Для реализации такого варианта мы сконструируем специальную плату-коммутатор на основе отдельного МК (возьмем тот же AT90S2313). Идея состоит в том, что мы выделяем специальные команды-адреса, которые воспринимает только этот МК, и в соответствии с ними переключает канал обмена на нужное устройство. Если переключать с помощью мультиплексоров/демультиплексоров 561КП2 (см. рис. 8.8), то можно адресовать до восьми устройств. В качестве команд адресации удобно выбрать числа от 0 до 7, тогда они прямо будут соответствовать коду, который требуется подать на мультиплексоры.

Естественно, среди команд управления устройствами и передаваемых к устройству данных байты с таким значением должны полностью отсутствовать (например, установку часов напрямую таким способом не передашь), и это накладывает ограничения, но не очень серьезные. В каждом конкретном случае можно что-нибудь придумать: например, дополнять данные со значением меньше 8 старшим битом, равным единице, или еще что-то в этом роде (с похожими проблемами сталкиваются при передаче произвольных данных — вложений — по электронной почте, и ничего, как видите, справляются). Разумеется, можно задействовать и дополнительные линии СОМ-порта для адресации коммутирующего МК отдельно от остальных, или использовать девятибитовые посылки адреса (так, как это делается в USART), чтобы отличить их от данных и т. п. Здесь я приведу только самый простой вариант.

Схема такого коммутатора показана на рис. 19.7.



Рис. 19.7.Коммутатор UART на 8 каналов


Выводы коммутатора, помеченные номером с буквой R, следует присоединить к выводам RxD устройств, а их выводы TxD (строго в том же порядке) следует соединить с выводами коммутатора, помеченными номером с буквой Т. Программа коммутатора (листинг 19.8) очень проста и даже не содержит таблицы векторов прерываний, поэтому я привожу ее целиком.

Листинг 19-8

;Тестовый коммутатор

;Кварц 4 МГц

.include <<2313def.inc»

;=======

.def    temp = r16

;======= программа

 ldi temp,low(RAMEND)  ;загрузка указателя стека

  out SPL,temp

          ldi temp,(1<  ;выкл. аналог, компаратор

  out ACSR,temp

          ldi temp, (1<

          out UCR,temp  ;разрешение приема/передачи 8 бит

ldi temp,25

          out UBRR,temp  ;скорость передачи 9600

    ldi temp,0b00000111  ;устанавливаем PB0-PB2 выходы

 out DDRB,temp

          clr temp

          out PortB,temp  ;по умолчанию адресуется устройство 0


G_cykle:

          rcall in_com

          cpi temp,9  ;если принятый байт больше или равен 9

brsh G_cykle  ;то ничего не делаем

out PortB,temp  ;иначе выводим его в порт В

rjmp G_cykle

in_com:  ;прием байта в temp с ожид. готовности

sbis USR,RXC

          rjmp in_com

          in temp,UDR

ret


Больше ничего делать не требуется — «верхняя» программа всегда начинает с того, что посылает номер устройства n от 0 до 7, мультиплексор коммутирует выходы