Прерывания по таймеру

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Может вы не умеете программировать?

void Do() {
  // какое-то действие
}

ISR (TIM1_COMPA) {
  static unsigned int i = 0;
  ++i;
  if (i >= 1000) {
    i = 0; Do();
  }
}

void setup(void) {
  // flip screen, if required
  // u8g.setRot180();
  TCCR1B = 1 << WGM12 | 1 << CS12 | 0 << CS11 | 0 << CS10 | 0 << WGM13; // делитель 256
  TIMSK1 = 1 << OCIE1A | 1 << ICIE1 | 0 << OCIE1B | 0 << TOIE1; // прерывание по совпадению
  OCR1AH = 0b11110100; // число для сравнения 62500
  OCR1AL = 0b00100100;
  Serial.begin(9600);
  sei();
}

void loop(void) {
  Serial.println(i);

  // rebuild the picture after some delay
  delay(100);
}

 

forfrends
Offline
Зарегистрирован: 24.02.2015

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

Вы проверяли скетч? У вас работает? У меня нет. В сериал гонит одни "0"

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

volatile потерял. Обязательно при прерывании. Как и вы тоже.

void Do() {
  // какое-то действие
  Serial.println("Tik");

}

ISR (TIM1_COMPA) {
  static volatile unsigned int i = 0;
  ++i;
  if (i >= 1000) {
    i = 0; Do();
  }
}

void setup(void) {
  // flip screen, if required
  // u8g.setRot180();
  TCCR1B = 1 << WGM12 | 1 << CS12 | 0 << CS11 | 0 << CS10 | 0 << WGM13; // делитель 256
  TIMSK1 = 1 << OCIE1A | 1 << ICIE1 | 0 << OCIE1B | 0 << TOIE1; // прерывание по совпадению
  OCR1AH = 0b11110100; // число для сравнения 62500
  OCR1AL = 0b00100100;
  Serial.begin(9600);
  sei();
}

void loop(void) {

  // rebuild the picture after some delay
  delay(100);
}
/*Скетч использует 1714 байт (5%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 192 байт (9%) динамической памяти, оставляя 1856 байт для локальных переменных. Максимум: 2048 байт.
*/

 

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

delay(1488);

forfrends
Offline
Зарегистрирован: 24.02.2015

У меня всеравно не работает. Ваш пример тоже молчит. Может сам указатель не правильный TIM1_COMPA?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

forfrends пишет:

У меня всеравно не работает. Ваш пример тоже молчит. Может сам указатель не правильный TIM1_COMPA?

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

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Я уже спать ушел. С телефона прочел. Вы блин абалдели!
Я же написал ВСЕ, БЛИН, настройки. TCCR1A Пушкин будет описывать?

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

wdrakula пишет:
Я же написал ВСЕ, БЛИН, настройки.

я всё отменил.

Слава НАТОвским базам вокруг Суберии!

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

И вектор, конечно, TIMER1_COMPA_vect.

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

wdrakula пишет:
И вектор, конечно, TIMER1_COMPA_vect.

ок. и, вектор отменил к ебеням Трампа.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

пропробуй, вдруг да влезеть

https://github.com/DetSimen/Arduino-

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016
int i=10;

ISR (TIMER1_COMPA_vect)
{
  // какое-то действие
  i++;
}

void setup(void) {
  cli();

  TCCR1A = 0; //вот про это я вчера, мля, писал. НУЖНО УКАЗЫВАТЬ ВСЕ ПАРАМЕТРЫ
  TCCR1B = (1<<WGM12) | (1<<CS12); // mode 4 и делитель 256
  TIMSK1 |=(1<<OCIE1A); // прерывание по совпадению
  TCNT1 = 0; // неожиданно? компилятор поддерживает 16-ти разрядные регистры!
  OCR1A = 62500U; // какой мудак научил тебя писать константы в битовом виде?

  sei();
  Serial.begin(57600);
  while (!Serial);// ждем пока сериал стартует
}

void loop(void) {
  Serial.println(i);
  delay(100);
}

Причесал я твою помойку. Оно компилируется и работает.

Жду "спасибо". Гиннес подойдет.

Понял, где у тебя была ошибка, кроме ДНК?

forfrends
Offline
Зарегистрирован: 24.02.2015

wdrakula, спасибо большое. Код работает. Единственны вопрос: я несколько раз натыкался на высказывание что число (с которым нужно сравнивать счетчик) нужно писать на 1 меньше. То есть не 62500U а 62499U. Это верно?

