Ранее на сайте рассматривались примеры создания генератора на модуле AD9833 и частотомера, но это были отдельные проекты, на этой странице будет рассмотрен пример создания генератора и частотомера в одном проекте.
AD9833 — генератор сигналов с низким энергопотреблением. Позволяет генерировать сигналы с частотой до 12.5 МГц синусоидальной, треугольной и прямоугольной формы. Управление осуществляется с использованием трехпроводного интерфейса SPI.
Основные характеристики микросхемы:
- Цифровое программирование частоты и фазы.
- Потребляемая мощность 12.65 мВт при напряжении 3 В.
- Диапазон выходных частот от 0 МГц до 12.5 МГц.
- Разрешение 28 бит (0.1 Гц при частоте опорного сигнала 25 МГц).
- Синусоидальные, треугольные и прямоугольные выходные колебания.
- Напряжение питания от 2.3 В до 5.5 В.
- Трехпроводной интерфейс SPI.
- Расширенный температурный диапазон: от –40°C до +105°C.
- Опция пониженного энергопотребления.
При генерации синусоидальных и треугольных импульсов амплитуда изменяется в диапазоне 38мВ…0,65В. При генерации импульсов прямоугольной формы на выходе присутствует сигнал уровня TTL.
Работа генератора контролируется при помощи 2-х кнопок и энкодера, при нажатии кнопки энкодера можно перебирать разряды и поворотом ручки энкодера можно установить число от 0 до 9 в каждом разряде. При изменении частоты генератора выход генератора отключается, после установки нужно частоты необходимо нажать кнопку «Генератор On/Off«, для изменения формы сигнала необходимо нажать кнопку «Форма сигн.». Так же при изменении формы сигнала выход генератора отключается.
Частотомер работает независимо от генератора и в управлении не нуждается.
Генератор в данном проекте ограничен максимальной частотой в 10 МГц, диапазон измерения частотомера от 0 до 6,5 МГц.
- Показания частотомера
- Показания генератора
- Индикатор выходного сигнала генератора
- Форма сигнала
// ATMEGA328 16 MHz // PD5 frequency input //AD9833 #define CS PC0 #define MCLK PC1 #define DATA PC2 // ENCODER #define DT PB0 #define CLK PB1 #define SW PB2 // BUTTON #define G_ON_OFF PB3 #define FORM PB4 #include <avr/io.h> #include <util/delay.h> #include <Wire_low.h> // http://forum.rcl-radio.ru/viewtopic.php?pid=5521#p5521 #include <Lcd1602_i2c_low.h> // http://rcl-radio.ru/wp-content/uploads/2022/03/Lcd1602_i2c_low.zip Lcd1602_i2c_low lcd(0x27);// адрес I2C volatile uint8_t _prevValueAB = 0; volatile uint8_t _currValueAB = 0; volatile int16_t newPosition = 0; int position = -999; int form; int timer2,times; volatile byte x; unsigned long f; bool w=1,on; long fg; long b,h_bit,l_bit; float k = 0.995265; // устраним погрешность кварца const long f25 = 25000000;// частота кварца, если нет эталонного частотомера установите частоту 25000000 Гц int a[7],i; int main(){ PCICR |= (1 << PCIE0); PCMSK0 |= (1 << PCINT0)|(1 << PCINT1); PORTB |= (1 << PB3)|(1 << PB4); PORTD |= (1<<PD5); // подтягивающий резистор на PD5 (вход T1) DDRC |=(1<<CS)|(1<<MCLK)|(1<<DATA); PORTC |=(1<MCLK)|(1<<CS)|(1<<DATA); _delay_ms(500); ad_AD9833(); cli(); // TIMER 1 TCCR1A = 0; TCCR1B = 0; TCCR1B &= ~(1 << CS12)|(1 << CS11)|(1 << CS10); //Внешний тактовый источник на выводе T1. Тактирование по фронту TIMSK1 |= (1 << TOIE1); // бит TOIE1 в регистре TIMSK1 взывает прерывание когда таймер переполняется // TIMER 2 TCCR2A = 0; TCCR2B = 0; OCR2A = 155; // 100 Hz TCCR2A |= (1 << WGM21); TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20); TIMSK2 |= (1 << OCIE2A); sei(); wire_set(16000000,100000); // тактовая частота контроллера, частота шины I2C lcd.setInit(); lcd.Clear(); // очистка экрана lcd.led(1); // включение и отключение подсветки экрана a[0] = EEPROM_read(0);a[1] = EEPROM_read(1);a[2] = EEPROM_read(2);a[3] = EEPROM_read(3);a[4] = EEPROM_read(4);a[5] = EEPROM_read(5);a[6] = EEPROM_read(6); form = EEPROM_read(7); while(1){ if(((PINB >> SW) & 1)==0){i++;if(on==1){w=1;}on=0;if(i>6){i=0;};_delay_ms(300);} if(((PINB >> G_ON_OFF) & 1)==0 && on==0){on=1;w=1;i=100;_delay_ms(300);} if(((PINB >> G_ON_OFF) & 1)==0 && on==1){on=0;w=1;fg=0;i=0;_delay_ms(300);} if(((PINB >> FORM) & 1)==0){form++;if(form>2){form=0;}w=1;fg=0;i=0;_delay_ms(300);} /////////////////////////////////////////////////// if(newPosition != position){position = newPosition; a[i] = a[i]+newPosition;newPosition=0; } /////////////////////////////////////////////////// if(on==0){fg=0;lcd.Curs(1,11);lcd.PrintString(" ");} if(on==1){lcd.Curs(1,11);lcd.PrintString("*");} if(w==1){w=0; EEPROM_write(0,a[0]);EEPROM_write(1,a[1]);EEPROM_write(2,a[2]);EEPROM_write(3,a[3]);EEPROM_write(4,a[4]);EEPROM_write(5,a[5]);EEPROM_write(6,a[6]); EEPROM_write(7,form); ad_AD9833(); lcd.Curs(1,13); switch(form){ case 0: lcd.PrintString("DAC");WriteAD9833(0x2028);break; case 1: lcd.PrintString("SIN");WriteAD9833(0x2000);break; case 2: lcd.PrintString("TRI");WriteAD9833(0x2002);break; } } /////////////////////////////////////////////////// lcd.Curs(0,0);lcd.PrintString("F "); lcd.PrintInt(f/1000000);lcd.PrintString("."); lcd.PrintInt(f/100000%10); lcd.PrintInt(f/10000%10); lcd.PrintInt(f/1000%10);lcd.PrintString("."); lcd.PrintInt(f/100%10); lcd.PrintInt(f/10%10); lcd.PrintInt(f%10); lcd.PrintString(" Hz "); /////////////////////////////////////////////////// if(a[i]<0){a[i]=0;}if(a[i]>9){a[i]=9;} lcd.Curs(1,0);lcd.PrintString("G "); lcd.Curs(1,2);if(i==0){if(timer2-times<50){lcd.PrintInt(a[0]);}if(timer2-times>=50){lcd.PrintString(" ");}if(timer2-times>100){times=timer2;}}else{lcd.PrintInt(a[0]);} lcd.Curs(1,3);lcd.PrintString("."); lcd.Curs(1,4);if(i==1){if(timer2-times<50){lcd.PrintInt(a[1]);}if(timer2-times>=50){lcd.PrintString(" ");}if(timer2-times>100){times=timer2;}}else{lcd.PrintInt(a[1]);} lcd.Curs(1,5);if(i==2){if(timer2-times<50){lcd.PrintInt(a[2]);}if(timer2-times>=50){lcd.PrintString(" ");}if(timer2-times>100){times=timer2;}}else{lcd.PrintInt(a[2]);} lcd.Curs(1,6);if(i==3){if(timer2-times<50){lcd.PrintInt(a[3]);}if(timer2-times>=50){lcd.PrintString(" ");}if(timer2-times>100){times=timer2;}}else{lcd.PrintInt(a[3]);} lcd.Curs(1,7);lcd.PrintString("."); lcd.Curs(1,8);if(i==4){if(timer2-times<50){lcd.PrintInt(a[4]);}if(timer2-times>=50){lcd.PrintString(" ");}if(timer2-times>100){times=timer2;}}else{lcd.PrintInt(a[4]);} lcd.Curs(1,9);if(i==5){if(timer2-times<50){lcd.PrintInt(a[5]);}if(timer2-times>=50){lcd.PrintString(" ");}if(timer2-times>100){times=timer2;}}else{lcd.PrintInt(a[5]);} lcd.Curs(1,10);if(i==6){if(timer2-times<50){lcd.PrintInt(a[6]);}if(timer2-times>=50){lcd.PrintString(" ");}if(timer2-times>100){times=timer2;}}else{lcd.PrintInt(a[6]);} fg=a[0]*pow(10,6)+a[1]*pow(10,5)+a[2]*pow(10,4)+a[3]*pow(10,3)+a[4]*pow(10,2)+a[5]*pow(10,1)+a[6]; }}// end main/while ISR (TIMER1_OVF_vect){x++;}// при переполнении увеличить переменную х на 1 ISR (TIMER2_COMPA_vect){ timer2++; if(timer2==1){ x = 0;TCNT1 = 0; TCCR1B |= (1 << CS12)|(1 << CS11)|(1 << CS10);} if(timer2==101){ TCCR1B &= ~(1 << CS12)|(1 << CS11)|(1 << CS10); f = ((x*65535) + TCNT1);timer2=0;} } void WriteAD9833(int data){ // SPI PORTC |=(1<<MCLK);PORTC &=~(1<<CS); for(int i = 15; i >= 0; i--){ PORTC |=(1<<MCLK); if(((data>>i)&0x01)==1){PORTC |=(1<<DATA);}else{PORTC &=~(1<<DATA);} PORTC &=~(1<<MCLK);_delay_ms(1);} PORTC |=(1<<CS); } void ad_AD9833(){ b = (fg*pow(2,28)/f25)*k; if(b<16383){l_bit = b + 0x4000 ;h_bit = 0x4000;} else{h_bit = (b>>14) + 0x4000;l_bit = b - (h_bit<<14) + 0x4000;} WriteAD9833(0x2100);// 0010 0001 0000 0000 - Reset + DB28 WriteAD9833(l_bit); // 0100 0000 0000 0000 - Freq0 LSB WriteAD9833(h_bit); // 0100 0000 0000 0000 - Freq0 MSB WriteAD9833(0xC000);// 1100 0000 0000 0000 - Phase0 WriteAD9833(0x2028);// 0010 0000 0000 0000 - Exit Reset } ISR(PCINT0_vect){ bool pinA = ((PINB >> DT) & 1); bool pinB = ((PINB >> CLK) & 1); _currValueAB = (pinA << 1) | pinB; switch(_prevValueAB | _currValueAB){ case 0b0001: newPosition++;break; case 0b0100: newPosition--;break; } _prevValueAB = _currValueAB << 2; } unsigned char EEPROM_read(unsigned int uiAddress){ while(EECR & (1<<EEPE)); // проверка готовности EEPROM EEARH = ((uiAddress & 0xF0) << 2); // регистр адреса H EEARL = uiAddress & 0x0F; // регистр адреса L EECR |= (1<<EERE);// чтение EEPROM return EEDR; // вывод значения } void EEPROM_write(unsigned int uiAddress, unsigned char ucData){ while(EECR & (1<<EEPE)); // проверка готовности EEPROM EEARH = ((uiAddress & 0xF0) << 2); // регистр адреса H EEARL = uiAddress & 0x0F; // регистр адреса L EEDR = ucData; // регистр данных EECR |= (1<<EEMPE);// Разрешение записи в EEPROM EECR |= (1<<EEPE); // Запись в EEPROM }
Форум — http://forum.rcl-radio.ru/viewtopic.php?pid=6247#p6247