Лабораторный генератор своими руками.
(Запускаем модуль DDS-AD9850)

Logo

Как-то, в порыве радиолюбительского азарта, мной был куплен модуль генератора для разного рода Ардуин на синтезаторе AD9850. Долго он лежал без дела, а тут мне понадобился генератор синуса, да еще и с возможностью изменения частоты. Делать нечего надо осваивать купленный ранее девайс.


Понятное дело, этот модуль уже изучен вдоль и поперек, и все же я тоже хочу добавить свои пять копеек в общую копилку радиолюбительских знаний.

Посмотрев документацию, находящуюся в Интернете по этому модулю я понял, что держу в руках серьезный агрегат который, не напрягаясь, может выдавать частоту от 0 до 40 МГц, да еще и с шагом 0,0291Гц. Максимальная рабочая частота синтезатора 125 МГц, а питание 3.3V либо 5V. Правда, производители делают сноску о том, что при напряжении 3.3V максимальная частота 110 МГц. Но это тоже не слабо. На выходе синус и меандр, да еще и по два выхода для разных фаз. Круть, одним словом. Даташит вроде понятный, хоть и на английском, значит надо приступать к изготовлению генератора.

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

Признаюсь, что при заказе на АЛИ я не особо обращал внимание на характеристики, а зря. Я был уверен, что покупаю модуль с кварцем на 125 МГц. Каково же было мое разочарование, когда я, взяв увеличительное стекло, рассмотрел тактовую частоту. Она оказалась всего 30 МГц. Ну, да и фиг с ней. Для поделки мне подойдет генератор до одного мегагерца, а значит, имеющийся частоты, хватит с лихвой.

Рассмотрев схемы подключения модуля, для себя решил, буду подключать его параллельно. Потому как SPI у меня задействован для работы с индикатором. К тому же инициализация генератора с последовательным подключением слегка мутна, а трудности с запуском устройства мне не нужны. Конечно, есть и обратная сторона параллельного подключения - большое количество используемых выводов. Но и генератор в моем случае играет ведущую роль, значит, все ресурсы я могу отдать именно ему.

Из-за того, что мне необходим простой генератор для прототипа другого устройства, я не стал наворачивать ни схему, ни программу разного рода примочками в виде запоминания рабочих частот, «быстрыми» кнопками или плавающей частотой. Все как в армии: параллельно, перпендикулярно и «покрашено в серый цвет».

Схема подключения совсем не сложная. О энкодере можно почитать здесь и здесь. О том, как работать с дисплеем Nokia5110, читаем здесь и здесь. Для генератора мне пришлось немного расширить библиотеку шрифта. Она, так же как и весь проект, находится в приложенных файлах.

Теперь немного о самом DDS-AD9850. Для параллельного подключения к Mege_8 понадобится:
PORTD — (D0...D7) байт передаваемых данных;
PB6 — (FU-UD) сигнал для обновления введенных данных;
PB7 — (W-CLK) сигнал для приема (тактирования) передаваемого байта;
PB0 — (RESET) сигнал сброса модуля при инициализации, да и если вдруг припечет;
ZOUT2 — выход синусоидального сигнала;
QOUT2 — выход меандра;
Vcc и GND — соответственно питание +5V.

Для работы с устройством я сделал небольшую библиотеку «Lib_DDS_AD8950.c» в которой помимо конфигурационных псевдонимов есть две основные подпрограммы: инициализация генератора «DDS_AD8950_init ()» и пересчет и пересылка данных в генератор «load_generator(unsigned long int frequency)».

Инициализация генератора:

void DDS_AD8950_init (void){
    LOAD_PORT = 0x00;                                           //Обнуляем порт 
    LOAD_DDR = 0xff;                                            //Устанавливаем порт как выходной 
    STROB_PORT &= (~(1<<FU_UD))|(~(1<<W_CLK))|(~(1<<RESET)); 	//Обнуляем биты управления
    STROB_DDR |= (1<<FU_UD)|(1<<W_CLK)|(1<<RESET);              //выводы как выходные 
    reset();                                                    //сбрасываем генератор
}

В этой подпрограмме нет ничего интересного. Просто инициализируем необходимые выводы и производим сброс генератора. Как говорит капитан Очевидность. Эту подпрограмму необходимо помещать в раздел «настройки», до основного цикла.

Пересчет и пересылка данных в генератор:

void load_generator(unsigned long int frequency){ 
    unsigned char c_temp = CLEAN;           //счетчик отправленных данных 
    //const float konst = 34.359738368;     //коэффициент для расчета значений 125 МГц
    //const float konst = 39.045157236;     //коэффициент для расчета значений 110 МГц
    const float konst = 143.16557653;       //коэффициент для расчета значений 30 МГц
    
    //пересчет данных 
    frequency = (unsigned long int) frequency * konst;    
     
    //передача W0
    LOAD_PORT = 0x00;			//загрузка W0 в порт для передачи
    w_clk();				//тактирование приема/передачи WO        
    //передача W1...W4
    for (c_temp = STEP_LOAD; c_temp > ZERO; c_temp--){  
        LOAD_PORT = (unsigned char) (frequency >> (8*(c_temp - ONE_STEP))); 	//выборка передаваемого байта
        w_clk();								//тактирование передачи 
    }; 
    fu_ud();									//обновление данных генератора     
}

Интерес в этой подпрограмме представляет только константа на которую необходимо умножить данные принимаемой часты - frequency. На основании даташита она находится по формуле:

Fout = (Din * Fgen)/232

Где:
      Fout - выходная частота генератора (frequency) (Гц);
      Din - число принимаемое счетным регистром генератора;
      Fgen - рабочая частота модуля;
      232 - полная емкость принимаемого счетного регистра (4 байта).

Для того чтобы найти Din нам необходимо:

Din=(Fout*232)/Fgen

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

Теперь откуда берется шаг в 0,0291Гц для рабочей частоты генератора 125 МГц. Все просто. Если принять Din за единицу (минимальное приращение счетного регистра генератора) и подставить в основную формулу из даташита, то мы как раз и получим это число на выходе. Необходимо отметить, что чем ниже рабочая частота модуля, тем меньше шаг приращения. Например, при частоте работы модуля в 30 МГц минимальный шаг представляет 0,006985Гц. Не знаю где это может пригодиться.

Дальше все прозаично. Первым должен передаваться регистр фазы (W0). Я особо разбираться с ним не стал, но одно могу сказать точно. Биты с нулевого по второй должны быть обнулены. Биты с третьего по седьмой, похоже, определяют отклонение фазы генерации. Или что-то в этом роде. Врать не буду, не изучал. Для простоты записываем в регистр нули и передаем их в генератор. Подпрограмма w_clk(); всего лишь подтягивает вывод W_CLK к единице, а потом снова сбрасывает в ноль. Далее идет передача в генератор данных новой частоты (W1...W4). Каждая передача сопровождается сигналом W_CLK. После того как все пять байт (W0...W4) переданы в генератор, подается команда на обновление данных генератора fu_ud(); которая в свою очередь подтягивает к единице вывод FU_UD, а после сбрасывает его в ноль. Всё, данные переданы и инициализированы. Генератор тут же меняет свою частоту сообразно полученным данным. Конец, занавес.

Реализовал я это таким образом.

Вот как это работает и выгладит на осциллографе.

Теперь как всегда ложка дегтя в бочку. Дело в том, что для работы генератора на выход меандра необходимо покрутить имеющийся на плате переменный резистор. Подозреваю, что этот переменный резистор задает опорное напряжение компаратора. Поэтому скважность меандра можно менять в очень узком диапазоне.

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

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

А на сегодня всё. Удачи.

29.04.20

Если вдруг найдете в статье неточности или заблуждения. Напишите мне об этом. Я подправлю.

Приложение:
Схема, разводка и программа.