Функциональный генератор НЧ сигналов на основе DDS с применением контроллера AVR ATMega16 / Блог им. Valik_666 / Блоги по электронике

На страницу с книгами, библиотеками, программами

Форум

Функциональный генератор НЧ сигналов на основе DDS с применением контроллера AVR ATMega16

В последнее время получили широкое распространение методы цифрового синтеза частоты(DDS), причем методы реализации очень многообразны. Способ и метод реализации зависит от требований к генератору.

Поскольку я часто сталкивался с написанием программ на контроллеры AVR и Microchip – я выбирал между ними… Но дешевле и функциональнее оказался AVR. По быстродействию и нужному количеству выводов подошел ATMega16. Теперь о расчетах…
F max = 16000000Hz(Частота атмеги)
15 циклов берем на изменение аккумулятора фазы, выборку из LUT и вывод.
Итого Fclk=16000000Hz/15=1066666,6667Hz
Для необходимой точности выбрал 32-битный аккумулятор фазы.
Теперь вычислим минимальный шаг:
Step(Hz)= 1066666,6667Hz/(2^32)= 0,0002483526865641276041667(Hz)
Код самого генератора:
while (1){ 
          
            #asm 
                        ADD  R1,R6
                	ADC  R2,R7
                        ADC  R3,R8
                	ADC  R4,R9
            #endasm
PORTC=LUT_of_Signal[Phase_acc.Phase_acc_8bits_of_byte[3]];  

При 50000Гц сигнал за период будет образовываться ~21 сменой напряжений на выходе ЦАПа.
В качестве ЦАП я выбрал обычную R-2R матрицу – она не требует стробов и 8 бит вполне удовлетворяют условиям. Т.е. (|12|+|-12|) / 2^8 = 0,09375~ 0,1V
Максимум и минимум для расчета шага
Для удобства и быстроты настройки частоты я использовал валкодер, по схеме предложенной радиолюбителем VK6BRO, из шагового двигателя.
Схема валкодера

Чтобы предотвратить ложные срабатывания от валкодера – контроллер несколько раз проверяет направления при шагах и только тогда фиксирует изменения.
Остальные параметры задаются 4-мя кнопками.

Собственно сама схема генератора и его управления

    Генератор имеет возможность воспроизводить следующие формы сигналов:
  • 1. Синусоида
  • 2. Меандр
  • 3. H-wave
  • 4. Лестница симметричная
  • 5. Трапеция
  • 6. Пила
  • 7. Прямоугольник симметричный
  • 8. Лестница асимметричная
  • 9. Прямоугольник асимметричный
  • 10. Постоянный "+"
  • 11. Постоянный "-"
Видео с работой
Так же добавил функцию сканирования заданного диапазона частот с регулируемым шагом.
Шаг устанавливается 0,01Гц-0.1Гц-1Гц-10Гц-100Гц и обратно. Для удобства отображения и простоты написания программы использовал LCD от Nokia 3310(84x48). В качестве самого валкодера использовал биполярный шаговый двигатель от старого винчестера. Все устройство и программу просимулировал в Proteus.

Аналоговая часть генератора
Аналоговая часть
Поскольку ЦАП выдает сигнал однополярный а задумка была зделать именно двухполярный генератор то необходимо использовать смещение на усилителе. В качестве источника опорного напряжения я выбрал TL431. Сам усилитель я реализовал на 2-х каскадах. Для усиления нагрузочной способности я применил повторитель напряжения на микросхеме TDA2030A.
Повторитель TDA2030
Сигнал на выходе устройства U3 повторяет по форме и амплитуде входной, но имеет большую мощность, т.е. схема может работать на низкоомную нагрузку. Повторитель использован для увеличения выходной мощности низкочастотного генератора (чтобы можно было непосредственно испытывать головки громкоговорителей или акустические системы). Полоса рабочих частот повторителя линейна от постоянного тока до 0,5...1 МГц, что более чем достаточно для генератора НЧ.

Источник питания — любой(импульсный или линейный), желательно стабилизированный с питаниями +5,+12/-12V.

О сборке

При сборке проблем особых не возникло, настройка заключается в подстройке аналоговой части симметричности и амплитуды выходного сигнала. Смещение настраивается резистором R1 и R6.Амплитуда первого каскада R5, второго R8.
Ссылки на схемы цифровой и аналоговой части(JPG и проект в Proteus) и прошивка:
Загрузить с Letitbit
На страницу с литературой и Proteus 7.10RUS+key
Форум

Исходник под CodeVisionAVR


/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7 beta 5 Professional
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com

Project : 
Version : 
Date    : 06.06.2010
Author  : F4CG                            
Company : F4CG                            
Comments: 


Chip type           : ATmega16
Program type        : Application
Clock frequency     : 16,000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*****************************************************/

#include mega16.h // Доставьте треугольные скобки - интерпритатор HTML их съедает
#include m8_128.h
#include delay.h  
#include math.h
#include stdio.h
#include Nokia_LCD.h
 #pragma regalloc-
signed long int FREQ=5000000;                    //   x0.01Hz  max =50000Hz
signed long int Scan_low_edge_of_FREQ=1;         //   x0.01Hz  =0.01Hz

#define Min_FREQ  1       // = 0.01Hz
#define Max_FREQ  6000000 // = 60kHz
signed long int SCR_FREQ;
// Variables of DDS //////////   
union data{
unsigned char     Phase_acc_8bits_of_byte[4];
unsigned int      Phase_acc_16bits[2] ;
unsigned long int Phase_acc_32bits;

};
 union data Phase_acc @1;
register u32 Phase  @1;
register u32 Freq_k @6;


union Freq_K{
unsigned char     Freq_koeff[4];
unsigned int      Freq_koeff_16bits[2];
unsigned long int Freq_koeff_32bits;

};
union Freq_K Freq_Koeff @6; 


#define Timer_1_OFF TIMSK=(0<Scan_low_edge_of_FREQ){
                                if((FREQ-Step_of_freq_scan)>Min_FREQ){FREQ-=Step_of_freq_scan;}
else {Timer_1_OFF;}}

Freq_Koeff.Freq_koeff_32bits=(u32)FREQ/Delta_Freq_per_Step;
Disp_Freq_and_Step();
LcdUpdate();

}



// External Interrupt 0 service routine     // VALCODER_CLK
interrupt [EXT_INT0] void ext_int0_isr(void)
{   Signal_OFF;
if(VALCODER_DATA){       state_pl++;state_mn=0;
                if (state_pl>=MIN_Steps_for_accept)   {state_pl=0;       
                if ((FREQ+Step_of_freq_change_value)=MIN_Steps_for_accept)   {state_mn=0;      
                if ((FREQ-Step_of_freq_change_value)>Min_FREQ){FREQ-=Step_of_freq_change_value;}
                Freq_Koeff.Freq_koeff_32bits=(u32)FREQ/Delta_Freq_per_Step; 
                PORTB.1=1;delay_ms(20);PORTB.1=0;
                //LcdInit ();LcdContrast(0x7f);
                Disp_Freq_and_Step();
                LcdUpdate();}
        }                                              

        }


// External Interrupt 1 service routine      // STEP-BUTTON
interrupt [EXT_INT1] void ext_int1_isr(void)
{Signal_OFF;  
#asm("cli")
delay_ms(100);
if (STEP){       //Защита от дребезга контактов
if  (Step_of_freq_change_value<=1000) {Step_of_freq_change_value*=10;} else {Step_of_freq_change_value=1;}
//LcdInit ();//LcdContrast(0x7f);
Disp_Freq_and_Step();
LcdUpdate();
Step_of_freq_scan=Step_of_freq_change_value;
delay_ms(30);}
#asm("sei")
}

// External Interrupt 2 service routine       // MENU WAVEFORM-BUTTON
interrupt [EXT_INT2] void ext_int2_isr(void)
{  Signal_OFF; 
#asm("cli")
delay_ms(200);
    if (Select_wave) {  delay_ms(100);
                        if  (Type_wave<=9) {Type_wave++;} else {Type_wave=0;} 
                        Type_of_wave(LUT_of_Signal,Type_wave);
                        //LcdInit ();//LcdContrast(0x7f);
                        LcdClear();
                        Disp_Freq_and_Step();
                        LcdUpdate();
                        }
  
    if (SCAN_Menu)    {  delay_ms(100);menu_on=1;Menu_scan(); delay_ms(100);Timer_1_ON;}  
    
   
// Place your code here
  
delay_ms(100);
#asm("sei")

}


void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTA=0x00;
DDRA=0x00;

// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=In Func1=Out Func0=Out 
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=P State1=0 State0=0 
PORTB=0x04;
DDRB=0xFB;

// Port C initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out 
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0 
PORTC=0x00;
DDRC=0xFF;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=P State6=P State5=P State4=P State3=P State2=T State1=T State0=P 
PORTD=0xF9;
DDRD=0x00;


// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 16000,000 kHz
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 250,000 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x03;
TCNT1H=0x4A;
TCNT1L=0x32;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;


// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Rising Edge
// INT1: On
// INT1 Mode: Falling Edge
// INT2: On
// INT2 Mode: Falling Edge
GICR|=0xE0;
MCUCR=0x0B;
MCUCSR=0x00;
GIFR=0xE0;


// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 15,625 kHz
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x05;
TCNT0=0x01;
OCR0=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
//TIMSK=0x01;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// Global enable interrupts
Signal_OFF;
Type_of_wave(LUT_of_Signal,Type_wave);
Freq_Koeff.Freq_koeff_32bits=(u32)FREQ/Delta_Freq_per_Step;
LcdInit ();
LcdContrast(0x7f);
LcdGotoXY(2,2);LcdStrF(1,"Waveform");    
LcdGotoXY(3,4);LcdStrF(1,"Generator");    
LcdGotoXY(1,6);LcdStrF(1,"@Polyakov V.V."); 
LcdUpdate();
delay_ms(2000);
LcdContrast(0x7f);
LcdClear();
LcdUpdate();
//////////////////////////////////////////////////////////////////////////////////////////
Disp_Freq_and_Step();
LcdUpdate();

#asm("sei")
   while (1){ 
          
            #asm 
                        ADD  R1,R6
                	ADC  R2,R7
                        ADC  R3,R8
                	ADC  R4,R9
            #endasm
PORTC=LUT_of_Signal[Phase_acc.Phase_acc_8bits_of_byte[3]];      
     
        }
     
} 
                	

