Ускорение функции micros()

Buldakov
Offline
Зарегистрирован: 17.01.2016

Все понял.

 

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

А Вы на чём делаете? На 32? Если да, так бы сразу и сказали, там чуть другая работа с таймером (но суть-то таже). А если нет, так смотрите правильный даташит.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Нашел правильный даташит. Почему то я думал что Atmel32 и Atmel32x одинаковые микросхемы. С битами разобрался.

Дальше вопрос. Делаю код таймера

void setup() {                                                                                                            
pinMode(11, OUTPUT);                    //Назначим вывод 11 на вывод сигналов                                    
//---------------------------------------------------------------------------------------------------
//Пример программирования 2 таймера 8 бит                                                                     
TCCR2A = (0<<COM2A1)|(1<<COM2A0)|(1<<WGM21)|(0<<WGM20); //CTC mode                                               
TCCR2B = (1 << CS22)|(0 << CS21)|(0 << CS20);  //Коэффициент предделителя 64                                             
OCR2A =127;                                      //Регистр сравнения. Сюда мы записываем то, с чем надо сравнить.       
//---------------------------------------------------------------------------------------------------
}                                                                                                           

Таймер делит тактовую частоту.

Добавляю прерывание все перестает работать.

void setup() {                                                                                                            
pinMode(11, OUTPUT);                    //Назначим вывод 11 на вывод сигналов                                    
//---------------------------------------------------------------------------------------------------
//Пример программирования 2 таймера 8 бит                                                                     
TCCR2A = (0<<COM2A1)|(1<<COM2A0)|(1<<WGM21)|(0<<WGM20); //CTC mode                                               
TCCR2B = (1 << CS22)|(0 << CS21)|(0 << CS20);  //Коэффициент предделителя 64                                             
OCR2A =127;                                      //Регистр сравнения. Сюда мы записываем то, с чем надо сравнить.       
TIMSK2=(1<<OCIE2A);                              //Подключение прерывания по совпадению Timer2                  
//---------------------------------------------------------------------------------------------------
}                                                                                                           

 

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

Buldakov, вы бы какую нибудь литературу по таймерам почитали. Где ваш обработчик прерывания? Если в программе не указано куда возвращаться после прерывания, то программа выкидывает на нулевой адресс, то бишь эквивалентно нажатию кнопки "ресет" 16000000/64/128 раз в секунду :)

Buldakov
Offline
Зарегистрирован: 17.01.2016

Да почитал.  знаю кр580ви53. Всем спасибо за помощь. Нашел более простой способ сделать металлоискатель. Ветку можно закрывать. вопросов по таймерам больше нет.

Берем 2 счетчика на 24 бит. (собранных например на 555ие5.)  1 считает число импульсов. 2 считает сколько импульсов пришло за это время с тактового генератора. Затем содержимое этих счетчиков считываем на двух микросхемах  кр580вв55а. Заводим в arduino  и там значение одного счетчика делим на другой. Этот способ я думаю будет более простым.

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

Если Вам с непривычки трудно понимать язык даташитов, попробуйте прочитать вот эту подборку статей. Только читайте подряд и разбирайет все примеры. После этого и даташиты понятнее станут.

 
Buldakov
Offline
Зарегистрирован: 17.01.2016

Так вам было трудно пример нормальный привести. Все таки нашел сам. писали обо всем кроме того, что нужно.

/* Arduino 101: timer and interrupts
   1: Timer1 compare match interrupt example 
   more infos: http://www.letmakerobots.com/node/28278
   created by RobotFreak 
*/


void setup()
{
  pinMode(13, OUTPUT);
  
  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 31250;            // compare match register 16MHz/256/2Hz
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // enable all interrupts
}

ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{
  digitalWrite(13, digitalRead(13) ^ 1);   // toggle LED pin
}

void loop()
{
  // your program here...
}

 

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

Buldakov пишет:

Так вам было трудно пример нормальный привести. Все таки нашел сам. писали обо всем кроме того, что нужно.

Ну, да. Так всегда на этом форуме. Сначала человек не может толком объяснить что ему нужно, а потом обвиняет всех, что ему это самое "что нужно" на тарелочке не принесли.

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

Так ногодрыг можно и побыстрее сделать. 8Мгц нужен? Готов поделиться кодом. Причём без обработчика прерываний - сам дрыгать ногой будет, без участия Вашей программы вообще.

Buldakov
Offline
Зарегистрирован: 17.01.2016

ЕвгенийП пишет:

Buldakov пишет:

Так вам было трудно пример нормальный привести. Все таки нашел сам. писали обо всем кроме того, что нужно.

Ну, да. Так всегда на этом форуме. Сначала человек не может толком объяснить что ему нужно, а потом обвиняет всех, что ему это самое "что нужно" на тарелочке не принесли.

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

Так ногодрыг можно и побыстрее сделать. 8Мгц нужен? Готов поделиться кодом. Причём без обработчика прерываний - сам дрыгать ногой будет, без участия Вашей программы вообще.

Вот привожу конкретно, что мне нужно. Используем два таймера. На вход первого таймера подаются импульсы входной частоты. Второй таймер считает внутренние клоки Arduino. Первый таймер сбрасывается и начинает считать с нуля по приходу первого фронта входного измеряемого импульса. По этому же сигналу происходит сброс и начало счета импульсов второго таймера. Спустя время больше или равного времени измерения (например 1 секунда)  и с приходом фронта входного импульса на таймер 1. Таймеры 1 и 2 останавливаются и их значения передаются для дальнейшей обработки. Например 1 таймер насчитал 78121 импульсов входной частоты. Второй таймер насчитал 5000020 импульсов.(длительность 1 импульса второго таймера равна 1 мкс.) Отсюда частота равна (78121/5000020)*1000000 = 15624.13 гц. Так мы получим измерение частоты на интервале измерения 1 сек и с точностью больше 1 гц. точность будет определяться только количеством импульсов насчитанных вторым таймером.

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

Buldakov, без обид. Поверьте, я настроен совершенно по-дружески к начинающим. Но вам пока рано планировать то, в чём вы пока ещё плохо разбираетесь. Вы сейчас пытаетесь создать устройство для измерения числа сантиметров в одном  метре. По крайней мере это следует из ваших же слов. Второй таймер при секундной выдержке насчитает ровно 16 000 000 тактов при делителе =1. Далее: Чисто аппаратно таймер не может считать  именно микросекундами . Если он будет выходить в прерывание для счёта каждую микросекунду - будет коллапс, контроллер ничего больше не сможет делать.  В результате имеем то, с чего начинали эту тему -подсчёт количества импульсов за 1 секунду. Но результатом будет число без запятой. Единственная ваша зацепка - то о чём я писал в #21  Другого пути просто не существует.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Мне нужно то что написано в книге - Микропроцессоры в измерительных приборах Г.Я. Мирский.  На стр 82 четвертый способ. Там написано как правильно измерять частоту Книга 1984г. Уже в то время знали как это делать и делали на обычной логике. Мне надо повысить относительную погрешность дискретизации на стр 83 формула 4.6 хотя бы в 100 раз. Это я и прошу помочь сделать.

Да в этом я плохо разбираюсь. Поэтому и пришел сюда за помощью. Максимум что я сумел получить программируя на Си - это 10 кгц с точностью 0.02 гц. При 1 секундном импульсе таймер насчитает 16 М. Но остаток от последнего импульса он обрежет. мне нужно посчитать с этим остатком. Микросекунда это конкретный случай. Мне надо 62 нс. Там же ясно сказано таймер 2 считает время не за 1 секунду, а 1 секунда плюс время до начала первого положительного фронта входного импульса.И не нужно от него прерывание каждые 1 мкс. А по поводу мерить сантиметрами метры - это да.

Вот вам другой пример. Более сложной задачки. Которую уже решили. Один из лучших программистов по Altera решал ее и решил. (Сергей Малахов).Световод длинной 1 км меняет длинну от температуры. Путем измерения длинны световода надо измерить температуру. Время переключения Плис 6 нс. Замерить надо время 4 пс. в 1000 раз меньше времени задержки микросхемы. В теории этого сделать нельзя. Но на практике это уже решили. Вот и я обращаюсь к тем кто может задачки такого класса решать на Arduino.

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

Buldakov, есть вариант подсчитать точную время суммы например 10 000 импульсов, но что бы  не отсекать при этом часть полезного импульса потребуется два 16-битных таймера. Это платы  леонардо/микро/мега2560. Одним счётчиком считается фиксированно-заданное число импульсов, например 10 000, при достижении этого значения таймер делает прерывание по регистру сравнения, и в прерывании считывается текущий счёт тактов процессора c другого таймера. Точность этого метода будет составлять (число импульсов * 0,0625)  +/- 0,0625 uS  .

ESV
Offline
Зарегистрирован: 16.12.2015

Это валюнтаризьм ©

Buldakov
Offline
Зарегистрирован: 17.01.2016

dimax пишет:

Buldakov, есть вариант подсчитать точную время суммы например 10 000 импульсов, но что бы  не отсекать при этом часть полезного импульса потребуется два 16-битных таймера. Это платы  леонардо/микро/мега2560. Одним счётчиком считается фиксированно-заданное число импульсов, например 10 000, при достижении этого значения таймер делает прерывание по регистру сравнения, и в прерывании считывается текущий счёт тактов процессора c другого таймера. Точность этого метода будет составлять (число импульсов * 0,0625)  +/- 0,0625 uS  .

Ну у меня нет двух 16 разрядных счетчиков. Почему нельзя сделать так? Запускаем таймеры на счет импульсов. 16 битный считает число входяших импульсов, 8 битный считает число клоков. С приходом первого фронта импульса сбрасываем оба триггера. Дальше они начинают считать.При переполнении триггера вызываем прерываение и прибавляем единичку. Зная частоту 2 таймера (например 2 мгц) через (256/2) = 128 мкс. он выдаст запрос на прерывание. Если число запросов на прерывание превысит (1000000мкс/128 мкс) =7813 то по фронту следующего импульса на вход триггера 1 мы останавливаем счет и зная количество прерываний и состояние регистров счетчика мы получим два значения количества импульсов в первом и втором таймерах. Почему это нельзя сделать? Я только не знаю как это сделать. - а то бы сделал сам.

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

Buldakov, можно и на 8 битном, но чем больше суеты в обработчиках прерывания -тем хуже точность результата. В идеале нужно вообще 32 битный счётчик для отсчёта тактов процессора, что б совсем обойтись без переполнений. Вот примерно так, скетч ниже. Но будет некоторая девиация итоговых данных, всё работает и так на пределе возможностей. И нужно иметь ввиду, в этом режиме у таймера нет защиты от помех. По-хорошему нужно заглушить системный таймер0, ( TCCR0B=0; ) он мешает . Девиация должна уменьшится. Но тогда не будет работать вывод в сериал. Впрочем этот вопрос уже не в рамках темы.

volatile uint32_t tic; 
volatile uint16_t ovf_tic; 

void setup(){
Serial.begin(9600);
pinMode (5,INPUT); // clock input 
TCCR1A=0; TIMSK1 = 1<<OCIE1A; //прерывание по совпадению с OCR1A
OCR1A=10000; //отсчитать 10 000 входных импульсов до прерывания
TCCR2A=0; TIMSK2=1<<TOIE2;//прерывание по переполнению таймера2
TCCR2B=1<<CS20;// делитель 1
TCCR1B = (1<<CS10)|(1<<CS11)|(1<<CS12)|(1<<WGM12); //СТС делитель 1
}
ISR (TIMER1_COMPA_vect){ 
TCCR1B=0; TCCR2B=0;//остановить таймеры
tic=((uint32_t)ovf_tic<<8)| TCNT2; //сложить что натикало
ovf_tic=0; //сбросить счёт переполнений
TCNT2=0; TCNT1=0; //сброс счётных регистров
TCCR1B=15; TCCR2B=1; //запустить таймеры
}

ISR (TIMER2_OVF_vect){ 
 ovf_tic++; //считать переполнения
     if (ovf_tic==62500){ //если досчитали до секунды
      ovf_tic=0; tic=0; TCNT1=0;TCNT2=0; //всё обнулить
      }
  }

void loop(){
delay(500);
float us= tic * 0.0625 /OCR1A;
Serial.print(tic);
Serial.print(" tic, time=");
Serial.print(us,3);
Serial.print(" uS,  Freq=");
Serial.print((float)1/(us/1000000.0),3);
Serial.println(" Hz");
}

Ограничение времени на измерение стоит 1 секунда, соответссно минимальная измеряемая частота должна быть выше, чем значение OCR1A

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

Buldakov, попробовал повесить кварцевый генератор 32768 на ардуину с предыдущим кодом , - результат измерения плохой. Показания гуляют 32759,240 ...32759,300  несмотря на то, что отключил нулевой таймер, и входящий сигнал очень чистый. В общем я ставлю точку в этом вопросе, интерес иссяк..

Buldakov
Offline
Зарегистрирован: 17.01.2016

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

Частота гуляет ужасно. Сейчас у меня гуляет 15623.4 - 15622.5

При этом у меня гуляла 15623.12 - 15623.18 при этом код таймера был написан на Си без прерывания.

 

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

Buldakov пишет:

При этом у меня гуляла 15623.12 - 15623.18 при этом код таймера был написан на Си без прерывания.

Вы про какой код сейчас говорите?

Buldakov
Offline
Зарегистрирован: 17.01.2016

На этом коде у меня гуляет 15624.75 - 15624.81

//---------------------------------------------------------------------------------------------------------------------------------//
#include <CyberLib.h>                                                                                                              //
#include <Wire.h>                                                                                                                  //
#include <LiquidCrystal_I2C.h>                                                                                                     //
//---------------------------------------------------------------------------------------------------------------------------------//
LiquidCrystal_I2C lcd(0x27, 16, 2); // Для экрана 16х2 (двухстрочный)                                                              //
//---------------------------------------------------------------------------------------------------------------------------------//
unsigned long per1             =1000000; //                                                                                        //
unsigned long interval_LCD     =  1000;  //Интервал смены показаний на LCD в миллисекундах                                         //
unsigned long lastTime_LCD     =     0;  //Время последнего изменения состояния для LCD в миллисекундах                            //
unsigned long DeltaTime_LCD    =     0;  //Время с момента последнего вывода на LCD в миллисекундах                                //
unsigned long currentTime      =     0;  //Текущее время в милисекундах                                                            //
//---------------------------------------------------------------------------------------------------------------------------------//
boolean sig_new,sig_old,triger_new,triger_old;                                                                                     //
unsigned long p1,t4;                                                                                                               //
unsigned long time_1,time_2,i;                                                                                                     //
float f;                                                                                                                           //
//---------------------------------------------------------------------------------------------------------------------------------//
void setup() {                                                                                                                     //
pinMode(5, INPUT);                     //Назначим вывод 5 на ввод сигналов                                                         //
digitalWrite(5, HIGH);                 //Включаем подтягивающий резистор                                                           //
D5_In;                                                                                                                             //
lcd.init();                             //Задаем размерность LCD дисплея                                                           //
lcd.backlight();                        //Включаем подсветку экрана                                                                //
lcd.clear();                            //Очистка экрана                                                                           //
delay(1000);                            //Ждем 1 секунду                                                                           //
lcd.setCursor(2, 0);                                                                                                               //
lcd.print("Program v30");                                                                                                          //
delay(1000);                            //Ждем 1 секунду                                                                           //
lcd.clear();                            //Очистка экрана                                                                           //
//---------------------------------------------------------------------------------------------------------------------------------//
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
void loop()                                                                                                                        //
{                                                                                                                                  //
//===================================================================================================================================
time_1=micros();                                                                                                                   //
p1=0;                                                                                                                              //
i=0;                                                                                                                               //
label_a :                                                                                                                          //
i=i+1;                                                                                                                             //
sig_old=sig_new;                                                                                                                   //
triger_old=triger_new;                                                                                                             //
sig_new=D5_Read;                                                                                                                   //
if (sig_old==0 && sig_new==1                              ) {triger_new=1;p1=p1+1;}                                                //
if (sig_old==0 && sig_new==1 && abs(micros()-time_1)>per1 ) triger_new=0;                                                          //
if (triger_old==0 && triger_new==1                        ) {time_1=micros();goto label_a;}                                        //
if (triger_old==1 && triger_new==0                        ) {;} else {goto label_a;}                                               //
t4=p1-1;p1=0;time_2=micros();                                                                                                      //
//---Условия для вывода показаний на LCD-------------------------------------------------------------------------------------------//
currentTime=millis();                                                                                                              //
DeltaTime_LCD  =abs(currentTime - lastTime_LCD);      //Время с момента последнего вывода на LCD                                   //
if (DeltaTime_LCD >= interval_LCD )                   //Условия вывода на LCD                                                      //
{                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
f=1000000.0/(abs(time_2-time_1)/(t4*1.0));                                                                                         //
lcd.clear();                            //Очистка экрана                                                                           //
lcd.setCursor(0, 0);lcd.print(t4);                                                                                                 //
lcd.setCursor(7, 0);lcd.print(abs(time_2-time_1));                                                                                 //
lcd.setCursor(0, 1);lcd.print(f,2);                                                                                                //
lcd.setCursor(9, 1);lcd.print(i);                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
lastTime_LCD = currentTime;                                                                                                        //
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//

 

Buldakov
Offline
Зарегистрирован: 17.01.2016

По поводу вашего кода tic считается неправильно.при времени 1 секунда tik не может быть 10241130. он должен быть либо около 8000000 или кратно ему. это первое. второе если частоту сделать 7813 гц то при этом tik = 0. такого не должно быть. и третье tik не должен зависеть от входной частоты. при частоте 15625 tik=10241113 при входной частоте 31246 tik=5120540. При частоте 62500 tik=2560500. и значения tik скачут примерно кратно 250.

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

Buldakov пишет:

По поводу вашего кода tic считается неправильно.при времени 1 секунда tik не может быть 10241130. он должен быть либо около 8000000 или кратно ему. это первое. второе если частоту сделать 7813 гц то при этом tik = 0. такого не должно быть. и третье tik не должен зависеть от входной частоты. при частоте 15625 tik=10241113 при входной частоте 31246 tik=5120540. При частоте 62500 tik=2560500. и значения tik скачут примерно кратно 250.

Вы снова путаете. Переменная tic -это количество тактов процессора, совершенных за 10 000 тактов входного сигнала. К примеру я с генератора подавал 10кГц, мне нужно узнать точно сколько герц измерит.  Счётчик насчитал 15941670 тиков. Делим на 10 000 получаем 1594,167 тиков за один период. Умножаем на 0,0625, получаем 99,6354375 uS длительность одного периода. Переводим в герцы, получаем 10036.589642114 Hz  Всё совершенно точно. По вашей второй заметке, что при частоте 7813  tic равен 0 - так я специально написал внизу скетча, что таймер обрубает всё что выше секунды. Если вы не поменяли значение в 8 строке - то 10 000 импульсов при частоте 7кГц будут измеряться дольше секунды, а значит таймер обрубит измерение. По поводу скачки показаний -да, скачут. Почему не знаю.

Ваш скетч посмотрю, сомнительно что он выдаёт точность выше аппартного таймера. Что-то тут не так..

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

Buldakov, загрузил ваш скетч. Подал 15625Гц. Даёт показания от 15604,31 до 15604,56.  Подал от кварцевого генератора 32768 Гц- показывает 32760,08 ... 32760,48 Гц . т.е. по результатам явно хуже, чем  вариант на таймере. Впрочем не тот не тот для реального измерения частоты после запятой непригодны.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Сейчас изучаю вашу программу. Решил изучить работу только таймера 2. За 1 секунду считает 16М импульсов. Только не могу настроить предделитель с 1 на 8. считает все равно 16М добавляю такую строку. TCCR2B = (0 << CS22)|(1 << CS21)|(0 << CS20); результат такой же.

volatile uint32_t tic; 
volatile uint16_t ovf_tic; 
long k1,t1,t2,t3,t4,n_oll,t5;
float n_tik;
void setup(){
Serial.begin(9600);
pinMode (5,INPUT); // clock input 
//---Настройка таймера 2--------------------------------------------
TCCR2A=0;
TIMSK2=1<<TOIE2;//прерывание по переполнению таймера2
TCCR2B=1<<CS20;// делитель 1
//------------------------------------------------------------------
}
//---Прерывание по таймеру 2----------------------------------------
ISR (TIMER2_OVF_vect){ 
ovf_tic=ovf_tic+1; //считать переполнения
}
//------------------------------------------------------------------
void loop(){
if (abs(micros()-t1)>1000000){TCCR2A=0;k1=ovf_tic;t3=t1;t4=TCNT2;t2=micros();t1=micros();ovf_tic=0; TCNT1=0;TCNT2=0;TCCR2B=1;
t5=abs(t2-t3);
n_oll=k1*256+t4;
Serial.print(" Time > 1 sec  =");
Serial.print(t5);
Serial.print(" mks , ovf_tic=");
Serial.print(k1);
Serial.print(" n , TCNT2 =");
Serial.print(t4);
Serial.print(" n , tik =");
Serial.print(n_oll);
Serial.println(" n.");
}
}

 

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

Buldakov, делитель вы ставите правильно. Но видимо не углядели, в 20 стоке у вас опять  TCCR2B=1 (делитель на 1 настраивается). Это выражение там лишнее, если вы не останавливали таймер, то зачем его повторно запускать.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Просьба заценить то что получилось. у меня погрешность 3 такта от 16М. вроде это нормально

unsigned int ovf_tic; 
long k1,t1,t2,t3,n_oll,t5;
int r1;
void setup(){
Serial.begin(9600);
//---Настройка таймера 2--------------------------------------------
TCCR2A=0;
TIMSK2=1<<TOIE2;//прерывание по переполнению таймера2
TCCR2B = (1 << CS20);  //Коэффициент предделителя 1
//------------------------------------------------------------------
}
//---Прерывание по таймеру 2----------------------------------------
ISR (TIMER2_OVF_vect){ 
ovf_tic=ovf_tic+1; //считать переполнения
if (ovf_tic>62500){TCCR2A=0;r1=TCNT2;k1=ovf_tic;t3=t1;t2=micros();
t5=abs(t2-t3);
n_oll=k1*256+r1;
Serial.print("ovf_tic=");Serial.print(k1);Serial.print(" ,"); 
Serial.print(" TCNT2 =");Serial.print(r1);Serial.print(" ,");
Serial.print("   tik =");Serial.print(n_oll);Serial.print(" n.");
Serial.print(" Time =");Serial.print(t5);Serial.println(" mks ");
t1=micros();ovf_tic=0;TCNT2=0;TCCR2B = (1 << CS20);
}
}
//------------------------------------------------------------------
void loop(){
}

 

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

Buldakov, посмотрел. С  источником сигнала в виде кварцевого генератор 32768Гц  ваш скетч показывает девиацию от 1000008 до 1000012 мкс, иногда выпадает значение 998988мкс,  это слишком много. 

Я сегодня сделал ещё один эксперемент. Собрал ардуину на макетке не с кварцем, а с хорошим кварцевым генератором на 16МГц. Попутно доработал свой скетч, в итоге добился точности:    +/-  0,0625мкс   По-моему это теоритический максимум. Причём такую точность он даёт если сигнал стабильный, (с того-же самого кварц.генератора на 32768Гц). Если брать в качестве источника сигнала другую ардуину -то болтанка гораздо больше.

Верхняя цифра - кол-во тактов МК за 30 000 тактов входного сигнала. Последняя цифра гуляет 10..12, т.е. как раз +/- 0,0625мкс.  На цифре частоты гуляет только третья цифра после запятой. Если разрешить работу системного таймера0 -то болтанка усиливается. Отсюда можно сделать вывод, что этот способ  даже если он даёт нужную точнсть -слишком капризный. Нужны очень хорошие генераторы тактовой частоты для ардуины и задающий  для измеряемой.

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

#include <avr/delay.h>
#include <LCD5110_SSVS.h>
extern uint8_t SmallFont[];
LCD5110 lcd(A2,A3,A4,A5);
volatile uint32_t tic; 
volatile uint16_t ovf_tic; 
void setup(){
lcd.InitLCD();
lcd.setFont(SmallFont);
pinMode (5,INPUT); // clock input 
TCCR0B=0;TIMSK0=0;// отключить системный таймер
TCCR1A=0; TIMSK1 = 1<<OCIE1A; //прерывание по совпадению с OCR1A
OCR1A=30000; //отсчитать 30 000 входных импульсов до прерывания (для частоты 32768Герц)
TCCR2A=0; TIMSK2=1<<TOIE2;//прерывание по переполнению таймера2
TCCR2B=1<<CS20;// делитель 1
TCCR1B = (1<<CS10)|(1<<CS11)|(1<<CS12)|(1<<WGM12); //СТС делитель 1
TCNT1=0; TCNT2=0;
}
ISR (TIMER1_COMPA_vect){ 
GTCCR=(1<<TSM)|(1<<PSRASY)|(1<<PSRSYNC);
TCCR1B=0; TCCR2B=0;//остановить таймеры
tic=((uint32_t)ovf_tic*256)+TCNT2+1; //сложить что натикало
ovf_tic=0; //сбросить счёт переполнений
TCNT2=0; TCNT1=0; //сброс счётных регистров
TCCR1B=15; TCCR2B=1; //запустить таймеры
GTCCR=0;
}

ISR (TIMER2_OVF_vect){ 
 ovf_tic++; //считать переполнения
     if (ovf_tic==62500){ //если досчитали до секунды
      ovf_tic=0; tic=0; TCNT1=0;TCNT2=0; //всё обнулить
      }
  }

void loop(){
float us= tic * 0.0625 /OCR1A;
lcd.printNumI(tic, LEFT, 0);
lcd.printNumF(us,5, LEFT, 8);
float freq= (float)1/(us/1000000.0);
lcd.printNumF(freq,3,LEFT,24,',',14); 
}

 

Buldakov
Offline
Зарегистрирован: 17.01.2016

Просьба помочь с кодом. Информация на экране обновляется когда на входе есть частота. Если на входе нет частоты информация не обновляется. Как можно по первому таймеру обновлять вывод на экран. если нет входной частоты?

//Пин 5 вход измерителя частоты. Пин 11 выход тестовой частоты. 11 - 5 перемычка
//---------------------------------------------------------------------------------------------------------------------------------//
#include <CyberLib.h>                                                                                                              //
//---------------------------------------------------------------------------------------------------------------------------------//
unsigned long interval_izm     =  1000;  //Время счета входной частоты в миллисекундах                                             //
unsigned long interval_LCD     =  1000;  //Интервал смены показаний на LCD в миллисекундах                                         //
unsigned long lastTime_LCD     =     0;  //Время последнего изменения состояния для LCD в миллисекундах                            //
unsigned long DeltaTime_LCD    =     0;  //Время с момента последнего вывода на LCD в миллисекундах                                //
unsigned long currentTime      =     0;  //Текущее время в милисекундах                                                            //
//---------------------------------------------------------------------------------------------------------------------------------//
boolean sig_new,sig_old,triger_new,triger_old;                                                                                     //
unsigned long p1,t4;                                                                                                               //
unsigned int ovf_tic; 
unsigned long tik,k1,t1,t2,t3,t5;
int r1;
float f;                                                                                                                           //
//---------------------------------------------------------------------------------------------------------------------------------//
void setup() {                                                                                                                     //
Serial.begin(9600);
pinMode(5, INPUT);                     //Назначим вывод 5 на ввод сигналов                                                         //
digitalWrite(5, HIGH);                 //Включаем подтягивающий резистор                                                           //
D5_In;                                                                                                                             //
//Программирования 2 таймер 8 бит                                                                                                  //
pinMode(11, OUTPUT);                    //Назначим вывод 11 на вывод сигналов                                                      //
TCCR2A = (0<<COM2A1)|(1<<COM2A0)|(1<<WGM21)|(0<<WGM20); //CTC mode                                                                 //
TCCR2B = (1 << CS22)|(1 << CS21)|(0 << CS20);  //Коэффициент предделителя 256                                                      //  
OCR2A =124;                                     //Регистр сравнения. Сюда мы записываем то, с чем надо сравнить.                   //  
//---Настройка таймера 1--------------------------------------------
TCCR1A = 0;
TCCR1B = 0;
TCNT1  = 0;
OCR1A=15999; //отсчитать 16 000 входных импульсов до прерывания
TIMSK1 |= (1 << OCIE1A);  
}                                                                                                                                  //
//---Прерывание по таймеру 1----------------------------------------
ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{ 
ovf_tic=ovf_tic+1; //считать переполнения
}
//---------------------------------------------------------------------------------------------------------------------------------//
void loop()                                                                                                                        //
{                                                                                                                                  //
//===================================================================================================================================
//---Прерывание по таймеру 1----------------------------------------
ovf_tic=0;                                                                                                                         //
TCCR1A=0;TCNT1=0;
p1=0;                                                                                                                              //
triger_old=0;
label_a :                                                                                                                          //
sig_old=sig_new;                                                                                                                   //
sig_new=D5_Read;                                                                                                                   //
if (sig_old==0 && sig_new==1                              ) {triger_new=1;p1=p1+1;} else goto label_a;                             //
if (sig_old==0 && sig_new==1 && ovf_tic>=interval_izm     ) {triger_new=0;}                                                        //
if (triger_old==0 && triger_new==1                        ) {TCCR1B=(1 << CS10)|(1<<WGM12);triger_old=1;goto label_a;}//
if (triger_old==1 && triger_new==0                        ) {;} else {goto label_a;}                                               //
TCCR1A=0;TCCR1B=0;r1=TCNT1;k1=ovf_tic;t3=t1;tik=k1*16000+r1;
t4=p1-1;p1=0;                                                                                                                      //
//---Условия для вывода показаний на LCD-------------------------------------------------------------------------------------------//
currentTime=millis();                                                                                                              //
DeltaTime_LCD  =abs(currentTime - lastTime_LCD);      //Время с момента последнего вывода на LCD                                   //
if (DeltaTime_LCD >= interval_LCD )                   //Условия вывода на LCD                                                      //
{                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
f=16000000/(tik/(t4*1.0));                                                                                                         //
Serial.print("ovf_tic=");Serial.print(k1); Serial.print(" ,"); 
Serial.print(" TCNT2 =");Serial.print(r1); Serial.print(" ,");
Serial.print("   tik =");Serial.print(tik);Serial.print(" n.");
Serial.print("   N =");Serial.print(t4);Serial.print(" n.");
Serial.print("  Freq =");Serial.print(f,4); Serial.println(" Hz ");
//---------------------------------------------------------------------------------------------------------------------------------//
lastTime_LCD = currentTime;                                                                                                        //
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//

 

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

Buldakov, у вас  такая примитивная  идеология программы, что  всё будет стоять в ожидании конца измерения , т.к. в Loop  сплошные петли (строки 52-52).  Выход один -не применять такую методику совсем. 

Buldakov
Offline
Зарегистрирован: 17.01.2016

Строки 52 потом уберем. Только пока не знаю как их лучше убрать. Пока приходит на ум только внешний D триггер.

Не знаю как заставить внутренний таймер работать по строке 52. Типа при подаче определенного бита на триггер он считает до ближайшего положительного перепада на счетном входе и выдает флаг прерывания.

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

Buldakov, средствами ардуино -только на полную использовать таймер,  в #76 я использовал все аппаратные возможности, лучше уже не сделать. Не вижу смысла в ваших дальнейших эксперементах, основанных на более грубой технологии.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Как тогда в посте 76 измерять частоту от 1 гц? Вашим методом вроде только от 30000 гц. Ну может быть чуточку ниже.

sva1509
Offline
Зарегистрирован: 07.12.2012

Доброго времени суток !

timer1 вам в помощь, мотрите input capture unit.

Принцип работы следующий:

1) Настраиваите таймер на частоту ядра.

2) настраиваите ICP на спад или фронт.

3) настраиваите прерывания - от переполнения и от события захвата.

4) создаете счетчик импульсов - допустим uint16_t conut;

5) создаете флаг uint8_t flag; где три положения - begin, process, end; при инициализации таймера указываете begin.

6) запускаете таймер. При срабатывании прерывания от захвата проверяете флаг - если begin тогда обнуляете счетчик count и счетчик таймера, меняете флаг на process.

7) при срабатывании прерывания от события проверяете флаг, если process тогда count++;

8) при срабатывании таймера от переполнения у вас в переменной count колличество импульсов, а в регистре IPC колличество счетов таймера при величине счета равной 1/16000000с что равно 0.0000000625c. Останавливаете таймер, меняете флаг на end.

8a) при срабатывании прерывания от переполнения и count == 0; изменить настройки частоты счета таймера и скорректировать длительность счета в секундах. Повторить с пункта 6; 

9) вычисляете частоту по формуле (1/(колличество счетов * 0.0000000625)) * count;

где то так.

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

Buldakov, скетч из #76 может измерять любую частоту от десятых долей герца до минимум 4МГц.  Но у него есть две особенности. Ему нужно сообщить сколько периодов сигнала нужно измерить, и это значение записать в регистр OCR1A.  Если нужно измерять частоту например 12Гц -то записать в регистр цифру 10.  Вторая особенность, что б программа не ждала ничего при отсутствии сигнала я ввёл ограничение в 1 секунду. Частоту менее 2х Герц не получится измерить с этим ограничением, но ограничение тоже можно изменить при необходимости.

Buldakov
Offline
Зарегистрирован: 17.01.2016

просьба покретиковать такой алгоритм для реализации частотомера.
типа указать ошибки в алгоритме измерения частоты.
Входная частота подается на счетный вход таймера 1 (вывод 5)
Алгоритм должен измерять длительность целых периодов входной частоты.
Длительность измерения входной частоты должна быть больше заданной. (62500 циклов переполнения триггера 2)

Запуск на счет.
Триггер 1 обнулить.и остановить Коэффициент деления =1.
Триггер 2 обнулить.и остановить Коэффициент деления =1.
Триггер 1 настраиваем на прерывание по сравнению и записываем в регистр значение = 0.
Триггер 2 настраиваем на прерывание по переполнению.
Триггер 1 запустить.
Когда триггер 1 выдал сигнал на переполнение. Это значит, что появился фронт первого импульса.
По этому сигналу с триггера 1 (на переполнение) запускаем Триггер 2 на счет.
Записываем в Триггер 1 в регистр сравнение значение = 999 коэффициент деления при этом = 1000.
Считаем количество переполнений Триггеров 1 и 2.

Остановка счета.
Если количество переполнений от триггера 2 больше 62500 (1 секунда). Останавливаем триггер 1, считываем значение регистра и записываем в регистр триггера 1 значение сравнения = 0. и запускаем триггер 1 на счет.
С приходом фронта следующего импульса останавливаем триггер 2. Считаем количество  полных переполнений триггера 2 и значение регистра триггера 2.
По этим значениям определяем количество тиков тактового генератора. Считаем значение переполнений триггера 1. Вычитаем из этого значения 2 значения переполнения и прибавляем 2 импульса (первый и последний импульс) и считаем количество импульсов.
 

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

Buldakov, фактически вы рассказали алгоритм моего скетча из #76, с разницей в одном нюансе -я не синхронизирую старт второго таймера по первому фронту входящего сигнала. Возможен вариант такой -у таймера1 будет две конфигурации. Одна основная -точно в том виде, в каком она сейчас. Вторая "синхронизирующая", в ней таймер настраивается на прерывание по первому же импульсу. Как пришёл импульс -таймер выпадает в прерывание, в нём перепрограммируется на основной режим, и запускается таймер 2.  Когда посчитано нужное число импульсов, то после уже существующей арифметики таймер опять программируется в режим прерывания по первому импульсу,  и всё повторяется.  Только мне кажется что существенного прироста точности уже не будет, т.к. нужен сам исходный сигнал очень высокой стабильности, да и без замены кварца на самой ардуине вообще ни о какой стабильности речи не может идти.

Buldakov
Offline
Зарегистрирован: 17.01.2016

По данному алгоритму попробовал написать программу. Но она не работает. Похоже я запутался в алгоритме.

Просьба подсказать где я допустил ошибку.

Входная частота подается на счетный вход таймера 1 (вывод 5)
Запуск на счет.
Триггер 1 обнулить.и остановить Коэффициент деления =1.
Триггер 2 обнулить.и остановить Коэффициент деления =1.
Триггер 1 настраиваем на прерывание по сравнению и записываем в регистр значение = 0.
Триггер 2 настраиваем на прерывание по переполнению.
Триггер 1 запустить.
Когда триггер 1 выдал сигнал на переполнение. Это значит, что появился фронт первого импульса.
По этому сигналу с триггера 1 (на переполнение) запускаем Триггер 2 на счет.
Записываем в Триггер 1 в регистр сравнение значение = 999 коэффициент деления при этом = 1000.
Считаем количество переполнений Триггеров 1 и 2.

Остановка счета.
Если количество переполнений от триггера 2 больше 62500 (1 секунда). Останавливаем триггер 1, считываем значение регистра и записываем в регистр триггера 1 значение сравнения = 0. и запускаем триггер 1 на счет.
С приходом фронта следующего импульса останавливаем триггер 2. Считаем количество  полных переполнений триггера 2 и значение регистра триггера 2.

//---------------------------------------------------------------------------------------------------------------------------------//
#include <Wire.h>                                                                                                                  //
#include <LiquidCrystal_I2C.h>                                                                                                     //
//---------------------------------------------------------------------------------------------------------------------------------//
LiquidCrystal_I2C lcd(0x27, 16, 2); // Для экрана 16х2 (двухстрочный)                                                              //
//---------------------------------------------------------------------------------------------------------------------------------//
unsigned long interval_izm     =  1000;  //Время счета входной частоты в миллисекундах                                             //
unsigned long interval_LCD     =  1000;  //Интервал смены показаний на LCD в миллисекундах                                         //
unsigned long lastTime_LCD     =     0;  //Время последнего изменения состояния для LCD в миллисекундах                            //
unsigned long DeltaTime_LCD    =     0;  //Время с момента последнего вывода на LCD в миллисекундах                                //
unsigned long currentTime      =     0;  //Текущее время в милисекундах                                                            //
unsigned long korr_timer_2     =    67;  //Коррекция времени запроса после вызова таймера 2 в тактах процессора                    //
//---------------------------------------------------------------------------------------------------------------------------------//
unsigned long ovf_tic_timer_1,out_ovf_tic_timer_1,tik_timer_1;                                                                     //
unsigned long ovf_tic_timer_2,out_ovf_tic_timer_2,tik_timer_2,interval_timer_2;                                                    //
unsigned int  out_TCNT2,out_TCNT1;                                                                                                 //
boolean       stop_for_timer_1,start_for_timer_1,flag_LED;                                                                         //
//---------------------------------------------------------------------------------------------------------------------------------//
void setup() {                                                                                                                     //
Serial.begin(9600);                                                                                                                //
pinMode(5, INPUT);                     //Назначим вывод 5 на ввод сигналов                                                         //
digitalWrite(5, HIGH);                 //Включаем подтягивающий резистор                                                           //
lcd.init();                             //Задаем размерность LCD дисплея                                                           //
lcd.backlight();                        //Включаем подсветку экрана                                                                //
lcd.clear();                            //Очистка экрана                                                                           //
//---Настройка таймера 1-----------------------------------------------------------------------------------------------------------//
TCCR1A = 0;                                                                                                                        //
TCCR1B = 0;                                                                                                                        //
TCNT1  = 0;                                                                                                                        //
OCR1A=999; //отсчитать 1000 входных импульсов до прерывания                                                                        //
TIMSK1 |= (1 << OCIE1A);                                                                                                           //
TCCR1B=(1 << CS10)|(1<<WGM12); //СТС делитель 1                                                                                    //
//---Настройка таймера 2-----------------------------------------------------------------------------------------------------------//
TCCR2A=0;                                                                                                                          //
TCNT2=0;                                                                                                                           //
TIMSK2 = 1<<TOIE2;//прерывание по переполнению таймера2                                                                            //
//---------------------------------------------------------------------------------------------------------------------------------//
start_for_timer_1=1;                                                                                                               //
}                                                                                                                                  //
//---Прерывание по таймеру 1-------------------------------------------------------------------------------------------------------//
ISR (TIMER1_COMPA_vect){                                                                                                           //
if (start_for_timer_1==1){TCNT1=0;OCR1A=999;TCCR2B=(1 << CS20);start_for_timer_1=0;stop_for_timer_1=0;}// запустить таймер 2       //
if (stop_for_timer_1==0 && start_for_timer_1==0){ovf_tic_timer_1=ovf_tic_timer_1+1;} //считать количество переполнений таймера 1   //
if (stop_for_timer_1==1){                                                                                                          //
TCCR1B=0;TCCR2B=0;                 //остановить таймеры                                                                            //
out_TCNT1=TCNT1;                                                                                                                   //
out_TCNT2=TCNT2;                                                                                                                   //
out_ovf_tic_timer_1=ovf_tic_timer_1;ovf_tic_timer_1=0;                                                                             //
out_ovf_tic_timer_2=ovf_tic_timer_2;ovf_tic_timer_2=0;                                                                             //
TCNT2=0; TCNT1=0; //сброс счётных регистров                                                                                        //
flag_LED=1;}                                                                                                                       //
}                                                                                                                                  //
//---Прерывание по таймеру 2-------------------------------------------------------------------------------------------------------//
ISR (TIMER2_OVF_vect){                                                                                                             //
ovf_tic_timer_2=ovf_tic_timer_2+1; //считать количество переполнений таймера 2                                                     //
if (ovf_tic_timer_2>=interval_timer_2){                                                                                            //
stop_for_timer_1=1;OCR1A=0;                                                                                                        //
}                                                                                                                                  //
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
void loop()                                                                                                                        //
{                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
interval_timer_2=(unsigned long)interval_izm*62.5;                                                                                 //
tik_timer_2=out_ovf_tic_timer_2*256  +out_TCNT2-korr_timer_2;                                                                      //
tik_timer_1=out_ovf_tic_timer_1*1000+out_TCNT1;                                                                                    //
//---Условия для вывода показаний на LCD-------------------------------------------------------------------------------------------//
currentTime=millis();                                                                                                              //
DeltaTime_LCD  =abs(currentTime - lastTime_LCD);      //Время с момента последнего вывода на LCD                                   //
if (DeltaTime_LCD >= interval_LCD && flag_LED==1)     //Условия вывода на LCD                                                      //
{                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
Serial.print("ovf_tic2=");Serial.print(out_ovf_tic_timer_2);Serial.print(" ,");                                                    //
Serial.print(" TCNT2 =") ;Serial.print(out_TCNT2)          ;Serial.print(" ,");                                                    //
Serial.print("  tik2 =") ;Serial.print(tik_timer_2)        ;Serial.print(" n.    ");                                               //
Serial.print("ovf_tic1=");Serial.print(out_ovf_tic_timer_1);Serial.print(" ,");                                                    //
Serial.print(" TCNT1 =") ;Serial.print(out_TCNT1)          ;Serial.print(" ,");                                                    //
Serial.print("  tik1 =") ;Serial.print(tik_timer_1)        ;Serial.println(" n.");                                                 //
lcd.clear();                            //Очистка экрана                                                                           //
lcd.setCursor(0, 0);lcd.print(out_ovf_tic_timer_2);                                                                                //
lcd.setCursor(8, 0);lcd.print(out_TCNT2);                                                                                          //
lcd.setCursor(0, 1);lcd.print(tik_timer_2);                                                                                        //
//---------------------------------------------------------------------------------------------------------------------------------//
lastTime_LCD = currentTime;                                                                                                        //
flag_LED=0;                                                                                                                        //
OCR1A=0;                                                                                                                           //
TCCR1B=(1 << CS10)|(1<<WGM12); //СТС делитель 1                                                                                    //
start_for_timer_1=1;                                                                                                               //
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//

 

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

Buldakov, меня честно говоря уже утомила эта тема. Единственно что я могу ещё добавить -после написания поста #85 я подумал, что кроме как регистром OCR1A описанные выше режимы таймера собссно не будут отличаться, и набил на скорую руку алгоритм, изложенный там. Понаблюдал без особого интереса, что  девиация стала ещё меньше и на этом успокоился. Вот этот последний скетч:

#include <avr/delay.h>
#include <LCD5110_SSVS.h>
extern uint8_t SmallFont[];
LCD5110 lcd(9, 10, 11, 12);
volatile uint32_t tic;
volatile uint16_t ovf_tic;
uint16_t ocr = 30000u; // сколько импульсов считать
void setup() {
  lcd.InitLCD();
  lcd.setFont(SmallFont);
  pinMode (5, INPUT); // clock input
  TCCR0B = 0; TIMSK0 = 0; // отключить системный таймер
  TCCR1A = 0; TIMSK1 = 1 << OCIE1A; //прерывание по совпадению с OCR1A
  OCR1A = 1; //после первого импульса вывалиться в прерывание
  TCCR2A = 0; TIMSK2 = 1 << TOIE2; //прерывание по переполнению таймера2
  TCCR2B = 1 << CS20; // делитель 1
  TCCR1B = (1 << CS10) | (1 << CS11) | (1 << CS12) | (1 << WGM12); //СТС делитель 1
  TCNT1 = 0; TCNT2 = 0;
}
ISR (TIMER1_COMPA_vect) {
  static boolean mode_tim = 0; // 0- синхро, 1 -основной
  if (mode_tim) {
    GTCCR = (1 << TSM); //|(1<<PSRASY)|(1<<PSRSYNC); //заглушить прескалер таймеров
    tic = ((uint32_t)ovf_tic * 256) + TCNT2; //сложить что натикало
    ovf_tic = 0; //сбросить счёт переполнений
    TCNT2 = 0; TCNT1 = 0; //сброс счётных регистров
    mode_tim = 0; OCR1A = 1;
    GTCCR = 0; //запустить прескалер таймеров
  }
  else {
    mode_tim = 1;
    ovf_tic; OCR1A = ocr;
    TCNT1 = 0; TCNT2 = 0;
  }
}

ISR (TIMER2_OVF_vect) {
  ovf_tic++; //считать переполнения
  if (ovf_tic == 62500) { //если досчитали до секунды
    ovf_tic = 0; tic = 0; TCNT1 = 0; TCNT2 = 0; //всё обнулить
  }
}

void loop() {
  _delay_ms(300);
  float us = tic * 0.0625 / ocr;
  lcd.printNumI(tic, LEFT, 0);
  lcd.printNumF(us, 5, LEFT, 8);
  float freq = (float)1 / (us / 1000000.0);
  lcd.printNumF(freq, 3, LEFT, 24, ',', 14);
}

 

Buldakov
Offline
Зарегистрирован: 17.01.2016

Не получается в вашем коде считать значение регистра TCNT1. Всегда показывает = 0. Из какой строки его можно считать? Хочу считать число 30000.

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

Buldakov, в данном режиме таймера значение TCNT1 нет смысла читать, оно не может быть больше значения OCR1A. Читать его есть смысл только в прерывании, но в прерывании его значение всегда будет = OCR1A.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Вот в этом куске кода

  static boolean mode_tim = 0; // 0- синхро, 1 -основной
  if (mode_tim) {
    GTCCR = (1 << TSM); //|(1<<PSRASY)|(1<<PSRSYNC); //заглушить прескалер таймеров
    tic = ((uint32_t)ovf_tic * 256) + TCNT2; //сложить что натикало
    ovf_tic = 0; //сбросить счёт переполнений
    TCNT2 = 0; TCNT1 = 0; //сброс счётных регистров
    mode_tim = 0; OCR1A = 1;
    GTCCR = 0; //запустить прескалер таймеров
  }
  else {
    mode_tim = 1;
    ovf_tic; OCR1A = ocr;
    TCNT1 = 0; TCNT2 = 0;
  }

не понятно условие наступления события. if (mode_tim) {

Можно как нибудь это расписать в понятном виде типа:   if (mode_tim==1) {

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

Buldakov, mode_tim  флаг режима таймера. При каждом входе в прерывание он инвертируется, соответссно выполняется либо "if" либо "else" часть кода.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Не получается передалать вашу программу. Не могу убрать число в регистре 1 таймера 30000. И время измерения меньше 1 секунды и зависит от числа 30000 и входной частоты. Мне что для каждой новой частоты записывать число в регистр? Поэтому буду форумчан спрашивать дальше. Уже по своей программе.

Не могу обнулить счетчик количества прерываний таймера 1. Значение счетчика постоянно увеличивается.

//---------------------------------------------------------------------------------------------------------------------------------//
unsigned long interval_izm     =  1000;  //Время счета входной частоты в миллисекундах                                             //
unsigned long interval_LCD     =  1000;  //Интервал смены показаний на LCD в миллисекундах                                         //
unsigned long lastTime_LCD     =     0;  //Время последнего изменения состояния для LCD в миллисекундах                            //
unsigned long DeltaTime_LCD    =     0;  //Время с момента последнего вывода на LCD в миллисекундах                                //
unsigned long currentTime      =     0;  //Текущее время в милисекундах                                                            //
unsigned long korr_timer_2     =    67;  //Коррекция времени запроса после вызова таймера 2 в тактах процессора                    //
//---------------------------------------------------------------------------------------------------------------------------------//
unsigned long ovf_tic_timer_1,out_ovf_tic_timer_1,tik_timer_1;                                                                     //
unsigned long ovf_tic_timer_2,out_ovf_tic_timer_2,tik_timer_2,interval_timer_2;                                                    //
unsigned int  out_TCNT2,out_TCNT1;                                                                                                 //
boolean       stop_for_timer_1,start_for_timer_1,flag_LED;                                                                         //
//---------------------------------------------------------------------------------------------------------------------------------//
void setup() {                                                                                                                     //
Serial.begin(9600);                                                                                                                //
pinMode(5, INPUT);                     //Назначим вывод 5 на ввод сигналов                                                         //
digitalWrite(5, HIGH);                 //Включаем подтягивающий резистор                                                           //
//---Настройка таймера 1-----------------------------------------------------------------------------------------------------------//
TCCR1A=0;                                                                                                                          //
TIMSK1 = 1 << OCIE1A; //прерывание по совпадению с OCR1A                                                                           //
OCR1A = 1; //после первого импульса вывалиться в прерывание                                                                        //
TCCR1B=(1 << CS10)|(1<<WGM12); //СТС делитель 1                                                                                    //
TCNT1=0;                                                                                                                           //
//---Настройка таймера 2-----------------------------------------------------------------------------------------------------------//
TCCR2A=0;                                                                                                                          //
TIMSK2 = 1<<TOIE2;//прерывание по переполнению таймера2                                                                            //
TCCR2B = 1 << CS20; // делитель 1                                                                                                  //
TCNT2=0;                                                                                                                           //
//---------------------------------------------------------------------------------------------------------------------------------//
}                                                                                                                                  //
//---Прерывание по таймеру 1-------------------------------------------------------------------------------------------------------//
ISR (TIMER1_COMPA_vect){                                                                                                           //
if (start_for_timer_1==1){TCNT1=0;OCR1A=999;TCCR2B = 1 << CS20;start_for_timer_1=0;stop_for_timer_1=0;}                            //
if (start_for_timer_1==0 && stop_for_timer_1==0){ovf_tic_timer_1=ovf_tic_timer_1+1;} //считать количество переполнений таймера 1   //
if (stop_for_timer_1==1){                                                                                                          //
TCCR1B=0;TCCR2A=0;                 //остановить таймер                                                                             //
out_TCNT1=TCNT1;                                                                                                                   //
out_TCNT2=TCNT2;                                                                                                                   //
out_ovf_tic_timer_1=ovf_tic_timer_1;ovf_tic_timer_1=0;                                                                             //
out_ovf_tic_timer_2=ovf_tic_timer_2;ovf_tic_timer_2=0;                                                                             //
TCNT2=0; TCNT1=0; //сброс счётных регистров                                                                                        //
flag_LED=1;stop_for_timer_1=0;                                                                                                     //
}                                                                                                                                  //
}                                                                                                                                  //
//---Прерывание по таймеру 2-------------------------------------------------------------------------------------------------------//
ISR (TIMER2_OVF_vect){                                                                                                             //
ovf_tic_timer_2=ovf_tic_timer_2+1; //считать количество переполнений таймера 2                                                     //
if (ovf_tic_timer_2>=interval_timer_2){                                                                                            //
stop_for_timer_1=1;OCR1A=1;                                                                                                        //
}                                                                                                                                  //
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
void loop()                                                                                                                        //
{                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
interval_timer_2=(unsigned long)interval_izm*62.5;                                                                                 //
tik_timer_2=out_ovf_tic_timer_2*256  +out_TCNT2-korr_timer_2;                                                                      //
tik_timer_1=out_ovf_tic_timer_1*1000+out_TCNT1;                                                                                    //
//---Условия для вывода показаний на LCD-------------------------------------------------------------------------------------------//
currentTime=millis();                                                                                                              //
DeltaTime_LCD  =abs(currentTime - lastTime_LCD);      //Время с момента последнего вывода на LCD                                   //
if (DeltaTime_LCD >= interval_LCD && flag_LED==1)     //Условия вывода на LCD                                                      //
{                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
Serial.print("ovf_tic2=");Serial.print(out_ovf_tic_timer_2);Serial.print(" ,");                                                    //
Serial.print(" TCNT2 =") ;Serial.print(out_TCNT2)          ;Serial.print(" ,");                                                    //
Serial.print("  tik2 =") ;Serial.print(tik_timer_2)        ;Serial.print(" n.    ");                                               //
Serial.print("ovf_tic1=");Serial.print(out_ovf_tic_timer_1);Serial.print(" ,");                                                    //
Serial.print(" TCNT1 =") ;Serial.print(out_TCNT1)          ;Serial.print(" ,");                                                    //
Serial.print("  tik1 =") ;Serial.print(tik_timer_1)        ;Serial.println(" n.");                                                 //
//---------------------------------------------------------------------------------------------------------------------------------//
lastTime_LCD = currentTime;                                                                                                        //
flag_LED=0;                                                                                                                        //
TCCR1B=(1 << CS10)|(1<<WGM12); //СТС делитель 1                                                                                    //
start_for_timer_1=1;                                                                                                               //
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//
}                                                                                                                                  //
//---------------------------------------------------------------------------------------------------------------------------------//

 

Buldakov
Offline
Зарегистрирован: 17.01.2016

Считываю значение количества импульсов на выводе 5. Настройка таймера:

TCCR1B = (1 << CS10) | (1 << CS11) | (1 << CS12) | (1 << WGM12)

Не получается для таймера 1 считать значение TCNT1. Всегда выдает ноль.Есть подозрение что надо читать TCNT1H и TCNT1L и потом их как то обьединять.

Оказалось все гораздо проще. При данных настройках таймера 1 нельзя менять значение регистра OCR1A. Если раньше пытался менять значение этого регистра, то теперь перестал его менять и частотомер заработал.

Вывод: при использовании таймера1 в качестве счетчика количества входных импульсов не дает никакого приемущества, по сравнению с программным определением частоты импульсов. точность измерения частоты получилась одного порядка. Возврашаюсь к старому и надежному методу определения частоты.

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

 

Buldakov
Offline
Зарегистрирован: 17.01.2016

Кто нибудь может подсказать как на Arduino  умножать числа с точностью более 7 знаков?