Частотомер на ардуино своими руками

Добавил пользователь Владимир З.
Обновлено: 05.10.2024

Среди измерительных приборов частотомеры занимают определенное положение. Частотомеры применяются для измерения частоты переменного или импульсного тока. А так же частотомер может быть полезен для отслеживания частоты переменного тока в составе умного дома. Принцип любого частотомера одинаков и состоит в подсчете количества колебания за одну секунду времени.
Предлагаю частотомер на Arduino, предел измерений мин. 250Гц макс. 500000Гц

volatile unsigned int tachBuf ;
unsigned long tachValue ;

Пробник-частотомер - это портативный инструмент, предназначенный для отладки электронных цепей, предоставляя визуальную индикацию частоты или напряжения. Для периодической формы сигнала прибор производит измерение частоты в диапазоне от 1 Гц до 5 МГц и с точностью не хуже 0,3%. В режиме вольтметра пробник показывает измеренное значение напряжения.

Прибор собран на микроконтроллере ATtiny84A и питается от небольшого литиевого аккумулятора.

Введение

Самый очевидный способ реализовать частотомер - это подсчитать количество импульсов в течение одной секунды, тогда напрямую результатом измерения будет частота. Я называю его частотным методом. Недостатком этого метода является то, что для точного измерения низких частот требуется длительное время выборки.

Другой способ - измерить интервал между двумя импульсами входного сигнала, обратная величина дает частоту. Я называю его интервальным методом. Например, если интервал между импульсами составляет одну секунду, частота равна 1 Гц. Недостатком этого метода является то, что для высоких частот нужно очень точно измерять интервал.

Идеальное решение - использовать частотный метод для высоких частот и интервальный метод для низких частот, что является подходом, который я применил в этом пробнике. Ниже я объясню, как рассчитать наилучшую точку для переключения между режимами измерения.

Первоначально я начал работу над этим проектом пару лет назад, но он оказался намного сложнее, чем я ожидал, и поэтому решил отложить его в сторону. Я вернулся к нему в начале этого года, и, к счастью, мне удалось решить все проблемы.

Процессор

Модуль USI микроконтроллера используется для управления OLED-дисплеем с I2C, который отображает показания в символах двойного размера с использованием символьного шрифта 6x8 пикселей.

Диапазон частот

Верхний предел частоты, которую можно измерить в частотном режиме, определяется максимальной частотой, которую можно измерять на входе T0. В описании сказано, что это clk/2,5, где clk - частота процессора. При напряжении питания 3 В максимальная частота процессора составляет около 12 МГц, что дает максимальную измеряемую частоту 4,8 МГц.

В интервальном режиме нижний предел частоты определяется тем, как долго пользователь готов ждать ответа. Я выбрал 1 Гц, чтобы показания обновлялись на дисплее раз в секунду.

Точность

В частотном режиме точность измерения частоты увеличивается с увеличением частоты, так как в выборке больше циклов выборки. В программе, описанной ниже, я использую Таймер/Счетчик 0 для подсчета циклов в прерывании от Таймер/Счетчик 1. Он синхронизируется с системной тактовой частотой 12 МГц, деленной на 16384 x 8, что дает прерывание 91,6 раза в секунду. Таким образом, точность измерения частоты составляет:

100 x 12000000 / (8 x 16384 x f) %

Например, при 100 кГц точность измерения составляет 0,09%.

В интервальном режиме точность уменьшается с частотой, поскольку между двумя переходами меньше циклов таймера. В программе используется захват по входу Таймера/Счетчика 1 для измерения интервала между двумя переходами входного сигнала. Таким образом, точность измерения частоты составляет:

100 x f / 12000000 %

Например, на частоте 10 кГц точность измерения составляет 0,083%.

Вот график, показывающий эти точки и общую точность каждого метода:

Наилучшая точность определяется местом пересечения линий, что достигается с помощью интервального режима для частот ниже 30 кГц и частотного режима выше 30 кГц, это дает точность не хуже примерно 0,3% по всем частотам.

Измеритель напряжения

Если на щупе есть постоянное напряжение, индикатор пробника отображает напряжение от 0 до 5 В с точностью до одной части от 1024, или 0,1%.

Напряжение считывается на аналоговом входе ADC1. АЦП использует внутренний источник опорного напряжения 1,1 В, поэтому используется входной делитель напряжения для приведения диапазона 0 - 5 В к диапазону 0 - 1,1 В.

Тестирование схемы

