Тахометр на Attiny.Продолжаем знакомство с AVR.

Для начала оговорим принцип работы и необходимые для изготовления детали.Прежде всего, нужен собственно датчик, который будет считать обороты.Его я взял из старого принтера.Там он использовался для определения наличия бумаги.
Датчик работает так: внутри него находится оптопара, состоящая из инфракрасного светодиода и фоторезистора.Когда объект(желательно белого цвета для лучшего отражения) находится над оптопарой свет, излучаемый светодиодом, отражается от него и попадает на фототранзистор.Это то же самое, что и обычный транзистор, только база управляется не подаваемым на нее током, а светом.Следовательно транзистор открывается, передавая логическую единицу на МК.

Так выглядит датчик, который использовал я:

Транзистор нужно подключать к питанию с резистором 4,7кОм, можно взять другой номинал, но 4,7кОм оптимальный вариант по току:

Для того чтобы, мы могли считать информацию понадобится дисплей.У меня под руками был однострочный 16-символьный алфавитно-цифровой экземпляр.Для регулировки контрастности дисплея желательно прикрутить переменный резистор.Даташит моего дисплея(ROHM 2034R) гласит, что нужен резистор от 10 до 20 кОм, но традицию использовать не то, что надо, а то что есть никто не отменял, поэтому я откопал резистор от 0 до 33 кОм.Если не найдете нужный переменный резистор или просто не захотите его ставить, можно сделать простой делитель напряжения из двух резисторов.Контрастность у дисплея при этом регулироваться не будет, конечно.

На этом скромный список деталек заканчивается, приступаем к составлению схемы.
Дисплей подключается к микроконтроллеру так, как показано на схеме(в данном случае для порта В).

Аналогично для порта С:

Переменный резистор, как уже было сказано используется для регулировки контрастности, подключается к 3му выводу дисплея(обозначен, как LCD HEADER V0).
Схема вышла, в общем, несложная.В архиве выложу ее полностью в формате spl(SPLAN).
Теперь время заняться прошивкой.
Любой проект с дисплеем начинается с того, что мы прописываем микроконтроллеру порт, на котором висит дисплей.Причем, это делается с тэгом #asm(тэг для ассемблерной команды).
Адрес конкретного порта ищем в библиотеке МК, которая валяется в папке с компилятором:

#asm
.equ __lcd_port=0x18
#endasm
Подключаем 2 необходимые библиотеки:
#include <lcd.h> //библиотека работы с дисплеем
#include <tiny2313.h> //библиотека МК
После подключения библиотек объявляем переменные.int-целые числа от -32768 до 32767.Для числа оборотов в секунду этого хватит.
int rps=0; //Rotates Per Second - число оборотов в секунду
int pr=0; //Счетчик переполнений таймера
Главная программа:
void main()
Порт D настраиваем на ввод и ставим единицы по умолчанию на все его биты.
{
DDRD=0x00;
PORTD=0xff;
Инициализируем дисплей.В скобках пишем число символов в строке.
lcd_init(16);
Дальше надо настроить таймер.Он примечателен тем, что тикает даже во время выполнения программы.В этом его главное отличие от обычной задержки(команды delay).Эта задержка полностью остановит МК, но в данном случае это недопустимо, так как прибор должен считать обороты без остановок.Тут и приходит на помощь таймер/счетчик.
Смысл программы такой: по внешнему прерыванию(от датчика) запускается цикл, в котором прибавляется единица к переменной rps.Таймер в это время продолжает тикать.Как только он доходит до 1 секунды, стартует другое прерывание по таймеру/счетчику.В нем переменная rps выводится на дисплей и обнуляется.Таким образом, частота обновления показаний 1 секунда.
В Attiny2313 есть 2 таймера 8 и 16 разрядный.Мы воспользуемся 8-разрядным.Он обозначается, как таймер/счетчик 0. 8 разрядов таймера означает, что в нем 2 в 8 степени позиций = 256.
Настройка таймера начинается с регистра управления TCCR0:

Расчеты таймера основаны на тактовой частоте, а в этом регистре мы выбираем предделитель тактовой частоты, с которой будет тикать таймер.Это очень сильно облегчает расчеты.К примеру, если МК работает с частотой 8 мегагерц, поделив ее на 1024, мы получаем сравнительно небольшое число, работать с которым намного легче.
Программируем биты в соответствии с таблицей:

TCCR0=0x05;//делим тактовую частоту на 1024
На этом этапе нужно определиться с начальным значением таймера.Оно следует из того, сколько раз должен переполниться таймер для достижения определенного времени в соответствии с тактовой частотой с предделителем.
Расчеты:
8000000/1024=7812,5 (делим тактовую частоту МК на выбранный предделитель)
7812,5/256=30,52 (считаем число переполнений)
Значит потребуется примерно 30 переполнений всего таймера(с 0) для достижения 1 секунды.
Ставим таймер в 0.
TCNT0=(0);
В регистр TIFR-флаг переполнения таймера.Когда таймер переполнен автоматически устанавливается 1.
Этот регистр нужно сбросить в 0:
TIFR=0;
TIMSK — регистр прерываний по таймеру.

Разрешаем прерывания по таймеру/счетчику 0.
TIMSK=0x02;
Также нам понадобятся и прерывания по внешнему сигналу(в данном случае с датчика).
Они управляются регистром GIMSK.INT1(PD3)-выход микроконтроллера, к которому будем цеплять датчик.

Разрешаем прерывания по внешнему сигналу с порта INT1:
GIMSK=0x80;
Регистр МCUCR управляет видом внешних прерываний.Для тахометра подойдет прерывание по спадающему фронту.Только в этом случае он будет показывать реальное число оборотов.

Программируем регистр в соответствии с таблицей:

MCUCR=0x09;
Ассемблерной командой разрешаем все прерывания:
#asm
sei
#endasm
Чтобы программа никогда не завершалась добавляем бесконечный цикл:
count:while(1);
goto count;
Помимо главной программы в проекте присутствуют еще 2 подпрограммы прерываний-по таймеру и по внешнему сигналу.
По таймеру:
Обозначаем начало подпрограммы прерываний:
interrupt[TIM0_OVF]void timer0_overflow(void)
Нужно снова обнулить таймер, чтобы он начал отсчитывать новую секунду.
{
TCNT0=(0);
Мы посчитали, что для отсчета одной секунды надо, чтобы таймер переполнился 30 раз.
Поэтому, как только счетчик прерываний(отдельная переменная pr, объявленная вначале)станет равен 30, число оборотов в секунду выводится на дисплей, а обе переменные обнуляются.
С выводом переменной на экран пришлось повозиться отдельно.Как выяснилось, выводить напрямую переменную нельзя, нужно либо сделать из нее строку, либо преобразовать ее в последовательность номеров из таблицы символов(есть в даташите на любой дисплей):

Первый способ можно устроить с помощью функции sprintf, но она ест слишком много памяти, поэтому на тиньках корректно не работает.
Воспользуемся вторым способом.Будем выводить переменную посимвольно с помощью команды lcd_putchar('код символа в таблице ').Переходим от цифры к коду символа путем деления переменной с остатком на числа кратные 10 и прибавлением числа 48(для совпадения с табличным значением).В этой программе прописан вывод четырех символов, но можно изменить ее для любого другого числа.Недостаток метода-вместо чисел превышающих 9999 будут выводится левые знаки, но вряд ли что-то сможет крутиться с частотой 10 килогерц, да и датчик от принтера потянет максимум 1 килогерц, если верить даташиту.
if (pr==30)
{
lcd_gotoxy(0,0);//переходим на 1-ую строку,1-ый символ(представьте координатную плоскость x и y)
lcd_putchar(rps/1000+48);
lcd_putchar(rps%1000/100+48);
lcd_putchar(rps%1000%100/10+48);
lcd_putchar(rps%1000%100%10+48);
rps=0;
pr=0;
}
Если таймер переполнился но, счетчик еще не достиг 30, просто прибавляем к нему 1 и ждем следующего переполнения.
else
pr=pr+1;
}
Вот и вся программа.Шьем МК и испытываем девайс в действии.
В таком оформлении выглядит не очень красиво, но работает.
Окончательное оформление. Добавлены 2 конденсатора на питание, кнопка и светодиод(для экспериментов), а также разъем под программатор Громова.


Наконец, видео.В качестве демонстрационного полигона пришлось соорудить из подручных средств «вентилятор»:
Еще испытал это изобретение на шуруповерте.Результат порадовал.Показал 4 оборота в секунду, производителем заявлено 250 об/мин.Из целых вариантов показаний 4 самый точный, который прибор мог вывести, т.к. 4*60=240, а 5*60 это уже 300 :).
В архиве: проект CVAVR под 8 мегагерц с исходниками, прошивка, схема(SPLAN), фьюзы для Attiny2313(8 мегагерц от встроенного тактового генератора).
Во втором архиве, на всякий случай, даташиты на дисплей и датчик.Мало ли что, может пригодятся…

