DominoEX from ad9850DominoEX.jpg


I have been using THOR-modes many times, and found them practical. THOR is handy especially because very tolerant tuning. Also I have used it with simply receiver on General Learning Machine. So I started to think of generating THOR signals directly on ad9850 DDS-module controlled by Arduino.

It seems THOR is developed from DominoEX-mode. Shortly THOR is DominoEX whit added error correction and different character set. It was natural starting point to develop some DominoEX-signal source.
Purpose is to make this "transmitter" somewhat usable at DominoEx and move to thinking of similar THOR-transmitter.

I used ad9850-driver code from webshed and added DominoEX-modulator directly on top of it. I used Uno just like on webshed. You can check hardware connections from there. I suppose code I have used is work by David Mills.

Picture: DominoEX "beacon" testing. This was one of first versions of code, it is still seen on very end of this page.
Usage: Basic idea is to create transmitter which can be used over serial connection. It can be used also with "serial monitor" on Arduino IDE while testing. (Not really practical on real use. Of course it can be used from inside loop() also if you need.)

On Arduino IDE Serial Monitor use "Carriage return".

Control over Serial Monitor:
This is premature code, but it works and can be used over serial monitor. You can start, stop and change frequency of transmitter from keyboard:
Character "¤" will start transmitter, it will idle until you type something and hit enter.
Character "#" will stop transmitting.
"%10000000&" will change frequency to 10mhz. So put "%" in front and "&" in the end of frequency (in hertz). Suppose it can be used at any frequency under about 40 MHZ. (Not saying it can be used legally, just that DDS should work about any freq under 40 MHZ.)

Only upper case characters and numbers are currently defined at domino11Char() -function, other should cause transmitting "?" -character.

tone1 is lowest frequency used at transmission. This code was generated DDS-module on top of receiver without any antenna or amplifier, so I just put some silent freq there as default.

Currently only DominoEX11-mode is supported, altought it seems really easy to use other baud rates only. I suppose only need to change timing at dominoTimer() and values of byteToneAdd[].

Most likely I will not develop this code any further. As mentioned before, this was meant to be a stepping stone for THOR-modulator.

Domino6.png

Picture: Oh my KISS, it is working!
/*
Code for testing DominoEX transmission from ad9850 DDS-module and Arduino
Tarmo Huttunen OH6ECF 2014
Note tone1 is lowest freq to use, other tones are added to that
 
*/
 
#define DDS_CLOCK 125000000
 //pin connections for DDS
#define  CLOCK  8
#define  LOAD 9
#define  DATA  10
#define  RESET 11
 
long symbolTime;//starting time of dominoEX-transmission, for symbol timing.
 
void setup()
{
   Serial.begin(9600);
  pinMode (DATA,  OUTPUT);
  pinMode (CLOCK, OUTPUT);
  pinMode (LOAD,  OUTPUT);
  pinMode (RESET, OUTPUT);
  AD9850_init();
  AD9850_reset();
  symbolTime = millis();
 
 
 
}
long tone1 = 10001000;
//incremental frequency in hertz, of tones used
byte byteToneAdd[] = {0,11,22,32,43,53,65,75,86,97,108,118,129,140,151,161,172,183};
byte next = 0; //used to count frequency of next symbol
 
byte dominoState =1;
byte txNible1;
byte txNible2;
byte txNible3;
byte txNible1a;
byte txNible2a;
byte txNible3a;
byte dominoBuffer;
 
//Simple converter function, from characters to nibles used on dominoEX
void domino11Char(char txChar){
switch(txChar){
  case 'A': domino11(5,11,0); break;
  case 'B': domino11(6,16,0); break;
  case 'C': domino11(5,14,0); break;
  case 'D': domino11(5,16,0); break;
  case 'E': domino11(5,10,0); break;
  case 'F': domino11(6,14,0); break;
  case 'G': domino11(7,10,0); break;
  case 'H': domino11(7,12,0); break;
  case 'I': domino11(5,12,0); break;
  case 'J': domino11(9,10,0); break;
  case 'K': domino11(8,12,0); break;
  case 'L': domino11(6,13,0); break;
  case 'M': domino11(6,10,0); break;
  case 'N': domino11(6,15,0); break;
  case 'O': domino11(5,13,0); break;
  case 'P': domino11(6,11,0); break;
  case 'Q': domino11(8,17,0); break;
  case 'R': domino11(5,15,0); break;
  case 'S': domino11(4,17,0); break;
  case 'T': domino11(4,16,0); break;
  case 'U': domino11(7,13,0); break;
  case 'V': domino11(8,15,0); break;
  case 'W': domino11(7,15,0); break;
  case 'X': domino11(7,17,0); break;
  case 'Y': domino11(8,11,0); break;
  case 'Z': domino11(9,11,0); break;
  case '1': domino11(6,12,0); break;
  case '2': domino11(6,17,0); break;
  case '3': domino11(7,11,0); break;
  case '4': domino11(8,10,0); break;
  case '5': domino11(7,14,0); break;
  case '6': domino11(7,16,0); break;
  case '7': domino11(8,14,0); break;
  case '8': domino11(8,13,0); break;
  case '9': domino11(8,16,0); break;
  case '0': domino11(5,17,0); break;
 
  case ' ': domino11(2,2,2); break;
 
  default: domino11(9,14,0);// ?-character
}
}
 
 
 
 
long symbolOn = 0;
char inChar=0;
char txString[20];
byte txIndex = 0;
byte txOnStoreIndex = 0;
byte dominoTransmitterOn = 0;
char serialString[20];
byte serialIndex = 0;
 
void loop()
{
if (dominoTransmitterOn == 1){
  dominoTimer();
  }
 
  if(Serial.available() > 0){
     inChar = Serial.read();
 
     switch(inChar){
 
     case '%' :
       //This is needed to avoid older serialString material to be mixed with frequency-characters from serial
       for (byte x = 0; x < 20; x++){
            serialString[x] = 'x'; //Any non-number character will work here fo atol()
           }
       break;
 
     case '&':
       tone1 = atol(serialString) - 100; //-100 to matc "center freq", tone1 is the lowest freq used
       Serial.print("New center frequency is: ");
       Serial.print((tone1+100));Serial.print("\n");
       txOnStoreIndex = 0;
       serialIndex = 0;
       break;
 
     case '¤': dominoTransmitterOn = 1; Serial.print("Domino transmitter on \n");break;
     case '#': dominoTransmitterOn = 0; AD9850_reset(); Serial.print("Domino transmitter off \n"); break;
     //13 = Carriage return
     case 13:{
       for (byte x = 0; x <= serialIndex; x++){
         txString[x] = serialString[x];
         }
       txOnStoreIndex = serialIndex;
       serialIndex = 0;
       break;
       }
       //All other characters are stored to serialString
       default:serialString[serialIndex++]=inChar; break;
    }
  }
 
 
 
if (dominoBuffer == 0 && txOnStoreIndex > 0){
  domino11Char(txString[txIndex]);
  txIndex++;
  }
 
if (txIndex >= txOnStoreIndex) {
  txIndex = 0; txOnStoreIndex = 0;
  }
 
}//End of loop()
 
//Function to store next character to be sent
void domino11(byte nible1, byte nible2, byte nible3)
{
 
 txNible1a = nible1;
 txNible2a = nible2;
 txNible3a = nible3;
 dominoBuffer = 1; //"How many" characters in store (but currently 1 is maximum)
}
 
//Actual transmitting function
void dominoTimer() {
 
  symbolOn = millis() - symbolTime;
 
  if (symbolOn > 92){
  /*DominoState == well, every caharacter on DominoEX is sent by 3 "nible"
  so dominoState corresponds to which nible(1-3) is currently on tx*/
   dominoState = dominoState + 1;
    if (dominoState > 3){
      dominoState = 1;
         if(dominoBuffer ==  0) {
          domino11(5,10,10); //Character for secondary transmission. Needed to keep sync. This one is "space"
          }
 
      if (dominoBuffer == 1){
        txNible1 = txNible1a;
        txNible2 = txNible2a;
        txNible3 = txNible3a;
        dominoBuffer = 0;
        }
    }
 
   symbolTime = symbolTime+93;//Actual timing, or baud-rate.
 
    switch(dominoState){
 
        case 1: {
            next = next + txNible1;
            txNible1 = 0;
              if (next >17){
                next = next - 18;
                }
            long nextFreq = tone1 + long(byteToneAdd[next]);
            SetFrequency(nextFreq);
            break;
            }
 
 
        case 2:{
            next = next + txNible2;
            txNible2 = 0;
              if (next >17){
              next = next - 18;
               }
            long nextFreq = tone1 + long(byteToneAdd[next]);
            SetFrequency(nextFreq);
            break;
            }
 
 
        case 3:{
             next = next + txNible3;
             txNible3 = 0;
               if (next >17){
                 next = next - 18;
                  }
             long nextFreq = tone1 + long(byteToneAdd[next]);
             SetFrequency(nextFreq);
             break;
             }
           }
    }
}
 
 
 
