Функциональный DDS rенератор на ПЛИС

    Недавно я увидел проект генератора сигналов на микроконтроллере AVR. Принцип генерации — DDS, на базе библиотеки Jesper максимальная частота — 65534 Гц (и до 8 МГц HS выход с меандром). И тут я подумал, что генератор — отличная задача, где ПЛИС сможет показать себя в лучшем виде. В качестве спортивного интереса я решил повторить проект на ПЛИС, при этом по срокам уложиться в два выходных дня, а параметры получить не строго определенные, а максимально возможные. Что из этого получилось, можно узнать под катом



    День нулевой


    До того, как наступят выходные, у меня было немного времени подумать над реализацией. Чтобы упростить себе задачу, решил сделать генератор не в виде отдельного устройства с кнопками и LCD экраном, а в виде устройства, которое подключается к ПК через USB. Для этого у меня есть плата USB2RS232. Плата драйверов не требует (CDC), поэтому, я думаю, что и под Linux будет работать (для кого-то это важно). Так же, не буду скрывать, что с приемом сообщений по RS232 я уже работал. Модули для работы с RS232 буду брать готовые c opencores.com.

    Для генерации синусоидального сигнала потребуется ЦАП. Тип ЦАП я выбрал, как и в исходном проекте — R2R на 8 бит. Он позволит работать на высоких частотах, порядка мегагерц. Убежден, что ПЛИС с этим должна справиться

    По поводу того, на чем написать программу для передачи данных через COM порт я задумался. С одной стороны, можно написать на Delphi7, опыт написания такой программы уже есть, к тому же размер исполняемого файла будет не большим. Еще попробовал набросать что-то для работы с Serial в виде java скрипта в html страничке, но более менее заработало только через Chrome serial API, но для этого надо устанавливать плагин… в общем тоже отпадает. В качестве новшества для себя попробовал PyQt5, но при распространении такого проекта, нужно тащить кучу библиотек. Попробовав собрать PyQt проект в exe файл, получилось больше 10 мб. То есть, будет ничем не лучше приложения, написанного на c++\Qt5. Стоит еще учесть, что опыта разработки на python у меня нет, а вот на Qt5 — есть. Поэтому выбор пал на Qt5. С пятой версии там появился модуль для работы с serial и я с ним уже работал. А еще приложение на Qt5 может быть перенесено на Linux и Mac (для кого-то это важно), а с 5.2 версии, приложения на QWidgets может быть перенесено даже на смартфон!

    Что еще нужно? Естественно плата с ПЛИС. У меня их две (Cyclone iv EP4CE10E22C8N на 10 тыс. ячеек, и Cyclone ii EP2C5 на 5 тыс. ячеек). Я выберу ту, что слева, исключительно по причине более удобного разъема. В плане объема проект не предполагает быть большим, поэтому уместится в любую из двух. По скорости работы они не отличаются. Обе платы имеют «на борту» генераторы 50 МГц, а внутри ПЛИС есть PLL, с помощью которого я смогу увеличить частоту до запланированных 200 МГц.



    День первый


    В связи с тем, что модуль DDS я уже делал в своем синтезаторном проекте, то я сразу взялся за паяльник и начал паять ЦАП на резисторах. Плату взял макетную. Монтаж делал с применением накрутки. Единственное изменение, которое коснулось технологии — я отказался от кислоты Ф38Н для лужения стоек в пользу индикаторного флюс-геля ТТ. Суть технологии проста: в печатную плату впаиваю стойки, на них со стороны печатного монтажа припаиваю резисторы. Недостающие соединения выполняю накруткой. Еще, стойки удобны тем, что я их могу вставить прямо в плату ПЛИС.

    К сожалению, дома в наличии не оказалось резисторов 1 и 2 килоома. Ехать в магазин было некогда. Пришлось поступиться одним из своих правил, и выпаять резисторы из старой не нужной платы. Там применялись резисторы 15К и 30К. Получился вот такой франкенштейн:


    Дальше я запустил Quartus, создал проект
    После создания проекта нужно задать целевое устройство: Меню Assigments -> Device



    Далее там же нажимаю кнопочку «Device and Pin options» потому что некоторые пины настроены так, что работать не будут. Настраиваю все, как «Use as regular I/O»


    В проекте я «нахадркодил» неуправляемый главный модуль DDS на фиксированную частоту.

    Модуль генератора на 1000 Гц
    module signal_generator(clk50M, signal_out);
    
    input wire clk50M;
    output wire [7:0] signal_out;
    
    wire clk200M;
    osc osc_200M
    
    reg [31:0] accumulator;
    
    assign signal_out = accumulator[31:31-7];
    
    //пробуем генерировать 1000 Гц
    //50 000 000 Hz - тактовая частота внешнего генератора
    //2^32 = 4 294 967 296 - разрядность DDS - 32 бита
    //делим 1000Hz / 50 000 000 Hz / 2 * 4294967296   => 42949,67296
    always @(posedge clk50M) begin
    	accumulator <= accumulator + 32'd42949;
    end
    
    endmodule

    После этого нажал «Start Compilation», чтобы среда разработки задалась вопросом, какие у нас линии ввода вывода есть в главном модуле проекта и к каким физическим PIN's они подключены. Подключить можно практически к любому. После компиляции назначаем появившиеся линии к реальным PIN микросхемы ПЛИС:

    Пункт меню Assigments -> Pin Planner
    На линии HS_OUT, key0 и key1 прошу пока не обращать внимание, они появляются в проекте потом, но скрин в самом начале я сделать не успел.



    В принципе, достаточно «прописать» только PIN_nn в столбце Location, а остальные параметры (I/O standart, Current Strench и Slew Rate) можно оставить по умолчанию, либо выбрать такие же, что предлагаются по умолчанию (default), чтобы не было warning'ов.

    Как узнать какому PIN соответствует номер разъема на плате?
    Номера контактов разъема подписаны на плате


    А пины ПЛИС, к которым подключены контакты разъема, описаны в документации, которая идет в комплекте с платой ПЛИС.




    После того, как пины назначены, компилирую проект еще раз и прошиваю с помощью USB программатора. Если у вас не установлены драйверы для программатора USB Byte blaster, то укажите Windows, что они находятся в папке, куда у вас установлен Quartus. Дальше она сама найдет.

    Подключать программатор нужно к разъему JTAG. А пункт меню для программирования «Tools -> Programmer» (либо нажать значек на панели инструментов). Кнопка «Start», радостное «Success» и прошивка уже внутри ПЛИС и уже работает. Только не выключайте ПЛИС, а то она все забудет.

    Tools -> Programmer

    ЦАП подключен к разъему платы ПЛИС. К выходу ЦАП подключаю осциллограф С1-112А. В результате должна получиться «пила» потому что на выход 8 бит выводится старшая часть слова DDS аккумулятора фазы. А оно всегда увеличивается, пока не переполнится.

    Каких-то 1.5 часа и для частоты в 1000 Гц я вижу следующую осциллограмму:



    Хочу заметить, что «пила» по середине имеет небольшой перелом. Он связан с тем, что резисторы имеют разброс значений.

    Еще один важный момент, который нужно было выяснить — это максимально возможная частота, с которой будет работать DDS генератор. При правильно настроенных параметрах TimeQuest, после компиляции в «Compilation Report» можно увидеть, что скорость работы схемы выше 200 МГц с запасом. А это значит, что частоту генератора 50 МГц я буду умножать с помощью PLL на 4. Увеличивать значение аккумулятора фазы DDS буду с частотой 200 МГц. Итоговый диапазон частот, который можно получить в наших условиях 0 — 100 МГц. Точность установки частоты:

     200 000 000 Гц (clk) / 2^32 (DDS) = 0,047 Гц

    То есть, это лучше, чем ~0.05 Гц. Точность в доли герца для генератора с таким диапазоном рабочих частот (0...100 МГц) считаю достаточной. Если кому-то потребуется повысить точность, то для этого можно увеличить разрядность DDS (при этом не забыть проверить TimeQuest Timing Analyzer, что скорость работы логической схемы укладывалась в CLK=200 МГц, ведь это сумматор), либо просто снизить тактовую частоту, если такой широкий диапазон частот не требуется.

    TimeQuest Timing Analyzer

    После того, как я увидел на экране «пилу», семейные дела заставили меня ехать на дачу (выходной же). Там я косил, варил, жарил шашлык и не подозревал о том сюрпризе, что ждал меня вечером. Уже ближе к ночи, перед сном, я решил посмотреть форму сигнала для других частот.

    Для частоты 100 КГц
    Для частоты 250 КГц
    Для частоты 500 КГц
    Для частоты 1 МГц

    Не буду скрывать, что форма сигналов меня расстроила, особенно на 1МГц (жалкий, никчемный мегагерц!). Я планировал получить частоты несколько других порядков. Почитав про R2R ЦАП стала ясна причина проблемы — паразитные емкости. Поэтому в планах на следующий день было решено сделать ЦАП на резисторах 100 и 200 Ом, которые у меня есть в наличии, а этот ЦАП оставить для будущих разработок, не требующих работы на таких высоких частотах, ведь в гладкости пилы тоже есть свой плюс.

    День второй


    В связи с тем, что было интересно, как будет работать ЦАП на резисторах 100 и 200 Ом, я сразу взялся за паяльник. На этот раз ЦАП получился более аккуратным, а времени на его монтаж ушло меньше.





    Ставим ЦАП на плату ПЛИС и подключаем к осциллографу



    Проверяем 1 МГц — ВО! Совсем другое дело!



    Пила 10 МГц
    Пила 25 МГц

    Форма пилы на 10 МГц еще похожа на правильную. Но на 25 МГц она уже совсем «не красивая». Однако, у С1-112а полоса пропускания — 10 МГц, так что в данном случае причина может быть уже в осциллографе.

    В принципе, на этом вопрос с ЦАП можно считать закрытым. Теперь снимем осциллограммы высокоскоростного выхода. Для этого, выведем старший бит на отдельный PIN ПЛИС. Данные для этой линии будем брать со старшего бита аккумулятора DDS.

    assign hs_out  =  accumulator[31];

    Меандр 1 МГц
    Меандр 5 МГц
    Меандр 25 МГц
    Меандр 50 МГц уже практически не виден

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

    Синус делается по таблице. Размер таблицы 256 значений по 8 бит. Можно было бы взять и больше, но у меня уже был готовый mif файл. С помощью мастера создаем элемент ROM с данными таблицы синуса из mif-файла.

    Создание ROM - Tools -> Mega Wizard Plugin manager

    Выбираем 1 портовую ROM и задаем название модулю



    Соглашаемся



    Тут тоже соглашаемся



    С помощью browse находим наш mif файл с таблицей синуса



    Тут тоже ничего не меняем



    Снимаем галочку с модуля sine_rom_bb.v — он не нужен. Дальше finish. Квартус спросит добавить модуль в проект — соглашаемся. После этого, модуль можно использовать так же, как любой другой модуль в Verilog.


    Старшие 8 бит слова аккумулятора DDS будут использоваться в качестве адреса ROM, а выход данных — значение синуса.

    Код
    //sine rom
    wire [7:0] sine_out;
    sine_rom sine1(.clock(clk200M), .address(accumulator[31:31-7]), .q(sine_out));

    Осциллограмма синуса на разных частотах выглядит… одинаково.



    При желании, можно рассмотреть проблемы ЦАП, связанные с разбросом резисторов:



    Чтож, на этом выходные кончились. А ведь еще не написано ПО для управления с ПК. Вынужден констатировать факт, что в запланированные сроки я не уложился.

    День третий


    Времени совсем мало, поэтому программу пишем на скорую руку (в лучших традициях). Местами, чтобы сократить количество букв и удобство ввода информации с клавиатуры, применяется фильтр событий по имени виджета. Прошу понять и простить.

    Интерфейс



    Исходный код на GitHub. Там же есть уже собранное под windows приложение.

    Код прост, как 5 копеек. В файл проекта .pro кроме всего прочего нужно добавить модуль serialport:

    QT       += core gui serialport

    Открытие COM порта
            QSerialPort serial;
            ...
            serial.setPortName(ui->lbSerialPortInfo->currentText());
            serial.setBaudRate(QSerialPort::Baud115200);
            serial.setDataBits(QSerialPort::Data8);
            serial.setParity(QSerialPort::NoParity);
            serial.setStopBits(QSerialPort::OneStop);
            serial.setFlowControl(QSerialPort::NoFlowControl);
            serial.open(QIODevice::ReadWrite);
    Формирование и отправка сообщения
        QByteArray source;
        QDataStream stream(&source, QIODevice::ReadWrite);
        stream << (qint8)(01); // set freq msg
        stream << waveform;
        stream << adder32;
        serial.write(source);

    День четвертый


    В спешном порядке доделываем прием данных по UART. Для приема сообщений по UART нужно поставить пару модулей. Один Baud генератор, второй — приемник. Для того, чтобы приемник работал на 115200, нужно произвести некоторые рассчеты, исходя, что основная тактовая частота у нас — 200 МГц.

    Модуль baud_gen
    parameter global_clock_freq = 200000000;
    parameter baud_rate = 115200;
    //бодген - модуль для генерации клока UART
    // first register:
    // 		baud_freq = 16*baud_rate / gcd(global_clock_freq, 16*baud_rate)
    //Greatest Common Divisor - наибольший общий делитель. http://www.alcula.com/calculators/math/gcd/
    // second register:
    // 		baud_limit = (global_clock_freq / gcd(global_clock_freq, 16*baud_rate)) - baud_freq 
    
    
    //можно добавить значения для других скоростей
    parameter GCD = (baud_rate==115200) ? 12800 : 0;
    
    parameter baud_freq  = 16*baud_rate / GCD;
    parameter baud_limit = (global_clock_freq / GCD) - baud_freq;
    
    wire uart_clk;
    baud_gen BG(.clock(clk), .reset(rst), .ce_16(uart_clk), .baud_freq(baud_freq), .baud_limit(baud_limit));

    Ставлю модуль приема сообщений, на него подается uart_clk и сигнал с физического входа ПЛИС.

    Модуль uart_rx
    //RCV
    wire [7:0] uart_command;
    wire uart_data_ready;
    
    uart_rx URX(.clock(clk), 
    		      .reset(rst),
    				.ce_16(uart_clk), 
    				.ser_in(rx), 
    				.rx_data(uart_command), 
    				.new_rx_data(uart_data_ready) );

    Дальше все это помещаю в отдельный модуль, который выдает только номер формы волны и значение приращения к регирстру аккумулятора фазы DDS.

    Модуль ctrl.v
    module ctrl(clk, rst, rx, wf, adder);
    input wire clk, rst, rx;
    output wire [7:0] wf; //wave form
    output wire [31:0] adder;// adder value
    
    reg [7:0] wf_reg;
    initial wf_reg <= 8'd0;
    
    reg [31:0] adder_reg;
    initial adder_reg <= 32'd1073741;

    Когда модуль uart_rx принял байт информации, он на один такт ставит в единицу линию uart_data_ready. В это время на линии uart_command находится принятый байт. Для приема сообщения пишу стейтмашину.

    Стейт-машина
    //rcv state machine
    parameter SM_READY     = 4'd0;
    parameter SM_FRQ_WF    = 4'd1;
    parameter SM_FRQ_DDS1  = 4'd2;
    parameter SM_FRQ_DDS2  = 4'd3;
    parameter SM_FRQ_DDS3  = 4'd4;
    parameter SM_FRQ_DDS4  = 4'd5;
    
    //messages
    parameter CMD_SETFREQ = 8'd1;
    
    reg [3:0] rcv_state;
    initial rcv_state <= SM_READY;
    
    always @ (posedge clk) begin 
    	
    	if (uart_data_ready==1) begin
    		if (rcv_state==SM_READY) begin
    			rcv_state = (uart_command==CMD_SETFREQ) ? SM_FRQ_WF : rcv_state;
    		end else if (rcv_state==SM_FRQ_WF) begin
    			wf_reg <= uart_command;
    			rcv_state <= rcv_state + 1'b1;
    		end else if (rcv_state==SM_FRQ_DDS1) begin
    			adder_reg <= (adder_reg << 8) + uart_command;
    			rcv_state <= rcv_state + 1'b1;
    		end else if (rcv_state==SM_FRQ_DDS2) begin
    			adder_reg <= (adder_reg << 8) + uart_command;
    			rcv_state <= rcv_state + 1'b1;
    		end else if (rcv_state==SM_FRQ_DDS3) begin
    			adder_reg <= (adder_reg << 8) + uart_command;
    			rcv_state <= rcv_state + 1'b1;
    		end else if (rcv_state==SM_FRQ_DDS4) begin
    			adder_reg <= (adder_reg << 8) + uart_command;
    			rcv_state <= SM_READY;
    		end else begin
    			rcv_state <= SM_READY;
    		end
    	end //ucom_ready
    	
    end

    Выводим данные на выходы модуля

    Вывод
    assign adder = adder_reg;
    assign wf = wf_reg;


    Добавляем модуль приема в главный модуль.

    Добавляем модуль ctrl в главный модуль
    //rs232 rcvr
    wire [31:0] adder_value;
    wire [7:0] waveform;
    ctrl ctrl_0(.clk(clk200M), .rst(rst), .rx(RS232in), .wf(waveform), .adder(adder_value));

    Значение приращения прибавляем к аккумулятору фазы

    Увеличиваем значение аккумулятора с каждым тактом
    always @(posedge clk200M) begin
    	accumulator <= accumulator + adder_value;
    end

    Из старшей части значения аккумулятора фазы получаем остальные волноформы. А в зависимости от выбранной формы — подключаем ее на выход.

    Волноформы
    // wave_forms
    parameter SINE      = 8'd0;
    parameter SAW       = 8'd1;
    parameter RAMP      = 8'd2;
    parameter TRIA      = 8'd3;
    parameter SQUARE    = 8'd4;
    parameter SAWTRI    = 8'd5;
    parameter NOISE     = 8'd6;
    
    wire [7:0] saw_out = accumulator[31:31-7];
    wire [7:0] noise_out = 8'd127; //!
    wire [7:0] ramp_out = -saw_out;	
    wire [7:0] square_out = (saw_out > 127) ? 8'b11111111 : 1'b00000000;
    wire [7:0] saw_tri_out = (saw_out > 7'd127) ?  -saw_out : 8'd127 + saw_out;	
    wire [7:0] tri_out = (saw_out>8'd191) ? 7'd127 + ((saw_out << 1) - 9'd511) : 
                         (saw_out>8'd063) ? 8'd255 - ((saw_out << 1) - 7'd127) : 7'd127 + (saw_out << 1);
    //sine rom
    wire [7:0] sine_out;
    sine_rom sine1(.clock(clk200M), .address(saw_out), .q(sine_out));
    
    wire [7:0] signal = (waveform ==     SINE) ? sine_out :
                        (waveform ==      SAW) ? saw_out : 
                        (waveform ==     RAMP) ? ramp_out : 
                        (waveform ==     TRIA) ? tri_out :
                        (waveform ==   SQUARE) ? square_out :
                        (waveform ==   SAWTRI) ? saw_tri_out :
                        (waveform ==    NOISE) ? noise_out : 8'd127; //TODO


    Я был почти не удивлен, что оно сразу заработало. Единственная ошибка, которую я нашел — была в рассчетах: я делил искомую частоту на CLK, потом еще на два, потом умножал на разрядность аккумулятора. Но этого делать не нужно, потому что у нас получается 1 период при изменении значении аккумулятора от 0 до МАХ. Делить дополнительно на 2 нужно только если в качестве выхода меандра брать старший бит аккумулятора частоты (в этом случае частота получается ниже в 2 раза). Но получение меандра я переделал.

    День четвертый


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

    Приступим к проверке. Сначала осциллографом.


    На радиочастотах от 28 и до 100 МГц я решил послушать генератор с помощью SDR приемника, поставив антенну рядом с платой.







    Выводы


    Как это часто бывает в ИТ — с оценкой сроков произошла ошибка в 2 — 2.5 раза. Цель достигнута: на коленке собран генератор до 100 МГц. Однако, чтобы эту поделку можно было назвать полноценным генератором, потребуется поработать еще. Поэтому есть большие перспективы для развития. Из за срыва сроков, я не добавил то, что в принципе мог: 1) генератор шума; 2) генератор волны, которую пользователь рисует сам; 3) генератор цифровой последовательности. Еще в генераторе нет регулировки амплитуды и смещения.

    Использовано 227 ячеек из 10000. Список того, что еще можно добавить в проект:
    • Расширить разрядность ЦАП
    • Увеличить количество выходов с генерируемыми сигналами
    • Применить микросхему ЦАП в большей разрядностью
    • Реализовать управление амплитудой и смещением
    • Добавить элементы управления, ЖК экран, для портативности
    • Добавить генератор шума и других простых форм волн
    • Добавить возможность загрузки произвольных форм волн

    Я думаю, что любой желающий сможет это сделать самостоятельно, расширив проект нужным функционалом. Добавить проще, чем сделать с нуля. Формат команды управления очень простой, поэтому генератором можно управлять с микроконтроллера.

    Исходные коды:https://github.com/UA3MQJ/fpga-signal-generator

    Ссылки с аналогами


    Далеко не полный список
    Функциональный DDS генератор. Создан базе AVR. Частоты 0… 65534 Гц.
    Обзор DDS-генератора GK101. Создан с применением ПЛИС Altera MAX240. Частоты до 10 МГц.
    Многофункциональный генератор на PIC16F870. Частотный диапазон: 11 Гц — 60 кГц.
    Аналоговый функциональный генератор. Частотный диапазон колеблется от 20 Гц до 300 кГц
    USB функциональный генератор на AD9833. На базе микросхемы DDS.
    Мини DDS — простейший передатчик на диапазон 137 кГц и не только. Частота 136 КГц.
    DDS — функциональный генератор с «джамперным» управлением на ПЛМ.
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 35
    • +1
      Конечно со специализированными DDS от ADI ни в какое сравнение не идёт, но как пища для ума интересно. Если доработать, то может полезная вещь получиться.
      • 0
        Недавно запустил готовый DDS-модуль на AD9850 с Arduino. Почему то на частоте ниже 10 Гц сигнал пропадает.

        Вы не проверяли как ваш модуль ведет себя крайне низких частотах? Сигнал имеет синусоидальную форму?
        • 0
          В DDS передается значение частоты или приращения? Вообще я микросхемы DDS воспринимаю только как генераторы в области радиочастот, для построения приемников, трансиверов. Запустить его на частоте в 10 Гц мне бы и в голову не пришло.
          Да, на низких частотах сигнал имеет синусоидальную форму. Генератор будет работать, если слово приращение больше ноля.
          • +2
            По идее, должен работать на низких частотах. Вплоть до значения = частота генератора / максимальное значение аккумулятора.
            Возможно в вашем модуле развязка по постоянке на выходе через конденсатор.
            Ну или какие-то особенности программной настройки. (Конкретно с этой микросхемой не работал, так что только предположение)
            • 0
              Много работал с DDS от ADI, правда более современных. Если судить по его архитектуре — не вижу никаких принципиальных проблем с работой на инфранизких частотах. Правда ниже 10 Гц не пытался опускаться. Возможно дело в том что вы просто пытались тактировать его слишком низкой частотой или не хватало разрешения? Кстати конкретно у этого прибора нижняя частота тактирования в даташите указана в 1 МГц. Значит какие то частотные ограничения снизу всё таки могут присутствовать.
          • +4
            Делал подобное в коммерческом проекте. На Cyclone I добился частоты инкремента аккумулятора в 240 МГц. Конкретно в данном приборе было выгоднее использовать ПЛИС чем готовый DDS.

            Но надо вот что добавить. Несмотря на то, что теоретически можно генерить до половины частоты квантования. При приближении к этому пределу DDS алгоритм дает очень большой джиттер. Форма сохраняется, но фаза дрожит. У меня получилось приемлемая стабильность фазы до 25-30 МГц. Но заказчику захотелось сигнал в 60 МГц — пришлось переделывать и ставить готовый DDS от Analog Device
            • 0
              Да, ADI давно на этой теме сидит и у них с обработкой нарастания фазы всё очень хорошо обстоит. Получается генерить сигнал с малыми фазовыми шумами даже на частотах всего в несколько раз ниже частоты тактирования, особенно если не нужен очень широкий диапазон и можно применить узкополосные выходные фильтры.
              • 0
                В моем случае программа посчитала, что до 247 МГц. Так что можно и 240.
                Про джиттер — согласен.
              • +2
                Для работы на высоких частотах делитель напряжения в ЦАП можно попробовать скомпенсировать. Для этого параллельно резисторам ставим конденсаторы, заведомо большие, чем паразитные ёмкости резисторов. Величины емкостей должны быть такими, чтобы комплексные сопротивления имели такие же соотношения, что и исходные чисто активные. Для простоты можно принять, что постоянные времени комплексных сопротивлений должны быть одинаковыми R1C1 = R2C2.
                • 0
                  Не могли бы вы, пожалуйста, продемонстрировать ваш тезис небольшим рисунком с конкретными значениями элементов?
                  • +4
                    В R-2R ЦАП добавить
                    — параллельно каждому резистору R конденсатор емкостью 2С
                    — параллельно каждому резистору 2R конденсатор емкостью С
                    С тем расчетом, что добавленная емкость (пФ-нФ) много больше паразитной (доли пФ).
                    По такому принципу осуществляется частотная компенсация резистивных делителей напряжения, например, в щупах осциллографов.
                    Сработает ли для ЦАП полной уверенности нет.
                    • +1
                      А, я понял! R-2R лестница превращается в C-2C лестницу на высоких частотах.
                  • +1
                    Но лучше просто поставить на выходе ЦАП. Если уж нашли деньги на ПЛИС то на нём не разоритесь.
                    • 0
                      Приобрел КР572ПА1 и ПА2. Как будет время, обязательно попробую.
                      • +1
                        Вау. Интересно будет почитать как можно скрестить советскую разработку времён моей юности с современным ПЛИС. Надеюсь вы поделитесь и этим своим опытом.
                  • –1
                    Вместо резисторов поставьте ЦАП.

                    Если хорошо оформить код в виде IP-ядра, возможно, даже удастся продать.
                    • +1
                      mua-ha-ha. За тыщу рублей штук пять продать может получится. Если рекламную кампанию провести. Все это пишется за день работы (оптимист скажет — за час) и зачастую удобнее сделать самому под свою конкретную задачу, чем интегрировать стороннее ядро.
                      • 0
                        Вам, самоделкиным, виднее…
                        А нам однажды только за проект такого устройства 4000 евро заплатили.
                        • +1
                          Ну кто-то и лекции по 1 млн за час читает…
                          И проект-то, наверное УСТРОЙСТВА был, а не ip-ядра, которое лишь малая часть его?
                    • 0
                      Есть разница между приемниками на фото? Верхний вроде более старый.
                      • 0
                        Верно. Разница есть. Белый на базе Elonics E4000 (снят с производства) и принимает 52 — 2200 MHz. Черный на R820T, принимает 24 — 1766 MHz. У первого часто был не впаян защитный диод, и они выходили из строя.
                        • 0
                          60$ за первый (верхний) просят (стоит ли он того?), а за черный 8, все мечтаю купить… Есть что слушать выше 1,4 ГГц? Софт один и тот же на оба подходит?
                          • +1
                            Не стоит он того. Цена из за того, что их сняли с продаж. В него диод еще надо впаивать. Мне черный больше нравится, он принимает с 24 МГц, то есть попадает и СИБИ и любительский 28-29 МГц диапазон. Выше 1 ГГц мне однажды удалось принять местный любительский маяк на 1.2 на штатную антенну. Сразу после появления R820T не поддерживался, потом поддерживался не полностью, сейчас все нормально. Причем недавно попробовал, белый свисток — почему-то не «завелся». А так, ПО использую — SDR Sharp, подходил к обоим.
                            Не надо мечтать, надо купить, попробовать и забросить на полку :) Стоит он не таких больших денег, чтоб потом сильно о них пожалеть. Параметры по качеству приема у него так себе, но возможности такие, что лет 10 назад о таких и мечтать не приходилось. Чтобы хоть что-то принять на УКВ, надо было портативку найти, а китайские еще были не сильно в ходу, ебей не развит. А тут за копейки виден спектр всего диапазона. Я не пожалел, но есть и недовольные.
                            • 0
                              Заказал уже (только что), куда уже откладывать, а то опять забуду до следующей статьи с упоминанием… (Там есть еще DVB-T2, они вроде не из той оперы.)
                              Че с входной частью? Для полноценного приема нужна полноценная антенна и согласование. Если сами проходили, можете и мне в этом направлении что-нибудь посоветовать почитать?
                              • +1
                                Скорее всего не из той. Тут нужно, чтоб был RTL2832 на борту.
                                У черного входная часть защищена. Но чутья у него бывает недостаточно. Поэтому я брал обычный антенный усилитель SWA2000 с блоком питания, который стоит на современных антеннах «сушилках». Полноценную антенну не подключал, а просто пару отрезков провода. Этого хватило, чтоб начали отлично приниматься все местные ЧМ станции (пришлось даже убавлять усиление, иначе перегруз, а динамики мало — всего 8 бит), авиация и прочее. Ну и потребуется для этого переходник IEC DVB-T TV PAL female jack to MCX male plug RF adapter connector чтоб подключать штекер от обычной антенны.
                      • +1
                        Да, использовать в ЦАП «рассыпные» резисторы — идея не очень хорошая.
                        Резисторные сборки (они же матрицы или массивы, «arrays») — другое дело, у них как минимум внутри одного корпуса разброс минимален.
                        Впрочем, проблема разброса между экземплярами в партии остаётся. Идеально, если получится найти матрицу, вмещающую все разряды.

                        Пункт 2 — лучше использовать один номинал — не «R — 2R», а «R — R+R». И разумеется, из одной партии (из одной упаковки).

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

                        А вот про что будет нелишне напомнить — так это про старую фишку сортировки по точности, из-за которой элементы с «грубыми» классами точности не просто имеют разброс относительно номинала — они никогда не попадают ближе к номиналу, чем следующий вышележащий класс точности. (Вроде, на эту тему статья на хабре была, но может, это глюк моей памяти)
                        Просто потому, что, скажем, 10% резисторы берутся из «отбраковки» при отборе 5%, 5% — от 1% и т.п.
                        И от этого эффекта «R — R+R» как раз помогает.
                        (для прецизионных классов, конечно, всё несколько по-другому — техпроцессы заточены под уменьшение разброса, вплоть до электроискровой или лазерной подгонки)

                        Для изготовления единичного экземпляра есть еще один почти беспроигрышный метод — ручной подбор номиналов из партии. «Почти» — потому что придётся затариться довольно приличным объёмом и кропотливо перемерять.
                        Ну и для уравнивания по ТКС это почти единственный вариант, если не применять «специальные» прецизионные резисторы с низким ТКС. А то ж если в партии 1% разброс по номиналу — это еще не значит, что по ТКС разброс тоже уложится в 1%.
                        • 0
                          Спасибо! Целая статья получилась.
                        • 0
                          Вспомнил про ваш проект и понял, что раз уж вы программируете ПЛИС, то отличным вариантом было бы запрограммировать дельта-сигма ЦАП c аналоговым фильтром на выходе. Представляете, вы программно генерируете с ножки нули и единицы, прогоняете их через RC-фильтр (хотя бы однополюсный), а на выходе красивый линейный аналоговый сигнал! Это будет более точный сигнал при меньшем количестве компонент.
                          • 0
                            Дельта-сигма использую, но для звуковых частот
                          • 0
                            Позвольте спросить, где берут готовый mif-файл?
                            Первое что приходит на ум, это генерировать в MatLAB, но ставить такой огромный пакет, только для этого как-то не хочется.
                            Гугление простой утилиты или скрипта генерации mif результата не принесло (допускаю, что гугулины у меня не из того места растут).
                            Поделитесь опытом.
                            • +1
                              Я сгенерировал mif файл самостоятельно. Его структура очень проста.
                              Содержимое mif файла
                              — Quartus II generated Memory Initialization File (.mif)
                              — discretization: any Hz, 8 bit, usigned

                              WIDTH=8;
                              DEPTH=256;
                              ADDRESS_RADIX=UNS;
                              DATA_RADIX=UNS;
                              CONTENT BEGIN
                              0: 127;
                              1: 130;
                              2: 133;
                              3: 136;

                              253: 121;
                              254: 124;
                              255: 127;
                              END;
                              Мне стыдно признаться, но я его генерировал, написав простой проект на Delphi 7, который писал данные в файл.

                              Но можно поступить еще проще! И я так делаю, когда нужно написать Verilog модули, в которых есть большой case или присвоение. Просто составляю в Excell таблицу по нужной формуле, а справа от нее, с помощью формулы СЦЕПИТЬ составляю конструкции языка Verilog. Эту конструкцию я тупо копирую и вставляю в .v файл.

                              Вот пример модуля преобразования номера ноты MIDI в константу для генератора частоты. Модуль из себя представляет описание массива, в initial описываются значения всех элементов. Фактичеси, синтезатор Quartus из такого модуля сделает элемент памяти, а для его инициализации сам создаст mif файл.



                              • 0
                                Вот нашел утилиту для генерации LUT, может кому пригодиться:
                                HDL Sine LUT Generator
                                Недостаток: ширина выходных данных зависит от разрядности фазы.
                                А пока, осваиваю Excel LibreOffice Calc
                            • 0
                              Можно вас попросить добавить в проект генератор прямоугольных импульсов со скважностью 25%. очень надо, для изучения резонансных явлений, а времени на освоение плис пока не найду. П.с. статья про генератор мечты)
                              • 0
                                Можно. Но не обещаю, что быстро.
                                • 0
                                  Тогда прошу. Как найдете свободный вечерок, осчастливьте меня!)

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