Делаю (пытаюсь сделать) частотомер по методу предложеному =GM= тут:
http://electronix.ru/forum/index.php?showtopic=29796. Но вот что то не получается. Верней получается но не совсем. Во первых если я меняю задержку между началом и концом измерения то получаю существенно разные варианты. Но в то же время, если задержка ровно 1 сек (delay_ms(1000)

то результат выводится либо равный расчетному либо чуть больше. Причем как будто бы не хватает одного переполнения таймера 1 т.е. Perepol_Timer1++; и число N1 получается на 65536 меньше. Из за чего результат чуть больше. Но куда оно может пропасть?
код вообще то взят в основном отсюда
http://pro-radio.ru/controllers/5978/(с комментариями) но там не работает регистрация поэтому спрашиваю тут.
Код
char gate;
static float Fx;
unsigned long int N, N0, M, M0, N1, M1;
unsigned int Perepol_Timer0=0, Perepol_Timer1=0, count_T1, count_T0,Perepol_Timer1_real;
//Прерывание по переполнению Timer/Counter 0
interrupt [TIM0_OVF] void timer0_ovf_isr(void){
Perepol_Timer0++;//Увеличение переменной на 1
}
//Прерывание по переполнению Timer/Counter 1
interrupt [TIM1_OVF] void timer1_ovf_isr(void){
if (gate){
Perepol_Timer1++;//Увеличение переменной на 1
}
}
//Прерывание по захвату Timer/Counter 1
interrupt [TIM1_CAPT] void timer1_capt_isr(void){
count_T1=ICR1;//Значение регистра ICR1 переписывается в переменную
count_T0=TCNT0;//Значение регистра TCNT0 переписывается в переменную
TIMSK&=0xDF;//Запрет прерывания по захвату
if (TIFR&0b00000100==1){
Perepol_Timer1++;
TIFR&=0b00000100;
}
}
while (1){//Бесконечный цикл
#asm("cli")
gate=0;
Perepol_Timer1=0;//Обнуление переменной
Perepol_Timer0=0;//Обнуление переменной
#asm("sei")//Разрешения прерываний
TIMSK|=0x20;//Разрешили захват
gate=1;
while ((TIFR&0x20)==0x20){}//Ожидание прерывания по захвату
Perepol_Timer1_real = Perepol_Timer1;
N0=(((unsigned long int)(Perepol_Timer1_real))<<16)+count_T1; //Расчет общего количества тиков системной частоты
M0=(((unsigned long int)(Perepol_Timer0))<<8)+count_T0;//Расчет общего количества тиков входной частоты
//sprintf(lcd_buffer, "PT1=%u;CT1=%u;PT0=%u;CT0=%u;\r", Perepol_Timer1_real, count_T1, Perepol_Timer0, count_T0);
//puts(lcd_buffer);
delay_ms(1000);//Задержка на 1 с
TIMSK|=0x20;//Разрешили захват
gate=0;
while ((TIFR&0x20)==0x20){}//Ожидание прерывания по захвату
Perepol_Timer1_real = Perepol_Timer1;
N1=(((unsigned long int)(Perepol_Timer1_real))<<16)+count_T1; //Расчет общего количества тиков системной частоты
M1=(((unsigned long int)(Perepol_Timer0))<<8)+count_T0;//Расчет общего количества тиков входной частоты
sprintf(lcd_buffer, "PT1=%u;CT1=%u;PT0=%u;CT0=%u;\r", Perepol_Timer1_real, count_T1, Perepol_Timer0, count_T0);
puts(lcd_buffer);
N=(N1-N0);//Расчет количества тиков системной частоты за время измерения
M=(M1-M0);//Расчет количества тиков входной частоты за время измерения
Fx=12000000.0*(float)M/(float)N;//Вычисление частоты входного сигнала
putchar('c');
sprintf(lcd_buffer, "F=%0.6fHz", Fx);
lcd_clear();
lcd_puts(lcd_buffer);
puts(lcd_buffer);
putchar(13);
} }
Сергей Борщ
Jun 10 2010, 06:02
Вы учитываете остаток в регистре таймера на момент окончания измерения, но не учитываете начальное значение таймера.
Еще одна ошибка у вас - gate сделайте volatile.
Цитата(Сергей Борщ @ Jun 10 2010, 10:02)

Вы учитываете остаток в регистре таймера на момент окончания измерения, но не учитываете начальное значение таймера.
а как же
Код
N0=(((unsigned long int)(Perepol_Timer1_real))<<16)+count_T1;
вроде учитываю сount_T1...
У вас ошибка: второе прерывание TOV1 никогда не даст инкремента, поскольку GATE=0. Число N1 естественно будет меньше на 2^16.
Далее, счётчик T0 у вас 8-битный, а переменная count_T0 у вас int, ну и добавляете вы ее, как int, а надо только 8 бит. Для вашего компилятора может и пройдёт, но лучше по-честному добавлять 8-битное число.
Цитата(=GM= @ Jun 11 2010, 01:36)

У вас ошибка: второе прерывание TOV1 никогда не даст инкремента, поскольку GATE=0. Число N1 естественно будет меньше на 2^16.
Таким образом пишем gate=0; после while ((TIFR&0x20)==0x20){}?
Цитата(=GM= @ Jun 11 2010, 01:36)

Далее, счётчик T0 у вас 8-битный, а переменная count_T0 у вас int, ну и добавляете вы ее, как int, а надо только 8 бит. Для вашего компилятора может и пройдёт, но лучше по-честному добавлять 8-битное число.
а здесь пишем count_T0=(int)TCNT0; с приведением типа. (хотя компилятор сам это делает, не делает а пишет варнинги если написать char*2, например, то он ругнется на переполнение чара.)
В общем результат тот же.

И число N1 то меньше на 65536 то ровно столько сколько надо. От чего это зависит не понимаю. 3 измерения так - 3 так.
Структура вашей программы в некотором смысле эклектична.
1) Вроде бы работаете по прерываниям, но и ждёте флаг прерывания ICF1 в фоне, и явно это будет другое прерывание, поскольку по завершению прерывания флаг ICF1 сбрасывается в самом прерывании. Зачем тогда его ждёте?
2) В начале цикла измерения вы разрешаете прерывания по захвату, но не учитываете, что физически захват как молотил со скоростью входной частоты, так и молотит. Следовательно, после разрешения прерывания TOIE1=1 надо бы сбросить флаг ICF1, также и в конце измерения.
Кстати, каков диапазон частот вы хотите измерять, какой проц и тактовая?
Цитата(=GM= @ Jun 12 2010, 01:28)

Структура вашей программы в некотором смысле эклектична.
Кстати, каков диапазон частот вы хотите измерять, какой проц и тактовая?
Да, программа такова, ибо сначала я пытался измерять методом ворот, но удачи не достиг

Вот код моего варианта:
Код
float result;
char buff[16], tim, numizm=2, state=0;
// Timer 1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
tim++; // увеличиваем счетчик переполнений.
}
// Timer 1 input capture interrupt service routine
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
{
if (state==0){ // первый фронт сигнала
tim=0; // обнуляем переменные и таймер
TCNT1=0;
}
state++; // прибавляем счетчик
if (state == numizm){ // если счетчик и кол-во измерений равны, то вычисляем частоту
result = 12000000 / ((float)tim*65535+ICR1); // вычисляем "частоту"
result *= (int)numizm; // и умножаем на кол-во измерений, получая действительную частоту
numizm = 255; //выставляем число измерений
if (numizm < result){ // если оно меньше полученной частоты то ставим ее равной частоте ( т.о. измерение будет длиться меньше секунды)
numizm = result;
}
sprintf(buff,"F = %0.3f Гц",result); // выводим на экран и в UART
puts(buff);
putchar(13);
lcd_clear();
lcd_puts(buff);
state = 0; // Обнуляем состояние
}
}
На мой взгляд совершенно логичный, но не работающий. Потом мне объяснили что таким способом, разрешающая способность будет не велика. Поэтому я решил пойти по пути Вашего метода. Ну и взял 2 варианта кода и их объединил

Ну а частоты которые мне нужно измерять - 2 кГц -- 10 кГц. Насколько я понимаю, Вашим методом можно достичь очень хорошей точности, что для струнного датчика (линейный размер -> частота) было бы очень неплохо

МК Atmega8 частота не принципиальна, но вот сейчас под рукой кварц на 12 мГц.
Для вашего случая погрешность измерения приблизительно будет +-0.0002 Гц без учёта нестабильности кварца. Вы не плодите варианты, а сконцентрируйтесь на первом, он вполне работоспособен. Как вы струну возбуждаете?
Цитата(=GM= @ Jun 12 2010, 16:51)

Как вы струну возбуждаете?
Для этого используется автогенератор. На выходе его 300 мВ 2 -10 кГц прямоугольные импульсы. Но вообще эти старые железки хочется переделать и встроить в свое устройство.
Кстати говоря, вы своим вопросом породили во мне другой вопрос: можно ли использовать в Вашем алгоритме компаратор встроенный в мегу8 для захвата и будет ли его достаточно для детектирования столь низкого по амплитуде сигнала?
Цитата(ps1x @ Jun 12 2010, 20:51)

можно ли использовать в Вашем алгоритме компаратор встроенный в мегу8 для захвата и будет ли его достаточно для детектирования столь низкого по амплитуде сигнала?
В принципе, думаю можно, хотя оффсет у компаратора достаточно большой, посмотрите на рис. 167-168 док. 2486.
Думаю, что для ваших целей подойдёт более простой код для измерения частоты, это модификация вашего варианта, я только немного упорядочил и заменил названия "непроизносимых" переменных. Есть правда один нюанс, но об этом позже. Компилятор WinAVR-20100110
CODE
static float volatile Fx;
uint32_t volatile N,N1,N2,M,M1,M2;
uint16_t volatile mtick1,mtick2,mover=0;
uint16_t volatile ntick1,ntick2,nover=0;
ISR (TIMER0_OVF_vect) //переполнение счётчика0 входных тиков
{
mover++; //учтём в mover
}
ISR (TIMER1_OVF_vect) //переполнение таймера1 системных тиков
{
nover++; //учтём в nover
}
int main(void)
{
TIMSK=(1<<TOIE1)|(1<<TOIE0); //разрешим TOV1 и TOV0
asm volatile("sei\n\t":

; //разрешим прерывания
while(1) //цикл непрерывного измерения частоты
{
TIFR=(1<<ICF1); //сбросим флаг захвата
while((TIFR&0x20)==0x20); //ждём начала измерения
ntick1=ICR1; //запомним ICR1 и TCNT0
mtick1=TCNT0; //в начале измерения
ntick2=nover;
mtick2=mover&0x00FF;
N1=((uint32_t)(ntick2)<<16)+(uint32_t) ntick1; //системные тики
M1=((uint32_t)(mtick2)<<8) +(uint32_t) mtick1; //входные тики
delay_ms(1000); //задержка на 1 с
TIFR=(1<<ICF1); //сбросим флаг захвата
while((TIFR&0x20)==0x20); //ждём конца измерения
ntick1=ICR1; //запомним ICR1 и TCNT0
mtick1=TCNT0; //в конце измерения
ntick2=nover;
mtick2=mover&0x00FF;
N2=((uint32_t)(ntick2)<<16)+(uint32_t) ntick1; //системные тики
M2=((uint32_t)(mtick2)<<8) +(uint32_t) mtick1; //входные тики
N=N2-N1; //системные тики за время измерения
M=M2-M1; //входные тики за время измерения
Fx=12000000.0*(float)M/(float)N; //вычисление частоты входного сигнала
// <<здесь должен быть код вывода на индикатор>>
}
}
Обращаю внимание СИСАДМИНА: при редактировании тег [ / codebox ] самопроизвольно меняется на тег < / div >, возникает ошибка.
Цитата(=GM= @ Jun 14 2010, 02:22)

В принципе, думаю можно, хотя оффсет у компаратора достаточно большой, посмотрите на рис. 167-168 док. 2486.
Но офсет ведь не влияет? Мы же начинаем измерение фронтом и заканчиваем, и теоретически, задержка прихода фронта не должна влиять. Естественно если она одна и та же для начала и окончания.
Спасибо за пример, единственно, надо будет разобраться с регистрами всяких настроек таймеров, прерываний и т.п. В CVAVR таких удобных обращений нет

p.s. Правильны ли эти дефайны:
Код
#define TOIE0 0x01
#define TOIE1 0x04
#define ICF1 0x20
Судя по всему нет, ибо МК перезагружается на первой же строчке:
TIMSK=(1<<TOIE1)|(1<<TOIE0);
1) Оффсет влияет на начало фронта, многое зависит от ваших требований к изделию, пробуйте, потом расскажете. Если оффсет не изменяется за время измерения, то задержки обоих фронтов будут одинаковые.
2) Дифайны неправильные, надо так
Код
#define TOIE0 0
#define TOIE1 2
#define ICF1 5
Но самому делать ничего не надо, всё сделано до нас, определения находятся в одном из io*.h, для атмеги8 в частности в файле WinAVR-20100110\avr\include\avr\iom8.h.
Цитата(=GM= @ Jun 13 2010, 21:22)

Обращаю внимание СИСАДМИНА: при редактировании тег [ / codebox ] самопроизвольно меняется на тег < / div >, возникает ошибка
Уважаемый rezident, спасибо за исправление, я тоже вначале так делал, но при этом искажается код программы, кроме того, вы разрушили моё красивое форматирование комментов, мне так не нравится. Обратите внимание, в исправленном вами коде вместо : : ) появилась заставка рожицы

. Это уже не дело, а ведь могут быть искажения и посерьёзнее, особенно там, где присутствуют угловые скобки < или >. Тогда уж не надо было вводить в практику и тег [ code ].
ЕЩЁ РАЗ ОБРАЩАЮ ВНИМАНИЕ: при редактировании сообщения тег [ / codebox ] самопроизвольно меняется на тег < / div >, приходится вручную забивать div и допечатывать codebox. При первом вводе сообщения такой замены не происходит. Похоже это баг системы, пожалуйста, разберитесь и по возможности исправьте.
CODE
#include <mega8.h> //Подключение
#include <stdio.h> // внешнх
#include <stdlib.h>
#include <delay.h> // библиотек
#include <16x2.h>//
#define TOIE0 0
#define TOIE1 2
#define ICF1 5
//Объвление переменных
static float volatile Fx;
unsigned long volatile N,N1,N2,M,M1,M2;
unsigned int volatile mtick1,mtick2,mover=0;
unsigned int volatile ntick1,ntick2,nover=0;
//Прерывание по переполнению Timer/Counter 0
interrupt [TIM0_OVF] void timer0_ovf_isr(void){
mover++;
}
//Прерывание по переполнению Timer/Counter 1
interrupt [TIM1_OVF] void timer1_ovf_isr(void){
nover++;
}
//Тело основной программы
void main(void){
unsigned char lcd_buffer[46];//Обявление локальной строковой переменной
PORTC=0x00;
DDRC=0xFF;
//Инициализация Timer/Counter 0
TCCR0=0x07;
//Инициализация Timer/Counter 1
TCCR1B=0x01;
UCSRA=0x00;
UCSRB=0x08;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x4D;
putsf("reset\r");
lcd_init();//Инициализация LCD HD44780
#asm("sei")
TIMSK=(1<<TOIE1)|(1<<TOIE0); //разрешим TOV1 и TOV0
putchar('a');
while (1){//Бесконечный цикл
TIFR=(1<<ICF1); //сбросим флаг захвата
putchar('b');
while((TIFR&0x20)==0x20); //ждём начала измерения
putchar('c');
ntick1=ICR1; //запомним ICR1 и TCNT0
mtick1=TCNT0; //в начале измерения
ntick2=nover;
mtick2=mover&0x00FF;
N1=((long)(ntick2)<<16)+(long) ntick1; //системные тики
M1=((long)(mtick2)<<8) +(long) mtick1; //входные тики
putchar('d');
delay_ms(1000); //задержка на 1 с
TIFR=(1<<ICF1); //сбросим флаг захвата
while((TIFR&0x20)==0x20); //ждём конца измерения
ntick1=ICR1; //запомним ICR1 и TCNT0
mtick1=TCNT0; //в конце измерения
ntick2=nover;
mtick2=mover&0x00FF;
N2=((long)(ntick2)<<16)+(long) ntick1; //системные тики
M2=((long)(mtick2)<<8) +(long) mtick1; //входные тики
N=N2-N1; //системные тики за время измерения
M=M2-M1; //входные тики за время измерения
Fx=12000000.0*(float)M/(float)N; //вычисление частоты входного сигнала
putchar('c');
sprintf(lcd_buffer, "F=%0.6fHz", Fx);
lcd_clear();
lcd_puts(lcd_buffer);
puts(lcd_buffer);
putchar(13);
} }
Это код который я гружу в контроллер.
CODE
bcdcF=1016.036315Hz
bcdcF=1017.031433Hz
bcdcF=1016.036315Hz
bcdcF=1021.588439Hz
bcdcF=1021.588439Hz
bcdcF=1021.588439Hz
bcdcF=1021.588439Hz
bcdcF=1016.036315Hz
bcdcF=1017.031433Hz
bcdcF=1016.036315Hz
bcdcF=1021.588439Hz
bcdcF=1021.588439Hz
bcdcF=1021.588439Hz
bcdcF=1021.588439Hz
bcdcF=1016.036315Hz
bcdcF=1017.031433Hz
bcdcF=1016.036315Hz
bcdcF=1016.036315Hz
bcdcF=1021.588439Hz
bcdcF=1021.588439Hz
bcdcF=1021.588439Hz
bcdcF=1016.036315Hz
bcdcF=1017.031433Hz
bcdcF=1016.036315Hz
bcdcF=1016.036315Hz
bcdcF=1021.588439Hz
bcdcF=1022.588989Hz
bcdcF=1021.588439Hz
bcdcF=1016.036315Hz
bcdcF=1016.036315Hz
bcdcF=1016.036315Hz
bcdcF=1016.036315Hz
А это то что на выходе в терминале.
1) Для начала неплохо. Каков был номинал входной частоты?
2) Поставьте putchar('b'); до сброса флага захвата TIFR=(1<<ICF1); Вы должны понимать, что сбрасываете предыдущее состояние, которое возникло неизвестно когда, и ожидаете свежего захвата, после которого немедленно запоминаете свежие значения TCNT0 и ICR1, пока они не поменялись снова.
3) Из тех же соображений уберите вывод putchar('c'); сразу после захвата, это достаточно длительная функция.
4) Неплохо бы для отладки выводить в цикле не только частоту, но и значения N1, N2, M1, M2.
Входная частота 1019.02173 Гц. Расчетная. По осцилографу тоже очень похоже (С117 с метками).
CODE
reset
N1=0 M1=97
N2=0 M2=1130
cF=1016.924499Hz
N1=0 M1=1168
N2=0 M2=2204
cF=1014.423889Hz
N1=0 M1=2242
N2=0 M2=3277
cF=1018.893310Hz
N1=0 M1=3315
N2=0 M2=4350
cF=1018.893310Hz
N1=0 M1=4388
N2=0 M2=5423
cF=1018.893310Hz
N1=0 M1=5461
N2=0 M2=6496
cF=1018.893310Hz
N1=0 M1=6534
N2=0 M2=7569
cF=1018.893310Hz
N1=0 M1=7607
N2=0 M2=8642
cF=1018.893310Hz
N1=0 M1=8680
N2=0 M2=9715
cF=1013.444702Hz
N1=0 M1=9753
N2=0 M2=10788
cF=1018.893310Hz
N1=0 M1=10827
N2=0 M2=11863
cF=1014.423889Hz
N1=0 M1=11902
N2=0 M2=12939
cF=1020.862243Hz
N1=0 M1=12978
N2=0 M2=14014
cF=1019.877746Hz
N1=0 M1=14053
N2=0 M2=15089
cF=1019.877746Hz
N1=0 M1=15128
N2=0 M2=16164
cF=1014.423889Hz
N1=0 M1=16203
N2=0 M2=17240
cF=1020.862243Hz
Такое ощущение, что нужно обнулять nover и mover. Но что то еще не так.
Смущает так же, что N1 и N2 = 0.
Если обнулять nover и mover то измерения такие:
CODE
reset
N1=0 M1=98
N2=0 M2=1131
F=1016.924499Hz
N1=0 M1=144
N2=0 M2=1178
F=1017.908935Hz
N1=0 M1=191
N2=0 M2=1225
F=1017.908935Hz
N1=0 M1=238
N2=0 M2=1272
F=1017.908935Hz
N1=0 M1=29
N2=0 M2=1062
F=1022.421386Hz
N1=0 M1=75
N2=0 M2=1108
F=1016.924499Hz
N1=0 M1=121
N2=0 M2=1155
F=1017.908935Hz
N1=0 M1=168
N2=0 M2=1202
F=1017.908935Hz
N1=0 M1=215
N2=0 M2=1249
F=1017.908935Hz
N1=0 M1=6
N2=0 M2=1038
F=1021.431579Hz
N1=0 M1=51
N2=0 M2=1084
F=1016.924499Hz
N1=0 M1=97
N2=0 M2=1130
F=1016.924499Hz
N1=0 M1=143
N2=0 M2=1177
F=1023.411132Hz
N1=0 M1=190
N2=0 M2=1224
F=1017.908935Hz
N1=0 M1=237
N2=0 M2=1272
F=1024.400878Hz
N1=0 M1=29
N2=0 M2=1062
F=1016.924499Hz
N1=0 M1=75
N2=0 M2=1108
F=1016.924499Hz
N1=0 M1=121
N2=0 M2=1155
F=1017.908935Hz
N1=0 M1=168
N2=0 M2=1202
F=1017.908935Hz
N1=0 M1=215
N2=0 M2=1249
F=1017.908935Hz
N1=0 M1=6
N2=0 M2=1038
F=1015.940002Hz
N1=0 M1=51
N2=0 M2=1084
F=1016.924499Hz
N1=0 M1=97
N2=0 M2=1130
F=1022.421386Hz
N1=0 M1=143
N2=0 M2=1177
F=1017.908935Hz
N1=0 M1=190
N2=0 M2=1224
F=1017.908935Hz
А вот и сам код:
CODE
while (1){//Бесконечный цикл
TIFR=(1<<ICF1); //сбросим флаг захвата
while((TIFR&0x20)==0x20); //ждём начала измерения
ntick1=ICR1; //запомним ICR1 и TCNT0
mtick1=TCNT0; //в начале измерения
ntick2=nover;
mtick2=mover&0x00FF;
N1=((long)(ntick2)<<16)+(long) ntick1; //системные тики
M1=((long)(mtick2)<<8) +(long) mtick1; //входные тики
sprintf(lcd_buffer, "N1=%i M1=%i\r", N1,M1);
puts(lcd_buffer);
delay_ms(1000); //задержка на 1 с
TIFR=(1<<ICF1); //сбросим флаг захвата
while((TIFR&0x20)==0x20); //ждём конца измерения
ntick1=ICR1; //запомним ICR1 и TCNT0
mtick1=TCNT0; //в конце измерения
ntick2=nover;
mtick2=mover&0x00FF;
N2=((long)(ntick2)<<16)+(long) ntick1; //системные тики
M2=((long)(mtick2)<<8) +(long) mtick1; //входные тики
sprintf(lcd_buffer, "N2=%i M2=%i\r", N2,M2);
puts(lcd_buffer);
N=N2-N1; //системные тики за время измерения
M=M2-M1; //входные тики за время измерения
Fx=12000000.0*(float)M/(float)N; //вычисление частоты входного сигнала
sprintf(lcd_buffer, "F=%0.6fHz", Fx);
lcd_clear();
lcd_puts(lcd_buffer);
puts(lcd_buffer);
putchar(13);
nover=0;
mover=0;
} }
Разница М2-М1 всё время где-то 1033-1034, это приемлемо, по крайней мере согласуется с количеством импульсов входной частоты за 1 сек.
А вот разница N2-N1=0, как это может быть? И как можно получить конечную частоту, деля на 0?
Скорее всего, неверный формат вывода. N1, N2, М1, М2 - целые числа формата long, значит формат вывода должен быть %li, а не %i.
Код
reset
aN1=65536 M1=98 ntick1=0 ntick2=1
N2=12582912 M2=1158 ntick1=0 ntick2=192
F=1016.187438Hz
N1=0 M1=205 ntick1=0 ntick2=0
N2=12451840 M2=1262 ntick1=0 ntick2=190
F=1018.644653Hz
N1=0 M1=52 ntick1=0 ntick2=0
N2=12451840 M2=1109 ntick1=0 ntick2=190
F=1018.644653Hz
N1=0 M1=155 ntick1=0 ntick2=0
N2=12517376 M2=1213 ntick1=0 ntick2=191
F=1014.270141Hz
N1=0 M1=3 ntick1=0 ntick2=0
N2=12451840 M2=1058 ntick1=0 ntick2=190
F=1016.717163Hz
N1=0 M1=105 ntick1=0 ntick2=0
N2=12517376 M2=1162 ntick1=0 ntick2=191
F=1013.311462Hz
N1=0 M1=208 ntick1=0 ntick2=0
N2=12451840 M2=1266 ntick1=0 ntick2=190
F=1019.608398Hz
N1=0 M1=56 ntick1=0 ntick2=0
N2=12451840 M2=1113 ntick1=0 ntick2=190
F=1018.644653Hz
N1=0 M1=159 ntick1=0 ntick2=0
N2=12451840 M2=1216 ntick1=0 ntick2=190
F=1018.644653Hz
Действительно, только %li
Теперь мне не нравится, что ntick1 всё время равен 0, так не должно быть. Хотя N2=ntick2<<16.
И почему такая разница во времени между циклами измерения? Для 12 МГц клока составит 65536*83нс=5,461 мс.
Признавайтесь, куда у вас подключена нога захвата :-)?
И ещё, попробуйте поставить все операторы sprintf после вычисления Fx, возможно они вносят неоднородную задержку в цикл измерения. Не забудьте ввести доп.переменные ntick3, 4... чтобы не затирать начало
А потом перейдём к решению "нюанса".
Цитата(=GM= @ Jun 15 2010, 18:05)