Чтобы протестировать пробник на высоких частотах, я использовал свой Programmable Signal Generator для генерации частот от 1 кГц до 5 МГц. Для более низких частот я использовал свой Tiny Function Generator для частот до 1 Гц, также проверил частоты по всему диапазону с помощью цифрового мультиметра.

Схема

При использовании модуля USI для обеспечения интерфейса I2C невозможно использовать внутренние подтягивающие резисторы на SCL и SDA, поэтому в схеме используются внешние на 4,7 кОм.

Единственные оставшиеся компоненты - это кварц на 12 МГц и кнопка для сброса процессора и вывода его из спящего режима.

Сборка

Сначала я протестировал конструкцию на макетной плате с использованием выводных компонентов. Для окончательной версии плата была спроектирована в Eagle и изготовлена в OSH Park, используя их сервис After Dark, в котором используется прозрачная паяльная маска на черной подложке и медные дорожки становятся видимыми.

ATtiny84A используется в корпусе SOIC, а резисторы, конденсаторы и диоды имеют размер 0805, поэтому их относительно легко припаять вручную. Размер кварца 5x3,2 мм. Дисплей представляет собой модуль OLED 128x32 I2C с драйвером SSD1306. Я использовал недорогой модуль с AliExpress. В качестве щупа используется обычная иголка для шитья.

Первоначально я планировал запитать схему от дискового элемента CR1225 на 3 В в держателе для поверхностного монтажа, но он не смог обеспечить достаточный ток. К счастью, на печатной плате были предусмотрены выводы для аккумулятора LiPo, поэтому я использовал элемент емкостью 40 мАч, который помещается в том же пространстве и крепится к плате с помощью двустороннего скотча:


Аккумулятор можно зарядить на месте с помощью зарядного устройства Li-Po. Чтобы избежать использования переключателя вкл/выкл, устройство автоматически переходит в спящий режим через 30 секунд, чтобы разбудить его необходимо нажать на кнопку.

Я использовал термофен Youyue 858D+ при температуре 250°C, чтобы припаять SMD-компоненты к задней части платы. Дисплей закреплен с помощью двустороннего скотча на передней части платы, а затем припаян с помощью четырех выводов, которые были обрезаны заподлицо с платой. Если нет термофена, вы сможете паять SMD-компоненты с небольшой осторожностью, используя паяльник с тонким наконечником.

Программа

Программа состоит из интерфейса дисплея для управления OLED-дисплеем, процедур для реализации трех режимов измерения и основного цикла, который выбирает, какой режим использовать для данного входного сигнала.

Интерфейс дисплея использует те же процедуры, что и мой предыдущий Tiny Function Generator, в котором использовался аналогичный I2C OLED-дисплей. Текст отображается с использованием набора символов 6x8 пикселей, но с удвоенным масштабом, чтобы получить эффективный размер символа 12x16 пикселей, с использованием процедуры сглаживания, описанной в статье Smooth Big Text.

Текущий режим определяется переменной Mode, которая имеет постоянное значение INTERVAL в интервальном режиме и FREQUENCY в частотном режиме. Схема запускается в частотном режиме.

Программа сначала настраивает Таймер/Счетчик 0, Таймер/Счетчик 1, сторожевой таймер и АЦП:

Первоначально Таймеры/Счетчики остановлены, сторожевой таймер отключен и прерывания отключены.

Для запуска частотного или интервального режима программа вызывает GetFrequency() или GetInterval(), которые настраивают периферийные устройства соответствующим образом для каждого режима.

Частотный режим

Вызов GetFrequency() выполняет измерение в частотном режиме:

Чтобы получить 16-битное значение счетчика, генерируется прерывание сравнения при переполнении регистра Таймера/Счетчика 0 и инкрементируется переменная FreqHigh (мы не можем использовать более логичный вектор прерывания TIM0_OVF_vect, потому что он уже используется ядром Arduino):

Частота 16-битного Таймера/Счетчика 1 в 8 раз меньше системной частоты. При значении OCR1A, равном 16383, это дает прерывание сравнения каждые 16384 x 8 циклов или частоту 91,6 Гц.

Подпрограмма обслуживания прерываний отключает прерывания Таймера/Счетчика и устанавливает для переменной Count значение, полученное путем объединения регистра счетчика Таймера/Счетчика 0 и FreqHigh:

Фактическая частота в Гц определяется следующим образом:

где 46875/512 - наилучшее приближение к 91,6 Гц.

Затем вызывается подпрограмма PlotFrequency() для построения графика частоты на дисплее с соответствующими единицами измерения:

Интервальный режим

Вызов GetInterval() выполняет измерение в интервальном режиме:

В этом режиме используется только Таймер/Счетчик 1. Сначала очищается счетный регистр, включается прерывания по захвату и переполнению Таймера/Счетчика и активируется счетчик, который будет синхронизироваться с системной тактовой частотой.

Прерывание по переполнению увеличивает переменную IntHigh:

Прерывание по захвату подсчитывает два захвата входных данных, а затем устанавливает переменную Count равной разнице между ними:

Если на входе нет сигнала, подпрограмма GetInterval() заблокируется, поскольку захват входных данных не происходит. Таким образом, прерывание от сторожевого таймера на частоте 8 Гц используется для обратного отсчета переменной Timer:

Подпрограмма DelaySecond() используется для генерации задержки в одну секунду и возврата из GetInterval() по истечении этого времени:

Режим вольтметра

Наконец, режим вольтметра просто использует АЦП для измерения напряжения на аналоговом входе ADC1:

Основной цикл

Основной цикл многократно снимает показания в текущем выбранном режиме, при необходимости переключает режим, если показание выходит за пределы диапазона для этого режима:

В точке переключения присутствует некоторый гистерезис, чтобы избежать переключения между режимами для частоты в самой точке переключения.

Режим сна

Чтобы исключить из схемы переключатель вкл/выкл, программа прерывается примерно через 30 секунд, если на щупе нет входного сигнала, а напряжение ниже 0,20 В. Затем происходит выключение дисплея и перевод процессора в спящий режим. Потребляемый ток в режиме сна составляет около 7,1 мкА, что обеспечивает срок службы батареи емкостью 40 мАч около года. Нажатие на кнопку сбрасывает процессор и пробуждает устройство.

Компилирование программы

Я скомпилировал программу с помощью Spence Konde's ATTiny Core. Для этого в Arduino IDE выберите вариант ATtiny24/44/84 под заголовком ATTinyCore в меню плат. Затем убедитесь, что следующие параметры установлены правильно:

Задача, собственно, возникла из необходимости считывать частоту вращения обтюратора с оптическим датчиком, установленного на самодельном чашечном анемометре. Сколько там дырок не сверли по окружности, но когда ветер слабенький, частота с выхода датчика будет составлять единицы герц (особенно, если датчик хорошо отбалансирован и облегчен для снижения порога трогания по максимуму).

Озадачившись таким вопросом, я первым делом выяснил, что ничего хорошего стандартные библиотеки в этом плане не предлагают. Есть, оно конечно, FreqMeasure и FreqPeriod, но они мне не понравились с первого взгляда: излишне усложненные и к тому же с почти полностью отсутствующей документацией. В довершение всего прилагаемые к ним примеры у меня просто не заработали с первого раза (я догадываюсь, почему, но возиться не стал — неинтересно копаться в чужих ляпах).

Пришлось делать самому. Малые частоты нужно измерять через период, потому идеальный конечный результат — нечто вроде функции pulseIn(), только измеряющей не длительность импульса, а период. Получилось несколько вариантов, которые и предлагаю аудитории в надежде, что кому-нибудь они пригодятся. Для каждого варианта определялись границы применимости и рассматривались достоинства и недостатки в сравнении друг с другом.

Вариант 1. Переделываем pulseInLong()

На функции pulseIn() я сначала и зациклился — а нельзя ли ее приспособить к этому делу? В недрах папок Arduino (в файле wiring_pulse.c) обнаружился ее более продвинутый вариант под названием pulseInLong(). Введен он был, как я выяснил, где-то около версии 1.6.5, но чем этот вариант лучше оригинальной функции, так и не понял. Судя по тому, что функция не введена в официальный перечень — или ничем, или имеет какие-то невыясненные ограничения. Но структура ее мне показалась более прозрачной и проще поддающейся переделке в нужном направлении. Выглядит вызов функции так же, как и pulseIn():


В функции последовательно работают три условно-бесконечных цикла (с принудительным выходом по заданному timeout). В первом цикле ожидается перепад в состояние, заданное параметром state (HIGH или LOW) — чтобы пропустить текущий импульс, в середину которого мы, возможно, попали. Во втором ожидается начало следующего периода (обратный перепад), после чего определяется количество микросекунд на старте. Наконец, третий цикл — измерительный, ожидается опять перепад в состояние state, фиксируется разность количества микросекунд и возвращается в значении функции.