- 0
- 30 июня 2011, 12:59
- rad
- 2
Файлы в топике:
taxometr.zip, datasheets.zip
count:while(1);
goto count;
хе-хе. если while не сработает, поможет goto?)
interrupt[TIM0_OVF]void timer0_overflow(void)
{
TCNT0=(0);
if (pr==30)
{
lcd_gotoxy(0,0);
lcd_putchar(rps/1000+48);
lcd_putchar(rps%1000/100+48);
lcd_putchar(rps%1000%100/10+48);
lcd_putchar(rps%1000%100%10+48);
rps=0;
pr=0;
}
работу с дисплеем лучше выкинуть из прерывания нафик. просто запоминаешь значение в памяти, затем юзаешь в основной проге. вообще, чем меньше кода в прерываниях, тем лучше. тем более, таких тяжёлых операций, как деление.
алсо, ставь пробелы после точки!
Нда. Неплохой пример, как не надо проектировать и реализовывать программу. В плане проектирования — низкая точность отсчета опорного интервала времени, хотя ничто не мешает сделать его с точностью до нескольких тактов, да и вообще считать количество оборотов в секунду идея так себе. И работа с дисплеем в прерывании туда же. В плане программирования… Ну, про это уже написали.
А можно поинтересоваться, чем грозит работа с дисплеем в прерывании?
Ну а в плане идеи, она специфическая, конечно, в повседневной жизни может и не каждому пригодится, но ее практическое применение все же возможно.
Ну а в плане идеи, она специфическая, конечно, в повседневной жизни может и не каждому пригодится, но ее практическое применение все же возможно.
Потому что не надо слоупочить в прерываниях, делая там ненужную работу. А либа дисплея еще и скорее всего содержит задержки. Да и не единственное это, чем код отличился.
Идея мерить частоту тоже так себе, точнее мерить период. Если же мерить частоту так, как это делаешь ты — разрешение получается всего лишь 60 RPM. Это в большинстве случаев больше влияет на точность, чем отсчитываемый с точностью около 2% опорный интервал.
Идея мерить частоту тоже так себе, точнее мерить период. Если же мерить частоту так, как это делаешь ты — разрешение получается всего лишь 60 RPM. Это в большинстве случаев больше влияет на точность, чем отсчитываемый с точностью около 2% опорный интервал.
В регистр TIFR-флаг переполнения таймера.Когда таймер переполнен автоматически устанавливается 1.Что-то ты намудрил с регистром флагов TIFR.
Этот регистр нужно сбросить в 0:
TIFR=0;
Во-первых в AVR флаги сбрасываются записью в них логической единицы.
Во-вторых зачем в инициализации записывать в этот регистр 0, если флаг не установлен то там 0, ну а если установлен то это ни к чему не приведёт.
Ну и добавлю насчёт прерывания по переполнению Тimer0, в данном случае не необходимости обнулять счётный регистр TCNT0=(0), т.к. при прирывании он уже обнулён.
Комментарии (17)
RSS свернуть / развернуть