Признавайтесь, куда у вас подключена нога захвата :-)?
Входной меандр подключен к Т0. ICP висит в воздухе...
Цитата(ps1x @ Jun 15 2010, 13:58)

Входной меандр подключен к Т0. ICP висит в воздухе...
Ну вот, приплыли :-). Не обижайтесь, шутю я, у всех бывает. У вас в ICR1 стоял 0х0000 по умолчанию, вот вы его и считывали.
Нога должна быть физически подключена к генератору и Т0. Иначе захват не будет работать. Далее, таймер0 и захват должны быть настроены на срабатывание от одного фронта, скажем 1-0.
А DDR соответствующих ножек влияет? Подтяжка? Сейчас вывод тини26 подключен напрямую к T0 меге и все. Надо добавить соединение с ICP? (пока что делаю на т.н. Breadboard пластиковая макетка с дырочками).
Да, надо соединить Т0 и ICP с генератором меандра. ICP настроить на ввод, подтяжка не нужна.
Появилась стабильность

Значения 1018.8184 - 1018.8183 около того. Наверное, можно списать на погрешность кварца генератора и частотомера, хотя один из вариантов прошлого (

) периодически показывал значения очень близкие к расчетному +0.00030 или около того.
Однако периодически происходят выбросы 1024.125... К сожалению перешел на другое рабочее место, где самодельный переходник USB-USART не определяется, посему не могу предоставить никакой отладочной информации, сам считываю с дисплейчика.
Ну, я рад за вас. Выбросы происходят из-за того самого "нюанса", который теперь стоит обсудить. Дело в том, что между считыванием ICR1 и считыванием nover есть временной зазор, если прерывание TOV1 попадёт на него, то nover изменится на 1, хотя не должно. Вот такой нюанс. Подумайте сами, как его можно разрешить, завтра обсудим. Кстати, нельзя ли взглянуть на схему генератора струны?
Автогенератор в институте, и схемы от него нет, даже боюсь его развинчивать, но постараюсь что нибудь добыть когда там буду.
Может быть запретить прерывания перед считыванием ICR1?

up: выбросы бывают и в меньшую сторону, около 1013 Гц.
Цитата(ps1x @ Jun 15 2010, 19:21)

Может быть запретить прерывания перед считыванием ICR1?

Всё равно между захватом и запретом можем пропустить переполнение tov1 или tov0, хотя и реже.
Расскажу, как работает код
Код
1 TIFR=(1<<ICF1); //сбросим флаг захвата
2 while((TIFR&0x20)==0x20); //ждём начала измерения
3 ntick1=ICR1; //запомним ICR1 и TCNT0
4 mtick1=TCNT0; //в начале измерения
5 ntick2=nover;
6 mtick2=mover&0x00FF;
1. Сбросили флаг захвата, это понятно, чтобы будущее содержимое ICR1 и TCNT0 было синхронизировано.
2. Ждём захвата, чтобы текущее содержимое системного времени в TCNT1 защёлкнулось в ICR1 и чтобы содержимое TCNT0 соответствовало защёлкнутому времени.
3-4. Сохраняем содержимое ICR1 и TCNT0 в памяти для дальнейшего использования.
5. Сохраняем старшую часть 32-битного счётчика nover в памяти для дальнейшего использования. Заметьте, я специально оттянул время запоминания nover подальше от захвата п.2, чтобы дать время сработать возможному прерыванию и скорректировать nover.
6. Сохраняем старшую часть 32-битного счётчика mover в памяти для дальнейшего использования. Тут статика, никаких особенностей.
Тут можно посмотреть варианты решения
http://electronix.ru/forum/index.php?showtopic=16900
Цитата(=GM= @ Jun 16 2010, 14:10)

4) Проверить наличие переноса из таймера1. При наличии переноса добавить единицу к Т6.
5) Проверить наличие второго захвата, если нет захвата перейти к п.4.
Вот так видимо?
Не понял, по-подробнее можно?
Ну что, ps1x, есть подвижки? В принципе, я нашёл решение для си, надо проверить...
Цитата(=GM= @ Jun 21 2010, 19:31)

Ну что, ps1x, есть подвижки? В принципе, я нашёл решение для си, надо проверить...
Да, вместо использования компаратора я сделал усилитель на 2 транзисторах. Изготовил плату и спаял

А вот по программе пока не продвигался
Попробуйте следующий код, для ваших целей должен подойти
Код
ISR (TIMER0_OVF_vect) //переполнение входных тиков
{
mover++; //учтём в mover
}
ISR (TIMER1_OVF_vect) //переполнение таймера1
{
nover++; //учтём в nover
}
int main(void)
{
TIMSK=(1<<TOIE1)|(1<<TOIE0); //разрешим TOV1 и TOV0
asm volatile("sei\n\t"::); //разрешим прерывания
while(1) //непрерывное измерение частоты
{
TIMSK &=~_BV(TOIE1); //запретим прерывания TOV1
do
{
if(TIFR &_BV(TOV1)) //при появлении
{ //переполнения поправим
nover++; //старшее слово
TIFR =_BV(TOV1); //и сбросим TOV1
}
}
while((TIFR&_BV(TOV1))==0); //ждём начала измерения
ntick1=ICR1; //запомним ICR1
ntick2=nover; //и старшую часть
if(TIFR &_BV(TOV1))
{
nover++;
TIFR =_BV(TOV1); //сбросим TOV1
if(ntick1<0x8000) ntick2=nover;
}
TIFR=(1<<ICF1); //сбросим флаг захвата
TIMSK |=_BV(TOIE1); //разрешим прерывания TOV1
mtick1=TCNT0; //запомним TCNT0
mtick2=mover&0x00FF; //и старшую часть
N1=((uint32_t)(ntick2)<<16)+(uint32_t) ntick1; //системные тики
M1=((uint32_t)(mtick2)<<8) +(uint32_t) mtick1; //входные тики
delay_ms(1000); //задержка на 1 с
TIMSK &=~_BV(TOIE1); //запретим прерывания TOV1
do
{
if(TIFR &_BV(TOV1)) //при появлении
{ //переполнения поправим
nover++; //старшее слово
TIFR =_BV(TOV1); //и сбросим TOV1
}
}
while((TIFR&_BV(TOV1))==0); //ждём конечного захвата
ntick1=ICR1; //запомним ICR1
ntick2=nover; //и старшую часть
if(TIFR &_BV(TOV1))
{
nover++;
TIFR =_BV(TOV1); //сбросим TOV1
if(ntick1<0x8000) ntick2=nover;
}
TIFR=(1<<ICF1); //сбросим флаг захвата
TIMSK |=_BV(TOIE1); //разрешим прерывания TOV1
mtick1=TCNT0; //запомним TCNT0
mtick2=mover&0x00FF; //и старшую часть
N2=((uint32_t)(ntick2)<<16)+(uint32_t) ntick1; //системные тики
M2=((uint32_t)(mtick2)<<8) +(uint32_t) mtick1; //входные тики
N=N2-N1; //системные тики за время измерения
M=M2-M1; //входные тики за время измерения
Fx=12000000.0*(float)M/(float)N; //вычисление частоты сигнала
// <<код вывода на индикатор>>
}
}
Работает прекрасно

+/- 0.0001 мкС
Большущее спасибо!
На здоровье.
Но 0,0001 мкс - это 0,1 нс, вы не ошиблись?
Меня ваш проект заинтересовал постольку, поскольку он написан на си (который я местами недолюбливаю :-), а на форуме было много вопросов, как реализовать мой алгоритм именно на си, но что-то все кодеры пропадали, не дойдя до конечного результата. Теперь одна из реализаций налицо, буду на неё ссылаться при случае :-).
Ещё, интересно было бы выяснить, какую максимальную частоту вы сможете измерить без переделки программы? С небольшими изменениями вы можете мерить до половины тактовой, в вашем случае до 6 МГц.
demiurg_spb
Jun 25 2010, 12:00
Цитата(=GM= @ Jun 25 2010, 15:19)

На здоровье.
Простите, что вклиниваюсь, но возникает у меня вопросик.
А что будет с прибором, когда частота очень низкая или её нет вовсе?
Может я что не понял, но в коде есть "бесконечные" циклы, ожидающие события от периферии...
Цитата(demiurg_spb @ Jun 25 2010, 11:00)