Переделка заключается в том, что после третьего цикла количество микросекунд не фиксируется, а добавляется четвертый цикл, идентичный второму. И только после достижения снова такого же перепада, как на старте, количество микросекунд фиксируется и возвращается в значении функции. Таким образом мы получаем длительность периода. Испытательный скетч с переделанной функцией, которую я переименовал в periodInLong, полностью выглядит так (часть комментов оставлена от оригинала):


Обратите внимание на вывод Tone_PIN и закомментированный вызов функции tone() в разделе setup(). Это сделано для проверки еще одного обстоятельства, о чем в конце статьи.

Для проверки работы на вывод 8 (произвольно выбранный в качестве IN_PIN) подавался сигнал от самодельного генератора на основе часового кварца и счетчика-делителя 561ИЕ16. На выходе его мы получаем частоты, кратные степеням двойки, от 2 до 2048 Гц, а также 16384 Гц (и при желании, еще 32768 Гц прямо с генератора).

Результаты выборки последовательных измерений для частот 2, 8, 64, а также 2048 и 16384 герца объединены в одну таблицу на рисунке (верхняя строчка — длительность в микросекундах, следующая — рассчитанная частота):

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

Способ 2. Идеологически правильный: задействуем Timer1

Вообще-то этот способ следовало бы реализовать на чистом ассемблере, а не в среде Arduino, тогда из него можно было бы вытянуть максимум возможного. Но и так, учитывая наш диапазон частот в единицы-десятки герц, получится неплохо.

Способ состоит в том, что мы запускаем 16-разрядный Timer1 сразу в двух режимах: счета и захвата событий. При счете удобно установить делитель тактовой частоты 1/8, тогда в 16-мегагерцовом контролере время подсчитывать будем тиками по половине микросекунды. При переполнении (65536 половин микросекунды) вызывается прерывание переполнения, которое инкрементирует счетчик третьего разряда (байта) длинного числа. Трех байтовых разрядов (16777216 половин микросекунды или около 8 секунд) вполне достаточно для наших целей подсчета периода частоты порядка единиц-десятков герц. По захвату события перепада уровня мы фиксируем трехразрядное число тиков, прошедших с предыдущего такого события (собирая его из значений регистров таймера плюс третий старший разряд), обнуляем все переменные и счетные регистры и ждем следующего перепада. По идее надо бы еще очищать счетчики предделителя тактовой частоты, но они все равно изменятся при работающем Timer0 (предделитель для таймеров 0 и 1 общий), а при делителе 1/8 эта ошибка будет незначимой.

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

Я предполагаю, что найдется немало народу, который захочет оспорить это утверждение. В Сети масса источников, выдвигающих тезис о том, что прерывания применять опасно, потому что якобы можно что-то потерять. Глубоко ошибочное мнение: все ровно наоборот — как раз в цикле loop() потерять легко, а в прерываниях очень трудно. Правильная программа должна работать преимущественно на прерываниях (за исключением процедур, которые там нельзя использовать — вроде команды sleep). Только тогда из контроллера можно выжать максимум возможного. Внутри контролера не бывает функций, которые длятся настолько долго, чтобы существенно помешать другим функциям в других прерываниях, даже если это операции c числами типа long или float. Самая долгая из операций — деление чисел типа long — выполняется примерно за 670-680 тактов, то есть где-то за 42 микросекунды и она редко бывает больше чем одна на все прерывание. Вот обмен с внешней средой длится гораздо дольше: так, передача байта со скоростью 9600 длится примерно миллисекунду. Но длинные процедуры обмена с ожиданием ответа вполне можно расставить в программе так, чтобы не мешать измерительным или иным операциям, критичным ко времени. А если все-таки ваш контроллер оказывается забит длительными вычислительными процедурами, то это значит, что неверно выбрана платформа: переходите на 32 разряда или вообще на Raspberry Pi.

Разберемся в этом плане с нашим примером подробнее. Сам подсчет тиков в Timer1 происходит аппаратно и ни от чего не зависит. Прерывания переполнения для наших условий (1/8 тактовой частоты 16 МГц) происходят каждые 32 миллисекунды. Само прерывание состоит из единственной операции инкрементирования переменной третьего разряда размером в один байт (см. далее). Если бы я реализовывал это на ассемблере, то хранил бы третий разряд в рабочем регистре и операция заняла бы ровно один такт. В случае Arduino (и вообще реализаций на С) переменная хранится в ОЗУ, потому еще теряется несколько тактов на извлечение/сохранение в памяти. Плюс вызов прерывания (7 тактов) и возврат (4 такта), то есть вся длительность процедуры составляет порядка микросекунды или чуть более. От длительности промежутка между переполнениями (32 мс) это составляет примерно 0,003%. И какова вероятность того, что некое случайное событие (например, нажатие внешней кнопки) произойдет именно в этот момент? Даже если вы будете все время нажимать на кнопку так быстро, как только сможете, вам едва ли удастся добиться совпадения.

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