void Type_of_wave(char wave[256],char type_of)
{ 

//H-wave  Creating LUT   
switch (type_of) {

//SINUS
case 0:{nn=0;for (x=0.00;x<6.28318;x+=0.02454) {wave[nn]=127*sin(x)+127;nn++;}}break; // Синус

//MEANDR
case 1:{for (nn=0;  nn<128;nn++){wave[nn]=255;wave[nn+128]=1;}

}break;
			

// H-WAVE
case 2: {nn=0;
			 for (nn=0;  nn<=32;nn++) {wave[nn]=128;}
			 for (nn=33; nn<=65;nn++) {wave[nn]=255;}
			 for (nn=66; nn<=98;nn++) {wave[nn]=192;}
			 for (nn=99; nn<=131;nn++){wave[nn]=255;}
			 for (nn=132;nn<=164;nn++){wave[nn]=128;}
			 for (nn=165;nn<=197;nn++){wave[nn]=0;}
			 for (nn=198;nn<=230;nn++){wave[nn]=64;}
			 for (nn=231;nn<=256;nn++){wave[nn]=0;}}break;
 
// Ladder Symmetric
case 3:{nn=0;for (ii=0;  ii<46;ii++){wave[nn]=128;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=192;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=255;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=128;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=64;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=1;nn++;}	}break;

// Trapezoid
case 4:{    nn=0;        for (ii=0;  ii<21; ii++){wave[nn]=127+6*ii;nn++;}
                         for (ii=0;  ii<86; ii++){wave[nn]=254;     nn++;}
                         for (ii=0;  ii<42; ii++){wave[nn]=252-6*ii;nn++;}
                         for (ii=0;  ii<86; ii++){wave[nn]=1;       nn++;}                         
                         for (ii=0;  ii<21; ii++){wave[nn]=6*ii;    nn++;}                         

                                                                        }break;  

			 
// SAW- WAVE
case 5 : for (nn=0;nn<256;nn++) {wave[nn]=nn;} break; // Saw-wave Creating LUT


//Square  symmetric
case 6:{nn=0;for (ii=0;  ii<64;ii++){wave[nn]=128;nn++;}
			 for (ii=0;  ii<64;ii++){wave[nn]=255;nn++;}
			 for (ii=0;  ii<64;ii++){wave[nn]=128;nn++;}
			 for (ii=0;  ii<64;ii++){wave[nn]=1;nn++;}
			}break;
// Ladder asymmetric
case 7:{nn=0;for (ii=0;  ii<46;ii++){wave[nn]=128;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=192;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=255;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=128;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=1;nn++;}
			 for (ii=0;  ii<42;ii++){wave[nn]=64;nn++;}	}break;


//Square  asymmetric
case 8:{nn=0;for (ii=0;  ii<96;ii++){wave[nn]=1;nn++;}
			 for (ii=0;  ii<32;ii++){wave[nn]=255;nn++;}
			 for (ii=0;  ii<32;ii++){wave[nn]=255;nn++;}
			 for (ii=0;  ii<96;ii++){wave[nn]=1;nn++;}
			}break;
// GPositive
case 9:{for (nn=0;  nn<256;nn++){wave[nn]=255;delay_us(6);}}break;

// GNegative
case 10:{for ( nn=0;  nn<256;nn++){wave[nn]=0;}}break;


default :for (x=1;x<255;x+=1) {wave[nn]=x;nn++;} break; // Saw-wave Creating LUT
} // End of switch
}



