Счетчик импульсов на atmega8 своими руками
В статье ATmega. Формирование ШИМ сигнала реализовывал сигнал ШИМ 25 кГц. Осциллографом не обладаю, но проверить результат хочется. Делаем счетчик импульсов, проверяем работу.
Задача
На базе ATmega 328P реализовать счетчик импульсов для проверки ШИМ 25 кГц, точность измерений до импульса не нужна, но порядок нужно знать.
Решение
Логика решения проста, отслеживаем импульсы, по которым инкрементируем глобальную переменную в течении секунды. Накопленное значение и будет частотой входящего сигнала.
Для считывания импульсов воспользуемся внешними прерываниями, они описаны на страницах 87-96 документации от производителя. В Atmega 328P есть два входа, которыми мы можем отслеживать внешние прерывания INT0(PD2) и INT1(PD3), для решения задачи воспользуемся INT0.
Настройка внешних прерываний
Первым делом необходимо настроить порт D как вход, а для избежания наводок подключу подтягивающий резистор.
Для определения по каким событиям будет вызываться обработчик прерывания нужно настроить регистр ERICA. Биты ISC00 и ISC01 отвечают за INT0, а ISC10 и ISC11 за INT1. Настройка отслеживаемых событий идентична, за разницей в битах:
00 — Низкий уровень сигнала;
01 — Любое логическое изменение сигнала;
10 — Нисходящий фронт сигнала;
11 — Восходящий фронт сигнала.
Для непосредственного включения входов прерываний служит регистр EIMSK, биты INT0 и INT1 отвечают за одноименные выходы. По вышеизложенному пишем код
Обработка внешних прерываний
Прерывания настроил, теперь надо их обработать. Для этого существует функция обработки прерывания ISR(), которой необходимо указать тип прерывания, в моем случае INT0_vect. В функции будем делать инкремент переменной Tic_Count:
Вывод результата
Для облегчения вывода результата, дабы не прикручивать дисплей воспользовался не чистой ATmega 328P, а Arduino UNO и Arduino NANO, на борту которых тот же МК.
Как писал выше точность измерений не столь важна, потому таймеров настраивать не буду, а просто в основном цикле один раз в секунду выведу накопленное значение переменной Tic_Count и обнулю ее. На время этих действий прекращаю обработку прерываний.
Ниже полный код решения задачи с комментариями:
Теперь остается подключить сигнал ШИМ к ножке PD2, и открыть монитор последовательного порта. Так же можно сформировать и проверить сигнал на одном МК.
Выводимые показания примерно равны ранее рассчитанной частоте, небольшие отличия ожидаемы из-за реализации. Для точного измерения наверное правильнее считать время между импульсами и от этого вычислять частоту.
Этот простой счётчик импульсов пригодится совместно с другими устройствами, например в станке для намотки трансформаторов и катушек или в самодельном пульсометре. В сегодняшнем устройстве в качестве образования импульсов для подсчёта выступает тактовая кнопка но вместо неё можно подключить другие устройства которые выдают импульсы и которые требуется подсчитывать.
Простой счётчик импульсов от 0 до 99
Необходимые детали:
Как сделать счётчик импульсов, инструкция:
Счётчик импульсов я собрал на макетной плате по такой схеме:
Простой счётчик импульсов от 0 до 99
При каждом нажатии правой кнопки SW1 на семисегментном индикаторе значение увеличивается на единицу. Левая кнопка обнуляет значение. Счётчик может отсчитывать от 0 до 99, после чего отсчёт начинается опять с нуля.
Простой счётчик импульсов от 0 до 99
Микросхема NE555 отвечает за генерацию импульсов, а две последующие микросхемы десятичные счётчики выводят цифры на семисегментные индикаторы.
Простой счётчик импульсов от 0 до 99
Чтобы увеличить количество цифр например до 3-х то нужно добавить ещё одну микросхему CD4026 (IC4) и к ней семисегментный индикатор подключив при этом 5 вывод микросхемы IC3 со входом 1 микросхемы IC4.
Простой счётчик импульсов от 0 до 99
Простой счётчик импульсов с отсчётом от 0 до 99 готов, в следующей статье мы как раз применим его на практике в мониторе сердечного ритма (пульсометре).
Радиолюбителям, схемотехникам иногда необходимо настроить какое-нибудь цифровое устройство, как например, счетчик импульсов, тахометр, осциллограф и т.п. Или просто узнать, работает ли оно. Очень удобно пользоваться генератором, выдающим прямоугольные импульсы различной частоты.
Проект такого генератора я и хочу предложить.
Сначала схема генератора импульсов:
Основу устройства составляет популярный микроконтроллер ATmega8 фирмы Atmel.
Описание схемы. Вся схема питается напряжением 5 В. Микроконтроллер тактируется частотой 8 МГц, которая стабилизирована кварцем Х1. Для генерации импульсов используется таймер/счетчик №1. В виде кнопок на схеме, подключенных к выводам PC3, PC4 и PC5 изображен энкодер. Две крайних кнопки заменяют переключение энкодера при вращении, а кнопка посередине – это кнопка энкодера, замыкающаяся при нажатии на его ось. Прямоугольные импульсы заданной с помощью энкодера частоты амплитудой 5 В снимаются с выхода таймера 1 (OCR1A). Для отображения выходной частоты применяется 16-и символьный однострочный ЖК-дисплей WH1601, подключенный к порту D микроконтроллера. Дисплей тоже распространенный, на драйвере HD44780. Резистором R1 регулируется контраст дисплея. Обмен данными между МК и дисплеем организован с помощью 4-х проводной шины. Разъем J1 для внутрисхемного программирования МК.
Теперь о программе для микроконтроллера.
Программа написана в среде разработки CodeVisionAVR. В данной среде имеются готовые библиотеки для работы с дисплеем, да и настройка МК понятна и проста. Я использовал версию до выхода CodeVisionAVR версии 3.12. Она немного отличается в генерации кода с использованием Wizarda. Но, в основном, все то же самое. Далее все описано на примере работы с CodeVisionAVR версии 3.12. В интернете полно ссылок для изучения данной среды, например: изучение интегрированной среды разработки CodeVisionAVR.
Запускаем CVAVR. Создаем новый проект (New Project). Программа предложит использовать мастер создания проекта.
Соглашаемся. Затем выбираем семейство контроллера.
Далее с левой стороны выбираем закладку Chip, выбираем контроллер (ATmega8), вводим его рабочую частоту (8 МГц).
Настраиваем порты ввода-вывода. Нужно сделать выходом бит 1 порта B (PB1) – с него снимается генерируемая частота. Порт D пока оставляем как есть. А выводы, с которых будет считываться состояние энкодера (PC3, PC4, PC5) настроить на вход (Data Direction: In) и включить внутреннюю подтяжку к питанию (Pullup/Output Value – значение P).
Переходим на вкладку Timers/Counters. Здесь нужно настроить 2 таймера: Timer0 и Timer1, остальные таймеры оставляем выключенными (Clock Value: Stopped).
Настраиваем Timer1. Нужно выбрать режим (Mode) CTC (Clear Timer on Compare – Сброс при совпадении). В этом режиме выход таймера будет переключаться в лог. 0 как только содержимое счетного регистра TCNT1 совпадет с регистром OCR1A. За счет изменения значения в регистре OCR1A мы и будет изменять частоту выходных импульсов. В схеме используется выход А таймера 1. Для него нужно выбрать значение Toggle on Compare Match (переключиться в другое состояние при совпадении). В общем, смотрим картинку:
Следующий шаг – подключение дисплея. В CodeVisionAVR достаточно указать к какому порту МК будет подключен дисплей. Выбираем порт D.
Теперь нужно сгенерировать код программы. Нажимаем Program ->Generate, Save and Exit
Программа предложит сохранить 3 файла: файл кода (расширение .с), файл проекта (.prj) и файл ресурсов (.cwp).
Теперь нужно зайти в настройки Project -> Configure и проверить, что правильно заданы тип МК и его тактовая частота:
Готовый проект для CVAVR
ATmega8_Generator (316,0 KiB, 1 708 hits)
Для прошивки МК нужен файл с расширением .hex. В готовом проекте это файл Gen_mega8.hex. Он расположен в папке Release/Exe/.
Если есть желание написать программу с нуля, то в проекте есть комментарии, какие команды для чего нужны. Или можно просто вставить готовый код из файла gen_mega8.c. И, изменяя его, смотреть как это отражается на готовом устройстве. Для генерации файла прошивки МК нужно нажать кнопку Build the project. Файл с расширением .hex сгенерится в папку Release/Exe/.
Fuse-биты контроллера программируются на работу с внешним кварцевым резонатором 8 МГц в соответствии с рисунком:
Теперь об управлении генератором импульсов.
Для увеличения нагрузочной способности генератора выход МК можно включить через транзистор.
О точности выходной частоты.
Значения выходной частоты проверялись осциллографом. На малых частотах, примерно до 200Гц, значения совпадают с измеренными на осциллографе, затем чем больше частота, тем больше погрешность (это получается из-за нецелых чисел, записываемых в регистр сравнения). Точность можно повысить, если в регистр сравнения заносить константы из массива (мне высокие частоты не нужны были, да и просто лень считать и заносить числа в массив)). На высоких частотах, чтобы повысить точность, нужно брать другую частоту таймера.
Недавно приобрел очень удобный и компактный мультиметр, которым можно померить частоту (до 9.999 МГц). Вот его видеообзор . А заказать можно по этой ссылке .
Микроконтроллер можно прошить специальным программатором либо сделать простой программатор самому. Например, я успешно использую программатор USBasp. Такой программатор можно заказать по ссылке по очень привлекательной цене.
[contact-form-7 title=”Контактная форма 1?]
Если Вы нашли что-то полезное, поделитесь с друзьями:
Приветствую! Радиолюбителям, схемотехникам иногда необходимо настроить какое-нибудь цифровое устройство, как например, счетчик импульсов, тахометр, осциллограф и т.п. Или просто узнать, работает ли оно. Очень удобно пользоваться генератором, выдающим прямоугольные импульсы различной частоты. Проект такого генератора я и хочу предложить. Сначала схема генератора импульсов: Основу устройства составляет популярный микроконтроллер ATmega8 фирмы Atmel. Описание схемы. Вся схема […]
Политика конфиденциальности и использования файлов сookie: Этот сайт использует файлы cookie. Продолжая пользоваться этим сайтом, вы соглашаетесь с их использованием.
Дополнительную информацию, в том числе об управлении файлами cookie, можно найти здесь: Политика использования файлов cookie
Сегодня мы узнаем, что такое таймеры-счётчики в микроконтроллерах и для чего они нужны, а также что такое прерывания и для чего они тоже нужны.
Таймеры-счётчики — это такие устройства или модули в микроконтроллере, которые, как видно из названия, постоянно что-то считают. Считают они либо до определённой величины, либо до такой величины, сколько они битности. Считают они постоянно с одной скоростью, со скоростью тактовой частоты микроконтроллера, поправленной на делители частоты, которые мы будем конфигурировать в определённых регистрах.
И вот эти таймеры-счётчики постоянно считают, если мы их инициализируем.
Таймеров в МК Atmega8 три.
Два из них — это восьмибитные таймеры, то есть такие, которые могут максимально досчитать только до 255. Данной величины нам будет маловато. Даже если мы применим максимальный делитель частоты, то мы не то что секунду не отсчитаем, мы даже полсекунды не сможем посчитать. А у нас задача именно такая, чтобы досчитывать до 1 секунды, чтобы управлять наращиванием счёта светодиодного индикатора. Можно конечно применить ещё наращивание переменной до определенной величины, но хотелось бы полностью аппаратного счёта.
Но есть ещё один таймер — это полноправный 16-битный таймер. Он не только 16-битный, но есть в нём ещё определённые прелести, которых нет у других таймеров. С данными опциями мы познакомимся позже.
Теперь коротко о прерываниях.
Прерывания (Interrupts) — это такие механизмы, которые прерывают код в зависимости от определённых условий или определённой обстановки, которые будут диктовать некоторые устройства, модули и шины, находящиеся в микроконтроллере.
В нашем контроллере Atmega8 существует 19 видов прерываний. Вот они все находятся в таблице в технической документации на контроллер
Какого типа могут быть условия? В нашем случае, например, досчитал таймер до определённой величины, либо например в какую-нибудь шину пришёл байт и другие условия.
На данный момент мы будем обрабатывать прерывание, которое находится в таблице, размещённой выше на 7 позиции — TIMER1 COMPA, вызываемое по адресу 0x006.
Теперь давайте рассмотрим наш 16-битный таймер или TIMER1.
Вот его структурная схема
Мы видим там регистр TCNTn, в котором постоянно меняется число, то есть оно постоянно наращивается. Практически это и есть счётчик. То есть данный регистр и хранит число, до которого и досчитал таймер.
А в регистры OCRnA и OCRnB (буквы n — это номер таймера, в нашем случае будет 1) — это регистры, в которые мы заносим число, с которым будет сравниваться чило в регистре TCNTn.
Например, занесли мы какое-нибудь число в регистр OCRnA и как только данное число совпало со значением в регистре счёта, то возникнет прерывание и мы его сможем обработать. Таймеры с прерываниями очень похожи на обычную задержку в коде, только когда мы находимся в задержке, то мы в это время не можем выполнять никакой код (ну опять же образно "мы", на самом деле АЛУ). А когда считает таймер, то весь код нашей программы в это время спокойно выполняется. Так что мы выигрываем колоссально, не давая простаивать огромным ресурсам контроллера по секунде или даже по полсекунды. В это время мы можем обрабатывать нажатия кнопок, которые мы также можем обрабатывать в таймере и многое другое.
Есть также регистр TCCR. Данный регистр — это регистр управления. Там настраиваются определенные биты, отвечающие за конфигурацию таймера.
Также у таймера существует несколько режимов, с которыми мы также познакомимся немного позденее.
Даный регист TCCR отвечает за установку делителя, чтобы таймер не так быстро считал, также он отвечает (вернее его определённые биты) за установку определённого режима.
За установку режима отвечают биты WGM
Мы видим здесь очень много разновидностей режимов.
Normal — это обычный режим, таймер считает до конца.
PWM — это ШИМ только разные разновидности, то есть таймер может играть роль широтно-импульсного модулятора. С данной технологией мы будем знакомиться в более поздних занятиях.
CTC — это сброс по совпадению, как раз то что нам будет нужно. Здесь то и сравнивются регистры TCNT и OCR. Таких режима два, нам нужен первый, второй работает с другим регистром.
Все разновидности режимов мы в данном занятии изучать не будем. Когда нам эти режимы потребуются, тогда и разберёмся.
Ну давайте не будем томить себя документацией и наконец-то попробуем что-то в какие-нибудь регистры занести.
Код, как всегда, был создан из прошлого проекта. Для протеуса также код был скопирован и переименован с прошлого занятия, также в свойствах контроллера был указан путь к новой прошивке. Проекты мы назовем Test07.
Попробуем как всегда скомпилировать код и запустить его в протеусе. Если всё нормально работает, то начинаем добавлять новый код.
Добавим ещё одну функцию, благо добавлять функции мы на прошлом занятии научились. Код функции разместим после функции segchar и до функции main. После из-за того, что мы будем внутри нашей новой функции вызывать функцию segchar.
Мало того, мы создадим не одну функцию, а целых две. В одну функцию мы разместим весь код инициализации нашего таймеру, а другая функция будет являться обработчиком прерывания от таймера, а такие функции они специфичны и вызывать их не требуется. Когда возникнет необходимость, они вызовутся сами в зависимости от определённых условий, которые были оговорены выше.
Поэтому первую функцию мы назвовём timer_ini
void timer_ini ( void )
Также давайте наши функции, а также какие-то законченные блоки с объявлением глобальных переменных, с прототипами функций будем отделять друг от друга вот такими чёрточками, которые за счет наличия двух слешей впереди компилятор обрабатывать не будет и примет их за комментарии. За счёт этих отчерчиваний мы будем видеть, где заканчивается одна функция и начинается другая.
Данная функция, как мы видим не имеет ни каких аргументов — ни входных, не возвращаемых. Давайте сразу данную функцию вызовем в функции main()
unsigned char butcount=0, butstate=0;
timer_ini ();
Теперь мы данную функцию начнём потихонечку наполнять кодом.
Начнем с регистра управления таймером, например с TCCR1B. Используя нашу любимую операцию "ИЛИ", мы в определённый бит регистра занесём единичку
void timer_ini ( void )
TCCR1B |= (1 WGM12 ); // устанавливаем режим СТС (сброс по совпадению)
Из комментария мы видим, что мы работает с битами режима, и установим мы из них только бит WGM12, остальные оставим нули. Исходя из этого мы сконфигурировали вот такой режим:
Также у таймера существует ещё вот такой регистр — TIMSK. Данный регистр отвечает за маски прерываний — Interrupt Mask. Доступен данный регистр для всех таймеров, не только для первого, он общий. В данном регистре мы установим бит OCIE1A, который включит нужный нам тип прерывания TIMER1 COMPA
TCCR1B |= (1 WGM12 ); // устанавливаем режим СТС (сброс по совпадению)
TIMSK |= (1 OCIE1A ); //устанавливаем бит разрешения прерывания 1ого счетчика по совпадению с OCR1A(H и L)
Теперь давайте поиграемся с самими регистрами сравнения OCR1A(H и L). Для этого придётся немного посчитать. Регистр OCR1AH хранит старшую часть числа для сравнения, а регистр OCR1AL — младшую.
Но прежде чем посчитать, давайте пока напишем код с любыми значениями данного регистра и потом поправим, так как дальше мы будем инициализировать делитель и он тоже будет учавствовать в расчёте требуемого времени счёта. Без делителя таймер будет слишком быстро считать.
TIMSK |= (1 OCIE1A ); //устанавливаем бит разрешения прерывания 1ого счетчика по совпадению с OCR1A(H и L)
OCR1AH = 0b10000000; //записываем в регистр число для сравнения
OCR1AL = 0b00000000;
TCCR1B |= ( ); //установим делитель.
Пока никакой делитель не устанавливаем, так как мы его ещё не посчитали. Давайте мы этим и займёмся.
Пока у нас в регистре OCR1A находится число 0b1000000000000000, что соответствует десятичному числу 32768.
Микроконтроллер у нас работает, как мы договорились, на частоте 8000000 Гц.
Разделим 8000000 на 32768, получим приблизительно 244,14. Вот с такой частотой в герцах и будет работать наш таймер, если мы не применим делитель. То есть цифры наши будут меняться 244 раза в секунду, поэтому мы их даже не увидим. Поэтому нужно будет применить делитель частоты таймера. Выберем делитель на 256. Он нам как раз подойдёт, а ровно до 1 Гц мы скорректируем затем числом сравнения.
Вот какие существуют делители для 1 таймера
Я выделил в таблице требуемый нам делитель. Мы видим, что нам требуется установить только бит CS12.
Так как делитель частоты у нас 256, то на этот делитель мы поделим 8000000, получится 31250, вот такое вот мы и должны занести число в TCNT. До такого числа и будет считать наш таймер, чтобы досчитать до 1 секунды. Число 31250 — это в двоичном представлении 0b0111101000010010. Занесём данное число в регистровую пару, и также применим делитель
OCR1AH = 0b01111010; //записываем в регистр число для сравнения
OCR1AL = 0b00010010;
TCCR1B |= (1 CS12 ); //установим делитель.
С данной функцией всё.
Теперь следующая функция — обработчик прерывания от таймера по совпадению. Пишется она вот так
ISR ( TIMER1_COMPA_vect )
И тело этой функции будет выполняться само по факту наступления совпадения чисел.
Нам нужна будет переменная. Объявим её глобально, в начале файла
unsigned char i ;
Соответственно, из кода в функции main() мы такую же переменную уберём
unsigned char i ;
Также закомментируем весь код в бесконечном цикле. Его роль теперь у нас будет выполнять таймер, и, я думаю, он с этим справится не хуже, а даже лучше, "никому" при этом не мешая.
while (1)
// for(i=0;i
// while (butstate==0)
// if (!(PINB&0b00000001))
// if(butcount
// butcount++;
// else
// i=0;
// butstate=1;
// else
// if(butcount > 0)
// butcount—;
// else
// butstate=1;
// segchar(i);
// _delay_ms(500);
// butstate=0;
Теперь, собственно, тело функции-обработчика. Здесь мы будем вызывать функцию segchar. Затем будем наращивать на 1 переменную i. И чтобы она не ушла за пределы однозначного числа, будем её обнулять при данном условии
if ( i >9) i =0;
segchar ( i );
i ++;
Теперь немного исправим код вначале функции main(). Порт D, отвечающий за состояние сегментов, забьём единичками, чтобы при включении у нас не светился индикатор, так как он с общим анодом. Затем мы здесь занесём число 0 в глобавльную переменную i, просто для порядка. Вообще, как правило, при старте в неициализированных переменных и так всегда нули. Но мы всё же проинициализируем её. И, самое главное, чтобы прерывание от таймера работало, её недостаточно включить в инициализации таймера. Также вообще для работы всех прерываний необходимо разрешить глобальные прерывания. Для этого существует специальная функция sei() — Set Interrupt.
Теперь код будет вот таким
PORTD = 0b11111111;
i =0;
sei ();
Также ещё мы обязаны подключить файл библиотеки прерываний вначале файла
Также переменные для кнопки нам пока не потребуются, так как с кнопкой мы сегодня работать не будем. Закомментируем их
//unsigned char butcount=0, butstate=0;
Соберём наш код и проверим его работоспособность сначала в протеусе. Если всё нормально работает, то проверим также в живой схеме
Всё у нас работает. Отлично!
Вот такой вот получился секундомер. Но так как у нас даже нет кварцевого резонатора, то данный секундомер нельзя назвать точным.
Тем не менее сегодня мы с вами много чему научились. Мы узнали о прерываниях, также научились их обрабатывать, Научились работать с таймерами, конфигурировать несколько новых регистров микроконтроллера, до этого мы работали только с регистрами портов. Также за счёт всего этого мы значительно разгрузили арифметическо-логическое устройство нашего микроконтроллера.
Купить программатор можно здесь (продавец надёжный) USBASP USBISP 2.0
Читайте также: