Я привожу основные фрагменты программы управления DDS. Реализация на Дэлфи. Постарался максимально прокомментировать строки, так что проблем с переносом на любой другой язык возникнуть не должно. Во всяком случае, когда я ваял управляющую программку под AVR AT90S8515, никакого дискомфорта я не испытывал. Работа была чисто механической.


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


procedure InitSynth;
begin
// Основной сброс синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] or M_RESET;
 Make_Strobe;
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT M_RESET);
 Make_Strobe;
// Остановить главную тактировку синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT CLK_ENABLE);
 Make_Strobe;
// Произвести выборку кристалла синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT CS_DDS) or CS_ADC;
 Make_Strobe;
// Сформировать импульс сброса регистра ввода
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] or IO_RESET;
 Make_Strobe;
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT IO_RESET);
 Make_Strobe;
// Последовательно загрузить адрес и слова управления синтезатором
 SendByte(InitReg);
 SendByte(CW1);
 SendByte(CW2);
 SendByte(CW3);
 SendByte(CW4);
// Разрешить главную тактировку синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] OR CLK_ENABLE;
 Make_Strobe;
// Снять выборку кристалла синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] OR CS_DDS;
 Make_Strobe;
end;

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


По включении синтезатора амплитуда выходного сигнала устанавливается в 0. Если эту фичку не вычитать из даташита, можно потом много времени потерять на выяснение вопроса: "А где же выходной сигнал?..". Так что, не забывайте, что амплитуду сигнала таки надо устанавливать.

procedure SetAmplitude(Ampl: integer);
begin
// Произвести выборку кристалла синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT CS_DDS) or CS_ADC;
 Make_Strobe;
 SendByte($08);
// Передать младший ниббл старшего байта амплитуды
 SendByte((Ampl and $0F00) shr 8);
// Затем передать младший байт амплитуды
 SendByte(Ampl and $0FF);
// Сформировать импульс обновления синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] or IO_UPDATE;
 Make_Strobe;
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT IO_UPDATE);
 Make_Strobe;
// Снять выборку кристалла синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] OR CS_DDS;
 Make_Strobe;
end;

    

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


Чтобы правильно установить частоту, надо предварительно просчитать Слово Управления Частотой. Как это делать подробно описано в даташите. Длина этого слова составляет 48 бит или 6 байт. Отдельно я приведу процедуру выдачи одного байта в синтезатор.

procedure SetSynthFreq(Frq: int64);
var i: integer;
begin
//
 FTWCalc(Frq);
// Произвести выборку кристалла синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT CS_DDS) or CS_ADC;
 Make_Strobe;
 SendByte(FrqReg);
 for i:= 0 to 5 do
  begin
   SendByte(ByteSend[i]);
  End;
// Сформировать импульс обновления синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] or IO_UPDATE;
 Make_Strobe;
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT IO_UPDATE);
 Make_Strobe;
// Снять выборку кристалла синтезатора
 Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] OR CS_DDS;
 Make_Strobe;
end;

    

Функция FTWCalc(Frq) вычисляет FTW (Frequency Tuning Word) - Слово Управления частотой и записывает его в массив из шести байтов. Приводить здесь эту функцию нет смысла, так как она чисто математическая и реализуется в каждом конкретном случае по разному. Безусловно, написание программы для микроконтроллера на языке "Си" значительно облегчит ее реализацию.

Однако есть еще один язык написания программ. Малоизвестный и редко применяемый. Но я многие свои проекты реализую именно на нем. Особенно, если нужен компактный код, получаемый при написании программ на Ассемблере, и скорость разработки, достижимая при работе на "Си". Это так называемый "АлгоБилдер" - язык алгоритмического написания программ. За два года работы с ним я действительно почувствовал его мощь и скорость. Для тех, кому любопытно - прошу взглянуть на два листа из программы (кстати, той самой программы управления синтезатором DDS).


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

Procedure SendByte(ByteToSend: Byte);
var i: Integer;
Begin
// Перевод линии тактировки в 0
  Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT SCLK);
  Make_Strobe;
// Передача байта старшим битом вперед
  for i:= 1 to 8 do
   begin
// Выделение старшего бита
    if (ByteToSend and $80) = $80 then
    begin
// Передача единицы
     Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] or SDATA;
     Make_Strobe;
    end
    else
    begin
// Передача нуля
     Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] and (not SDATA);
     Make_Strobe;
    end;
// Фронт тактового ипульса
     Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] OR SCLK;
     Make_Strobe;
// Переход следующему биту
     ByteToSend:= ByteToSend shl 1;
/// Спад тактового импульса
     Form1.Port.Byte[Base]:= Form1.Port.Byte[Base] AND (NOT SCLK);
     Make_Strobe;
   end;
End;