void SetFrequency(unsigned long frequency)
{
  unsigned long tuning_word = (frequency * pow(2, 32)) / DDS_CLOCK;
  digitalWrite (LOAD, LOW);
 
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 8);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 16);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 24);
  shiftOut(DATA, CLOCK, LSBFIRST, 0x0);
  digitalWrite (LOAD, HIGH);
}
 
void AD9850_init()
{
 
  digitalWrite(RESET, LOW);
  digitalWrite(CLOCK, LOW);
  digitalWrite(LOAD, LOW);
  digitalWrite(DATA, LOW);
}
 
void AD9850_reset()
{
  //reset sequence is:
  // CLOCK & LOAD = LOW
  //  Pulse RESET high for a few uS (use 5 uS here)
  //  Pulse CLOCK high for a few uS (use 5 uS here)
  //  Set DATA to ZERO and pulse LOAD for a few uS (use 5 uS here)
 
  // data sheet diagrams show only RESET and CLOCK being used to reset the device, but I see no output unless I also
  // toggle the LOAD line here.
 
  digitalWrite(CLOCK, LOW);
  digitalWrite(LOAD, LOW);
 
  digitalWrite(RESET, LOW);
  delay(5);
  digitalWrite(RESET, HIGH);  //pulse RESET
  delay(5);
  digitalWrite(RESET, LOW);
  delay(5);
 
  digitalWrite(CLOCK, LOW);
  delay(5);
  digitalWrite(CLOCK, HIGH);  //pulse CLOCK
  delay(5);
  digitalWrite(CLOCK, LOW);
  delay(5);
  digitalWrite(DATA, LOW);    //make sure DATA pin is LOW
 
    digitalWrite(LOAD, LOW);
  delay(5);
  digitalWrite(LOAD, HIGH);  //pulse LOAD
  delay(5);
  digitalWrite(LOAD, LOW);
  // Chip is RESET now
}
 
 


Older even more premature version vor generating endles beaconing:
//AD9850 DDS test
 
#define DDS_CLOCK 125000000
 
#define  CLOCK  8  //pin connections for DDS
#define  LOAD 9
#define  DATA  10
#define  RESET 11
 
void setup()
{
  pinMode (DATA,  OUTPUT);
  pinMode (CLOCK, OUTPUT);
  pinMode (LOAD,  OUTPUT);
  pinMode (RESET, OUTPUT);
  AD9850_init();
  AD9850_reset();
 
//  SetFrequency(10000000);
 
 
}
long tone1 = 10001000;
//incremental frequency in hertz, of tones used
byte byteToneAdd[] = {0,11,22,32,43,53,65,75,86,97,108,118,129,140,151,161,172,183};
byte next = 0; //used to count frequency of next symbol
long symbolTime;//starting time of dominoEX-transmission, for symbol timing.
 
 
void domino11(byte nible1, byte nible2, byte nible3)
{
 
symbolTime = millis();
 
//secondary transmission, "idle-tx" = "NUL"
if (nible1 == 0) {
 
  next = next + 2;
  if (next >17){next = next - 18;}
 
  long nextFreq = tone1 + long(byteToneAdd[next]);
  SetFrequency(nextFreq);
  symbolDelay();
 
  next = next + 2;
  if (next >17){next = next - 18;}
 
  nextFreq = tone1 + long(byteToneAdd[next]);
  SetFrequency(nextFreq);
  symbolDelay();
 
  next = next + 2;
  if (next >17){next = next - 18;}
 
  nextFreq = tone1 + long(byteToneAdd[next]);
  SetFrequency(nextFreq);
  symbolDelay();
  }
 
if (nible1 != 0){
  next = next + nible1;
  nible1 = 0;
  if (next >17){next = next - 18;}
 
  long nextFreq = tone1 + long(byteToneAdd[next]);
  SetFrequency(nextFreq);
  symbolDelay();
  }
 
if(nible2 != 0){
  next = next + nible2;
  nible2 = 0;
  if (next >17){next = next - 18;}
 
  long nextFreq = tone1 + long(byteToneAdd[next]);
  SetFrequency(nextFreq);
  symbolDelay();}
 
if(nible3 != 0){
  next = next + nible3;
  nible3 = 0;
  if (next >17){next = next - 18;}
 
long nextFreq = tone1 + long(byteToneAdd[next]);
  SetFrequency(nextFreq);
  symbolDelay();}
 
 
}
 
