'Ивпользуем ATmega48, но для данного функционала подойдет любая мега $crystal = 8000000 'Используем встроенный генератор 8МГц (Div8 - выключен) '------------------------------------------------------------ 'Настраиваем порты в режим входов (0) или выходов (1) Ddrb = &B00101110 Ddrc = &B00001100 Ddrd = &B10000000 '------------------------------------------------------------ 'Назначаем ножкам понятные имена I2 Alias Pinb.0 'Вход 2 - используем также для измерения периода Q0 Alias Portc.3 'Выход 0 Q1 Alias Portc.2 'Выход 1 '------------------------------------------------------------ 'Автоматической настройке Config Timer... почему-то не доверяю 'Настраиваем счетчик 1 для измерения периода Tccr1b = &B11000011 'Режим таймера 0 , K(делителя)=64 Частота счета = 8Мгц/64 = 125 кГц 'Настраиваем прерывание по захвату значения счетчика On Icp1 Icp1_int Nosave 'Задаем вектор прерывания Icp1 захвата таймера 1 On Ovf1 Ovf1_int Nosave 'Задаем вектор прерывания Ovf1 переполнения таймера 1 Timsk1 = &B00100001 'Включаем прерывания ICP1 (5й бит) и OVF1 (0й бит) '------------------------------------------------------------ Enable Interrupts 'Включаем прерывания '------------------------------------------------------------ Dim Pwm As Word , Pwm_pos As Word , Pwm_k As Word 'Переменные для программного шима Dim Cap As Word 'Сюда будем писать данные захвата таймера Dim Process As Byte 'Разрешение на обработку данных (см. далее) Dim Qi As Byte 'Режим работы выходов Pwm = 0 : Pwm_pos = 0 'Задаем начальние значения ШИМа '------------------------------------------------------------ Do 'Основной цикл If Qi = 1 Then 'Режим переключения 1 Q0 = I2 Q1 = Q0 Elseif Qi = 2 Then 'Режим переключения 2 Q0 = Not I2 Q1 = Q0 End If If Process = 1 Then 'Если прошло прерывание, обрабатываем данные Process = 0 Select Case Cap 'В "Cap" программа прерывания положила период (время между импульсами) Case 0 To 4166 'Если частота выше 30 Гц Qi = 1 'Включаем режим переключения 1 Case 4167 To 15625 'Если частота от 30 до 8 Гц Qi = 2 'Включаем режим переключения 2 Case Else 'Если частота менее 8 Гц Toggle Q0 'Просто меняем состояние выхода при каждом импульсе Q1 = Not Q0 Qi = 0 End Select Else If Cap > 65200 Then 'Если долго нет импульсов на входе, 'При помощи ШИМа плавно включаем светодиоды 'до уровня Pwm_mid, в моем примере - в "пол-накала" Qi = 0 If Pwm_pos =< Pwm Then Reset Q0 : Reset Q1 Else Set Q0 : Set Q1 End If Const Pwm_max = 500 'Дискретность модуляции = 500 Const Pwm_mid = Pwm_max / 2 'Уровень постоянного свечения 0...500 If Pwm < Pwm_max Then 'Инкрементируем счетчик ШИМа Pwm = Pwm + 1 Else Pwm = 0 End If Pwm_k = Pwm_k + 1 If Pwm_k > 2000 Then 'Pwm_k - делитель нарастания яркости (определяет скорость нарастания) Pwm_k = 0 If Pwm_pos < Pwm_mid Then 'Если уровень яркости меньше уровня постоянного свечения Pwm_pos = Pwm_pos + 1 'добавляем еще яркость (плавное включение) End If End If Else Pwm_pos = 0 'Если пришли импульсы на вход, выключаем ШИМ End If End If 'При желании ШИМ можно приспособить к таймеру 0 - будет намного меньше кода. Loop End '----------------------------------- 'Правилом хорошего тона в прораммировании прерывания является написание 'максимально быстрого и короткого кода. 'По-этому пошем его на ассемблере и с опцией "No Save" '----------------------------------- 'Прерывание по захвату значения таймера 1 по импульсу на входе ICP1 - он же - I2 Icp1_int: push r25 : push r24 : push r30 : push r31 'Сохраняем используемые нами регистры в стек 'Поскольку ни одна из команд не влияет на флаги, их не сохраняем ldi r24,0 !Out Tcnt1h , R24 !Out Tcnt1l , R24 'Timer1 = 0 '-- in r24,icr1l in r25,icr1h Loadadr Cap , Z St Z+ , R24 St Z , R25 'Cap = Capture1 это надо только для измерения периода '-- Loadadr Process , Z ldi r24 , 1 st Z , r24 'Process = 1 '-- pop r31 : pop r30 : pop r24 : pop r25 'Восстанавливаем старые значения регистров Return '----------------------------------- 'Прерывание по переполнению таймера 1 'Делаем это для того, чтобы значения таймера было всегда близко к максимуму 'тогда при приходе импульса после длительной паузе не будет захвачен ошибочный период Ovf1_int: push r24:push r25 'Сохраняем используемые нами регистры в стек ldi r24 , 0 'Timer1.low = 0 ldi r25 , 255 'Timer1.high = 255 !out Tcnt1h , r25 'то же самое: Timer1=65280 !out tcnt1l , r24 '-- Loadadr Cap , Z St Z+ , R24 'Cap = 65280 St Z , R25 pop r25:pop r24 Return