Простите, что вклиниваюсь, но возникает у меня вопросик.
А что будет с прибором, когда частота очень низкая или её нет вовсе? Может я что не понял, но в коде есть "бесконечные" циклы, ожидающие события от периферии...
Я уж испугался, что будет вопросик про Балеру или, того хуже, про прачечную :-). Это у вас с переездом связано, что ли?
1) У автора стоит автогенератор, вырабатывающий частоты в диапазоне 2-10 кГц, так что частота должна быть.
2) С другой стороны, если нет частоты, то вроде бы нечего отображать.
3) Однако, вы абсолютно правы в том, что данная программа имеет некоторые ограничения (как и всё в этом мире, впрочем). В программе нет модуля оценки входной частоты, просто в данном топике задача была не представить полностью коммерческий продукт, а немного другая, именно - написать программу на си для измерения частоты с минимальной погрешностью. Код программы открыт, так что, кому надо могут модифицировать его под себя.
Задача в основном достигнута, 1000 Гц измеряются с СЕМЬЮ верными цифрами. Кстати, методом ворот можно достичь только ТРИ значащие цифры.
Цитата(=GM= @ Jun 25 2010, 15:19)

Но 0,0001 мкс - это 0,1 нс, вы не ошиблись?
Наверное ошибся. +/-0.0001 Гц было точно при частоте ~1018 гц
Цитата(=GM= @ Jun 25 2010, 15:19)

Ещё, интересно было бы выяснить, какую максимальную частоту вы сможете измерить без переделки программы? С небольшими изменениями вы можете мерить до половины тактовой, в вашем случае до 6 МГц.
Завтра попробую.
demiurg_spb
Jun 27 2010, 19:19
Цитата(=GM= @ Jun 25 2010, 17:06)

Я уж испугался, что будет вопросик про Балеру или, того хуже, про прачечную :-). Это у вас с переездом связано, что ли?
Да нет:-) Разработчики пока на старом месте останутся.
Цитата
1) У автора стоит автогенератор, вырабатывающий частоты в диапазоне 2-10 кГц, так что частота должна быть.
2) С другой стороны, если нет частоты, то вроде бы нечего отображать.
3) Однако, вы абсолютно правы в том, что данная программа имеет некоторые ограничения (как и всё в этом мире, впрочем). В программе нет модуля оценки входной частоты, просто в данном топике задача была не представить полностью коммерческий продукт, а немного другая, именно - написать программу на си для измерения частоты с минимальной погрешностью. Код программы открыт, так что, кому надо могут модифицировать его под себя.
Задача в основном достигнута, 1000 Гц измеряются с СЕМЬЮ верными цифрами. Кстати, методом ворот можно достичь только ТРИ значащие цифры.
Понятно. Но я вижу лёгкое неудобство в том что надо детектировать наличие частоты. Я в
своём тахометре работаю по двум алгоритмам и когда надо перехожу с одного на другой. Но как всегда есть одно но. Хочется повысить быстродействие измерительного канала и при этом не потерять в точности, вот я и приглядываюсь к Вашему методу. Пока лишь смотрю, т.к. сразу не вижу простого и красивого способа его применить.
1) В чём конкретно вы видите лёгкое неудобство от детектирования?
2) Так всё таинственно, ну расскажите, что за алгоритмы...
3) Не знаю, что вы понимаете под красиво и легко, но посмотрел вполглаза на паспорт вашего тахометра, и увидел, что ваша "детская" погрешность в 1 Гц для 1000 Гц моим методом может быть легко превышена за время измерения 0,001 с (с погрешностью не более +-0,1 Гц).
demiurg_spb
Jun 28 2010, 17:34
Цитата(=GM= @ Jun 28 2010, 15:34)

1) В чём конкретно вы видите лёгкое неудобство от детектирования?
Теряется единообразие для быстрых и медленных частот (у меня же от 0,001Гц индикация работает).
Да и со свободными таймерами могут быть напряги. Надо попробовать т.к это лишь предположения:-)
Цитата
2) Так всё таинственно, ну расскажите, что за алгоритмы...
Считаю либо кол-во импульсов за секунду, либо интервалы между импульсами, а переключаюсь по условию наименьшей погрешности измерения.
Цитата
3) Не знаю, что вы понимаете под красиво и легко, но посмотрел вполглаза на паспорт вашего тахометра, и увидел, что ваша "детская" погрешность в 1 Гц для 1000 Гц моим методом может быть легко превышена за время измерения 0,001 с (с погрешностью не более +-0,1 Гц).
При этом имеем динамическую индикацию и клавиатуру, modbus до 921.6КБод, DAC, счётчик импульсов и счётчик времени наработки, внешние логические входы-выходы и ещё всякого разного функционала немало наберётся, да и бутлоадер отъедает часть флеша.
Я не спорю, что можно лучше, я даже уверен в этом. Но ресурсов контроллера уже особо не хватает на сильный импрувмент:-(
Приборчик то старенький уже, сейчас бы на кортексе делал а не на 16 (162)-ой меге.
Вот я и примеряюсь, обдумываю неспешно, т.к. текущий проект много сил требует...
Как вспомню, сколько нервов истратил на его сертификацию в Морском регистре, уууу.... то дисплей им с регулировкой яркости подавай, то корпус горючий, то питание =24В с нехилым гистерезисом и все эти требования постепенно выдают ведь собаки такие:-)
Цитата(demiurg_spb @ Jun 28 2010, 16:34)

Теряется единообразие для быстрых и медленных частот (у меня же от 0,001Гц индикация работает)
Ну, это вы ещё не прочувствовали мой алгоритм, в этом плане он универсальный. Можно или задаться числом периодов M=1, когда измеряете период длинного импульса, близкого к интервалу наблюдения, или целое число периодов М>1, также близкого к интервалу наблюдения, когда измеряете более короткие импульсы. Вся прелесть метода в том, что погрешность измерения минимальна всегда, невзирая на номинал входной частоты, поэтому не надо переключаться с одного алгоритма на другой, полное "единообразие" по-вашему.
Пожалуй, подниму тему из глубин форума. Встала задача измерять 2 сигнала 2-10кГц.
Судя по всему нужно подключать датчики и считать по очереди. Достаточно ли для этого транзистора на каждый вход, базой соединенных с выводами МК, которые и будут определять подключенный вход?
В mega8 есть аналоговый компаратор, имеющий доступ к модулю CAPT таймера1 и непосредственно вход ICP1. Переключаясь программно между двумя режимами работы таймера, можно измерить обе частоты.
Цитата(=GM= @ Jun 29 2010, 01:56)

...Можно или задаться числом периодов M=1, когда измеряете период длинного импульса, близкого к интервалу наблюдения, или целое число периодов М>1, также близкого к интервалу наблюдения, когда измеряете более короткие импульсы. Вся прелесть метода в том, что ...
Задаваться числом М можно, но ведь заранее неизвестно, какой сигнал придет, а вот подсчитать целые числа M и N за выбранный примерно интервал измерения - запросто.
Цитата(akl @ Nov 3 2010, 20:19)

В mega8 есть аналоговый компаратор, имеющий доступ к модулю CAPT таймера1 и непосредственно вход ICP1. Переключаясь программно между двумя режимами работы таймера, можно измерить обе частоты.
Мне тут умные люди подсказали что при измерении двух струн собственно измерения должны проводиться одновременно. В таком случае может использовать T0 и T1 входы таймеров а подключать их программно через транзистор? без ICP...
А на входе поставить внешний компаратор, например lm339n.
ps. А вот и GM!
Цитата(ps1x @ Nov 3 2010, 15:50)

Судя по всему нужно подключать датчики и считать по очереди.
Я исходил из этого.
По новой вводной можно сказать
-если две струны это составляющие одного преобразователя
-если они возбуждаются индивидуальными автогенераторами, причем исключена возможность их взаимосинхронизации
-если будут учтены различия в работе Т0 и Т1,
то, наверно, можно. ИМХО, я бы, в таком случае, поставил две ATtiny2313 с тактированием от одного кварцевого генератора.
Если измерять частоты надо одновременно, то вот вам пара путей реализации.
1) Взять два МК, те же тайни, в них - одна и та же уже готовая программа с единственным отличием - один МК получает измеренную частоту из второго, скажем, по spi или уарт, не суть важно.
2) Взять один МК с двумя схемами захвата, программа будет посложнее, но тоже реализуема в обозримое время.
Какой вариант реализации выбрать - зависит от вашей лености.
Но ведь вроде T0 и T1 асинхронные, и считают независимо от исполнения других комманд? Сейчас автогенератор работает в режиме перевозбуждения (что не есть гуд и приводит к нелинейности), вообще планируется выходной сигнал через прецезионный выпрямитель пустить на АЦП и таким образом завести ООС, чтобы датчик был с линейной характеристикой. Поскольку частоты не велики, нет ну правда, всего-то два сигнала 10кГц каждый, думается можно вполне все успевать.
Цитата(=GM= @ Nov 4 2010, 00:40)

2) Взять один МК с двумя схемами захвата, программа будет посложнее, но тоже реализуема в обозримое
Т.е. все таки без двух захватов не обойтись? А если программно отключать входы?
Мне сверх высокая точность не нужна, достаточно двух знаков после запятой, дальше все равно уже шумы, вибрации, и т.п.
Ну, вот вам ещё вариант: берёте МК с тремя таймерами и одним захватом. Входные сигналы на таймер0 и таймер2, и их же подаёте через коммутатор на схему захвата. Дальше, думаю, не надо объяснять.
Вообще, для оценки частоты с двумя знаками после запятой, вам достаточно взять время измерения 10 мс. Предположим, что время интеграции вашей системы будет больше, т.е. вы измеряете постоянную величину. Исходя из этого, вот вам ещё вариант - измеряете частоты последовательно: F1, F2, F1, F2, F1,... Измерения затем можно статистически обработать. Стандартная девиация - очень важный параметр для оценки качества, не пренебрегайте.
=GM= Заинтересовался Вашим способом, давно хотел сделать частотомер на работу, но как то не получалось..
Скомпоновал исходник из вашего поста 32 и поста 13
ps1x. Пробовал в протеусе и vmlab 315, показания пляшут. Из темы понял что это окончательный вариант алгоритма и все должно работать. Переменную Fx не выводил, смотрел в окне watch (я пока не знаю как вывести float, не используя fprint и т.п.).
Ну и вот тут у меня не понятки
Код
TIMSK &=~_BV(TOIE1); //запретим прерывания TOV1
do
{
//
}
while((TIFR&_BV(TOV1))==0); //ждём начала измерения
ntick1=ICR1; ntick2=nover; //запомним ICR1 и старшую часть
if(TIFR &_BV(TOV1))// <----------------?
{
nover++;
TIFR =_BV(TOV1); //сбросим TOV1
if(ntick1<0x8000) ntick2=nover;
}
Зачем нужен if, ведь после while((TIFR&_BV(TOV1))==0); флаг TOV1 будет поднят, а прерывания запрещены? И к стыду не понял это if(ntick1<0x8000) ntick2=nover;
Прикрепил архив с проектом для протеуса и vmlab, алгоритм разбил на две функции, иначе в vmlab нельзя было поставить точки останова и увидеть N и M.
Ну, трудно обьяснить, попробую. Цикл do ... while((TIFR&_BV(TOV1))==0) достаточно длинный и может так случиться, что возник TOV1, сделана проверка и выход, хотя переменная nover не была должным образом модифицирована. Случается это редко, но случается, ps1x как раз с этим и столкнулся. Вот для этого такие навороты. Реализаций программы может быть множество, возможно именно эта - не самая лучшая.
Для просмотра полной версии этой страницы, пожалуйста,
пройдите по ссылке.