/* ////////////////////////////////////////////////////////////////// ARDUINO/Genuino Project "NINGMOD", an DDS SYNTHESISER AD9953 https://www.changpuak.ch/electronics/Arduino-Ningmod.php Software Version 1.0 - 20.04.2022 by ALEXANDER SSE FRANK, The Serial Parser is from here : Easy_Diseqc V1.2, Monstein ETH Zurich, 20.06.2018 ////////////////////////////////////////////////////////////////// */ #include #include #include // DISPLAY #define OLED_MOSI 11 #define OLED_CLK 13 #define OLED_DC 9 #define OLED_CS 8 #define OLED_RESET 10 // ROTARY ENCODER const int RotEnc1 = 4 ; // PRESSED const int RotEnc2 = 3 ; const int RotEnc3 = 2 ; volatile boolean LEFT = false ; volatile boolean RIGHT = false ; volatile boolean READY = true ; // DDS VARIABLES float FREQ = 38.9 ; float AMPLI = 7.0 ; const float FREQ_MAX = 123.456 ; const float FREQ_MIN = 0.999 ; const float AMPLI_MIN = -25.0 ; const float AMPLI_MAX = +15.0 ; unsigned int RAWAMPLI = 0 ; int CursorPos = 5 ; volatile bool CursorMove = true ; // REF CLK bool LOCKED = false ; int LockPin = 12 ; const float REF = 400.000000 ; Adafruit_SH1106 display(OLED_MOSI,OLED_CLK,OLED_DC,OLED_RESET,OLED_CS); #if (SH1106_LCDHEIGHT != 64) #error("Height incorrect, please fix Adafruit_SH1106.h!"); #endif // ///////////////////////////////////////////////////////////// // Serial Communication Routines : EEPROM & DDS // ///////////////////////////////////////////////////////////// #define EEPROM_24C01_I2CADDR 0x50 // Single-bit serial 2-wire mode int DDS_RESET = 6 ; // Active High Reset Pin int DDS_SYNCIO = 7 ; int DDS_SDAT = A2 ; int DDS_SCLK = A1 ; int DDS_CS = A0 ; int DDS_IO_UPDATE = A7 ; int DDS_PS0 = A3 ; int DDS_PS1 = A6 ; char tempbuf[80]; // keeps the command temporary until CRLF String buffer; // /////////////////////////////////////// void ResetDDS() // /////////////////////////////////////// { digitalWrite(DDS_RESET, HIGH) ; delay(1) ; digitalWrite(DDS_RESET, LOW) ; } // ////////////////////////////////////////////////////////// void Energy() // ////////////////////////////////////////////////////////// { digitalWrite(DDS_IO_UPDATE, HIGH) ; delay(1) ; digitalWrite(DDS_IO_UPDATE, LOW) ; } // ////////////////////////////////////////////////////////// void WriteDDS1(byte instruct,byte data) // ////////////////////////////////////////////////////////// { digitalWrite(DDS_CS, LOW) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, instruct ) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, data) ; digitalWrite(DDS_CS, HIGH) ; Energy() ; } // ////////////////////////////////////////////////////////// void WriteDDS2(byte instruct,byte d1,byte d2) // ////////////////////////////////////////////////////////// { digitalWrite(DDS_CS, LOW) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, instruct ) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, d1) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, d2) ; digitalWrite(DDS_CS, HIGH) ; Energy() ; } // ////////////////////////////////////////////////////////// void WriteDDS3(byte instruct,byte d1,byte d2,byte d3) // ////////////////////////////////////////////////////////// { digitalWrite(DDS_CS, LOW) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, instruct ) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, d1) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, d2) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, d3) ; digitalWrite(DDS_CS, HIGH) ; Energy() ; } // ////////////////////////////////////////////////////////// void WriteDDS4(byte instruct,byte d1,byte d2,byte d3,byte d4) // ////////////////////////////////////////////////////////// { digitalWrite(DDS_CS, LOW) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, instruct ) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, d1) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, d2) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, d3) ; shiftOut(DDS_SDAT, DDS_SCLK, MSBFIRST, d4) ; digitalWrite(DDS_CS, HIGH) ; Energy() ; } // ////////////////////////////////////////////////////////// void UpdateFTW() // ////////////////////////////////////////////////////////// { // fout = FTW * fs / 2^32 // fs is constant. = 400.000 MHz // fs / 2^32 is constant. = 0.093132257461547900000 // 2^32 / fs is constant. = 10737418.24 // /////////////////////////////////////////////////////// unsigned long FTW = (unsigned long)(FREQ * 10737418.24 ) ; int Byte1 = (0xFF000000 & FTW) >> 24 ; int Byte2 = (0x00FF0000 & FTW) >> 16 ; int Byte3 = (0x0000FF00 & FTW) >> 8 ; int Byte4 = (0x000000FF & FTW) ; WriteDDS4(0x04, Byte1, Byte2, Byte3, Byte4) ; } // ////////////////////////////////////////////////////////// void UpdateASF() // ////////////////////////////////////////////////////////// { // AMAX = 2^14 - 1 = 16383 // AMIN = 100 unsigned int ASF = pow(2.71828182846, (( AMPLI + 68.284 )/8.6606)) ; int LSB = (0x00FF & ASF) ; int MSB = (0x3F00 & ASF) >> 8 ; WriteDDS2(0x02, MSB, LSB) ; } void SetASF(unsigned raw) { int LSB = (0x00FF & raw) ; int MSB = (0x3F00 & raw) >> 8 ; WriteDDS2(0x02, MSB, LSB) ; } // ///////////////////////////////////////////////////////////// // SUBROUTINES DISPLAY. // ///////////////////////////////////////////////////////////// void UpdateDisplay() { float AUX = AMPLI ; if(AMPLI < 0.0) AUX = -1.0 * AMPLI ; int x, y ; display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); display.print("****"); display.setCursor(42,0); display.print("NINGMOD"); display.setCursor(104,0); display.print("****"); display.drawLine(0, 12, 128, 12, WHITE); // -------------------------------------------- // FREQUENCY // -------------------------------------------- display.setTextSize(2) ; display.setCursor(0, 21); if (FREQ < 100.000) display.print(" "); if (FREQ < 10.000) display.print(" "); display.print(FREQ,3); display.setCursor(92, 21); if(digitalRead(LockPin) == 0) display.print(" ??"); else display.print("MHz"); // -------------------------------------------- // AMPLITUDE // -------------------------------------------- display.setTextSize(2) ; display.setCursor(0, 44); if ((AMPLI < 9.99) && (AMPLI > -9.99)) display.print(" "); if (AMPLI == 0.000) display.print(" "); if (AMPLI > 0.001) display.print("+"); if (AMPLI < 0.001) display.print("-"); display.print(" "); display.print(AUX,2); display.setCursor(92, 44); display.print("dBm"); // -------------------------------------------- // CURSOR // -------------------------------------------- if(CursorMove) { switch(CursorPos) { case 0 : display.setCursor(12, 25) ; break ; case 1 : display.setCursor(24, 25) ; break ; case 2 : display.setCursor(48, 25) ; break ; case 3 : display.setCursor(60, 25) ; break ; case 4 : display.setCursor(72, 25) ; break ; case 5 : display.setCursor(36, 48) ; break ; case 6 : display.setCursor(60, 48) ; break ; } display.print("_") ; } else { switch(CursorPos) { case 0 : x = 12 ; y = 25 ; break ; case 1 : x = 24 ; y = 25 ; break ; case 2 : x = 48 ; y = 25 ; break ; case 3 : x = 60 ; y = 25 ; break ; case 4 : x = 72 ; y = 25 ; break ; case 5 : x = 36 ; y = 48 ; break ; case 6 : x = 60 ; y = 48 ; break ; } display.fillTriangle(x,y+15,x+4,y+12,x+8,y+15,WHITE); } display.display(); } // ///////////////////////////////////////////////////////////// // S E T U P // ///////////////////////////////////////////////////////////// void setup() { byte b0, b1, b2, b3 ; Serial.begin(115200) ; Wire.begin() ; // INIT OLED display.begin(SH1106_SWITCHCAPVCC); // SHOW STARTUP SCREEN display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); display.print("****"); display.setCursor(42,0); display.print("NINGMOD"); display.setCursor(104,0); display.print("****"); display.drawLine(0, 12, 128, 12, WHITE); display.setTextSize(1); display.setCursor(0, 21); display.println("AN RF DDS GENERATOR"); display.setCursor(0, 33); display.println("WITH THE AD9953. "); display.setCursor(0, 45); display.println("(C) ETH QUANTUMOPTICS"); display.setCursor(0, 57); display.println("BUILT 29.12.2546"); display.display(); delay(999) ; pinMode(RotEnc1, INPUT_PULLUP); pinMode(RotEnc2, INPUT_PULLUP); pinMode(RotEnc3, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(RotEnc1), RotEncISR1, FALLING); attachInterrupt(digitalPinToInterrupt(RotEnc2), RotEncISR2, FALLING); attachInterrupt(digitalPinToInterrupt(RotEnc3), RotEncISR3, FALLING); delay(1999); pinMode( DDS_RESET, OUTPUT) ; pinMode( DDS_SYNCIO, OUTPUT) ; pinMode( DDS_SDAT, OUTPUT) ; pinMode( DDS_SCLK, OUTPUT) ; pinMode( DDS_CS, OUTPUT) ; pinMode( DDS_IO_UPDATE, OUTPUT) ; pinMode( DDS_PS0, OUTPUT) ; pinMode( DDS_PS1, OUTPUT) ; digitalWrite(DDS_CS, HIGH) ; digitalWrite(DDS_IO_UPDATE, LOW) ; digitalWrite(DDS_SYNCIO, LOW) ; digitalWrite(DDS_PS0, LOW) ; digitalWrite(DDS_PS1, LOW) ; digitalWrite(DDS_RESET, LOW) ; // INIT DDS ////////////////////////// ResetDDS() ; delay(9) ; // CFR 1 ////////////////////////////// b0 = 0x42 ; // 0100 0010 sync/comp disabled b1 = 0x00 ; // 0000 0010 2 wire SPI mode b2 = 0x00 ; // 0000 0000 no linear sweep b3 = 0x02 ; // 0000 0010 man/OSK RAM disabled WriteDDS4(0x00,b3,b2,b1,b0) ; // CFR 2 ////////////////////////////// b0 = 0x24 ; // MUL = 4, VCO = 1, CP-Current = 0 b1 = 0x00 ; // b2 = 0x18 ; // NOT USED WriteDDS3(0x01,b2,b1,b0) ; // AMPLITUDE SCALE FACTOR ///////////// b0 = 0xFF ; // b1 = 0x3F ; // WriteDDS2(0x02,b1,b0) ; // AMPLITUDE RAMP RATE //////////////// b0 = 50 ; // WriteDDS1(0x03,b0) ; // FTW //////////////////////////////// b0 = 0x00 ; // 50 MHz, fs = 400 MHz b1 = 0x00 ; // b2 = 0x00 ; // b3 = 0x20 ; // WriteDDS4(0x04,b3,b2,b1,b0) ; // WE KNOW THAT THE ABOVE IS NOT NECESSARY, BUT ... UpdateASF() ; UpdateFTW() ; // LOCKSTATUS pinMode(LockPin, INPUT) ; UpdateDisplay() ; Serial.println("NINGMOD 1.0 BY CHANGPUAK.CH") ; Serial.println("-----------------------------") ; Serial.print("Frequency : ") ; Serial.print(FREQ,3) ; Serial.println(" MHz") ; Serial.print("Amplitude : ") ; Serial.print(AMPLI,3) ; Serial.println(" dBm") ; if(digitalRead(LockPin) == 1) Serial.println("VCXO is locked.") ; else Serial.println("VCXO is unlocked.") ; Serial.println("-----------------------------") ; Serial.println("READY.") ; } // ///////////////////////////////////////////////////////////// // M A I N L O O P // ///////////////////////////////////////////////////////////// void loop() { // KEY ROTATED ? // ////////////////////////////////// if(LEFT) // ////////////////////////////////// { switch (CursorPos) { case 0: if(CursorMove) CursorPos = 6 ; else { FREQ -= 10.0 ; if(FREQ < FREQ_MIN) FREQ += 10.0 ; UpdateFTW() ; } break ; case 1: if(CursorMove) CursorPos = 0 ; else { FREQ -= 1.0 ; if(FREQ < FREQ_MIN) FREQ += 1.0 ; UpdateFTW() ; } break ; case 2: if(CursorMove) CursorPos = 1 ; else { FREQ -= 0.1 ; if(FREQ < FREQ_MIN) FREQ += 0.1 ; UpdateFTW() ; } break ; case 3: if(CursorMove) CursorPos = 2 ; else { FREQ -= 0.01 ; if(FREQ < FREQ_MIN) FREQ += 0.01 ; UpdateFTW() ; } break ; case 4: if(CursorMove) CursorPos = 3 ; else { FREQ -= 0.001 ; if(FREQ < FREQ_MIN) FREQ += 0.001 ; UpdateFTW() ; } break ; case 5: if(CursorMove) CursorPos = 4 ; else { AMPLI -= 1.0 ; if(AMPLI < AMPLI_MIN) AMPLI += 1.0 ; UpdateASF() ; } break ; case 6: if(CursorMove) CursorPos = 5 ; else { AMPLI -= 0.1 ; if(AMPLI < AMPLI_MIN) AMPLI += 0.1 ; UpdateASF() ; } break ; } delay(299) ; READY = true ; LEFT = false ; RIGHT = false ; } // ////////////////////////////////// if(RIGHT) // ////////////////////////////////// { switch (CursorPos) { case 0: if(CursorMove) CursorPos = 1 ; else { FREQ += 10.0 ; if(FREQ > FREQ_MAX) FREQ -= 10.0 ; UpdateFTW() ; } break ; case 1: if(CursorMove) CursorPos = 2 ; else { FREQ += 1.0 ; if(FREQ > FREQ_MAX) FREQ -= 1.0 ; UpdateFTW() ; } break ; case 2: if(CursorMove) CursorPos = 3 ; else { FREQ += 0.1 ; if(FREQ > FREQ_MAX) FREQ -= 0.1 ; UpdateFTW() ; } break ; case 3: if(CursorMove) CursorPos = 4 ; else { FREQ += 0.01 ; if(FREQ > FREQ_MAX) FREQ -= 0.01 ; UpdateFTW() ; } break ; case 4: if(CursorMove) CursorPos = 5 ; else { FREQ += 0.001 ; if(FREQ > FREQ_MAX) FREQ -= 0.001 ; UpdateFTW() ; } break ; case 5: if(CursorMove) CursorPos = 6 ; else { AMPLI += 1.0 ; if(AMPLI > AMPLI_MAX) AMPLI -= 1.0 ; UpdateASF() ; } break ; case 6: if(CursorMove) CursorPos = 0 ; else { AMPLI += 0.1 ; if(AMPLI > AMPLI_MAX) AMPLI -= 0.1 ; UpdateASF() ; } break ; } delay(299) ; READY = true ; LEFT = false ; RIGHT = false ; } UpdateDisplay() ; // ////////////////////////////////// while (Serial.available() > 0) // ////////////////////////////////// { int tmp; char st[20]; char rx = Serial.read(); // read a single character buffer += rx; //add character to the string buffer if ((rx == '\n') || (rx == '\r')) { buffer.toCharArray(tempbuf, 40); if (buffer.startsWith("SETF:")) { sscanf(tempbuf,"SETF:%s",&st); // extract frequency as floating point number FREQ = strtod(st,NULL); if(FREQ < FREQ_MIN) FREQ = FREQ_MIN ; if(FREQ > FREQ_MAX) FREQ = FREQ_MAX ; UpdateFTW() ; Serial.print("New frequency : ") ; Serial.print(FREQ,3) ; Serial.println(" MHz") ; } else if (buffer.startsWith("SETA:")) { sscanf(tempbuf,"SETA:%s",&st); // extract amplitude as floating point number AMPLI = strtod(st,NULL); if (AMPLI < AMPLI_MIN) AMPLI = AMPLI_MIN ; if (AMPLI > AMPLI_MAX) AMPLI = AMPLI_MAX ; UpdateASF() ; Serial.print("New amplitude : ") ; Serial.print(AMPLI,3) ; Serial.println(" dBm") ; } else if (buffer.startsWith("CALA:")) { sscanf(tempbuf,"CALA:%s",&st); // extract raw amplitude RAWAMPLI = (int)(strtod(st,NULL)); SetASF(RAWAMPLI) ; Serial.print("New ASFR : ") ; Serial.println(RAWAMPLI,DEC) ; } else if (buffer.startsWith("*LOCK?")) { if(digitalRead(LockPin) == 1) Serial.println("VCXO is locked.") ; else Serial.println("VCXO is unlocked.") ; } else if (buffer.startsWith("*IDN?")) { Serial.println("NINGMOD 1.0 BY CHANGPUAK.CH") ; Serial.println("-----------------------------") ; Serial.print("Frequency : ") ; Serial.print(FREQ,3) ; Serial.println(" MHz") ; Serial.print("Amplitude : ") ; Serial.print(AMPLI,3) ; Serial.println(" dBm") ; if(digitalRead(LockPin) == 1) Serial.println("VCXO is locked.") ; else Serial.println("VCXO is unlocked.") ; Serial.println("-----------------------------") ; Serial.println("READY.") ; } buffer = "" ; //erase buffer for next command } } } // ///////////////////////////////////////////////////////////// // INTERRUPT SERVICE ROUTINES // ///////////////////////////////////////////////////////////// void RotEncISR1() { if(READY) { CursorMove = !CursorMove ; } } void RotEncISR2() { if(READY) { LEFT = false ; RIGHT = false ; byte autre = digitalRead(RotEnc3) ; if (autre > 0) RIGHT = true ; if (autre < 1) LEFT = true ; } } void RotEncISR3() { if(READY) { LEFT = false ; RIGHT = false ; byte autre = digitalRead(RotEnc2) ; if (autre > 0) LEFT = true ; if (autre < 1) RIGHT = true ; } } // ///////////////////////////////////////////////////////////// // END OF FILE. // /////////////////////////////////////////////////////////////