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

          brne corr_K

          ldi temp,100  ;если = FF, то по умолчанию 100

corr_K:

            ldi ZH,1

            ldi ZL,KorrRAM

            st Z,temp


Величина задержки по умолчанию определяется на основе предварительных изысканий по уходу конкретного кварца (здесь часы спешили примерно на 1 с в сутки). Не забудем заменить в четвертой сверху строке таблицы прерываний reti на rjmp TIM2_comp. Отведем для счета прерываний регистр count_msek (пусть будет r12). Бит 4 регистра Flag будет сигнализировать о том, отстают часы или спешат (если отстают, то бит установлен). Значение этого бита и регистра count_msek будем устанавливать в момент, когда будет вызываться процедура коррекции (см. далее), там же часы останавливаются. Сам обработчик приведен в листинге 19.3.

Листинг 19.3

ТIМ2_СОМР:

            dec count_msek

            brne end_t  ;если еще не 0, то на выход

sbrs Flag,4

            rjmp k_minus  ;если спешат, то пропустить если отстают

cbr Flag,8  ;очищаем бит к следующему разу; устанавливаем часы на 01 сек. и заводим их

ldi temp,1

            rcall IniSek

            ldi count_sek,1  ;меняем значение в регистре секунд

ldi ZH,1

            ldi ZL,Sek

            st Z,count_sek

            rjmp end_korr

k_minus:

;если спешат, просто заводим часы обратно

clr temp

            rcall IniSek

end_t:

reti  ;конец прерывания коррекции


Теперь собственно процедура вызова коррекции: будем выполнять ее в полночь. Она довольно громоздкая, потому что приходится определять момент, когда полночь настала. Мы можем вклинить ее туда, где часы проверяются на кратность трем (0 минут и 0 секунд нам обеспечены). Однако сразу после этого производится запись во flash (а иногда и не производится), и она нам будет непредсказуемо тормозить коррекцию. Потому будем ее проводить в одну минуту первого (00:01:00). Сразу после метки sek_0 вписываем фрагмент кода, содержащийся в листинге 19.4.

Листинг 19.4

sek_0:

          ldi ZL,1  ;загружаем минуты

    ld temp,Z+;

          cpi temp,1  ;сравниваем минуты, если 1 — коррекция

breq min_1

          cpi temp,0  ;сравниваем минуты, если 0 — запись

breq mm0  ;на проверку трехчасового цикла

reti  ;иначе выходим

min_1:

          ld temp,Z  ;загружаем часы

cpi temp,0  ;сравниваем часы = 0

          breq hour_0  ;если равны 0, то на коррекцию

 reti  ;иначе выходим

hour_0:

         ldi ZL,KorrRAM  ;коэффициент коррекции Id

count_msek,Z  ;в счетчик

  ldi temp,128

         ср count_msek,temp  ;определяем его величину

brlo k_plus

         com count_msek  ;если больше 127, то вычитаем из 255

  sbr Flag,8  ;значит отстают

k_plus:  ;если меньше — часы отстают

  ldi temp,$80

         rcall IniSek  ;останавливаем часы

   ldi temp,0Ь00000110  ;заводим таймер

out TCCR2,temp  ;Timer2 1:256

reti

mm:  ;далее по тексту программы


Разумеется, если вы используете сторожевой таймер, то его следует останавливать на время проведения этой операции (и запускать в прерывании Timer 2 по окончании коррекции.) В правильно организованной программе, кроме того, необходимо также запрещать за некоторое время до наступления момента коррекции длинные процедуры с запрещением прерываний (вроде чтения данных из Flash), иначе коррекцию можно пропустить.


Измерение частоты

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

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

Предположим, измеряемая частота находится в диапазоне до 4 МГц, и нам желательно измерить ее с разрешением до 1 Гц. Прежде всего отметим, что тактовая частота контроллера должна превышать измеряемую не менее, чем в два раза — таково требование руководства. Обнаружение изменения внешнего сигнала производится по фронту тактового, и если период измеряемого сигнала слишком короткий, то в регистрации могут быть пропуски.

Замечание

