Стабильный генератор синусоидального сигнала 0 — 10 Мгц

Опубликованная на amatar.by схема генератора сигналов на AD9850 вызвала немало вопросов и просьб для продолжения этой темы. В этой статье я «палю» для Вас исходники программ и представляю вниманию ещё один вариант программы для фиксированной частоты, которую можно изменять с помощью ПК.

Синтезатор частоты на DDS AD9850

Прошлая моя статья здесь: http://amatar.by/news-view-468.html

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

Для тактирования микросхемы DDS-синтезатора можно применить кварцевые генераторы: 100 Mhz, 80 Mhz, а также широко встречающиеся на материнских платах ПК — 66.667 Mhz. Для этих трёх случаев есть три соответствующие прошивки здесь ниже…

Принцип работы.

Схему можно рассматривать как два законченных модуля — построенных на соответствующей микросхеме. PIC18F2550 — управляет по трём портам (проводам) микросхемой DDS-синтезатора AD9850, которая в свою очередь выдаёт нам необходимую частоту.  Схема включения обоих микросхем типовая. Управление микросхемой DDS выглядит так:

Управление синтезатором DDS AD9850

По порту RC0 от PICа передаются пять байт информации об устанавливаемой частоте. Причём сначала должны передаваться младшие разряды, потом старшие.

Пример.

Имеем собранную схему по рисунку выше. Кварцевый генератор на 100 МГц.

Как получить 1000 Гц на выходе устройства?

Для начала нам необходимо вычислить коэффициент деления K:

K = Fген / 2^32 = 100 000 000 / 4 294 967 296 = 0.02328306…

Если Вы недалеки от программирования, то понятное дело, что K будет float типа (С++).

Находим код, который необходимо отослать в DDS:

Код = Fвых / K = 1000 (Гц) / 0.02328306 = 42949.68101…. = 42950

Но число 42950 — десятичное и его надо представить в шестнадцатиричной степени… Как это сделать программно каждый программист решает сам. Можно воспользоваться инженерным калькулятором:

Перевод 10-ричного числа в 16-тиричную систему счисления

Я в программах на С++ если аппаратной мощности хватает, то пользуюсь следующей конструкцией:

long code = 42950 / 0.0232;
byte b1 = code % 256;    // b1 = 198 = 0xC6; (остаток от деления 42950 на 256)
byte b2 = code / 256;    // b2 = 167 = 0xA7;
byte b3 = code / 65536;  // b3 = 0 = 0x00;
byte b4 = code / 16777216; // b4 = 0 = 0x00;

Получается, что для установки частоты в 1000 Гц на выходе DDS синтезатора нам необходимо в него записать число 0000A7C6. Где же пятый байт? В наших конструкциях он будет равен нулю — 0x00 . Его изменение отражается на фазе сигнала — это Вы можете прочитать в даташите на AD9850 (страница 12). Пятый байт добавляется вперёд:

Проект QuaDS — 1.

Чтобы построить генератор фиксированной частоты для схемы выше, воспользуйтесь одной из прошивок микроконтроллера quads.hex (зависит от используемого генератора). По умолчанию устройство будет выдавать звуковой сигнал частотой 1000 Гц , который легко проконтроллировать наушниками ТОН-2. Чтобы изменить частоту — подключаем устройство к ПК, если не опознано, то подкидываем драйвера — drivers.pic18.win32, затем запускаем программу QuaDS-1.win32

Программа QuaDS - 1 скачать

После установки частоты с помощью этой программы — значение кодового слова заносится в постоянную энергонезависимую память EEPROM. После перезагрузки (и автономно без ПК) контроллер самостоятельно берёт это слово и записывает в DDS.

Исходные коды программы QuaDS.win32.sources

Основной механизм программы для Windows:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    BYTE send_buf[64],receive_buf[64];
    DWORD RecvLength=4;

    int freq = StrToInt(Edit1->Text) / k[gen];
    int b1 = freq % 256;
    int b2 = freq / 256;
    int b3 = freq / 65536;
    int b4 = freq / 16777216;

    send_buf[0] = 0x01;      // Command установки F
    send_buf[1] = b1;
    send_buf[2] = b2;
    send_buf[3] = b3;
    send_buf[4] = b4;
    SendReceivePacket(send_buf,5,receive_buf,&RecvLength,250,250);

    send_buf[0] = 0x03; // запись в EEPROM
    send_buf[1] = 0x51; // адрес 1-го байта
    send_buf[2] = b1;
    SendReceivePacket(send_buf,3,receive_buf,&RecvLength,250,250);

    send_buf[1] = 0x61; // адрес 2-го байта
    send_buf[2] = b2;
    SendReceivePacket(send_buf,3,receive_buf,&RecvLength,250,250);

    send_buf[1] = 0x71; // адрес 3-го байта
    send_buf[2] = b3;
    SendReceivePacket(send_buf,3,receive_buf,&RecvLength,250,250);

    send_buf[1] = 0x81; // адрес 4-го байта
    send_buf[2] = b4;
    SendReceivePacket(send_buf,3,receive_buf,&RecvLength,250,250);
}

Исходный код для PIC18F2550 : QuaDS-1.pic.sources

Основной механизм программы для PIC18F2550:

/** I N C L U D E S **********************************************************/
#include

#include 
#include "system\typedefs.h"

#include "system\usb\usb.h"

#include "io_cfg.h"             // I/O pin mapping
#include "user\user.h"

/** V A R I A B L E S ********************************************************/
#pragma udata
byte counter;
double k = 0.02328306;		// 100 Mhz = 0.02328306;  80 Mhz = 0.01862645;  66.667 Mhz = 0.01552212
DATA_PACKET dataPacket;

/** P R I V A T E  P R O T O T Y P E S ***************************************/

void BlinkUSBStatus(void);
void ServiceRequests(void);
BOOL UBit(unsigned char byteB, unsigned char bit0);
void UsartFunc(unsigned char dataByte);
void setCodeFreq(byte b1, byte b2, byte b3, byte b4);
void setFreq(long frequency);
byte ReadEEPROM(byte addr);

/** D E C L A R A T I O N S **************************************************/
#pragma code

void UserInit(void)
{
    // 0 - output
    TRISA &= 0b00101111; LATA &= 0x00;
    TRISB &= 0b00011111; LATB &= 0x00;
    TRISC &= 0b00111000; LATC &= 0x00;
    ADCON2=0b10111100;
    mLED_1 = 1;								// произвести запись в DDS (порт RC0 в "1"  _-)
    mLED_1 = 0;								// перевести порт RC0 в "0"  -_

    setCodeFreq(ReadEEPROM(0x51), ReadEEPROM(0x61), ReadEEPROM(0x71), ReadEEPROM(0x81));
    //setFreq(40000);
}

void ProcessIO(void)
{
    BlinkUSBStatus();
    if((usb_device_state < CONFIGURED_STATE)||(UCONbits.SUSPND==1)) return;
    ServiceRequests();
}

void ServiceRequests(void)
{
    byte index;

    if(USBGenRead((byte*)&dataPacket,sizeof(dataPacket)))
    {
        counter = 0;
        if(dataPacket.CMD == PKTBYTE){		

		// принятая команда
		setCodeFreq(dataPacket.ID1, dataPacket.ID2, dataPacket.ID3, dataPacket.ID4);
		dataPacket._byte[0] = dataPacket.ID1;	// ok
		dataPacket._byte[1] = dataPacket.ID2;	// ok
		dataPacket._byte[2] = dataPacket.ID3;	// ok
		dataPacket._byte[3] = dataPacket.ID4;	// ok
             	counter=0x04;	// 4 байт

	}else if(dataPacket.CMD == READ_INFO){

		EEADR = dataPacket.ADDR;	// адрес чтения
		EECON1bits.EEPGD = 0; // сбрасываем управляющий бит
		EECON1bits.RD = 1; // устанавливаем управляющий бит чтения данных
		dataPacket._byte[0] = EEDATA;

		//EECON1 = 0b00000000;

		counter = 0x01;

	}else if(dataPacket.CMD == SAVE_INFO){

		EEADR = dataPacket.ADDR; // адрес ячейки
		EEDATA = dataPacket.VALUE;    // данные
		EECON1 = 0b00000100;

		EECON2 = 0x55;
		EECON2 = 0x0AA;
		EECON1bits.WR = 1;  // устанавливаем чтобы начать запись

		EECON1bits.WREN = 0;	// запрещаем дальнейшую запись
		dataPacket._byte[0] = EEDATA;

		counter = 0x01;

	}else if(dataPacket.CMD == RESET) Reset();	

        if(counter != 0){
            if(!mUSBGenTxIsBusy())
                USBGenWrite((byte*)&dataPacket,counter);
        }
    }

}

byte ReadEEPROM(byte addr){
	EEADR = addr;	// адрес чтения
	EECON1bits.EEPGD = 0; // сбрасываем управляющий бит
	EECON1bits.RD = 1; // устанавливаем управляющий бит чтения данных
	return EEDATA;
}

void BlinkUSBStatus(void)
{
    static word led_count=0;

    if(led_count == 0){
	  led_count = 30000U;
	  LATAbits.LATA4 = 0;	// BUSY выключить
    }
    if(led_count == 5000)LATAbits.LATA4 = 1;
    led_count--;
}

void setCodeFreq(byte b1, byte b2, byte b3, byte b4){
	UsartFunc(b1);
	UsartFunc(b2);
	UsartFunc(b3);
	UsartFunc(b4);
	UsartFunc(0x00);
	mLED_1 = 1;								// произвести запись в DDS (порт RC0 в "1"  _-)
	mLED_1 = 1;								// произвести запись в DDS (порт RC0 в "1"  _-)
	mLED_1 = 0;								// перевести порт RC0 в "0"  -_
}

void setFreq(long frequency){
	frequency = frequency / k;
	UsartFunc(frequency % 256);
	UsartFunc(frequency / 256);
	UsartFunc(frequency / 65536);
	UsartFunc(frequency / 16777216);
	UsartFunc(0x00);
	mLED_1 = 1;								// произвести запись в DDS (порт RC0 в "1"  _-)
	mLED_1 = 1;								// произвести запись в DDS (порт RC0 в "1"  _-)
	mLED_1 = 0;								// перевести порт RC0 в "0"  -_
}

void UsartFunc(unsigned char dataByte)					// dataByte - байт переданных данных
{
	static byte i;
	for(i = 0; i < 8; i++){
		mLED_3 = UBit(dataByte, i);						// перевести RC2 в состояние i-того бита байта переданных данных
		mLED_2 = 1;										// сигнал CLOCK в "1" 		_-
		mLED_2 = 0;										// сигнал CLOCK в "0"		-_

	}
}// end UsartFunc

BOOL UBit(unsigned char byteB, unsigned char bit0)
{
	if(((byteB >> bit0) & 1) > 0)
	{
		return 1;
	}else{
		return 0;
	}
}
Запись опубликована в рубрике C++, Программирование, Радиолюбитель, Технологии с метками , , , , . Добавьте в закладки постоянную ссылку.

Добавить комментарий