//Timing of invidual symbols (nibles).
void symbolDelay(){
  long symbolOn = 0;
  while(symbolOn < 92){
    symbolOn = millis() - symbolTime;
    }
symbolTime=millis();
}
 
 
void domino11Char(char txChar){
switch(txChar){
  case 'A': domino11(5,11,0); break;
  case 'B': domino11(6,16,0); break;
  case 'C': domino11(5,14,0); break;
  case 'D': domino11(5,16,0); break;
  case 'E': domino11(5,10,0); break;
  case 'F': domino11(6,14,0); break;
  case 'G': domino11(7,10,0); break;
  case 'H': domino11(7,12,0); break;
  case 'I': domino11(5,12,0); break;
  case 'J': domino11(9,10,0); break;
  case 'K': domino11(8,12,0); break;
  case 'L': domino11(6,13,0); break;
  case 'M': domino11(6,10,0); break;
  case 'N': domino11(6,15,0); break;
  case 'O': domino11(5,13,0); break;
  case 'P': domino11(6,11,0); break;
  case 'Q': domino11(8,17,0); break;
  case 'R': domino11(5,15,0); break;
  case 'S': domino11(4,17,0); break;
  case 'T': domino11(4,16,0); break;
  case 'U': domino11(7,13,0); break;
  case 'V': domino11(8,15,0); break;
  case 'X': domino11(7,17,0); break;
  case 'Y': domino11(8,11,0); break;
  case 'Z': domino11(9,11,0); break;
  case '1': domino11(6,12,0); break;
  case '2': domino11(6,17,0); break;
  case '3': domino11(7,11,0); break;
  case '4': domino11(8,10,0); break;
  case '5': domino11(7,14,0); break;
  case '6': domino11(7,16,0); break;
  case '7': domino11(8,14,0); break;
  case '8': domino11(8,13,0); break;
  case '9': domino11(8,16,0); break;
  case '0': domino11(5,17,0); break;
 
 
  default: domino11(9,14,0);// ?-character
}
}
 
 
void loop()
{
 
domino11Char('O');//A pri
domino11Char('H');//B pri
domino11Char('6');
domino11Char('E');
domino11Char('C');
domino11Char('F');
domino11(0,0,0);
domino11(0,0,0);
 
 
}
 
 
 
void SetFrequency(unsigned long frequency)
{
  unsigned long tuning_word = (frequency * pow(2, 32)) / DDS_CLOCK;
  digitalWrite (LOAD, LOW);
 
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 8);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 16);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 24);
  shiftOut(DATA, CLOCK, LSBFIRST, 0x0);
  digitalWrite (LOAD, HIGH);
}
 
void AD9850_init()
{
 
  digitalWrite(RESET, LOW);
  digitalWrite(CLOCK, LOW);
  digitalWrite(LOAD, LOW);
  digitalWrite(DATA, LOW);
}
 
void AD9850_reset()
{
  //reset sequence is:
  // CLOCK & LOAD = LOW
  //  Pulse RESET high for a few uS (use 5 uS here)
  //  Pulse CLOCK high for a few uS (use 5 uS here)
  //  Set DATA to ZERO and pulse LOAD for a few uS (use 5 uS here)
 
  // data sheet diagrams show only RESET and CLOCK being used to reset the device, but I see no output unless I also
  // toggle the LOAD line here.
 
  digitalWrite(CLOCK, LOW);
  digitalWrite(LOAD, LOW);
 
  digitalWrite(RESET, LOW);
  delay(5);
  digitalWrite(RESET, HIGH);  //pulse RESET
  delay(5);
  digitalWrite(RESET, LOW);
  delay(5);
 
  digitalWrite(CLOCK, LOW);
  delay(5);
  digitalWrite(CLOCK, HIGH);  //pulse CLOCK
  delay(5);
  digitalWrite(CLOCK, LOW);
  delay(5);
  digitalWrite(DATA, LOW);    //make sure DATA pin is LOW
 
    digitalWrite(LOAD, LOW);
  delay(5);
  digitalWrite(LOAD, HIGH);  //pulse LOAD
  delay(5);
  digitalWrite(LOAD, LOW);
  // Chip is RESET now
}