Вот что способно реально помешать нашим прерываниям — это периодическое обновление функции millis() через прерывание переполнения Timer0, которое возникает каждую миллисекунду и длится несколько микросекунд (см. эту функцию в файле wiring.c). Относительно системного времени наши прерывания возникают также в случайный момент, но вероятность наткнуться на прерывание Timer0 составляет уже порядка процента, что немало. Если хотите пойти на поводу вашего перфекционизма, то на время измерений следует, строго говоря, отключать Timer0. Но если учесть, что максимальная ошибка составляет единицы микросекунд, а мы измеряем периоды длительностью от тысяч до сотен тысяч микросекунд, то на эту ошибку можно не обращать внимания.

Скажу сразу: все будет несколько иначе выглядеть тогда, когда все эти события не являются случайными относительно друг друга. Такое будет, если запускающий перепад синхронизирован с тактовым генератором самого контроллера. Именно с целью проверки, что происходит в этой ситуации, в скетче имеется закомментированная функция tone(). О результатах такой проверки – в конце статьи.

Скетч для проверки этой идеи выглядит следующим образом:


При выводе результатов мы учли, что один тик таймера равен 0,5 микросекунды. Кроме того, здесь введен флаг, который на время вывода препятствует изменению подсчитанной величины периода. На сам процесс измерений он не повлияет. Большая задержка в конце setup обусловлена необходимостью выждать некоторое время, иначе первые измерения будут ошибочными. Минимальная ее величина равна периоду измеряемых колебаний.

Кстати, действительно, а что будет, если частоты на входе нет вовсе? Эта ситуация никак не отрабатывается, потому что для безопасного выполнения программы это не требуется. Если оставить вход 8 подключенным к какому-либо потенциалу, то значение периода (переменная ttime) просто не будет меняться – в ней задержится то, что было ранее. Если это была какая-то частота, она и будет демонстрироваться. Если с момента загрузки ни одного импульса не проскочило, то будет ноль (на этот случай и ограничение вывода). А если, кстати, оставить вход 8 висящим в воздухе, то c довольно высокой точностью будет измеряться помеха 50 Гц.

Конечно, если по условиям измерений требуется как-то отрабатывать ситуацию отсутствия частоты на входе, то можно ввести таймаут на ожидание захвата. Например, это можно сделать через контроль в основном цикле состояния того же флага flag, значение которого нужно будет изменять в прерывании захвата. Теперь он будет сообщать, что прерывание произошло (или не произошло в течение заданного времени). А использовать результат измерения целесообразно будет тогда, когда измерение действительно произойдет, согласно состоянию флага.

Результаты измерений для тех же частот 2, 8, 64, а также 2048 и 16384 герца объединены в таблицу:

image

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

Такой способ можно рекомендовать при необходимости проведения наиболее точных измерений длительных периодов. Недостаток его также очевиден: измерения можно проводить только через вывод номер 8 (вывод PB0 контроллера).

Способ 3. Самый простой: по внешнему событию

Это самый простой и довольно очевидный способ. Мы запускаем внешнее прерывание (по перепаду на выводе) и одновременно фиксируем системное время все той же функцией micros(). Как только произойдет второе такое прерывание, мы вычисляем разницу и таким образом получаем период.

Источников ошибок здесь должно быть больше, потому что счетчик и внешние прерывания системно разделены и до фиксации показаний проходит определенное время. Но, во-первых, можно ожидать, что для больших периодов они не будут иметь значения, во-вторых, в реальности получилось даже лучше, чем ожидалось.

Скетч, реализующий эту идею, выглядит таким образом:

image


Как мы видим, здесь ошибка в измерениях находится в полном соответствии с разрешением функции micros(), равным 4 мкс — то есть фактически результат колеблется в пределах плюс-минус одного тика системного таймера, все законно. Это не лучшим образом сказывается на измерениях высоких частот, но для нашего диапазона вполне подходит. Потому этот способ можно рекомендовать для обычных применений.

Измерение контроллером частоты, генерируемой им самим

