DDS gadgeteer board is on the way too

I’m designing a DDS board using AD9851 chip from Analog Device. I’ve used few years ago with other applications, but now I want a gadgeteer version with some modifications.
The board has an AD9851 chip as DDS and I’ve added a small RF amplifier AD8008 to get 40mW output on a 50ohm load (antenna). This board can generate sinusoidal signal from 0 to 60Mhz wihout problem. Analog device doesn’t help with standard interfaces, but this chip can be bitbanged with SPI signals or with GPIO signals from an Y socket. It needs 40bits (5bytes) to be programmed. The board is still in development and I hope to do all the math for filters and amplifier in this days.
I will try to use this board for two main porpouse:

  1. Antenna analisys and match (Reflected power).
  2. RTTY (Radio teletype) slow speed (45.45bauds) trasmissions on HF (7mhz) band of continuos reading from adc sensors. This feature need fast freq change (FSK) on 170Hz shift, so I hope I can use C# and not need to swap on native code.

Signal output through BNC connector (50ohm unbalanced line).

3 Likes

Looks cool - look forward to seeing the finished board :slight_smile:

Nice module!

Yes … DDS is now working with correct components, until 40Mhz ! Some more math for the low pass filter.
Also SPI bitbanging works very easy on Cerberus
In the snapshot you can see 2 40Mhz sine wave out of fase.


        void ThreadDDS()
        {
            // extender board on socket 5 (S), Cerberus
            byte[] data = new byte[] { 0x9c, 0x71, 0xc7, 0x1c, 0x80 }; // 40Mhz, 6XRefClock, power on
            Socket socket = Socket.GetSocket(extender.ExtenderSocketNumber, false, null, null);
            SPI.Configuration spiconf = new SPI.Configuration((Cpu.Pin)socket.CpuPins[6], false, 0, 0, false, true, 100, socket.SPIModule);
            SPI dds = new SPI(spiconf);
            Thread.Sleep(200);
            while (true)
            {
                //PulseDebugLED();
                dds.Write(data);
                Thread.Sleep(500);
            }
        }

Next will test FSK modulation …

Nice job! I look forward to seeing what you make with it.

I’m very happy, that RTTY trasmission works fine also with C# code, no need for RLP. I needed some trick to fasten the code.


    enum CURRENT_MODE
    {
        NONE,
        LETTERS,
        FIGURES
    }

    public class Rtty
    {
        // constant define
        const int ARRAY_LEN = 32;
        const int LETTERS_SHIFT  = 31;
        const int FIGURES_SHIFT = 27;
        const int LINEFEED = 2;
        const int CARRRTN = 8;
        const int DDS_REF = 180000000; // 30 MHZ osc x 6 PLL
        const int RTTY_SHIFT = 170;
        const string letters_arr = "§E\nA SIU\rDRJNFCKTZLWHYPQOBG MXV ";
        const string figures_arr = "§3\n- \a87\r$4',!:(5\")2#6019?&.; ";
        SPI _dds;
        //public vars
        int _rtty_freq;
        int _rtty_offs;

        ulong _frequency; // actual freq in HZ
        byte _phase;

        public Rtty(ulong frequency, SPI dds, byte phase)
        {
            _frequency = frequency;
            _dds = dds;
            _phase = phase;
        }

        public ulong ChangeFrequency(ulong new_freq, byte phase)
        {
            ulong old_freq = _frequency;                
            _frequency = new_freq;
            _phase = phase;
            SetDDSFrequency(_frequency);
            return old_freq;
        }

        private void SetDDSFrequency(ulong freq)
        {
            byte[] data = new byte[5];
            ulong tuning_word = (freq * (ulong)System.Math.Pow(2D, 32D)) / DDS_REF;
            data[3] = Reverse( (byte)((tuning_word >> 24) & 0xFF));
            data[2] = Reverse( (byte)((tuning_word >> 16) & 0xFF));
            data[1] = Reverse( (byte)((tuning_word >> 8) & 0xFF));
            data[0] = Reverse( (byte) (tuning_word & 0xFF));
            data[4] = 0x80; // 6xref enabled
            _dds.Write(data);
        }

        byte Reverse(byte x)
        {
            return BitReverseTable.BitReverseTable256[x];
        }

        bool IsLowerCase(char ch)
        {
            return ((ch) >= 'a' && (ch) <= 'z');
        }

        bool IsUpperCase(char ch)
        {
            return ((ch) >= 'A' && (ch) <= 'Z');
        }

        public void RttyTxString(string str)
        {
          CURRENT_MODE current_mode = CURRENT_MODE.NONE;
          char c;
          byte b;
          int cc = 0;
          for( int slen = 0; slen < str.Length; slen++)
          {
                c = str[cc];
                /* some characters are available in both sets */
                if (c == '\n')
                {
                      RttyTxByte(LINEFEED);
                }
                else if (c == '\r')
                {
                      RttyTxByte(CARRRTN);
                }
                else if (IsLowerCase(str[cc]) || IsUpperCase(str[cc]))
                {
                      if (IsLowerCase(str[cc]))
                      {
                            c -= (char)32;
                      }
                      if (current_mode != CURRENT_MODE.LETTERS)
                      {
                            RttyTxByte(LETTERS_SHIFT);
                            current_mode = CURRENT_MODE.LETTERS;
                      }
 
                      RttyTxByte(char_to_baudot(c, letters_arr));
                }
                else
                {
                  b = char_to_baudot(c, figures_arr);
 
                  if (b != 0 && current_mode != CURRENT_MODE.FIGURES)
                  {
                    RttyTxByte(FIGURES_SHIFT);
                    current_mode = CURRENT_MODE.FIGURES;
                  }

                  RttyTxByte(b);
                }
 
                cc++;
              }
        }

        public void RttyTxByte(byte b)
        {
            byte i;

            RttyTxBit(0);

            /* TODO: I don't know if baudot is MSB first or LSB first */
            //for (i = 4; i >= 0; i--)
            for (i = 0; i < 5; i++)
            {
                if ((b & (1 << i)) != 0 )
                    RttyTxBit(1);
                else
                    RttyTxBit(0);
            }
            RttyTxBit(1);
        }

        // Transmit a bit as a mark or space
        public void RttyTxBit (int bit)
        {
          if (bit == 1)
          {
            // High - mark   
            SetDDSFrequency(_frequency + RTTY_SHIFT);
          }
          else 
          {
            // Low - space    
            SetDDSFrequency(_frequency);
          }
  
          // Delay appropriately - tuned to 45.45 baud.
          Thread.Sleep(20); //sets the baud rate
        }

        byte char_to_baudot(char c, string array)
        {
            byte i;
            for (i = 0; i < array.Length; i++)
            {
                if (array[i] == c)
                    return i;
            }
            return 0;
        }
    }

    public static class BitReverseTable
    {
        public static byte[] BitReverseTable256 = new byte[]
        {
          0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
          0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
          0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
          0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
          0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
          0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
          0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
          0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
          0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
          0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
          0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
          0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
          0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
          0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
          0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
          0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
        };
    }


Very exciting … board is working nice, but RF amplificator is too noisy damned … need some more work about that.

1 Like

Amazing!!!

Tell more about the setup you are using in this demo, please.

@ Architect, the setup is very easy.
I’ve an old 2008 board done in china with a specific idea but I’ve only tested with small pic mcu at that time and no more used becouse was not reliable. Now I’ve modified the board components to be a little better (the schematic is similar to that I want for gadgeteer) but without RF amplifier.
I use a Cerberus, an extender and the ad9851 smashed board. The Cerberus talk to ad9851 via SPI interface at 1MHZ clock. The AD9851 has 30Mhz oscillator that is use 6x for 180Mhz reference clock for the DAC. In this example the board was working at 14.080Mhz.
In the code you can see all major points like setup AD9851 frequency and FSK modulation
A part of the RTTY code is from Tim Zaman, very good stuff as a starting point.

@ dobova - QRZ

@ Mike : IW2CLM

@ dobova - K2VPX

@ Mike - Ahhahaha nice to know! Mike may be we get on the air…

Do you know Pietro IZ2EWR? He is nearby in Piantedo.

I don’t get on the air much. I can get on 10,15 or 20. I usually have good signal into Italy.

@ Mike, I know him although not personally but I had few QSO with him. I’ve not much time to spend on HF, but I switch on often the rig.

Are you on Google+? If so, we can try a hangout now.

Sorry Mike I got the message too late. Will try today in (your) afternoon.
at 17:00 UTC I will QRX on 14.310.

Hi,

How difficult would it be to adapt your design for sound synthesis instead of RF ? I know almost nothing about DDS chips and design…

Regards,

@ dobova - weekends are the only time I can get on the air. how about this Sunday at 1300UTC? 14.310

@ Mike - yes fine Mike!

In the meantime I’m working on BPSK31 modulator software.

Hi Fradev,
This DDS is not well suited for range in 0-20Khz domain. It can do that but the the embedded DAC create lot of distortion at so low frequency, probably needing a very sofisticated Chebyshev filter .
In any case it depends what are trying to achieve. If you want a DDS for sinusoidal signal you can use a cheap MCU and a good 12-14bit DAC that can work at 40khz using a wavetable and timer. Other solution are specialized chips like Crystal, Atmel Midi generators. A well known chip for DDS in low frequency is XR2206, but not it’s very easy to interface.
The AD9851 (but many other of the same line IC) scope is to generate a base good signal carrier.