Запуск функции с заданной частотой

nkk
nkk аватар
Offline
Зарегистрирован: 18.03.2016

В этой - http://arduino.ru/forum/programmirovanie/etyudy-dlya-nachinayushchikh-bl... - теме подробно описывается, как инвертировать определённый пин по прерыванию, но не понятно, как по нему запускать процедуру.

Или я не нашел, гдк это описано.

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

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Вызывай функцию в обработчике прерывания таймера, например:

ISR(TIMER2_COMPA_vect) { // прерывание Таймера2 по совпадению числа в счетном регистре TCNT2  
                         // с числом в регистре сравнения OCR2A (для режима CTC)
//ISR(TIMER2_OVF_vect) { // прерывание Таймера2 по переполнению счетного регистра TCNT2 
                         // (для режима Normal Mode)
	MyFunction(); // вызываемая функция
}

Естественно, перед этим нужно настроить сам таймер и разрешить прерывания.

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

Да, можно так. А посмотреть как это делается можно открыв wiring.c "из коробки". Там есть работа и настройка только нулевого таймера и на обруботку переполнения счетчика. Для такой большой задержки ещё можно воспользоваться макросом everyOVF(). Но его точная периодичность вызова - кооперативная и зависит от остальной части программы.

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

 А чем занят основной цикл, что для 3750 мсек таймер задействуется? Может достаточно

if (millis()-t > 3750)
{
  t=millis();
  yourFunction();
}

 

nkk
nkk аватар
Offline
Зарегистрирован: 18.03.2016

Вычисляется текущий каденс. Нагуглил тему [на сайте, похожем на хабр], посмотрел и понял, что код там совсем не код. Пишу свой:

https://gist.github.com/ircphp/ec427a16e0ab5cb9269b9175379d62a8

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

Тестирую на Atmega128, вместо геркона использую кнопку, но работать будет на Atmega328 и, кроме указанного кода, там еще будет чтение из последовательного порта и запись на SD-карту.

Уже и это почитал - http://avrprog.blogspot.com/2013/03/t2-8.html - всё равно не понятно.

Оставлю, навенрое, как етсь - и так работает.

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014
Я когда писал выше, не обратил внимание, что Вам нужна задержка аж 3750 мсек между вызовами функции. Вот так "в лоб" на 8-ми битном Таймере2 (или Таймере0) не получится, таймер переполняется слишком быстро. Если взять тактовую 16 МГц и самый большой прескалер 1024, то 16000000/1024/256 = ~61.04 переполнений в секунду (~16.38 мсек максимальная задержка между прерываниями по переполнению). Можо ввести дополнительную переменную, которую инкрементировать при каждом прерывании (вместо вызова функции). А в условии if проверять эту переменную и при достижении определённого значения уже вызывать функцию. Всё это надо рассчитывать, иногда удобней использовать меньший прескалер.
 
Или же использовать 16-битный Таймер1, его должно хватить: 16000000/1024/65536 = ~0.24 переполнений в секунду (~ 4194.63 мсек максимальная задержка между прерываниями по переполнению).
nkk
nkk аватар
Offline
Зарегистрирован: 18.03.2016

Нашел почти готовте решение в теме о разряде аккумулятора.

                              // Начальное значение счётчика,
                              // обеспечивающее задержку в 4 секунды.
#define CLK_INIT_VAL 0b0000101111011100

void setup() {
  TIMSK1 = 0x01;              // enabled global and timer overflow interrupt;
  TCCR1A = 0x00;              // normal operation page 148 (mode0);
  TCNT1 = CLK_INIT_VAL;
  TCCR1B = 0b00000101;        // Делитель частоты, x4 от того, что в примере.
}

ISR (TIMER1_OVF_vect) {
  TCNT1 = CLK_INIT_VAL;
                              // Код, выполняющийся раз в 4 секунды
}

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

Счётчик считает инкрементом до переполнения, при переполнении вывзывается функция ISR (TIMER1_OVF_vect). Переполнение наступает когда счётчик доходит до 1111111111111111 - 65535. Таким образом, 65535 минус 3036 (значение из скрипта равное задержке 4 сек) = 62499 инкрементов счётчика. Из пропорции:

62499 - 4000 мсек
58592 - 3750 мсек

Для запуска функции с периодом раз в 3750 мс, нужно щёлкать 58592 тактов, а значение CLK_INIT_VAL должно быть 65535-58592=6943=0b0001101100011111.

Проверил - работает:

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

nkk, решение слегка кривовато. У таймера есть специальный режим сброс по совпадению (CTC).

void setup(){
TCCR1A=0; 
TCCR1B=(1<<WGM12)|(1<<CS12)|(1<<CS10); // mode4(CTC), divider 1024
TIMSK1=(1<<OCIE1A);
OCR1A=58593;
}

ISR (TIMER1_COMPA_vect) { 
// что-то , что нужно выполнять раз в ..
}

void loop() {   }
nkk
nkk аватар
Offline
Зарегистрирован: 18.03.2016

Забавно:

Переписал скрипт, чтобы выводило только i если a-b != 3750, - выводит те же строки: 6, 8, 14, 18, 22, 27, 30
 

 

Okmor
Okmor аватар
Offline
Зарегистрирован: 16.10.2015

Вот пришел в ступор от таймеров.

Два примера:

Таймер Т1 код:

int n = 0;
void setup(){
TCCR1A=0;
TCCR1B=(1<<WGM12)|(1<<CS12)|(1<<CS10); 
TIMSK1=(1<<OCIE1A);
OCR1A=100;
}
ISR (TIMER1_COMPA_vect) { 
n++;
if (n>10) {digitalWrite(13, !digitalRead(13)); n = 0;}
}
void loop() {   }

Тот же код переделанный для таймера Т2

int n = 0;
void setup(){
TCCR2A=0;
TCCR2B=(1<<WGM22)|(1<<CS22)|(1<<CS20); 
TIMSK2=(1<<OCIE2A);
OCR2A=100;
}
ISR (TIMER2_COMPA_vect) { 
n++;
if (n>10) {digitalWrite(13, !digitalRead(13)); n = 0;}
}
void loop() {   }

У меня для таймера Т2 получается задействовать, или счетчик, или пресаклер, а мне нужно таймер Т2 с счетчиком и пресаклером. Что я делаю не так. Может у таймера Т2 какая то особенность.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Okmor, не ясно по какому принципу вы переделывали. 4 строкой второго скетча вы задаёте несуществующий режим для таймера 2 , (даташит страница 155,  Table 18-8. Waveform Generation Mode Bit Description ) и прескалер тоже не на 1024, а на 32.

Okmor
Okmor аватар
Offline
Зарегистрирован: 16.10.2015

Вот.

Тут есть особенности. Не знаю документировано ли, но таймер Т2 в режиме СТС самостоятельно не обнуляется, потому использование параметра (1<<WGM21) бессмысленно. Может кто то объяснит где я ошибся. 

Дял всех оставлю этот кусок работающего таймера с счетчиком и пресаклером.

int n = 0;
void setup(){
TCCR2A=0;
TCCR2B=(1<<CS22)|(1<<CS21)|(1<<CS20); 
    // (1<<WGM21)  Clear Timer on Compare Match (CTC) mode
    //  Для таймера Т2 не работает, приходится вручную обнулять. 
    //001 - CLK
    //010 - CLK/8
    //011 - CLK/32
    //100 - CLK/64
    //101 - CLK/128
    //110 - CLK/256
    //111 - CLK/1024
OCR2A=40; // Максимум 255
TIMSK2=(1<<OCIE2A);
}

ISR (TIMER2_COMPA_vect) 
{ 
  TCNT2 = 0; // Работает, если вручную обнулять
  if (++n>250) {digitalWrite(13, !digitalRead(13)); n = 0;}
}

void loop() {;}

dimax. Спасибо за наводку.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Okmor пишет:

таймер Т2 в режиме СТС самостоятельно не обнуляется, потому использование параметра (1<<WGM21) бессмысленно. Может кто то объяснит где я ошибся.

Да, маленькая ошибочка у вас, поэтому и не обнуляется. Бит WGM21 "приписан" к регистру TCCR2A, а вы его пытались внести в регистр TCCR2B, поэтому он и не включался.

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

nkk пишет:
Помогите, пожалуйста, нужно выполнять определённые команды через равные зсданные промежутки времени (раз в 3750 мсек).

 вам надо выполнять команду раз в  3750 милисек  (3.750)секунды или раз в 3750 микросекунд.

Если первый вариант то лучше так.

uint8_t non_stop_program1(uint16_t span) {
  static uint32_t future = 0;
  if (millis()<future) return 0;
  future += span;
  return 1;
}

void setup() {
  Serial.begin(9600);
}
void loop() {
     if (non_stop_program1(3750))  {
          / / Здесь вы организовываете подачу команды
           Serial.println("knok");
           }
 }
 

 

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

qwone пишет:

или раз в 3750 микросекунд.

3750000 микросекунд