Реклама ⓘ
Главная » Измерения
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Похожие статьи:


Реклама ⓘ

Частотомер до 200 МГц

Хочу предложить Вашему вниманию свою схему частотомера с диапазоном измерения 1Гц-200МГц. Он построен на связке микроконтроллёр - ПЛИС. Идея данного устройства возникла у меня в качестве первого более-менее серьёзного (пробного) проекта на ПЛИС, после того как просто мигать светодиодом стало неинтересно и хотелось сделать что-то более интересное. В моём распоряжении была Altera MAXII на 240 ячеек в виде отладочной платы, максимальная частота около 230 МГц (что и ограничило верхний порог измерения частотомера) чего вполне достаточно для радиолюбительских целей. 

Итак, задача поставлена, приступим к реализации проекта. Начнём с принципа работы частотомера: как известно - это устройство, которое считает количество импульсов за фиксированный промежуток времени. Исходя из этого разделим наш прибор на две основные части: первая - это быстрый счётчик импульсов на ПЛИС, вторая - устройство формирования точной задержки в одну секунду и вывода информации, этим будет заниматься микроконтроллер.

Начнём с быстродействующей части. Казалось бы, что может быть проще, просто берём двоичный счётчик достаточной разрядности (бита 24, а лучше 32), запускаем его ровно на одну секунду и получаем желаемый результат. Но! это при условии, что мы будем выводить результат с помощью 32 битного МК (например STM32, который может и 64 битные числа переваривать на ура), но если взять AVR, то возникнут проблемы (есть ограничение на логический сдвиг больше 16 бит). А мне хотелось сделать что-то универсальное. Поэтому я решил выводить информацию поразрядно (единицы, десятки, сотни герц и т.д.). Для этого я взял каскад десятичных счётчиков с функцией переноса (в моём случае - девять штук, чего достаточно для счёта до 999 миллионов импульсов). Каждый счётчик - четырёхбитный, а значит имеет 4 выхода (Q0-Q3), девять счётчиков - это 36 выводов, согласитесь, не каждый МК имеет такое количество портов. Значит - нужен мультиплексор, который будет коммутировать счётные регистры счётчиков. А к мультиплексору нужен свой отдельный счётчик, который будет им управлять. В итоге получилось что-то, вроде этого (см картинку):

Поясню логику устройства. Каждый десятичный счётчик имеет счётный вход (clk), который реагирует на возрастающий фронт импульса, разрешающий вход (EN), который разрешает счёт, при подаче на него лог нуля, сигнал сброса счётчика (res), который обнуляет счётчик по возрастающему фронту поданного на него импульса, выход переноса pr, который даёт сигнал переноса, когда счётчик досчитал до 10. Все счётчики соединены в виде каскада. Счётные импульсы (измеряемого сигнала) подаются на счётный вход clk первого счётчика (со входа count), на входы EN всех счётчиков (вывод cs_count) подаём лог ноль ровно на одну секунду. Первый счётчик начинает считать, досчитывает до девятого импульса, а на десятый - обнуляется и даёт импульс на вывод pr, который соединён с входом clk следующего счётчика. И так по цепочке до 9 разряда. Когда на входах EN появляется лог единица, счётчики прекращают счёт, но посчитанные импульсы сохраняются на их выходных регистрах.

Для вывода информации микроконтроллёр будет подавать тактовый сигнал на вход clk, который соединён со счётным входом счётчика мультиплексора и входом стробирования самого мультиплексора. Счётчик мультиплексора практически аналогичен входным счётчикам, кроме того, что он считает до 15 и у него нет выхода переноса (он там не нужен). При подаче тактового импульса на clk и разрешающего сигнала (лог ноль на вход cs_data и соответственно на вход EN счётчика) счётчик начинает считать и выводить результат на счётный вход С мультиплексора, а тот в зависимости от результата счёта коммутирует свои входные шины I1-I9 с выходной шиной О. Таким образом мы поочередно выводим значения каждого счётчика на выходную четырёхбитную шину data (изначально я пытался организовать вывод по последовательной шине по типу spi интерфейса, и при симуляции даже всё выглядело очень прилично, но в "железе" так и не заработало, выводились случайные числа, поэтому я отказался от данной идеи в пользу четырёхбитной шины данных).

Вот так это всё выглядит на языке описания аппаратуры "Verilog" (к слову, который мне удалось освоить, поверхностно, конечно, за неделю):

//Основной модуль
module main(count,cs_count,cs_data,reset,dat,clk,g_clk,out_0,out_2_16,led);
 input count,cs_count,cs_data,reset,clk,g_clk;
 output out_0,led;
 output[3:0]dat,out_2_16;
 wire pr1,pr2,pr3,pr4,pr5,pr6,pr7,pr8,rst;
 wire [3:0]QI1,QI2,QI3,QI4,QI5,QI6,QI7,QI8,QI9;
 wire [5:0]CQ;
 
 cnt_10 my_cnt1(.clk(count),.EN(cs_count),.pr(pr1),.res(rst),.Q(QI1));
 cnt_10 my_cnt2(.clk(pr1),.EN(cs_count),.pr(pr2),.res(rst),.Q(QI2));
 cnt_10 my_cnt3(.clk(pr2),.EN(cs_count),.pr(pr3),.res(rst),.Q(QI3));
 cnt_10 my_cnt4(.clk(pr3),.EN(cs_count),.pr(pr4),.res(rst),.Q(QI4));
 cnt_10 my_cnt5(.clk(pr4),.EN(cs_count),.pr(pr5),.res(rst),.Q(QI5));
 cnt_10 my_cnt6(.clk(pr5),.EN(cs_count),.pr(pr6),.res(rst),.Q(QI6));
 cnt_10 my_cnt7(.clk(pr6),.EN(cs_count),.pr(pr7),.res(rst),.Q(QI7));
 cnt_10 my_cnt8(.clk(pr7),.EN(cs_count),.pr(pr8),.res(rst),.Q(QI8));
 cnt_10 my_cnt9(.clk(pr8),.EN(cs_count),.res(rst),.Q(QI9));
 
 no my_no(.A(reset),.B(rst));
  
 cnt_mux my_cmux(.clk(clk),.EN(cs_data),.res(rst),.Q(CQ));
 
 cnt_16 my_16(.clk(g_clk),.Q(out_2_16));

 mux31 my_mux (.I1(QI1),.I2(QI2),.I3(QI3),.I4(QI4),.I5(QI5),.I6(QI6),
                  .I7(QI7),.I8(QI8),.I9(QI9),.C(CQ),.EN(cs_data),.out(dat),.strobe(clk));
                  
assign out_0 = g_clk;
assign led = ~cs_count;

endmodule

module cnt_10(clk,EN,Q,pr,res); //Десятичный счётчик
 input clk,EN,res;
 output reg[3:0]Q;
 output reg pr;
  
    
 always@(posedge clk or posedge res)
 begin
  if(res) begin pr <= 1'b0;  
                Q <= 4'b0000; end
   else
    begin
     if(EN==0) 
      begin
       Q <= Q+1'b1;
        if(Q==4'b1001) begin pr <= 1'b1; 
                             Q <= 4'd0; end
                              
        else begin pr <= 1'b0; end
      end
    end
  end
 endmodule 
 
module no(A,B); //Логический элемент НЕ
input A;
output B;

assign B=~A;
endmodule

module cnt_mux(clk,EN,Q,res); //Счётчик мультиплексора
 input clk,EN,res;
 output reg[3:0]Q;
  
    
 always@(negedge clk or posedge res)
 begin
  if(res) begin Q <= 4'b0000; end
   else
    begin
     if(EN==0) 
      begin
       Q <= Q+1'b1;
      end
    end
  end
 endmodule 
 
module mux31(I1,I2,I3,I4,I5,I6,I7,I8,I9,C,strobe,out,EN);//Мультиплексор
input [3:0]I1,I2,I3,I4,I5,I6,I7,I8,I9;
input strobe,EN;
input [3:0]C;
output reg [3:0]out;

always@(posedge strobe)
 begin
  if(strobe)begin
   if(EN==0) begin
  case(C)
  4'b0001: out <= I1;
  4'b0010: out <= I2;
  4'b0011: out <= I3;
  4'b0100: out <= I4;
  4'b0101: out <= I5;
  4'b0110: out <= I6;
  4'b0111: out <= I7;
  4'b1000: out <= I8;
  4'b1001: out <= I9;
    
  default: out <= 4'd0;
  endcase
 end
 end
 end
endmodule

module cnt_16(clk,Q,res);//Дополнительный счётчик-делитель 
 input clk,res;          //частоты для демонстрации
 output reg[3:0]Q;       //не связан с основным устройством
                         //делит частоту кварцевого генератора на 50 мГц
 always@(posedge clk)    //установленного на плате ПЛИС
 begin
  if(res) begin Q <= 4'b0000; end
   else
      begin Q <= Q+1'b1; end
  end
 endmodule 

Вторая часть устройства, как я уже писал выше, реализована на микроконтроллере (у меня это atmega328, можно любой другой с достаточным количеством выводов и памятью от 2 килобайт). Его задачи - сформировать секундную задержку, подать в это время разрешающий сигнал на счёт импульсов и вывести полученный результат. Реализуем это всё на Си.

С первой задачей я "схалтурил", задержку сделал встроенной функцией "delay_ms" из CVAVR. Хотя, я честно пытался организовать задержку на таймере 1, но так и не понял как его однократно запустить на счёт по команде, а не постоянно выдавать прерывания (если кто знает как это сделать, напишите в комментариях). Кстати результат получился очень даже приемлемый показания частоты не плавают в ходе постоянного измерения (не более 1Гц). 

Теперь о выводе результатов. Он у нас происходит по нисходящему фронту сигнала clk подаваемого при низком уровне разрешающего сигнала cs_data, просто даём импульс на clk и после считывает данные с выводов data_0 - data_3 девять раз, а данные записываем в массив. Вся эта красота отображается, в моём случае, на дисплее HD44780, подключенном через расширитель портов pcf8574T по последовательной шине i2c. Просто выводим по очереди каждый разряд, начиная со старшего, попутно проверяем, если старший разряд равен нулю, то просто ставим пробел (чтобы отсечь нули слева от числа и не получать значения типа 00000125 Герц) . При желании индикацию можно сделать любую (хоть на семисегментных индикаторах, хоть на LCD дисплее или вообще выводить в терминал через uart).

Код на Си выглядит так (весь проект будет приложен к статье):

unsigned char result[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

void cpld_res(void){
 res=0;
 delay_us(50);
 res=1;
 }

void count(void){
  
        
   cs_count = 0; //Запускает плисину на счёт
   delay_ms(1000);
   cs_count = 1;
   delay_ms(1);    
}

void processing(void){ //Пишем результат в массив
  unsigned int j; 
  
    cs_data=0;
    clk=0;
    delay_us(5);
    
    for(j=0;j<10;j++){
     
     clk=1;
     delay_us(10);
     clk=0;
     delay_us(5);
     result[j]=((data_0|(data_1<<1)|(data_2<<2)|(data_3<<3))&0x0F);
     delay_us(5);
     
    }
    
    cs_data=1;
    delay_us(50);
    
    cpld_res(); //Сброс счётчика плисины  
       
            
    lcd_gotoxy(4,0);         //Выводим на индикатор
    lcd_print("Chastota");
    lcd_gotoxy(2,1);
    
    if(result[9]>0){lcd_print_char(result[9]+0x30);
    lcd_print_char(result[8]+0x30);
    lcd_print_char(result[7]+0x30);
    lcd_print_char(result[6]+0x30);
    lcd_print_char(result[5]+0x30);
    lcd_print_char(result[4]+0x30);
    lcd_print_char(result[3]+0x30);
    lcd_print_char(result[2]+0x30);
    lcd_print_char(result[1]+0x30);
    lcd_print(" Hz");}
    
    else if(result[8]>0){lcd_print(" ");
    lcd_print_char(result[8]+0x30);
    lcd_print_char(result[7]+0x30);
    lcd_print_char(result[6]+0x30);
    lcd_print_char(result[5]+0x30);
    lcd_print_char(result[4]+0x30);
    lcd_print_char(result[3]+0x30);
    lcd_print_char(result[2]+0x30);
    lcd_print_char(result[1]+0x30);
    lcd_print(" Hz");}
    
    else if(result[7]>0){lcd_print("  ");
    lcd_print_char(result[7]+0x30);
    lcd_print_char(result[6]+0x30);
    lcd_print_char(result[5]+0x30);
    lcd_print_char(result[4]+0x30);
    lcd_print_char(result[3]+0x30);
    lcd_print_char(result[2]+0x30);
    lcd_print_char(result[1]+0x30);
    lcd_print(" Hz");} 
    
    else if(result[6]>0){lcd_print("   ");
    lcd_print_char(result[6]+0x30);
    lcd_print_char(result[5]+0x30);
    lcd_print_char(result[4]+0x30);
    lcd_print_char(result[3]+0x30);
    lcd_print_char(result[2]+0x30);
    lcd_print_char(result[1]+0x30);
    lcd_print(" Hz");}
    
    else if(result[5]>0){lcd_print("    ");
    lcd_print_char(result[5]+0x30);
    lcd_print_char(result[4]+0x30);
    lcd_print_char(result[3]+0x30);
    lcd_print_char(result[2]+0x30);
    lcd_print_char(result[1]+0x30);
    lcd_print(" Hz");}
    
    else if(result[4]>0){lcd_print("     ");
    lcd_print_char(result[4]+0x30);
    lcd_print_char(result[3]+0x30);
    lcd_print_char(result[2]+0x30);
    lcd_print_char(result[1]+0x30);
    lcd_print(" Hz");} 
    
    else if(result[3]>0){lcd_print("      ");
    lcd_print_char(result[3]+0x30);
    lcd_print_char(result[2]+0x30);
    lcd_print_char(result[1]+0x30);
    lcd_print(" Hz");} 
    
    else if(result[2]>0){lcd_print("       ");
    lcd_print_char(result[2]+0x30);
    lcd_print_char(result[1]+0x30);
    lcd_print(" Hz");} 
    
    else{lcd_print("        ");
    lcd_print_char(result[1]+0x30);
    lcd_print(" Hz");}
    
    for(j=0;j<10;j++){result[j] = 0;}
    
}

Фьюзы атмеги: LOW 0xFF, HIGH 0xDD, EXT 0x05;

Некоторые могли обратить внимание, что в массиве 10 байт, а разрядов у меня только девять. Это не моя ошибка. У меня постоянно получался ноль в первом разряде, а всё значение смещалось влево. Так я убрал этот лишний ноль. Я думаю, это ошибка в ПЛИС (т.е. моей схеме), по первому импульсу clk у меня считывается значение default с мультиплексора, а оно равно нулю.

Теперь схематически устройство в сборе:

Выводы ПЛИС на схеме не обозначены, так как они назначаются произвольно, так же не обозначены конденсаторы кварца и резистор ножки reset с подтяжкой к плюсу атмеги.

И, конечно же фото готового устройства (собрано на демо-платах, соединённых проводами).

Результат со встроенного в плату ПЛИС генератора на 50 МГц (как видно есть погрешность в 35438 Гц, можно подправить секундную задержку, хотя и в точности кварцевого генератора я не сильно уверен):

После делителя на 2 (есть проекте плис на отдельном счётчике-делителе):

Делитель на 4:

Знаю, что найдутся люди, которые скажут, зачем такие сложности, можно было просто на МК или на рассыпухе типа cd4017. Я повторюсь, это моя первая проба на ПЛИС. Это не совсем готовое устройство, оно не имеет входного буфера, щупа. Это скорее демонстрация работы ПЛИС в связке с микроконтроллером.

На этом всё, спасибо за внимание, пишите свои замечания в комментариях (желательно по делу).

Список радиоэлементов

Обозначение Тип Номинал Количество ПримечаниеМагазинМой блокнот
ПЛИС МикросхемаEPM240T100C51 Можно любую ПЛИС от 80 ячеекПоиск в магазине ОтронВ блокнот
МК AVR 8-бит
ATmega328P
1 Поиск в магазине ОтронВ блокнот
LCD-дисплейHD447801 Поиск в магазине ОтронВ блокнот
ИС I2C интерфейса
PCF8574
1 Поиск в магазине ОтронВ блокнот
Добавить все

Скачать список элементов (PDF)

Прикрепленные файлы:

Теги:

Опубликована: 0 0
Я собрал 0 1
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 5 Проголосовало: 1 чел.

Комментарии (10) | Я собрал (0) | Подписаться

0
Публикатор #
На форуме автоматически создана тема для обсуждения статьи.
Ответить
0
andro #
Интересно получилось, молодец!
Ответить
0
sboldenko #
Хмммм, я тут прикинул, при погрешности в 35 кГц параметр ppm вашего кварца составляет аж 700, что как-то уж слишком неправдоподобно. Поправьте меня если я ошибаюсь: вы генерируете секундный интервал микроконтроллером, за время которого инкрементируется счетчик в ПЛИС, а после вычисляете частоту по прошествии сгенерированного микроконтроллером секундного интервала?
Ответить
0

[Автор]
AndrejChoo #
Да, именно так.
А по поводу погрешности, я думаю, что всё-таки проблема в неточной секундной задержке плюс время на выполнение контроллером включения/выключения разрешающего счёт сигнала. Но это всё легко исправимо уменьшением значения задержки (delay_ms(1000)).
Ответить
0
sboldenko #
Не стоит делать секундный интервал на микроконтроллере, это плохой тон. Сделайте его на ПЛИС, так будет грамотнее, в виду высокой дискретности при тактовой частоте в 50 МГц, интервал секунды будет достаточно точным. На микроконтроллере просто сделайте запуск счетчиков ПЛИСа. С точки зрения принципиальной схемы у вас ничего не изменится, только в код программы ПЛИС придется добавить счетчик секунды и инициацию этого счетчика. Тогда считать будет точнее и не нужно будет подбирать значение задержки микроконтроллера.
Ответить
0
Vascom #
Почему было не запихнуть всё в ПЛИС?
Ответить
0
sboldenko #
Потому что автор не знает, как выводить данные с ПЛИС на дисплей
Если не ошибаюсь, под эту альтеру можно использовать стандартное ip-ядро для работы с lcd-дисплеями на HD44780.
Ответить
0

[Автор]
AndrejChoo #
Сильно сомневаюсь, что на 240 ячейках можно прикрутить 1602. Одна инициализация займёт команд 15, а это 15 байт. Вот семисегментники это более реалистичный вариант.
Если вы внимательно читали статью, то прочитали, что это пробный проект, можно сказать первая проба на ПЛИС. А так же проба совместного использования плис-мк.
В одной из статей я уже как-то отвечал на замечания, мол можно было проще на чем-нибудь. Так вот, проще на гвоздиках с верёвочками, а у меня вот так и работает стабильно.
Ответить
+1
sboldenko #
Я конечно извиняюсь, если Вас задел, но Вы не сомневайтесь, прикрутить можно. Зайдите на сайт марсохода, там на Вашу ПЛИС TFT панель прикрутили, и все работает, а Вы говорите 1602 нельзя.
Я внимательно читал статью и могу Вам сказать (я уже говорил выше но повторюсь), что представленный Вами способ вычисления частоты посредством связки МК и ПЛИС противоречит философии ПЛИС (и здравому смыслу тоже). Ваш МК отсчитывает время микросекундными долями, а ПЛИС наносекундными! Неудивительно, что ваша "эталонная" секунда, которую формирует МК совсем не секунда во временных рамках ПЛИС (представьте сколько раз успеет "тикнуть" ПЛИС за хотя бы милисекунду погрешности формируемого МК временного интервала), но при этом, при вычислении частоты, Вы опираетесь на заведомо неточный результат и абсолютно ничего не делаете, чтобы это как-то компенсировать, ведь чем более высокую частоту Вы будете измерять, тем больше будет ее погрешность. Именно поэтому я посоветовал Вам реализовать вычисление частоты в рамках одной только ПЛИС.
Так что не нужно вводить всех в заблуждение, говоря, что у Вас все работает стабильно. Это не правда.
Ответить
0

[Автор]
AndrejChoo #
В плане секуедной задержки я согласен. Можно было сделать дополнительный счётчик делитель, который по команде мк отсчитывал бы секундный интервал. Получилось бы намного точнее, можно попробовать. Вот с ЖК я не понимаю, как без дополнительной периферии можно засунуть его движок в 240 ячеек, если у меня 32 битный счётчик занимал больше 32 ячеек. Про эмуляцию ядра авр на этом кристалле я читал. Но чисто схематически, я не представляю как на такой маленькой cpld можно реализовать всё это вместе (nios, hd44780 и ещё сам частотомер с дополнительным делителем частоты для секундной задержки). Может быть я сильно ошибаюсь.
P.S. сейчас жду плату циклон 4 из Китая с кучей периферии на борту (в том числе и 1602), плпробую поэкспериментировать с ним, посмотрю, сколько ячеек займёт подобное устройство. Правда сравнивать cpld и fpga будет немного не корректно, так как первая очень критична к количеству триггеров в схеме. Может быть родится очередная статья, которую будем также горячо обсуждать
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется электрическое сопротивление?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

USB осциллограф DSO-2090
USB осциллограф DSO-2090
Программатор Pickit3 FM-модуль RDA5807M
вверх