В общем-то это чисто познавательная задача, вероятно, не имеющая практических применений. Но мне стало интересно: а что будет, если измеряемая частота исходит от самого контролера? Удобно для этого использовать функцию tone(), которая занимает Timer2, и, следовательно, не будет пересекаться ни с системным временем, ни с Timer1 в случае его использования для измерения. Именно для этого в каждом из скетчей вставлена эта функция, работающая через вывод Tone_PIN.

Для каждого из скетчей функция раскомментировалась, и сначала проверялось, не влияет ли параллельная работа функции tone() на измерения при разных сочетаниях частот, как измеряемой, так и генерируемой. Ни в одном из вариантов явного влияния не было замечено. Затем вывод Tone_PIN подключался непосредственно ко входу измерения частоты IN_PIN и запускался монитор порта для контроля результатов.

Вообще-то я ожидал увидеть в результате один из двух вариантов: а) измерения будут работать, как ни в чем ни бывало; б) или, скорее, измерения будут безбожно врать, причем с регулярной систематической ошибкой (что должно было быть обусловлено сложением колебаний двух зависимых таймеров). Действительность оказалась интереснее предположений: измерения во всех трех случаях нормально работали, но в ограниченном диапазоне задаваемых частот. Причем нижняя граница определялась точно: входной период правильно измерялся, начиная от частоты 32 герца. Верхнюю границу точно определять я не стал — слишком хлопотно, но приблизительно она располагается несколько выше 1000 Гц. Все, что ниже или выше — определяется абсолютно неправильно. Причем, что самое интересное, без особой закономерности: показания в одной серии одни и те же, но после перезагрузки с теми же заданными значениями они становятся совсем другими.

Объяснить я эти результаты не берусь, но, наверное, это и не очень надо. На практике такой режим, как я уже говорил, бесполезен, а при нужде достаточно вспомнить эти эмпирические закономерности.

Последнее время мне очень часто требуется измерять частоту, уж очень много электронных проектов я делаю и поэтому появилось нужда в измерительном приборе - частотомере. Покупать данный прибор - я ещё школьник в 8 классе учусь а такая техника очень дорогая для меня. Сильно большие частоты мне измерять пока нет необходимости, хотя в скором времени возможно будет нужно. И поэтому я решил сделать свой частотомер своими руками! Стремясь к минимализму за основу взял AVR микроконтроллер ATtiny2313 и ЖКИ 16*1. Набросал проект в Proteus, написал прошивку и нарисовал принципиальную схему:

Собственно ничего сложного, всё очень просто. Собрал всё на бредборде, кто не знает это - макетная плата с механическими контактами. Проверил, работает! Вот фото отчёт:

DSC04480.jpg

Ну теперь надо реализовать прибор, сделать печатную плату и поместить в корпус.

DSC04484.jpg

И так, теперь когда все детальки собраны, пора делать печатную плату. Её я сделал универсальной, добавил контактные площадки, мало-ли захочется что нибудь добавить. Чертил печатную плату я программе Sprint Layout 4.0, найти чертёж можно в файлах к статье. Плату я делал лазерно-утюжным методом, вот что получилось:

DSC04482.jpg

Самое главное это хорошо и качественно припаять микроконтроллер, ведь он в SOIC корпусе.

DSC04477.jpg

Не проблема, и мельче паяли! Главное не переборщить припоя и не жалеть канифоли.

DSC04490.jpg

Запаиваем остальные детальки, вот что получилось:

DSC04492.jpg

Кстати, от лишнего канифоля на плате можно избавиться с помощью технического спирта. Так намного лучше:

DSC04493.jpg

DSC04495.jpg

После сборки прошиваем микроконтроллер, я прошивал с помощью программы SinaProg программатором USBtiny. Вот фьюз биты:

fuse.jpg

Подключить программатор к микроконтроллеру можно проводками, подключить их к разъёму для ЖКИ:

DSC04497.jpg

А reset припаять:

DSC04500.jpg

Распиновку подключения программатора к микроконтроллера не привожу, я думаю вы её знаете. После прошивки и установки фьюз-битов, устанавливаем ЖКИ и подаём питание на устройство:

DSC04504.jpg

Заработало, отлично! Теперь устанавливаем устройство в корпус:

Устройство в корпусе

Как вы видите я свой частотомер сделал на базе своего велокомпьютера, дело в том что я себе собрал более навороченный велокомпьютер (с большим дисплеем на Atmega32, скоро про него напишу статью) а из этого и решил сделать частотомер, только плату переделал. И конечно видео работы устройства:

На видео видно что в качестве генератора я использую компьютер и программу NCH Tone Generator.

Читайте также: