Таймеры.....

a_sel
Offline
Зарегистрирован: 29.10.2015

Люди! Помогите...

Есть такая задача: вычислить частоту сигнала на ноге (100Гц...5кГц), умножить на коэффициент (скажем, к=1.78)  и отправить вычисленную частоту на другую ногу. Всё с максимальной возможной точностью.

Реально это на одной Ардуине Нано?.....

Все примеры, что я нашёл, делают это отдельно на T1. А единовременно?..........

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013
a_sel
Offline
Зарегистрирован: 29.10.2015

Да! Практически моя задача. Спасибо большое! "Пошёл" вникать в детали......

 

a_sel
Offline
Зарегистрирован: 29.10.2015

Глубоко вникнуть не удалось - у моей наны, похоже, попросту нет таймера 3. Я прав?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

a_sel, нету, да. Я не помню почему я написал тот скетч под ардуину-микро, видимо  два 16-битных таймера было удобно применить в целях упрощения кода. В принципе ничто не мешает переписать под другой контроллер и таймер, но у меня спортивного интереса к этой теме больше нет :(

MagicianT
Offline
Зарегистрирован: 03.10.2015

На 5кГц можно и библами обойтись, как мне кажется, чё там с таймерами выёживаться. Возьмите фреквенси коунтер готовый, помножте на 1.78 и скормите Тоне().

http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-frequency-counter-library/

https://www.arduino.cc/en/Reference/Tone

a_sel
Offline
Зарегистрирован: 29.10.2015

Добрый день всем!

Вроде бы разобрался я с этик кодом. Спасибо dimax за отличную идею. Переписал я под 2-й таймер, хоть и восьмибитный...

const int pini = 8;
const int pino = 11;

#define _DEBUG  1

volatile unsigned int icr_tic;
volatile unsigned int int_tic;
volatile unsigned long tic;

unsigned int _ostat = 0;
unsigned int _totover = 0;
unsigned int _over = 0;
unsigned long _tim = 50000;

void setup(){
  Serial.begin(57600);
  pinMode (pino,OUTPUT);  // выход сигнала 
  pinMode (pini,INPUT);  // вход сигнала

//настройка 16 бит таймера-счётчика 1 
TCCR1B = 0; TCCR1A = 0; TCNT1 = 0;
TIMSK1 = (1<<ICIE1)|(1<<TOIE1); //создавать прерывание от сигнала на пине ICP1
TCCR1B = (1<<ICNC1)|(1<<ICES1)|(1<<CS10);//div 1

//настройка таймера-генератора 2
TCCR2A=0; TCCR2B=(1<<WGM22)|(1<<CS20); //div1 CTC
TIMSK2=1<<OCIE2A; //прерывание по переполнению OCR2A
delay(100);
}

/// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ISR (TIMER1_CAPT_vect) { //прерывание захвата сигнала на входе ICP1
  TCNT1 = 0; 
  icr_tic=ICR1;
  tic = ((uint32_t)int_tic<<16) | icr_tic; //подсчёт тиков
  int_tic=0; 
}

/// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ISR (TIMER1_OVF_vect) { int_tic++; } //прерывания счёта по переполнению uint
/// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

ISR (TIMER2_COMPA_vect) { //прерывание счёта тактов и управления выходом
  static volatile unsigned long temp_ocr; 

  if (temp_ocr <= 255) { 
     OCR2A = temp_ocr;
     PORTB ^= 0x10; // D12 ===> D8
    #if  _DEBUG  
      _ostat = temp_ocr;      
      _totover = _over;      
      _over=0;      
      temp_ocr = _tim ;      
    #else 
      temp_ocr = tic; 
    #endif
    
    return;
  }
  if (temp_ocr > 255) {
     OCR2A = 255; 
     temp_ocr=temp_ocr - 255;
    _over++;
  } 
}
/// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  
void loop(){
 static int cntl=44;
 for ( ; cntl > 0; cntl--) {
   Serial.print("  tic = ");    
   Serial.print(tic,DEC);
   Serial.print("  _ticT2 ");   
   Serial.println(_totover * 255 + _ostat,DEC);
 }
}

Правда, стала набегать погрешность из-за постоянных прерываний, наверное. Чуть поясню... Я "зарядил" timer2 на формирование меандра на d11 (50000 тиков), а 1-м таймером его (меандр) ловлю на d4 и вычисляю. Это для отладки. Потом все будет наоборот. И вот что показывает Serial:

  tic = 100780  _ticT2 50000
  tic = 100780  _ticT2 50000
  tic = 100780  _ticT2 50000
  tic = 100773  _ticT2 50000
  tic = 100778  _ticT2 50000
  tic = 100778  _ticT2 50000
  tic = 100778  _ticT2 50000
  tic = 100778  _ticT2 50000
  tic = 100777  _ticT2 50000
  tic = 100777  _ticT2 50000
  tic = 100777  _ticT2 50000
  tic = 100778  _ticT2 50000
  tic = 100883  _ticT2 50000
  tic = 100671  _ticT2 50000
  tic = 100779  _ticT2 50000
  tic = 100779  _ticT2 50000

................................................

Почти 800 лишних тиков набежало... Не подскажите - каким образом можно скомпенсировать эту погрешность? Хотя бы натолкните на путь истинный...
 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

a_sel, ну коли вы решили в качестве теста посвистеть тамером в 12 пин, то хорошо бы его (пин) сделать как Output, а то у вашего генератора выходное сопротивление под 100 МОм получилось :)

a_sel
Offline
Зарегистрирован: 29.10.2015

dimax, сорри. Согласен, спасибо. На работе совершенно не возможно работать, будь она не ладна...

А по существу вопроса есть какие нибудь мысли?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

a_sel, это и было по существу. Могло глючить ещё сильнее.

a_sel
Offline
Зарегистрирован: 29.10.2015

Да, я конечно же тут же поправил этот ляп. Увы, как мёртвому припарка. Ни на тик не изменилась картинка.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

a_sel, я сам не особо заморачивался с погрешностями. Может это и норма. Методика захвата через ICP для не очень высоких частот, если перевести это в герцы -то погрешность уйдёт "за запятую" и особо волновать не будет. Даже подсчитал сейчас -в вашем случае это дельта  158,9 ... 158,5 Герц.  Если есть боевой настрой, то могу посоветовать покапаться внутрях популярной  библы FreqMeasure там по идее тот-же принцип, можно вытащить переменную, аналогичную моей tic и посмотреть, сильно ли её мандражит...

a_sel
Offline
Зарегистрирован: 29.10.2015

Не, фигня какая то.... Нет тут никакой связи с обилием прерываний.... Тут другая собака порылась. Без "поллитры" или осцилоскопа не разберёшься.

  tic = 1956  _over 3  _ostat 235  _ticT2 1000
  tic = 1956  _over 3  _ostat 235  _ticT2 1000
  tic = 1956  _over 3  _ostat 235  _ticT2 1000
  tic = 1956  _over 3  _ostat 235  _ticT2 1000
  tic = 1955  _over 3  _ostat 235  _ticT2 1000

  tic = 4004  _over 7  _ostat 215  _ticT2 2000
  tic = 4004  _over 7  _ostat 215  _ticT2 2000
  tic = 4003  _over 7  _ostat 215  _ticT2 2000
  tic = 4005  _over 7  _ostat 215  _ticT2 2000
  tic = 4004  _over 7  _ostat 215  _ticT2 2000

  tic = 5782  _over 11  _ostat 195  _ticT2 3000
  tic = 5794  _over 11  _ostat 195  _ticT2 3000
  tic = 5539  _over 11  _ostat 195  _ticT2 3000
  tic = 5795  _over 11  _ostat 195  _ticT2 3000
  tic = 5541  _over 11  _ostat 195  _ticT2 3000

  tic = 7587  _over 15  _ostat 175  _ticT2 4000
  tic = 7588  _over 15  _ostat 175  _ticT2 4000
  tic = 7844  _over 15  _ostat 175  _ticT2 4000
  tic = 7844  _over 15  _ostat 175  _ticT2 4000
  tic = 7844  _over 15  _ostat 175  _ticT2 4000

  tic = 9886  _over 19  _ostat 155  _ticT2 5000
  tic = 9891  _over 19  _ostat 155  _ticT2 5000
  tic = 9635  _over 19  _ostat 155  _ticT2 5000
  tic = 9635  _over 19  _ostat 155  _ticT2 5000
  tic = 9891  _over 19  _ostat 155  _ticT2 5000

  tic = 11939  _over 23  _ostat 135  _ticT2 6000
  tic = 11938  _over 23  _ostat 135  _ticT2 6000
  tic = 11684  _over 23  _ostat 135  _ticT2 6000
  tic = 11683  _over 23  _ostat 135  _ticT2 6000
  tic = 11683  _over 23  _ostat 135  _ticT2 6000

  tic = 13732  _over 27  _ostat 115  _ticT2 7000
  tic = 13986  _over 27  _ostat 115  _ticT2 7000
  tic = 14075  _over 27  _ostat 115  _ticT2 7000
  tic = 13732  _over 27  _ostat 115  _ticT2 7000
  tic = 13987  _over 27  _ostat 115  _ticT2 7000

  tic = 15689  _over 31  _ostat 95  _ticT2 8000
  tic = 15673  _over 31  _ostat 95  _ticT2 8000
  tic = 15688  _over 31  _ostat 95  _ticT2 8000
  tic = 15690  _over 31  _ostat 95  _ticT2 8000
  tic = 15689  _over 31  _ostat 95  _ticT2 8000

  tic = 17737  _over 35  _ostat 75  _ticT2 9000
  tic = 17738  _over 35  _ostat 75  _ticT2 9000
  tic = 17736  _over 35  _ostat 75  _ticT2 9000
  tic = 17738  _over 35  _ostat 75  _ticT2 9000
  tic = 17738  _over 35  _ostat 75  _ticT2 9000

  tic = 19796  _over 39  _ostat 55  _ticT2 10000
  tic = 20123  _over 39  _ostat 55  _ticT2 10000
  tic = 20043  _over 39  _ostat 55  _ticT2 10000
  tic = 19785  _over 39  _ostat 55  _ticT2 10000
  tic = 19784  _over 39  _ostat 55  _ticT2 10000

  tic = 22431  _over 43  _ostat 35  _ticT2 11000
  tic = 22435  _over 43  _ostat 35  _ticT2 11000
  tic = 22435  _over 43  _ostat 35  _ticT2 11000
  tic = 22436  _over 43  _ostat 35  _ticT2 11000
  tic = 22435  _over 43  _ostat 35  _ticT2 11000
 

a_sel
Offline
Зарегистрирован: 29.10.2015

Спасибо. Попробую завтра на работе осцилографом глянуть, как это выглядит визуально.

a_sel
Offline
Зарегистрирован: 29.10.2015

Посмотрел. Ну дрожит немного задний фронт, это если растянуть в "период на экран". Да и фиг с ним. В целом очень красивая картинка. Подал эталонный килогерц - получилось 15 983 +- 2 тика. Ну,.. вот такой китайский кварц. Для моей задачи это абсолютно некритично. Хоть в попугаях, поскольку выходной сигнал формируется тем же кварцем.

В общем, спасибо всем большое!

MagicianT
Offline
Зарегистрирован: 03.10.2015

Ни чё не работает, ни на 11, ни на Д4.

a_sel
Offline
Зарегистрирован: 29.10.2015

MagicianT, вот прога прямо с работающего макета. D12 и D8 - перемычка.

 

const int pini = 8;
const int pino = 12;

volatile unsigned int icr_tic;
volatile unsigned int int_tic;

unsigned long tic;

unsigned int _ostat = 0;
unsigned long _totover = 0;
unsigned long _over = 0;
unsigned long _tim = 5000;

void setup(){
  
  Serial.begin(9600);
  pinMode (pino,OUTPUT); 
  pinMode (pini,INPUT);  
  digitalWrite(pini, HIGH);  

//настройка 16 бит таймера-счётчика 1 
 TCCR1B = 0; TCCR1A = 0; TCNT1 = 0;
 TIMSK1 = (1<<ICIE1)|(1<<TOIE1); //создавать прерывание от сигнала на пине ICP1
 TCCR1B = (1<<ICNC1)|(1<<ICES1)|(1<<CS10);//div 1

//настройка таймера-генератора 2
 TCCR2A=0; TCCR2B=(1<<WGM22)|(1<<CS20); //div1 CTC
 TIMSK2=1<<OCIE2A; //прерывание по переполнению OCR2A
 delay(100);
}

/// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ISR (TIMER1_CAPT_vect) { //прерывание захвата сигнала на входе ICP1
  TCNT1 = 0; 
  icr_tic=ICR1;
  tic = ((uint32_t)int_tic<<16) | icr_tic; //подсчёт тиков
  int_tic=0; 
}

/// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ISR (TIMER1_OVF_vect) { int_tic++; } //прерывания счёта по переполнению uint
/// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

ISR (TIMER2_COMPA_vect) { //прерывание счёта тактов и управления выходом
  static volatile unsigned long temp_ocr = 1000; 
  if (temp_ocr <= 255) { 
      OCR2A = temp_ocr;
      PORTB ^= 0x10; // PortB.4 == 12 ноге!!! pino 
      _ostat = temp_ocr;      
      _totover = _over;      
      _over=0;      
      temp_ocr = _tim ;      
     return;
    }
    else {
     OCR2A = 255; 
     temp_ocr -= 255;
    _over++;
    }
}
/// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  
void loop(){
 int n; 
 
   Serial.print("  tic = ");    
   Serial.print(tic,DEC); // тиков на входе pini
   tic = 1; // если не будет прерываний....
   Serial.print("  _ticT2 ");   
   Serial.println(_totover * 255 + _ostat,DEC); // тиков в выходнлм полупериоде
 
   delay(300); 
   
   n = Serial.available();
   if (n) {
     n = Serial.read();
     if (n == 32) { // " " --> пауза
       while (!Serial.available() ); // ждём любой символ по rs232
       Serial.read(); // удаляем  этот символ из буфера
     }
     else { 
      _tim += 1000; //  увеличиваем полупериод на 1000 тиков
       delay(100); 
       Serial.println();
     }  
   }   
}

В терминале -

  tic = 9881  _ticT2 5000
  tic = 9879  _ticT2 5000
  tic = 9875  _ticT2 5000
  tic = 9877  _ticT2 5000
  tic = 9876  _ticT2 5000
  tic = 9893  _ticT2 5000
  tic = 9880  _ticT2 5000 и т.д.
 

А что Ваш терминал выдаёт?

 

 

 

MagicianT
Offline
Зарегистрирован: 03.10.2015
Странный способ генерировать пульсы, башку свернуть можно, самый нелепый который я только встречал.
Печатает примерно тоже самое, что и у вас. Это скорее плохой признак, ардуино же разные, разные и кварцы, ошибка где-то в математике.
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

a_sel пишет:

Без "поллитры" или осцилоскопа не разберёшься.

a_sel пишет:

Посмотрел. Ну дрожит немного задний фронт ...

...

Да и фиг с ним

Рано сдаваться! Вы ведь только осциллограф попробовали. Может поллитра лучший результат даст? Отпишитесь, как проверите.

a_sel
Offline
Зарегистрирован: 29.10.2015

Евгений, "поллитра" - это крайняя мера. Тем более в будний день... Не! Не наш метод!

MagicianT, интересно бы узнать ещё хотя бы один способ 8-битным таймером генерить герцовые импульсы с точностью 62.5 наносек... Поделитесь, плиииз.....

MagicianT
Offline
Зарегистрирован: 03.10.2015

a_sel пишет:
MagicianT, интересно бы узнать ещё хотя бы один способ 8-битным таймером генерить герцовые импульсы с точностью 62.5 наносек... Поделитесь, плиииз.....

Я не понимаю о какой точности 62.5 вы говорите, у вас пин дрыгается в прерывании, а прерывание +-3 микросекунды, это в лучшем случае, если другое прерывание не пришло и не сдвинуло. Генерить правильно через хардваре, пин 3 или 11, но согласен что 8-битный таймер до 62 кГц только хорош, потом преселектор надо заюзать, а тогда хоть частотно-временое разрешение и ухудшится пропорционально, и станет как сейчас, но по крайней мере фаза прыгать-дёргаться не будет.

MagicianT
Offline
Зарегистрирован: 03.10.2015
Я тут ещё подумал, и решил выложить свои соображения.
Проблема мне видится в том, что не хватает ресурсов воспроизвести правильную частоту по ВЫХОДУ. Таймер 16-бит всего один, а на 8-битном не получится. Можно как вы сделали, нарастить разрядность таймера-2 в софте, тогда хоть 16-бит хоть 20 получится, но чтобы пин двигать без джитера (фазовых скачков) это не решение, как я уже написал выше софтовое прерывание прыгает постоянно. Остаётся применить внешние аппаратные средства как то ФАПЧ или DDS(АД9850), это позволило бы синтезировать частоту с любой высокой разрешающей способностью. Если использование внешних средств не вариант, тогда отдать 16-битный таймер на выход. Что кажется логичным, т.к. нет смысла точно подсчитывать пульсы на входе через ICP (инпут каптюре), если нет возможности правильно их воспроизвести на выходе.  
По входу вообще можно считать чем попало, например через микрос() - а потом просто сгладить значение. Выход через тоне(). Вот пример:

const     byte          interruptPin  = 2;

volatile  unsigned long mikrosekynd    = 0;
volatile  unsigned long mikros_last    = 0;

          float         multiplier     = 1.78;
          int           update_rate    = 100;  // 100 millisekynd.

          unsigned long millis_last    = 0;
          
void setup(){
  // Serial.begin(9600);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, RISING);
  tone(3, 1);  
}

void loop() {

  unsigned long millisekynd = millis();

  if((millisekynd - millis_last) > update_rate) {
  tone(3, ( multiplier *1000000.0/((float) mikrosekynd)));  
  millis_last = millisekynd;
  }
}

void blink() {
  unsigned long temp = micros();
  unsigned long filter = temp - mikros_last;

  mikros_last = temp;
  mikrosekynd = (255 * mikrosekynd + filter)/ 256;
  if(mikrosekynd   < 200) mikrosekynd =   200;
//  if(mikrosekynd > 10000) mikrosekynd = 10000;
//  tone(3, ( 1000000.0/((float) mikrosekynd)));  
}

на выходе джиттер всё равно есть, когда обновляется тоне(). Программа работает, но если свербит и хочется совершенства, то таймер-1 свободен, примерно до 122 герц в фейз-корект ПВМ, что несколько выше тех. задания. Если ниже надо, то с прескалером на 8 разрешение по частоте падает до 13 Гц (гранулярность?) на 5000 кГц. Всего 0.25%

16000000 3200 5000.00  
16000000 3201 4998.44 0.03
16000000 3202 4996.88 0.03
       
div1:8     %
2000000 400 5000.00  
2000000 401 4987.53 0.25
2000000 402 4975.12 0.25