А за помощь спасибо. Я перечитал около 5-7-и статей, пересмотрел кучу роликов, все о чем пишется перепробовал сотню раз прежде чем писать на форуме... везде говорится только о явном объявлении. То есть если нужно записать OCIE1A в TIMSK1 то только его и прописывать, и БОЛЬШЕ НИЧЕГО... вот по этому я и не мог понять при чем здесь TCCR1A и TCNT1 если в их биты ничего записывать не нужно... вот такой вот подвох...

nik182
Offline
Зарегистрирован: 04.05.2015

Единственный правильный источник - родной даташит на МС. Там написано как правильно действовать. Инициализировать первый раз надо все регистры, значения которых отличны от дефолтных. Последняя команда пуск таймера. Потом можно менять всё что угодно и как угодно и сколько угодно регистров, при условии что таймер предварительно остановлен.
К сожалению часто статьи пишут люди, для которых это давно известно и на этой мелочи они не упоминают.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

forfrends пишет:

 То есть не 62500U а 62499U. Это верно?

Да, счет идет от 0.

forfrends пишет:

А за помощь спасибо. Я перечитал около 5-7-и статей, пересмотрел кучу роликов, все о чем пишется перепробовал сотню раз прежде чем писать на форуме... везде говорится только о явном объявлении. То есть если нужно записать OCIE1A в TIMSK1 то только его и прописывать, и БОЛЬШЕ НИЧЕГО... вот по этому я и не мог понять при чем здесь TCCR1A и TCNT1 если в их биты ничего записывать не нужно... вот такой вот подвох...

это не подвох, а недомыслие. Ты неправильно применил информацию из книг. Там, вероятно, речь шла о контроллере ВНЕ среды Ардуино.

Я написал вчера: "посмотите свой wiring.c". Там настраиваются ВСЕ таймеры, для ШИМ вывода (analogWrite()). То есть регистры таймеров НЕ ЧИСТЫЕ. Значит, перепрограмируя таймер, придется отказаться от analogWrite() на соответствующих ногах и установить по-своему ВСЕ регистры таймера.

И  еще ты забыл, что 0 тоже число. Если мода таймера = 4, то это значит 0100, а не только 1 в WGM12.

forfrends
Offline
Зарегистрирован: 24.02.2015

В ардуиновском wiring.c "черт ногу сломит"... Для людей, которые только начинают более глубоко изучать МК, там вообще ничего не понятно. Про ШИМ знаю, уже успел прочитать. Я его и не планировал использовать. 

Еще раз спасибо за помощь.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

forfrends пишет:

В ардуиновском wiring.c "черт ногу сломит"... Для людей, которые только начинают более глубоко изучать МК, там вообще ничего не понятно.

Да????? От оно как.... ну ладно.

 

forfrends
Offline
Зарегистрирован: 24.02.2015

Ну, про "черт ногу сломит" это конечно я погарячился :) но там много команд из асемблера. Простому начинающему ардуинщику это как какие-то магические заклинания :)

Я имел в виду что переходя с Ардуино ИДЕ на нормальный С++ вдруг сталкиваешься еще с другим языком - от этого по первой голова пухнет.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

forfrends пишет:

Я имел в виду что переходя с Ардуино ИДЕ на нормальный С++ вдруг сталкиваешься еще с другим языком - от этого по первой голова пухнет.

1. Чем С++ в IDE ненормален?

2. С каким "другим языком" Вы столкнулись?

А чёрт ногу сломит просто потому, что Вы не знаете языка (С и С++). Для тех, кто знает, там нет ничего сложного.

b707
Offline
Зарегистрирован: 26.05.2017

проштудировал эту тему, вроде все понятно :)

Но при переходе к практике возник вопрос -

Почему в определение прерывания ISR(TIMER1_COMPA_vect) вместо имени прерывания TIMER1_COMPA_vect можно вставить абсолютно любую комбинацию букв и цифр - и это будет компилироваться без ошибок? Разве при обнаружении в программе неизвестного идентификатора не должна возникать ошибка компиляции что-то вроде "xx is not declared" ?

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

b707 пишет:

Почему в определение прерывания ISR(TIMER1_COMPA_vect) вместо имени прерывания TIMER1_COMPA_vect можно вставить абсолютно любую комбинацию букв и цифр - и это будет компилироваться без ошибок?

Ну, во-первых, предупреждение то Вам выдаётся, просто Вы, видимо, не включили предупреждения компилятора в "Настройках". По умолчанию разработчики IDE выключили выдачу предупреждений, чтобы типа новички не пугались предупреждений компилятора. Оно ж завсегда удобно спрятать голову в песок и работать вслепую.

Зайдите в "Настройки" и включите печать предупреждений компилятора. И, если у Вас есть ISR(kaka){}, то увидите нечто вроде:

C:\...\Kaka0.ino:6:5: warning: 'kaka' appears to be a misspelled signal handler, missing __vector prefix [-Wmisspelled-isr]

Но ошибкой это действительно не является и всё компилируется правильно. Попробую объяснить почему.

Ваша 

ISR(kaka)

разворачивается (с точностью до аттрибутов) в 

extern "C" void kaka (void) 

Во-первых это очевидно, если посмотреть на текст макроса ISR (находится в файле interrupt.h), а во вторых в этом легко убедиться косвенно. Попробуйте скомпилировать такой текст:

extern "C" void kaka (void) {}
void setup(void) {}
void loop(void) {}
ISR(kaka) {}

Получите ругань на двойное определение функции kaka.

Таким образом, если Вы пишете ISR(kaka) {} и при этом у Вас в программе нет функции с таким именем, то ничего страшного не происходит - просто заводится функция с таким именем и всё. Никаких ошибок.

 

b707
Offline
Зарегистрирован: 26.05.2017

Евгений, огромное спасибо за разъяснения.

Предупреждений компилятора не вижу, потому что мучаю код в симуляторе (живой ардуины под рукой сейчас нет).

 

Brathobbit
Offline
Зарегистрирован: 06.07.2017

Всем СПАСИБО, разобрался!)))

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ну дополню темку .. кроме написания своей millis(), что сделал ТС заюзав таймер по-своему, можно вполне спокойно вставить внутрь типового обработчика таймера вызов нужной для себя функции. Это освобождает таймер для работы с ШИМ. А если это сделать грамотно, обрамив "контрольным выстрелом" от двойного вызова и "спец.защитой" контекста исполнения, то вполне можно вызывать и даже большие задачки, но только по одной.

В моем варианте реализации предлагался  timerHook для таких целей..

Eprinter
Offline
Зарегистрирован: 16.02.2018

Прорабатываю путь, которым дуть, для нового проекта, столкнулся со следующим багом (или фичей??) при использовании таймера. Часть скетча (Мега2560):

void setup()
{ 
  Serial.begin(9600);
/* Init the digital pins. */
  pinMode(QE_595, OUTPUT);
  pinMode(LOAD_595, OUTPUT);
  pinMode(CLC_595, OUTPUT);
  pinMode(LOAD_7219, OUTPUT);
  pinMode(CLC_7219, OUTPUT);

  DDRL = B11111111; //port L output
  DDRK = B00000000; //port K input
  PORTK = B11111111; //pull up K

  cli(); // отключить глобальные прерывания 
  TCCR5A = 0;
  TCCR5B = 0;
  OCR5A = 15624; // установка регистра совпадения 1c
  TCCR5B |= (1 << WGM12); // включение в CTC режим
  TCCR5B |= (1 << CS10)|(1 << CS12); // 1024 prescaler (коэффициент деления предделителя)
  TIMSK5 |= (1 << OCIE1A);  // включение прерываний по совпадению
  sei(); // включить глобальные прерывания
  
//  TCNT1 = value;                         //Preload timer value (3035 for 4 seconds)
//  TIMSK1 |= (1 << TOIE1);               // enable timer overflow 
}
 
void loop()
{
}

ISR(TIMER5_COMPA_vect) { // процедура обработки прерывания переполнения счетчика
//  TCNT1 = value;
  
  if( tactNo == 0 ) { //-------------- Load UART
    currentMicros = micros();
    for(byte i = 0; i < 64; i++) {
      massivLED[i] = random(0, 255); //bitRead(massivKey[i / 8], 7 - i % 8); //
    }//*/
    Serial.print("time us  UART="); Serial.print(micros() - currentMicros);
  }
  
  if( tactNo == 1 ) { //-------------- Load Key
    currentMicros = micros();
    keyNew = 0;
    for(byte i = 0; i < 16; i++) {
      PORTL = i;
      if( PINK < 255 ) {
        if( PINK == B11111110 ) keyNew = 128;
        if( PINK == B11111101 ) keyNew = 129;
        if( PINK == B11111011 ) keyNew = 130;
        if( PINK == B11110111 ) keyNew = 131;
        if( PINK == B11101111 ) keyNew = 132;
        if( PINK == B11011111 ) keyNew = 133;
        if( PINK == B10111111 ) keyNew = 134;
        if( PINK == B01111111 ) keyNew = 135;
        keyNew += i << 3;;
        Serial.print("  keyNew="); Serial.print(keyNew);
        break;
      }
    }
    PORTL = 0;
    Serial.print("  Key="); Serial.print(micros() - currentMicros);
  }
  
  if( tactNo == 2 ) { //-------------- OutPut LED
    currentMicros = micros();
    led8_write();
    PORTL = 0;
    Serial.print("  LED="); Serial.println(micros() - currentMicros);
  }
  
  tactNo++; if( tactNo > 2 ) tactNo = 0; //
}

В итоге получаю такой отчёт:

time us  UART=816  Key=64  LED=268
time us  UART=816  Key=64  LED=268
time us  UART=816  Key=64  LED=268
time us  UART=820  Key=64  LED=268
time us  UART=816  Key=64  LED=268
time us  UART=1844  Key=64  LED=268
time us  UART=816  Key=64  LED=268
time us  UART=820  Key=64  LED=268
time us  UART=1844  Key=64  LED=268
time us  UART=828  Key=64  LED=268
time us  UART=824  Key=64  LED=268
time us  UART=1844  Key=64  LED=268
time us  UART=820  Key=64  LED=268
time us  UART=820  Key=64  LED=268
time us  UART=1832  Key=64  LED=268
time us  UART=812  Key=64  LED=272
time us  UART=812  Key=64  LED=268
time us  UART=820  Key=64  LED=268
time us  UART=816  Key=64  LED=268
time us  UART=824  Key=64  LED=268
time us  UART=816  Key=64  LED=268
time us  UART=1832  Key=64  LED=268
time us  UART=828  Key=64  LED=268
time us  UART=816  Key=64  LED=268
 
То есть первая секция (UART, в которой пока рандомно заполняется массив) периодически выполняется на 1 мс (то есть вдвое!) дольше.
Это если настроить прерывание по совпадению. Если использовать тот же таймер в режиме переполнения, показания практически стабильны. Первоначально вместо прерывания была конструкция  if( (millis() - currentMillis) > 500 ), время выполнения всех секций также плавало в пределах 8 мкс, то есть практически не менялось.
 
Кто-нибудь может просветить, с чем может быть связано такое поведение?
 
ПС Изменил код в секции UART:
  if( tactNo == 0 ) { //-------------- Load UART
    currentMicros = micros();
    for(byte i = 0; i < 64; i++) {
      massivLED[i] = (i * i + 789)/ 3 ; //random(0, 255); 
    }//*/
    Serial.print("time us  UART="); Serial.print(micros() - currentMicros);
  }
Теперь вывод такой:
time us  UART=1088  Key=60  LED=272  micros=91552620
time us  UART=64  Key=60  LED=272  micros=91743788
time us  UART=1088  Key=60  LED=272  micros=91935980
time us  UART=1088  Key=60  LED=272  micros=92128172
time us  UART=1092  Key=60  LED=272  micros=92320364
time us  UART=1088  Key=60  LED=272  micros=92512556
time us  UART=1088  Key=60  LED=272  micros=92704748
time us  UART=1088  Key=60  LED=272  micros=92896940
time us  UART=1088  Key=60  LED=272  micros=93088108
time us  UART=1088  Key=64  LED=272  micros=93280300
time us  UART=1088  Key=60  LED=272  micros=93472492
time us  UART=1088  Key=60  LED=272  micros=93663660
time us  UART=1088  Key=60  LED=272  micros=93855852
time us  UART=1088  Key=60  LED=272  micros=94048044
time us  UART=1088  Key=60  LED=276  micros=94239216
time us  UART=1088  Key=60  LED=272  micros=94431404
time us  UART=1088  Key=60  LED=272  micros=94623596
time us  UART=64  Key=60  LED=272  micros=94814764
time us  UART=1088  Key=60  LED=272  micros=95006956
 
То есть каждый 16-й расчёт пропускается!! Я в ауте...

 

Eprinter
Offline
Зарегистрирован: 16.02.2018

Поторопился я с выводами... Поигрался с прескалером для режима overflow, получил такой же эффект - в среднем один такт из пяти расчёт занимает на 1 мс больше времени. Было:

float value = 64000;
TCCR5B |= (1 << CS50)|(1 << CS52);//1024

Стало:

float value = 40000;
TCCR5B |= (1 << CS50)|(1 << CS51);//64

То есть частота вызова прерывания осталось почти та же. 

 

ПС Возникла мысль - может кто-то сбрасывает предделитель??