Прерывания по таймеру
- Войдите на сайт для отправки комментариев
Ср, 24/10/2012 - 20:38
Прошу помощи в пояснении по принципу работы с таймерами. Искал долго и много но так ответы на вопросы и не были найдены.
Пример кода из интернета
01 | volatile unsigned int tcnt2; |
02 | void setup() |
03 | { |
04 | TIMSK2 &= ~(1<<TOIE2); //разрешения прерывания по переполнению таймера/счетчика Т2 |
05 | TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); // Режим работы таймера/счетчика |
06 | TCCR2B &= ~(1<<WGM22); // Режим работы таймера/счетчика |
07 | ASSR &= ~(1<<AS2); //Выбор источника синхронизации таймера если AS2=0 от системного генератора |
08 | tcnt2 = 1; // 16000000/64/f=tcnt2 |
09 | TIMSK2 |= (1<<TOIE2); //Разрешение прерывания по переполнению Т2. |
10 | } |
11 |
12 | void loop() |
13 | { |
14 | } |
15 |
16 | void MyInterupt() |
17 | { |
18 | //обработчик вашего прерывания |
19 | } |
20 | //****************обработчик прерывания******************** |
21 | ISR(TIMER2_OVF_vect) |
22 | { |
23 | TCNT2 = tcnt2; |
24 | MyInterupt(); |
25 | } |
Я его немного переделал чтобы визуально всё было понятно.
01 | int led = 13; |
02 | volatile unsigned int tcnt2; |
03 | volatile int count = 0; |
04 | void setup() { |
05 | //Serial.begin(9600); |
06 | TIMSK2 &= ~(1<<TOIE2); //разрешения прерывания по переполнению таймера/счетчика Т2 |
07 | TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); // Режим работы таймера/счетчика |
08 | TCCR2B &= ~(1<<WGM22); // Режим работы таймера/счетчика |
09 | ASSR &= ~(1<<AS2); //Выбор источника синхронизации таймера если AS2=0 от системного генератора |
10 | tcnt2 = 25000; // 16000000/64/f=tcnt2 |
11 | TIMSK2 |= (1<<TOIE2); |
12 | pinMode(led, OUTPUT); |
13 | } |
14 | boolean t=0; |
15 |
16 | void loop() { |
17 | } |
18 | void MyInterupt() |
19 | { |
20 | digitalWrite(led, t); |
21 | t=!t; |
22 | //Serial.println("000"); |
23 | } |
24 | ISR(TIMER2_OVF_vect) |
25 | { |
26 | TCNT2 = tcnt2; |
27 | MyInterupt(); |
28 | } |
В интернете я нашёл таблицу в которой написаны все прерывания для arduino. Но приведённый пример я так и не смог разобрать. Мне в нём непонятно ВСЁ. Весь setup включая знаки & | ~ (в конкретном случае), TIMSK2, TCCR2A, WGM22, ASSR и прочее. Откуда всё это берётся?
Основной смысл: сделать блинк на прерываниях по таймеру.
Библиотеки не предлагать!
Также мне интересно как сделать прерывания по зависанию мк? Название прерывания из таблицы WDT_vect
про символы & | ~ читать про битовые операции переведите или поищите "битовые операции на с" - должно мнего найтись. Вообще, поизучать с будет полезно (хотя бы основы, к коим и относятся битовые операции)
TIMSK2, TCCR2A, WGM22 - это регистры управления процессора, связанные с таймерами. Они объявлены в общем хедере для процессора, так что ненужно их искать. Прежде чем работать с таймерами, почитайте про них в статьях или даташите
Про прерывание по зависанию, ищите про ватчдог.
Про символы я разобрался.
НО всё остальное просто жесть какая-то. Прочитал статью. Понял на 50% хотя и то не уверен.
Нашёл сборник статей на эту тему. Но они не очень хорошо написаны.(((
Читайте как можно больше, чтобы работать с таймерами, нужно понимать, как они работают. Легче всего понять, если Вы разбираетесь в схемах и немножко читаете по аглицки, потому что о принципе работы, на самом деле, даташит дает исчерпывающую информацию, но трудноусваимую.
Не знаю, что входит в 50%, которые Вы не поняли, но поясню по программе:
&= ~(1<<TOIE2) - сброс бита номер TOIE2. Берется "1", сдвигается влево на 6-ю позицию (TOIE2=6), получается 0b01000000, инвертируется операцией "~", получается 0b10111111, производится логическое умножение (операция И), результат кладется обратно. Таким образом "0" попадает в нужный бит регистра. Если не инвертировать и применить операцию битового сложения (ИЛИ), то в нужное место попадет единичка.
В общем случае, то, что в скобках, зовется маской, ее накладывают на регистр через "&", если нужно сбросить биты там, где у маски "0", или через "|", чтобы установить биты там, где у маски "1".
Все это можно делать и через функции bitSet() и bitClear(), но будет медленнее и "неуниверсально"
Я …. Что то вообще ничего не понял(((
По статьям:
Чтобы установить бит в регистре в значение 1, не изменяя значения других битов, используется команда вида:
регистр |= (1 << номер_бита);
А чтобы установить бит в регистре в значение 0, так же не изменяя значения других битов, ис-пользуется команда вида:
регистр &= ~ (1 << номер_бита);
Следовательно давайте разберём первую строчку. TIMSK2 &= ~(1<<TOIE2);
По статьям:
TIMSK - регистр маски прерываний таймеров/счетчиков
Бит 7 - OCIE2: прерывание по совпадению ТС2
Бит 6 - TOIE2: прерывание по переполнению ТС2
Бит 5 - TICIE1: прерывание по захвату ТС1
Бит 4 - OCIE1A: прерывание по совпадению A ТС1
Бит 3 - OCIE1B: прерывание по совпадению В ТС1
Бит 2 - TOIE1: прерывание по переполнению ТС1
Бит 1 - не используется
Бит 0 - TOIE0: прерывание по переполнению ТС0
Если соответствующий бит установлен в "1" и бит I (7-й бит) регистра состояний SREG установлен в "1", тогда соответствующее прерывание будет срабатывать.
В этом месте уже непонятно. Как я это вижу:
При старте мк в регистрах TIMSK записано 0b00000000 (так ли я это понимаю?)(то есть нули и всё прерывания выключены) или всё наоборот ?
Ок ступил. Всё логично получается с единицами. Только я не понял раз стоит 0 то значит прерывание включено а если 1 то выключено?
По статьям:
TIMSK - регистр маски прерываний таймеров/счетчиков
Бит 7 - OCIE2: прерывание по совпадению ТС2
Бит 6 - TOIE2: прерывание по переполнению ТС2
Бит 5 - TICIE1: прерывание по захвату ТС1
Бит 4 - OCIE1A: прерывание по совпадению A ТС1
Бит 3 - OCIE1B: прерывание по совпадению В ТС1
Бит 2 - TOIE1: прерывание по переполнению ТС1
Бит 1 - не используется
Бит 0 - TOIE0: прерывание по переполнению ТС0
Не можете ли указать источник столь полезной информации.
По моим сведениям (да и Atmel так считает - см. даташит) в микроконтроллерах ATMega48/88/168/328 существует аж 3 регистра, занимающихся маскированием - соответственно TIMSK0, TIMSK1, TIMSK2 (страницы 111, 139 и 163 "большого" даташита). И у этих регистров только младшие биты имеют значение (3, 4 и 3 бита соответственно). А у вас - целый байт...
Только я не понял раз стоит 0 то значит прерывание включено а если 1 то выключено?
Еще раз - даташит на соответствующий контроллер является истиной в последней инстанции. Там все предельно ясно сказано. Например, бит 2 регистра TIMSK0:
"Bit 2 – OCIE0B: Timer/Counter Output Compare Match B Interrupt Enable
When the OCIE0B bit is written to one, and the I-bit in the Status Register is set, the
Timer/Counter Compare Match B interrupt is enabled. The corresponding interrupt is executed if
a Compare Match in Timer/Counter occurs, i.e., when the OCF0B bit is set in the Timer/Counter
Interrupt Flag Register – TIFR0."
Ок ступил. Всё логично получается с единицами. Только я не понял раз стоит 0 то значит прерывание включено а если 1 то выключено?
Если "1" - то включено, если "0" - то выключено.
У Вас в первом скетче просто ошибка в комментарии, первая строчка ЗАПРЕЩАЕТ прерывание, там все сделано правильно:
Делается так для того, чтобы во время настройки таймера у нас прерывание не сработало случайно.
Дело всё в том что скетч из примера вообще не работает! один раз только срабатывает прерывание!
Хотел написать сам как в статьях всё равно не работает!
01
int
led = 13;
02
volatile unsigned
int
tcnt2;
03
volatile
int
count = 0;
04
05
void
setup() {
06
Serial.begin(9600);
07
TIMSK2=0;
08
TCCR2A=0;
09
TCCR2B=3;
10
TCNT2=150;
11
TIMSK2=64;
12
13
14
// TIMSK2 &= ~(1<<TOIE2); //разрешения прерывания по переполнению таймера/счетчика Т2
15
// TCCR2A &= ~((1<<WGM21) | (1<<WGM20));// Режим работы таймера/счетчика
16
// TCCR2B &= ~(1<<WGM22);// Режим работы таймера/счетчика
17
// ASSR &= ~(1<<AS2); //Выбор источника синхронизации таймера если AS2=0 от системного генератора
18
// tcnt2 = 25000; // 16000000/64/f=tcnt2
19
// TIMSK2 |= (1<<TOIE2);
20
pinMode(led, OUTPUT);
21
}
22
boolean t=0;
23
24
void
loop() {
25
}
26
void
MyInterupt()
27
{
28
digitalWrite(led, t);
29
t=!t;
30
Serial.println(
"000"
);
31
}
32
ISR(TIMER2_OVF_vect)
33
{
34
TCNT2 = 150;
35
MyInterupt();
36
}
В строке 11 скетча вы присваиваете регистру TIMSK2 значение 64. Другими словами, устанавливаете бит 6.
Если открыть даташит на микроконтроллер и посмотреть, что же там пишется об этом регистре, то можно уяснить, что смысл имеет лишь установка трех младших битов:
Это так - в качестве примера достоверности тех данных, которыми вы пытаетесь пользоваться. Неудивительно, что ваше прерывание сыплется.
UPD1: Далее, в процедуре обработки прерывания вы вызываете функцию, которая слишком тяжела для обработки в прерываниях: в ней вызывается не слишком быстрая функция digitalWrite(), а также Serial.print, которой для вывода 3 символов (да плюс еще CR+LF) потребуется около 5 миллисекунд. Сколько раз за это время произойдет очередное переполнение вашего таймера? 40? или 50? (таймер "щелкает" с частотой 500 кГц в диапазоне 150-255, т.е. переполняется где-то 5 раз в миллисекунду).
UPD2: Упс, у вас выбран режим 0 - нормальный режим счета. В этом режиме счетчик считает от 0 до 255. Величина в TCNT2 значения не имеет. Стало быть счет в диапазоне 0-255 и переполнение "всего" 2 раза в миллисекунду.
Скачал даташит. На 12 главе описываются прерывания по таймеру. Подобного рисунка с надписью TIMSK2 замечено не было. Я не знаю английского языка поэтому в даташит и не лазаю. Пробовал перевести переводчиком. Полная чушь!
(да плюс еще CR+LF)-что это такое?
Мне кажется надо как то систематизировать данные по даташиту. Ибо возможно мы говорим об одном и том же, а я ничего понять не могу. Также мне не понятно как высчитывать секунды или полсекунды.
Используя пример из интернета вот что получилось.
01
volatile
long
cntr;
02
03
boolean flip;
04
05
void
setup() {
06
07
pinMode(13, OUTPUT);
08
TCCR2A = 0;
09
TCCR2B = 2;
10
TCNT2=59;
11
TIMSK2 |= (1 << TOIE2);
12
13
}
14
15
ISR(TIMER2_OVF_vect) {
16
TCNT2=59;
//55;
17
cntr++;
18
if
(cntr>9999)
19
{
20
flip = !flip;
21
cntr = 0;
22
}
23
}
24
25
void
loop()
26
{
27
if
(flip) digitalWrite(13, HIGH);
28
else
digitalWrite(13, LOW);
29
}
Мне не понятно как настроить и вычислить такты. То есть как рассчитать так чтобы прерывание срабатывало скажем каждую секунду.
И мне не всё понятно с регистром маски прерываний так как в строчке TIMSK2 |= (1 << TOIE2); мы чётко ставим 1 в бит 6! А если записать как 64 то ничего не работает.
Также не ясно. Какие режимы может принимать регистр управления В. (выбор тактирования таймера)
PS пример который я привёл получился методом тыка!!! Мне стыдно что так и не смог разобраться((((
(да плюс еще CR+LF)-что это такое?
CR (Carriage Return - возврат каретки) и LF (Line Feed - перевод строки) - те символы, которые добавляются к пересылаемой строке в случае использования функции Serial.println().
И мне не всё понятно с регистром маски прерываний так как в строчке TIMSK2 |= (1 << TOIE2); мы чётко ставим 1 в бит 6! А если записать как 64 то ничего не работает.
А с чего вы взяли, что TOIE2 равен 6? А не, скажем, 0?
Попробуйте в скетче вывести его значение на экран:
Serial.println(TOIE2,DEC);
Скачал даташит. На 12 главе описываются прерывания по таймеру.
"Preliminary"? Но и там в 12 главе описывается таймер/счетчик0, а второй - соответственно - в 15-й.
[quote]
Я не знаю английского языка поэтому в даташит и не лазаю
[/quite]
Ну, тогда вам остается мигать диодом с помощью delay().
Если уж совсем никак, найдите книжку Евстифеева. Практически даташит, только переведенный на русский язык, ну и урезанный мало-мало.
Всё разобрался (вроде как). Смотрел какой-то левый даташит + не дошёл до раздела который нужно было использовать. Спасибо!
Замечу, что Serial.print использовать в прерываниях нерекомендуется. Если все же очень надо, советую почитать статью о неблокирующем Serial
Ок спасибо. Статья интересная. Но я не буду применять печать в прерываниях.
Я так понял что мало кто разбирается на тему прерываний и настройки таймеров.
Сейчас ищу про WATCHDOG. К сожалению описания на русском нет. Также удалось выяснить, что для нормальной работы необходимо перепрошить бутлогер. Конечно всё бы решил даташит! Но я пока не нашёл переводчика))) а гугл сами знаете что выдаёт.
По сторожевому таймеру уже было на форуме. Что гугль выдает - не знаю, потому что в свое время зарекся переводить технические тексты компьютерными переводчиками (это было после фразы "ОСТРЫЙ не несет ответственности при ношении устройства в запасных карманах Вашего времени")
Найдите "Англо-русский словарь по программированию" (как-то видел в продаже в книжном магазине и обратил на него внимание) и переводите потихоньку по-старинке - заодно и язык выучите, пригодится. Я в свое время выучил читая хелп для с++.
LEVV2006
Если у вас остались какие-нибудь конкретные вопросы по таймерам, то я с радостью попытаюсь на них ответить (не так давно пришлось разбиратся в этой теме :) )
А у меня мечта... с таймерами в STM32 разобраться... :)
:)
А у меня мечта... с таймерами в STM32 разобраться... :)
Скоро и у меня такая же мечта появится))))
Хотел рассказать, чем же закончилась моя эпопея с таймерами))))
Я вроде разобрался, как всё это работает и даже написал «блик» основываясь на таймере. (Кстати данный совет: чем больше вы будите перечитывать информации о таймере, тем лучше поймёте его работу!!!!! Действительно работает!!!! Я начел понимать после 20 прочтений текстов!!!).
Но дело далеко не в блинке. Я делаю свой проект и в нём я решил использовать прерывание по таймеру, чтобы он в свою очередь управлял переменными отвечающие за задержки. К сожалению, я не смог придумать лучить код (г..о-код) для реализации псевдо-многозадачности.
Код который был написан для блинка не стал работать в моём проекте. Я поленился разбираться в чём дело и самая главная отговорка была в том что блинк был написан под МК 328 а проект на 168. Так и задвинул это дело. Выходом из ситуации стала библиотека, которая вроде как стабильно работает.
Почему «вроде». Сейчас я просто не понимаю что происходит с контроллером. У меня для проекта есть две версии кода. Одна вообще убогая, но рабочая (без прерываний и на одних delay). Вторая более менее структурированная и с прерываниями по таймеру.
Дело всё в том, что первая работает как часы: МК не зависает. Одним словом всё чётко!
А вот со второй проблемы и не понятно, какого характера. МК может работать день нормально на второй зависнуть 2 раза подряд. В чём кроется причина так и не ясно. Ковыряюсь с этой проблемой уже месяц. Перелопатил весь код (улучшил его, исправил ошибки, в том числе и ошибки по использованию библиотеке для таймера).
В результате ничего не изменилось. МК по-прежнему зависает правда не часто. А однажды слетела толи прошивка толи eeprom. МК работал, отвечал на нажатия клавиш, но не выполнял никаких действий. Лечился только пере заливкой скетча.
И да оставшейся оперативной памяти после компиляции остаётся много (200). Так что вряд ли зависания от того что заканчивается оперативная память.
Могу порекомендовать использовать другой таймер или разобраться с вачдогом...
С вачдогом я тоже разобрался. Я использую другой загрузчик и стандартную библиотеку. В принципе он может решить проблему зависания. Но он не устраняет саму причину.
Ну вот и я "докатился" до прерываний. Как уже было выше написанно: "чем больше об этом читаешь, тем понятнее". Пока я только читал и моргал диодиком, вроде всё понятно. Окончательно разобраться помогла вот эта статья. Понятия не имею почему именно она стала ключём к разгадки, но я кое-что понял.
Но хотелось бы уточнить парачку нюансов:
1) что такое Prescaler и зачем он нужен? (Вроди как для точности счётчика !?)
2) вот по этой формуле высчитывается количество тиков для определённого времени:
т.е. если у меня Prescaler = 1024 и тактер 16MHz то для одной секунды я решаю так:
количество тиков = (1 с) / (1024 / 16000000) = 15625-1 = 15624
но если я прескалер не устанавливал вовсе, как я должен решать? С 0 или с 1 ?
Во я блин даю, пока писал вопрос сам с ним разобрался
но один фиг оставлю это на форуме, может кому поможет разобраться с этой темой.
А ответ на мой вопрос "1", ведь МК работает с частотой 16MHz что значит 16000000 "тиков/операций/инструкций или как это ещё называется
" в секунду. => чтобы прерыване сработало через 1 сек. нужно подождать 16000000 - 1 = 15999999 "тиков".
К стати вопрос номер (1. что такое Prescaler и зачем он нужен?) так и не отпал. Один фиг не понятно, если без Prescalerа счётчик считает точней всего, зачем оно тогда надо?
1) что такое Prescaler и зачем он нужен? (Вроди как для точности счётчика !?)
Ну, можно и так сказать (тогда уж - для снижения точности счета). Скорее, аналогом прескалера в окружающей нас действительность будет селектоор диапазонов радиоприемника: с одним положением ручки "слушаем" УКВ1, с другим - УКВ2, с надцатым и до ДВ добираемся. При этом на шкале самая левая частота больше самой правой (или в другую сторону? давно на приемник не смотрел) в одно и то же число раз - раза в два-четыре (в зависимости от модели приемника):
2) вот по этой формуле высчитывается количество тиков для определённого времени:
(# timer counts + 1) = (target time) / (timer resolution)
т.е. если у меня Prescaler = 1024 и тактер 16MHz то для одной секунды я решаю так:
количество тиков = (1 с) / (1024 / 16000000) = 15625-1 = 15624
но если я прескалер не устанавливал вовсе, как я должен решать? С 0 или с 1 ?
Если переводить дословно, то прескалер суть делитель. Частота без обработки прескалером - суть число без делителя или поделенное на что? Правильно, на единичку.
Я тоже напишу - как это я понимаю.
Допустим, мы таймером измеряем длину импульса (запуск по переднему фронту, захват значения таймера - по заднему). Тогда:
Точность измерения (относительная) зависит от разрядности счетчика, а абсолютная - от прескалера. И от этих же двух параметров зависит диапазон измерения. Это понятно, что чем точнее, тем лучше, то есть берем 16 - разядный счетчик. Масимальное значение (максимальная длина импулься) будет 65535/16000000=0,0041(с) - тоесть 4,1 мс. Точность измерения ±1 тик, то есть 0,0625 мкс. А если у нас, скажем, импульс 1с ? Вводим прескалер. Для 1024 максимальная длина импульса 4,2 с. Но и точность падает в 1024 раза (получается 64 мкс)
Здравствуйте..
Подскажите по такому вопросу..Какой таймер/счетчик в Мега 2560 может синхронизироваться с внешней частотой(Т0,Т2)..? Только 2-ой..? Мне необходимо посчитать входящие импульсы и ни одного не пропустить, а так же, если возможно, высчитать их другие параметры(скважность, длительность и т.д.).
И еще..Можно ли в устанавливать биты в регистрах таким образом EIMSK=0x58, TIFR=0x55..?
Спасибо..
Судя по картинке со схемой единственного 8-битового таймера в этих мегах, считать можно как импульсы от предделителя, так и внешние:
Судя по картинке для таймеров 1,3-5, у них те же возможности по тактированию.
А диапазон интересующих вас частот какой?
Один счетчик запускается в нормальном режиме с тактированием от внешнего источника - вот вам точное количество импульсов за определенный период времени.
Другой счетчик запускается по переднему фронту внешнего сигнала и ведет подсчет системных тактов до момента поступления заднего фронта - вот вам длительность (положительного) импульса.
Третий счетчик запускается по заднему фронту внешнего сигнала и ведет подсчет системных тактов до момента поступления переднего фронта - вот вам длительность паузы.
"Длительность"+"пауза" = "период" вашего сигнала. Скважность определяется простым делением одной величины на другую...
А почему бы и нет? Если в описании регистра для всех его битов указано ".../w", то запишете - хоть по-отдельности, хоть скопом. Ну а если не указано - то никак (ну или это не будет иметь никакого влияния на поведение микроконтроллера).
..Другой счетчик запускается по переднему фронту внешнего сигнала и ведет подсчет системных тактов до момента поступления заднего фронта - вот вам длительность (положительного) импульса.
Третий счетчик запускается по заднему фронту внешнего сигнала и ведет подсчет системных тактов до момента поступления переднего фронта - вот вам длительность паузы.
"Длительность"+"пауза" = "период" вашего сигнала. Скважность определяется простым делением одной величины на другую...
А на одном счетчике это сделать разве нельзя..?
У меня мысль реализовать это так: Захват по переднему и вкл.подсчета импульсов, переключение на захват по заднему..По приходу заднего -- записать количество в ячейку, переключение на захват по переднему с продолжением счета..По приходу второго переднего -- остановить счет и вывести оба результата на ЛСД..
Для запуска счетчика 1 мне нужно установить регистр TCCR1B=0x07(внешн.1->0) или TCCR1B=0x06(внешн.0->1) + сбросить флаг переполнения TIFR=0x01(если было), разрешить глобальные прерывания SREG=0x80, TCCR1A=0 и вроде все..Или еще что упустил..?
И еще..Судя по этому синтаксису attachInterrupt(interrupt, function, mode) можно сколько угодно использовать обработчиков прерваний..Я правильно понял..?
А на одном счетчике это сделать разве нельзя..?
Можно. После того, как освоитесь с использованием таймеров.
Опять же - все весьма существенно зависит от диапазона частот, который вы пытаетесь оседлать.
Судить лучше не по синтаксису, а по даташиту. А в даташите (ATmega1280-2560) говорится о восьми векторах запросов внешних прерываний - INT0...INT7. Кстати, судя по описанию функции attachInterrupt(), в Мегах поддерживаются только шесть из них.
Можно. После того, как освоитесь с использованием таймеров.
Уже потихоньку дело идет..Счетчик запускается по прерыванию TNT0, но пока не считает то, что нужно :(
Опять же - все весьма существенно зависит от диапазона частот, который вы пытаетесь оседлать.
Диапазон частот..Вообщем есть оптический энкодер и мне необходимо измерить кол-во импульсов, которое он выдает..Для начала-окончания отсчета исп.НОЛЬ-метка на нем(подкл. к TNT0)..Скорость вращения энкодера может быть разной(в зависимости от ск-ти вращения двигателя) и период импульсов тоже разный..И нужно подсчитать кол-во импульсов.
На ПИКе было полегче(меньше регистров и таймеров)
001
program Timer0_Interrupt
002
' Lcd connection
003
dim LCD_RS
as
sbit at RA3_bit
004
LCD_EN
as
sbit at RB1_bit
005
LCD_D4
as
sbit at RB2_bit
006
LCD_D5
as
sbit at RB3_bit
007
LCD_D6
as
sbit at RB5_bit
008
LCD_D7
as
sbit at RB4_bit
009
010
LCD_RS_Direction
as
sbit at TRISA3_bit
011
LCD_EN_Direction
as
sbit at TRISB1_bit
012
LCD_D4_Direction
as
sbit at TRISB2_bit
013
LCD_D5_Direction
as
sbit at TRISB3_bit
014
LCD_D6_Direction
as
sbit at TRISB5_bit
015
LCD_D7_Direction
as
sbit at TRISB4_bit
016
' End Lcd connections
017
018
' Переменные
019
dim
null
as
byte
020
dim pulse
as
word
021
dim counter
as
word
022
dim null_ex
as
string
[5]
023
dim pulse_ex
as
string
[5]
024
dim z_imp
as
word
025
dim null_s
as
word
026
dim z_imp_ex
as
string
[5]
027
028
029
sub procedure Interrupt()
030
If PIR1.TMR1IF = 1 then 'Если произошло переполнение TMR1, то
031
Inc(counter)
032
TMR1H = 0x80 'Загрузим его по-новой
033
TMR1L = 0x00
034
PIR1.TMR1IF = 0 'Сбросим флаг, вызвавший прерывание
035
end
if
036
if
INTCON.INTF = 1 then 'Если нажата кнопка Старт/стоп(INT), то
037
null
=
null
+ 1 ' Считаем количество импульсов 0-метки
038
null_s=null_s + 1
039
if
null_s=2 then ' Кол-во импульсов от 0-метки до 0-метки
040
z_imp = (TMR1H*256)+TMR1L ' Выводим на экран
041
Null_s=0
042
pulse=0
043
TMR1H = 0
044
TMR1L = 0
045
end
if
046
047
IF T1CON.0 = 0 then 'Если TMR1 не запущен(не считает приходящие импульсы, то
048
T1CON.0 = 1 'запустим его
049
ELSE 'если же он работает, то
050
T1CON.0 = 0 'остановим
051
END IF
052
INTCON.INTF = 0 'очистим флаг, вызвавший прерывание
053
end
if
054
end sub
055
056
057
main:
058
CMCON= 7
059
PORTB = 0xFF
060
TRISB = %010000001
061
062
T1CON = 0x0E ' настройки таймера
063
'T1CON.TMR1ON = 0 '
Выкл. Таймер1(вкл. происходит в ПРЕРЫВАНИИ)
064
'T1CON.TMR1CS = 1 '
Выбор ист. сигнала (Внешний на RB6)
065
'T1CON.T1SYNC = 1 '
не синхрониз. внешний сигнал
066
'T1CON.T1OSCEN = 1 '
Вкл. внутренний тактовый генератор
067
068
TMR1H = 0x80
069
TMR1L = 0x00
070
071
PIR1 = 0 ' Сброс флага прерывания
072
PIE1 = 1 'Разрешаем прерывание от TMR1 ()
073
INTCON = %11010000
074
pulse = 0
075
counter = 0
076
null
= 0
077
z_imp_ex=0
078
z_imp=0
079
null_s =0
080
081
Lcd_Init()
082
Lcd_Cmd(_LCD_CLEAR)
083
Lcd_Cmd(_LCD_CURSOR_OFF)
084
Lcd_Out(1,3,
"TECT ДATЧИKOB"
)
085
Lcd_Out(2,4,
"BE-178.A..."
)
086
Delay_Ms(2000)
087
Lcd_Cmd(_LCD_CLEAR)
088
Lcd_Cmd(_LCD_CURSOR_OFF)
089
Lcd_Out(1,5,
"ПPOBEPKA"
)
090
Lcd_Out(2,4,
"ПOДKЛЮЧEHИЙ"
)
091
Delay_Ms(3000)
092
Lcd_Cmd(_LCD_CLEAR)
093
Lcd_Cmd(_LCD_CURSOR_OFF)
094
if
PORTA.2 = 0 then
095
Lcd_Out(1,5,
"ПOДKЛЮЧEH"
)
096
Lcd_Out(2,5,
"BE-178A.5"
)
097
else
098
Lcd_Out(1,5,
"ПOДKЛЮЧEH"
)
099
Lcd_Out(2,5,
"BE-178A"
)
100
101
end
if
102
Delay_Ms(3000)
103
Lcd_Cmd(_LCD_CLEAR)
104
Lcd_Out(2,1,
"Z="
)
105
Lcd_Out(2,9,
"Zi="
)
106
if
PORTA.2 = 0 then
107
Lcd_Out(1,1,
"BE-178A5 0м="
)
108
else
109
Lcd_Out(1,1,
"BE-178A 0м="
)
110
end
if
111
while
TRUE
112
Delay_Ms(200)
113
114
115
if
PORTA.0 = 0 then
116
pulse= 0
117
counter= 0
118
TMR1H = 0
119
TMR1L = 0
120
null
= 0
121
null_s= 0
122
z_imp_ex=0
123
z_imp=0
124
INTCON.INTF = 0
125
end
if
126
127
128
pulse = (TMR1H*256)+TMR1L
129
' Вывод количества импульсов общее
130
WordToStr(pulse,pulse_ex)
131
Lcd_Out(2,12, pulse_ex)
132
' Вывод количества 0-меток
133
ByteToStr(
null
,null_ex)
134
Lcd_Out(1,14,null_ex)
135
' Вывод количества импульсов на оборот
136
WordToStr(z_imp, z_imp_ex)
137
Lcd_Out(2,3, z_imp_ex)
138
139
wend
140
end.
Вот это и хочу перенести на Ардуину и использовать ее дополнительные возможности, которых нет на ПИКе
И с Ардуино осилим.. :)
Подскажите, плизз, с флагами прерываний..Если флаг установлен, то он в 0, а если сброшен, то в 1..Так..?
Подскажите, плизз, с флагами прерываний..Если флаг установлен, то он в 0, а если сброшен, то в 1..Так..?
Так.
С точностью до наоборот.
Так.
С точностью до наоборот.
А как же даташит
Читать следует весь абзац (в конкретном случае даже два, но маленьких):
"When an edge or logic change on the INT1 pin triggers an interrupt request, INTF1 becomes set (one). If the I-bit in SREG and the INT1 bit in EIMSK are set (one), the MCU will jump to the corresponding Interrupt Vector.
The flag is cleared when the interrupt routine is executed. Alternatively, the flag can be cleared by writing a logical one to it. This flag is always cleared when INT1 is configured as a level interrupt."
В (любое) прерывание вы входите с соответствующим флагом прерывания в соответствующем управляющем регистре установленным в единицу. Эта единица сбрасывается аппаратно при выполнении операции IRET. Либо - альтернативно - программным способом, а именно посылкой единицы в соответствующий бит. При этом происходит чудо чудное - была в флаге единица, мы туда посылаем единицу. А в результате получается нулик.
Странно? да не совсем - это особенность флаговых регистров: при засылке битов в них происходит не загрузка бита, а операция двоичного сложения с потерей переноса. Грубо: 1+1 должно получиться 10, но единичка в старший разряд не переносится (это же не арифметический регистр, а флаговый) а просто теряется. То есть получаем 1+1=0...
В результате, кстати, экономится как минимум два программных такта. Ведь для манипуляции битами в тех же портах необходимо:
1) считать значение порта в РОН
2) модифицировать содержимое РОН
3) сохранить значение из РОН в порт
(РОН - регистр общего назначения)
А тут - имеем, скажем, такое состояние флагового регистра: b11110000. Посылаем в флаговый регистр байт b00100000. В итоге получаем в регистре b11010000. И все это за один такт.
step962, cпасибо..уже успел и сам разобраться, наложив на TIFR1 маску 0xFF..
Подскажите еще, что не правильно делаю..В прерывании не хочет запускаться Timer1 на тактирование по внешнему сигналу на Т1..
Вот сама функция прерывания..По второму обращению к ней необходимо запустить Counter1, а по третьему -- остановить и сбросить таймер в 0
01
void
inerrupt_1(){
02
nul_s++;
03
nul++;
04
if
(nul_s==2){
05
TCCR1B=0x06;
06
}
07
if
(nul_s==3){
08
lcd.clear();
09
TCCR1B=0x00;
10
TCNT1H=0x00;
11
TCNT1L=0x00;
12
TIFR1=0xFF;
13
nul_s=0;}
14
}
что здесь не так..?
И вопрос по внешним прерываниям INT0-INT7..При поступлении события на эти прерывания я могу вывести обработку их в отдельные функции(как в attachInterrupt), или мне только через goto можно на них выйти..
что здесь не так..?
"не так" здесь прежде всего так называемое "применение магических чисел" (например, TCCR1B=0x06;). Таким образом вы запутываете и себя и тех, к кому вы обращаетесь за советом.
Куда как понятнее было бы написать:
TCCR1B = (1<<CS12) | (1<<CS11);
Сразу видно, что вы пытаетесь установить режим работы предделителя "110" для первого канала таймера 1. Осталось открыть соответствующую табличку и узнать, что это работа от внешнего источника тактовых сигналов по переднему фронту.
Во-вторых - отсутствие комментариев в приводимых участках кода и описания назначения переменных в сопроводительном тексте. Как мне догадаться, для чего служит переменная nul и чем она отличается от переменной nul_s?
В-третьих, приведенный участок кода - это далеко не все, относящееся к теме управления таймером. Что записано в регистр TCCR1A, остается только догадываться, а ведь от этого очень сильно зависит поведение таймера/счетчика.
В-четвертых, как уже предлагалось ранее, ходить надо шагами. А не прыжками. Отладьте части своей задачи (их три, если я правильно помню?) по-отдельности. В процессе отладки более-менее изучите, как настраивается и работает таймер/счетчик. А отладив отдельные подзадачи и повысив уровень своих знаний вы сможете более уверенно браться за решение этих трех подзадач в рамках одной функции.
Не совсем понятен вопрос. attachInterrupt именно с внешними прерываниями и работает.
step962, не ругайтесь, мне легче с HEXсом работать, поэтому и пишу так..
Вот пример скетча
01
#include <LiquidCrystal.h>
02
//enum { LCD_WIDTH = 16, LCD_HEIGHT = 2 };
03
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
04
05
//Объявляем переменные для счета
06
volatile
int
nul;
// Кол-во импульсов нульметки
07
volatile
int
z_imp;
// Кол-во импульсов датчика
08
volatile
int
nul_s;
// Начало отсчета импульсов датчика
09
volatile
int
menu;
//
10
11
void
setup
()
12
{ lcd.clear();
13
lcd.begin(16, 2);
// start the library
14
lcd.setCursor(4,0);
15
lcd.print(
"Provepka"
);
// print a simple message
16
lcd.setCursor(4,1);
17
lcd.print(
"datchika"
);
// print a simple message
18
delay(2000);
19
lcd.clear();
20
attachInterrupt(0, inerrupt_1, RISING);
// Разрешить прерывание(pin2) по фронту на подъём
21
TCCR5A=0x00;
// обнуляем
22
TCCR5B=0x00;
// регистры
23
TCCR5B=0x00;
// маски
24
TIMSK5=0x00;
// и
25
TIFR5=0xFF;
// флаги
26
TCNT5H=0x00;
// для
27
TCNT5L=0x00;
// TMR1
28
SREG=0x80;
// разрешаем глобальные
29
nul==0;
30
nul_s==0;
31
z_imp==0;
32
}
33
34
35
36
void
loop
(){
37
if
(nul==0){
38
lcd.setCursor(3, 0);
39
lcd.print(
"Vkl. motop"
);
40
lcd.setCursor(7, 1);
41
lcd.print(TCNT5);}
42
43
if
(nul>=1) {
44
lcd.setCursor(3, 0);
45
lcd.print(
"Proverka"
);
46
lcd.setCursor(11, 0);
47
lcd.print(
" "
);
48
lcd.setCursor(3, 1);
49
lcd.print(nul);
// всего оборотов датчика
50
lcd.setCursor(7, 1);
51
lcd.print(TCNT5);}
52
lcd.setCursor(15, 1);
53
lcd.print(nul_s);
54
}
55
56
void
inerrupt_1(){
//если поступило прерывание
57
nul_s++;
58
nul++;
59
if
(nul_s==2){
// начало отсчета входящих импульсов на TMR5
60
TCCR5B=0x05;
// уст. тайммер по подъему фронта на внешнем тактировании
61
62
}
63
if
(nul_s==3){
// после оборота дачика вводим их на экран
64
lcd.clear();
// обнуляем
65
TCCR5B=0x00;
// все
66
TCNT5H=0x00;
// для
67
TCNT5L=0x00;
// следующего
68
TIFR5=0xFF;
// цикла
69
nul_s=0;}
// отсчета
70
}
А какие таймеры можно подключить на внешнее тактирование в МегаР3..Только Т0 и Т5..?
step962, не ругайтесь,
Ну это вы еще не слышали, как я ругаюсь...
И упускаете смысл работаемого. Вот, например, строка 21 ("
TCCR5A=0x00;
// обнуляем
"). Обнулением регистра это было бы только в том случае, если дальше шла бы установка нужных битов. А так как ее нет (и вообще это единственная инструкция в скетче, где производится манипуляция с регистром TCCR5A), то здесь не обнуление, а установка режима работы таймера, а именно:WGM51..WGM50=00, да еще WGM53..WGM52=00 (в "обнуляемом" регистре TCCR1B). Итого 0000 - нормальный режим работы счетчика.
А тут необходимо внимательно прочитать разделы 16.3, 17.4, 18.1 и 18.3 в даташите, а также посмотреть на распиновку камня. Можно увидеть, что
T0 - это 50-й вывод (PD7)
T1 - это 49-й вывод (PD6)
T3 - это 8-й вывод (PE6)
T4 - это 27-й вывод (PH7)
T5 - это 37-й вывод (PL2)
Кстати, внешнее прерывание INT0 сидит на 19-м выводе (PB0). Но это так - к слову...
А тут необходимо внимательно прочитать разделы 16.3, 17.4, 18.1 и 18.3 в даташите, а также посмотреть на распиновку камня. Можно увидеть, что
T0 - это 50-й вывод (PD7)
T1 - это 49-й вывод (PD6)
T3 - это 8-й вывод (PE6)
T4 - это 27-й вывод (PH7)
T5 - это 37-й вывод (PL2)
Кстати, внешнее прерывание INT0 сидит на 19-м выводе (PB0). Но это так - к слову...
Про распиновку и Даташит камня я в курсе...Я про распиновку ArduinoMega 2560R3.
В Ардуине не все прерывания и счетчики можно задействовать..Большинство(Т1,Т3 и Т4) висят в воздухе..Подпоять контакты к ним для меня не проблема..
WGM51..WGM50=00, да еще WGM53..WGM52=00 (в "обнуляемом" регистре TCCR1B). Итого 0000 - нормальный режим работы счетчика.
Но у меня при подключенном внешнем такитровании Т5 на вывод 28 Ардуино счетчик не работает, т.е. он в нуле. Или мне еще надо переключить вывод 37 камня на вход(pinMode(37, INPUT))..?
приводя номера выводов, я имел в виду выводы камня, а не Arduino-платки. Так что тут еще надо перевести 37-й вывод камня в Dxx.
Про распиновку и Даташит камня я в курсе...
При этом в скетче никак не упоминается вывод No37 (Dxx?), с которого таймер/счетчик5 будет подсчитывать импульсы в режиме тактирования от внешнего источника.
При этом в скетче никак не упоминается вывод No37 (Dxx?), с которого таймер/счетчик5 будет подсчитывать импульсы в режиме тактирования от внешнего источника.
Я чуть выше написал pinMode(37, INPUT) -- вот этого еще в скетче не хватает..? Или еще какие манипуляции с 37 выводом делать надо..?
Да. Необходимо проверить, соответствует ли 37-й вывод камня 37-му выводу Arduino Mega.
PS: Судя по этой картинке - нет такого вывода, доступного через плату Arduino Mega.
PS: Судя по этой картинке - нет такого вывода, доступного через плату Arduino Mega.
Эта картинка для Ардуино Мега 2560, а у меня 2560R3..Я выше выкладывал скрин, и там этот вывод задействован на 28 выводе Ардуино..
Так какой вывод платы соответствует 37-му выводу камня (т.е. PL2)?