void Disp_Freq_and_Step(){

for (i=1;i<=64; i++){LcdLine(i,20-LUT_of_Signal[(i-1)*4]/13,i,20-LUT_of_Signal[i*4]/13,1);} 
LcdLine(i,11,i,20-LUT_of_Signal[256]/13,1);
Freq_on_Disp(5,4,FREQ);
//////////////////////////////////////////////////////////////////////////////////////////
LcdGotoXY(2,4);LcdStrF(1,"FR:"); 
//LcdGotoXY(2,4);sprintf(lcd_buffer,"FR:%u %uHz",FREQ/1000,FREQ%1000);LcdStr(1,lcd_buffer); 

            
LcdGotoXY(2,6);LcdStrF(1,"St:");
switch (Step_of_freq_change_value)
{
case 1:      {LcdGotoXY(5,6);LcdStrF(1,"0.01Hz  ");break;}
case 10:     {LcdGotoXY(5,6);LcdStrF(1,"0.1Hz   " );break;}
case 100:    {LcdGotoXY(5,6);LcdStrF(1,"1Hz     "   );break;}
case 1000:   {LcdGotoXY(5,6);LcdStrF(1,"10Hz    "  );break;}
case 10000:  {LcdGotoXY(5,6);LcdStrF(1,"100Hz   " );break;}
default:{LcdGotoXY(5,6);LcdStrF(1,"Error   ");break;}
                        }
}                                     

void Menu_scan(void)
{   u32 temp;
LcdInit ();  
LcdClear();
LcdUpdate();  

while (menu_on){ 
LcdGotoXY(3,2);LcdStrF(2,"SCAN");
LcdGotoXY(2,4);LcdStrF(1,"From current");
LcdGotoXY(2,5);LcdStrF(1,"to:");
LcdGotoXY(2,6);LcdStrF(1,"Step:");
if (STEP) {    delay_ms(40);if (STEP){
                if  (Step_of_freq_scan<=1000) {Step_of_freq_scan*=10;} else {Step_of_freq_scan=1;}
                delay_ms(400);
                }
                }


 Freq_on_Disp(5,5,Scan_low_edge_of_FREQ);
 /////////////////////////////////////////////////////////
if (SCAN_Menu){ delay_ms(40); if (SCAN_Menu){
         if ((Scan_low_edge_of_FREQ+Step_of_freq_scan)<=Max_FREQ){Scan_low_edge_of_FREQ+=Step_of_freq_scan;}
         PORTB.0=1;delay_ms(20);PORTB.0=0;  }}

if (Select_wave){ delay_ms(40); if (Select_wave){
                if ((Scan_low_edge_of_FREQ-Step_of_freq_scan)>=Min_FREQ){Scan_low_edge_of_FREQ-=Step_of_freq_scan;}
                PORTB.1=1;delay_ms(20);PORTB.1=0;  }}                        

 if (MENU) {delay_ms(40); if (MENU){
 if  (Scan_low_edge_of_FREQ>FREQ){temp=FREQ; FREQ=Scan_low_edge_of_FREQ;Scan_low_edge_of_FREQ=temp; }

 menu_on=0;
 Step_of_freq_change_value=Step_of_freq_scan; 
 LcdClear();
 LcdUpdate();
 }}
   
////////////////////////////////////////////////////////
 
 
 
switch (Step_of_freq_scan)
{
case 1:      {LcdGotoXY(7,6);LcdStrF(1,"0.01Hz   ");break;}
case 10:     {LcdGotoXY(7,6);LcdStrF(1,"0.1Hz    " );break;}
case 100:    {LcdGotoXY(7,6);LcdStrF(1,"1Hz      "   );break;}
case 1000:   {LcdGotoXY(7,6);LcdStrF(1,"10Hz     "  );break;}
case 10000:  {LcdGotoXY(7,6);LcdStrF(1,"100Hz    " );break;}
default:{LcdGotoXY(7,6);LcdStrF(1,"Error   ");break;}
                        }


delay_ms(20); 
LcdUpdate();                                  

                }

} 


void Freq_on_Disp(u8 x, u8 y, u32 SCR_FREQ)
{ 
if (SCR_FREQ/1000000) {LcdGotoXY(x,y);sprintf(lcd_buffer,"%u",SCR_FREQ/1000000);LcdStr(1,lcd_buffer); } 
        else {LcdGotoXY(x,y);sprintf(lcd_buffer,"0");LcdStr(1,lcd_buffer);}
        SCR_FREQ=SCR_FREQ%1000000;

if (SCR_FREQ/100000) {LcdGotoXY(x+1,y);sprintf(lcd_buffer,"%u",SCR_FREQ/100000);LcdStr(1,lcd_buffer); } 
        else {LcdGotoXY(x+1,y);sprintf(lcd_buffer,"0");LcdStr(1,lcd_buffer);}
        SCR_FREQ=SCR_FREQ%100000;
  
if (SCR_FREQ/10000) {LcdGotoXY(x+2,y);sprintf(lcd_buffer,"%u",SCR_FREQ/10000);LcdStr(1,lcd_buffer); } 
        else {LcdGotoXY(x+2,y);sprintf(lcd_buffer,"0");LcdStr(1,lcd_buffer);}
        SCR_FREQ=SCR_FREQ%10000;
            
if (SCR_FREQ/1000) {LcdGotoXY(x+3,y);sprintf(lcd_buffer,"%u",SCR_FREQ/1000);LcdStr(1,lcd_buffer); } 
        else {LcdGotoXY(x+3,y);sprintf(lcd_buffer,"0");LcdStr(1,lcd_buffer);}
        SCR_FREQ=SCR_FREQ%1000;

if (SCR_FREQ/100) {LcdGotoXY(x+4,y);sprintf(lcd_buffer,"%u",SCR_FREQ/100);LcdStr(1,lcd_buffer); } 
        else {LcdGotoXY(x+4,y);sprintf(lcd_buffer,"0");LcdStr(1,lcd_buffer);}
        SCR_FREQ=SCR_FREQ%100;

if (SCR_FREQ/10) {LcdGotoXY(x+5,y);sprintf(lcd_buffer,".%u",SCR_FREQ/10);LcdStr(1,lcd_buffer); } 
        else {LcdGotoXY(x+5,y);sprintf(lcd_buffer,".0");LcdStr(1,lcd_buffer);}
        SCR_FREQ=SCR_FREQ%10;

if (SCR_FREQ/1) {LcdGotoXY(x+7,y);sprintf(lcd_buffer,"%uHz",SCR_FREQ/1);LcdStr(1,lcd_buffer); } 
        else {LcdGotoXY(x+7,y);sprintf(lcd_buffer,"0Hz");LcdStr(1,lcd_buffer);}
        SCR_FREQ=SCR_FREQ%1;


}


Жалобы на неработающие ссылки не принимаются :) Все это делалось исключительно для себя

Написать мне письмо
Hosted by uCoz
раскрутка сайта недорого, раскрутка сайтов цены Счетчик и проверка тИЦ и PR