Отметим, что перепад уровней внешнего сигнала регистрирует автономная асинхронная схема. Потому из изложенного в руководстве не следует, что тактовая частота должна быть выше измеряемой именно в два раза — достаточно простого превышения. И по моему опыту AVR прекрасно измеряют частоту всего в полтора раза ниже тактовой. Однако окончательное суждение по этому вопросу я оставляю на усмотрение читателей — изложение в руководстве не очень толковое (не до конца прояснен вопрос с задержками регистрации фронта сигнала), и, конечно, лучше «на всякий случай» следовать фирменным рекомендациям.

Так что нам потребуется МК с тактовой частотой не менее 8 МГц. Выберем АТ9 °C2313 (Classic— при необходимости легко модифицировать алгоритм к любому AVR) с частотой 8 МГц. Для измерения мы используем два таймера— один 16-разрядный (Timer 1) для отсчета собственно внешней частоты, и второй (Timer 0) для отсчета измерительного интервала.

Заметки на полях

Результат измерения частоты 4 МГц с точностью до герца в принципе займет не менее трех 8-битовых регистров. Но реальное их число, которое требуется задействовать, будет зависеть от нижнего предела измеряемой частоты. В самом деле, предположим, что частота может меняться не более чем на 256 Гц. Тогда старшие два регистра всегда будут показывать одно и то же число (и точно известно, какое), а все изменения будут регистрироваться только в самом младшем регистре счетчика. Если же частота 4 МГц не меняется более чем на 65 кГц, то можно оставить только два регистра (собственно таймера). Здесь важно только, чтобы в процессе изменений частота не «переваливала» за границу, когда старший регистр тоже меняется (что в нашем случае произойдет, например, если средняя частота колеблется около значения 222 = 4 194 304), иначе возникнет неоднозначность (которую, впрочем, также в некоторых случаях можно учесть). Но мы не будем в этот вопрос углубляться, а тупо предположим, что частота в пределах емкости трех регистров (т. е. с большим запасом — до 16,7 МГц) может быть любой.

Для измерения нам потребуется ввести прерывание Timer 1 по переполнению, в котором третий регистр (назовем его count3) будет всякий раз увеличиваться на единицу. Входной сигнал подадим на вход Т1 (вывод 9 для 2313), с которого внешние импульсы поступают прямо на счетчик таймера, если ему задать соответствующий режим.

Теперь разберемся с формированием измерительного интервала. При 8 МГц тактовой частоты и коэффициенте предделителя для Timer 0, равном 1/256, прерывания будут происходить с частотой 122,07 Гц. Нам же требуется 1 с (1 Гц), потому мы введем счетчик (count_sek) и будем его с каждым прерыванием увеличивать, пока он не отсчитает ровно 122 таких прерывания. После этого можно фиксировать число импульсов, сосчитанное к тому времени в регистрах Timer 1. Но если кварцевый резонатор идеально точный, то секунда получится чуть меньше настоящей (неучтенные 0,07 Гц дадут ошибку 576 мкс со знаком «минус»), и перед чтением значений мы введем задержку для компенсации этой недостачи, с помощью которой наш частотомер можно еще дополнительно калибровать (т. е. учесть исходную неточность кварца). В начале и в конце интервала будем переключать разряд 6 порта D (вывод 11), чтобы контролировать измерительный интервал в процессе калибровки. На рис. 19.5 представлен МК AT90S2313 с обозначением выводов для нашей цели.



Рис. 19.5.Подключение AT90S2313 для измерения частоты


Программа частотомера приведена в листинге 19.5 (определение регистров я опускаю, в данном случае их потребуется всего три — temp, count3 и count_sek). В секции прерываний введем прерывания Timer 0 и Timer 1 по переполнению (по меткам TIM0 и TIM1). Инициализация таймеров в секции начальной загрузки сводится к разрешению прерываний, но обязательно здесь же нужно очистить счетные регистры таймеров и все дополнительные

Листинг 19.5

ldi temp,(1<  ;разр. прер. Timer0 и Timerl

out TIMSK,temp

clr temp

out TCNT1H,temp

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

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

clr count3

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

ldi temp,0b00000100;