element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Open Source Hardware
  • Technologies
  • More
Open Source Hardware
Blog Building A Direct Digital Synthesis Dual-Channel Signal Generator
  • Blog
  • Forum
  • Documents
  • Events
  • Polls
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Open Source Hardware to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: shabaz
  • Date Created: 11 Aug 2018 6:37 AM Date Created
  • Views 23260 views
  • Likes 17 likes
  • Comments 148 comments
  • mbed enabled
  • ad9834
  • signal generator
  • direct digital synthesis (dds)
  • arm mbed
  • freescale
  • mbed os 5
  • dds
  • kinetis
  • direct digital synthesizer
  • ad9954
  • frdm
  • frdm-kl25z
  • radiofrequencych
  • mbed
  • frdm-family
  • frequency synthesizer
  • mbed-os
  • nxp
Related
Recommended

Building A Direct Digital Synthesis Dual-Channel Signal Generator

shabaz
shabaz
11 Aug 2018

Introduction

This blog post discusses some practical implementations of super-low-cost ($30 upward) direct digital synthesis (DDS) based signal generator for home use. To save effort and time, they can all be built around an off-the-shelf microcontroller board, and an off-the-shelf direct digital synthesis (DDS) board.

image

There are several different ideas discussed in this blog post, and all are similar – the principles are exactly the same, just that frequency range and desired control/monitoring capabilities can be chosen to suit needs. They are all be lumped together since they are all so similar.

 

There is a three-minute demo of the user interface here:

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

What Does it Do?

This project allows for the creation of controlled sine waves, at a desired frequency. This has uses for testing radio receivers, measuring the frequency response of filters, creating a radio transmission, creating a stimulus for testing amplifiers, and so on.

 

Manipulations such as frequency sweeping, frequency modulation and frequency shift keying (FSK) is possible with this project.

 

This project relies on an external amplifier or attenuator to adjust the signal level, although some limited adjustment is possible without additional hardware.

 

How Does it Work?

For a more in-depth look at DDS, see the blog post here Building a Frequency Synthesiser but briefly, the DDS board constructs a sine wave digitally by sending a table of numbers to a digital to analog converter (DAC). DDS devices allow a desired frequency to be programmed using an internal register (e.g. programmed via an SPI or other I/O bus). The programmed value is fed into a summing device or counter, known as a phase accumulator. This is just responsible for repeatedly adding the programmed value to the existing sum, on each clock cycle. So, if the desired frequency is a low value then the phase accumulator will increment in low values too. If the desired frequency is higher, then the phase accumulator will increment in larger chunks The phase accumulator loops back to zero once it reaches its maximum. Each possible value that the output of the accumulator can take represents a different point on a sinewave. A look-up table is used to convert from the phase accumulator output to an amplitude on the curve of a sine wave. This then drives a DAC and and external filter.

 

Some of the benefits are:

  • Extremely low noise close to the oscillation frequency
  • High granularity (sub-Hz is possible)
  • On-board RAM and instruction set allow for very fast frequency hopping or ramping (it is possible to achieve FM and FSK modulation using this method)

 

A disadvantage of DDS is that there can be some spurious content (usually relatively far away from the selected output frequency) that may need filtering for very demanding applications. Since filters can be expensive, often just a fixed low-pass filter is used, as in the project described here.

 

DDS boards are available from ebay, with a digital input interface, and an RF connector ready for providing the signal output. All that is required is to configure the DDS board to output the desired frequency, using a microcontroller for example.

 

For the project described here, a FRDM-KL25Z microcontroller boardmicrocontroller board is used. It is very low cost (about $15) and easy to program using a USB cable. For more detail about the board and the programming process, see here: Working with FRDM Boards and ARM mbed 

 

The board has 128 kbyte Flash and 16 kbyte RAM, and lots of input/output pins, on a near-credit-card-sized board around 5mm thick - it's really great!

(image source: NXP website)image

 

 

The easiest implementation was to not have any buttons or display on the device, and just enable the USB connection on the FRDM board to become a USB Serial interface. Thus whenever the user wishes to modify the frequency output, all that needs to be done is to connect the board to a PC via USB, and run terminal software such as PuTTY on Windows or ‘screen’ on Mac or Linux. A text-based menu system becomes the user interface.

image

 

With this method, the project can be constructed for about $30 or a bit more, depending on which DDS board is used. I tried two boards; a 0-10 MHz one, and a 0.05-160 MHz board.

 

If a permanent display is desired, an LCD screen can be attached. It adds another $20 to the cost. If buttons are needed, they can be added too. A rotary encoder or even a keypad could also be attached. There are plenty of free pins available on the FRDM board. Although I didn’t do it for this project, for information on how to attach a keypad, see here: Building a USB Keypad: A Mini-Project

 

In summary there’s a lot of flexibility concerning implementation. The core functionality uses the USB Serial menu system however. Code can be added to make use of the display, and buttons/keypad/encoder. I'd also like to incorporate a USB based SCPI interface (based on Jan Cumps SCPI project on element14) at some stage. This blog post is just to give ideas, not all combinations are implemented today so if you modify the code to implement a new combination, please do share the code.

 

The current code implements the text menu system, and rotary encoder for frequency selection, and display update indication of the menu selections and frequency. The plan is to extend rotary encoder usage so that it works with the menu system. So, if the text menu has the options such as sine and ramp at a particular menu level for instance, then the rotary encoder should also allow for selection of these two options before going into the next menu level in the hierarchy.

 

For this project I decided to develop my own menu system called picocli (I originally planned to take a Linux shell’s source code and run it Linux-kernel-less since the FRDM board has just a microcontroller). I may still revert to that, but for now I’m using custom code). It is at proof-of-concept level but functions reliably enough for now to use in this project, and it implements things like help, tab completion and a last-command history. It will be explored more in Part 2.

 

I also had to write a custom rotary encoder library rather than use an existing one, because I wanted to integrate things like velocity based modification of behaviour. Anyway, this was a short exercise compared to writing the menu system.

 

Making the Basic Configuration

The basic implementation (using a DDS board and the FRDM-KL25Z board connected to the PC for control via a USB serial interfaced menu) takes about five minutes to assemble. One or two boards are purchased and wired together with jumper cables.

image

 

For the DC to 10 MHz range, AD9834 boards are purchased. I have only tested a single AD9834 board, but two could be connected in theory if desired, for dual output, but there is no way to synchronise the two board outputs with the AD9834 chip.

 

For approx. 50 kHz to 160 MHz, AD9954 boards are used.

 

Nearly all the wiring throughout the project relied on jumper wires. To make these to custom lengths, this crimp toolthis crimp tool is great. It works with the crimpscrimps and then the result is inserted into SIL and DIL header socket shells. Standard pre-assembled jumper wires could be used too.

image

 

Here is a table of the specific wiring shown in the diagram above:

 

AD9834 DDS board markingFRDM-KL25Z board marking (conn. J9)
FSY  (fsync)B11 (PTB11) J9 pin 7
PS (phase select)B9 (PTB9)   J9 pin 3
SCK (clk)E3 (PTE3)   J9 pin 11
FS (freq select)B10 (PTB10) J9 pin 5
SDA (data)E2 (PTE2)   J9 pin 9
RST (reset)B8 (PTB8)   J9 pin 1
GND (0V)GND           J9 pin 12
5V5V              J9 pin 10

 

 

For using the 160 MHz boards, the connection diagram is below. Please note that I believe this diagram is correct, but I don’t have these actual boards to verify. I used some custom boards (based on the same chip) that I created a while back. However I see no reason why the ebay boards wouldn’t work.

image

 

With a bit of effort, it will be possible to have phase locked and phase adjustable outputs with these 160 MHz boards, since the AD9954 chip supports this capability. I have not tried it yet, because I’m still considering the optimal way to do that (I didn’t originally design my custom boards with that capability in mind : (

 

Still, there is plenty of room in the enclosure to add this capability at a future date once I’ve figured it out.

image

 

Building the Software

Instructions on how to use the development environment and how to push the compiled code into the board are described at the location mentioned earlier: Working with FRDM Boards and ARM mbed 

 

The code can be compiled and then programmed into the board via the USB cable. The next blog post will cover the software, but if you wish to use it now (it won’t be pretty code, it still needs tidying) then please let me know.

 

Using It

Connect up the output of the DDS board to an oscilloscope or spectrum analyzer, plug in the USB connection into the PC and the project should power up! Open up a serial terminal and connect to the board. Hit the Enter key and a command prompt (‘$’) should appear. You can type help or ? for assistance. Generally the menu structure has been designed to accept a command, or a command followed by parameters. Some commands will drop you into a new level in the menu, and you can exit that level by typing exit.

 

So, to output a sinewave at 1.2 MHz, type the following to enter the sinewave mode and invoke the signal output at the desired frequency:

 

$ sine
sine$ frequency 1.2M 
sine$ on

 

Here is another example, to generate a frequency sweep from 2.1 MHz to 2.2 MHz. Here the sweep command will drop you into the sweep mode, so type exit afterwards if you want to go into a different mode. You can type help or ? at any time to get help for the mode that you’re in:

 

sweep
range 2.1M 2.3M
on

 

There are extra parameters that can be configured (press ‘?’ to see the commands to do so) for adjusting the sweep steps and so on. I'd like to add a trigger output on the rear panel, so that the project can be used for basic scalar analysis of things like filters.

image

 

Adding a Display and User Controls

I decided to use an enclosure and front panel similar to what I’ve used before for this project: Building a SMT Pick-and-Place Buddy and so both projects rely on a Hammond instrument case and LCD on the front, and FRDM board stuck at the back so that the USB connector is accessible. The two signal outputs will also be at the back (using SMA connectors) because the front panel ended up becoming too full. It's not very convenient, but I can live with it.

image

 

This time I used a parallel mode LCD which is more inconvenient to use than the I2C displays, but I wanted to use it for the high quality output. The display uses ‘VATN’ LCD technology, which looks very close to Organic LED (OLED) in terms of contrast, when viewed at the right angle. So, it has near-OLED looks, but with LCD display lifetime, which seemed ideal for a test instrument. For the user input, I used a rotary encoder. An optical encoder with no detents is quite good for setting frequency, because it can be rapidly spun for getting from one end of the spectrum to the other in a short time. I used a Marquardt switch too. These almost feel like a keyboard switch, so they are light to press.

 

The Hammond case comes with aluminium plates, but plastic is easier to machine so I replaced the front panel with a black ABS plastic sheet instead, and cut a few other bits of plastic to hold the switch and rotary encoder at a convenient depth. A 3D printer could be used to make these, although I did it manually with sheet plastic.

image

 

After lots of epoxying, the front panel was semi-complete.

image

 

Sanding parts makes them easier to epoxy together. Thread-lock glue was used to hold some of the screwed bits together.

image

 

The particular rotary encoder I had operated at 5V whereas the FRDM-KL25Z board has 3.3V logic inputs, so a couple of potential divider resistor pairs were used on the quadrature encoder output lines, to drop from 5V to 3.3V (I used 1.8k and 3.3k resistors); the resistors can be seen attached to the header socket that will be connected to the rotary encoder in the photo below.

image

 

The LCD screen was a couple of millimetres taller than the inside of the case! : ( so I had to cut a shallow groove in the top and bottom of the case, to accommodate it. A piece of wood was glued to hold the display in the correct position. Jumper cables were used everywhere in case things ever need disassembly.

image

 

Performance

The performance is quite good, but that’s to be expected from DDS technology.

 

Here is what it looks like on an oscilloscope, using the AD9834 board; the output extends down to DC incidentally, because there is no output capacitor on the board.

image

 

The AD9954 board provides for up to 160 MHz output although at the low end it is limited; much lower than 50 kHz results in reduced amplitude, because my board has a transformer on the output. The ebay versions do not have this according to the photos on ebay, so they should extend down to DC.

image

 

It is hard to tell how good or bad a signal is from the ‘scope trace, so the FPC1500 spectrum analyzer was used. A span of 2MHz was examined when the DDS was set to 9.55MHz, and also 134MHz in the case of the AD9954. The result was good. The close-in noise is almost entirely due to the crystal oscillator module. The module on the AD9954 board for example contains an internal phase locked loop (PLL) to generate a 100MHz clock. Another PLL inside the AD9954 multiplies this by 4 to generate a 400MHz internal clock. I have a 400MHz oscillator module (which still uses a PLL) that I may try at some point; it has worse jitter than the 100MHz module, but it means that I can then disable the PLL inside the AD9954. Or I could try to source a better module, but it could get expensive.

 

The AD9834 board uses a similar style of oscillator module, with a PLL inside it too I suspect.

image

 

To see how this compared with a commercial signal generator, I connected a Rigol DG1022Z to the FPC1500. The DG1022Z has a 14-bit DAC internally, as does the AD9954 DDS chip.

 

The result is fairly similar when set to approximately the same power level. At higher output level this situation can change! - see the second spectrum trace below.

image

 

The DG1022Z operates to 25MHz, and I only tested at 10MHz. The AD9954 can go to 160MHz. Of course the DG1022Z is more flexible since it has arbitrary waveform capability too, but if just a sine wave and a few frequency modulation based schemes are required, then the AD9834 or AD9954 are great options.

 

Summary

A simple design has been presented to create a fairly usable signal generator. It costs less than $30 and perhaps half an hour of time to build and program it all. The full enclosure version takes longer of course.

 

It could be extended with an amplifier circuit if desired. If you build this project as-is or extend or modify it, it would be great to hear about it to bounce off ideas.

 

image

  • Sign in to reply

Top Comments

  • Jan Cumps
    Jan Cumps over 7 years ago +6
    This is a great project. I'm looking forward to that next post with the code. Also saw that your DDS post Building a Frequency Synthesiser is from 2013. That's a long running project you have here. I'd…
  • fmilburn
    fmilburn over 7 years ago +6
    Hi Shabaz, Your project, as usual, is professional in both build and documentation and I have been working to get my projects to this level. I really like the ability to control settings over serial. For…
  • michaelkellett
    michaelkellett over 7 years ago +5
    Very nice, I like the display. I've taken to using rotary encoders for primary user control. What amplitude of output do the ready made boards offer. MK
Parents
  • derick007
    derick007 over 5 years ago

    Hi Shabaz,

     

    I have re-written the sketch entirely. My understanding now is alot simpler than when I started 6 months ago - hopefully it is correct ?

     

    It would appear all that is required is to get the ARDUINO to write data to the registers in the AD9954 ?

     

    I have worked all the values for all the registers, for the AD9954, on a spreadsheet so that I only need to use the sketch to send these values to the DDS.

     

    However there are a few things I still am not clear about.

     

    1. UPD - is this required ? If so when do I activate it > at the end of every transfer for each register ?

     

    2. Surely I only need to write the values to the AD9954 once ? This being the case, then the part of the sketch which writes the values should be included in the void setup, rather than the void loop ?

     

    One other thing, I have an old Tetronix 2465 scope which I am using for this project. I bought it some time ago and have rarely used it. Unfortunately it doesnt appear to trigger so well as I am getting a lot of jitter. This is really just a hobby so I have a very limited budget for test equipment - any suggestions apart from the obvious.

     

    Many thanks, Derek

     

     

    //************************************************ 

    //* SCLK test code for MKRZERO 

    //* rev 0.1 December 2019 

    //************************************************

     

     

    // DECLARING / DEFINING VARIABLES & THEIR TYPES

     

    char clockpin = A1;       // declaring or defining the variable named clockpin as a character type which has a value A1

    char datapin = A2;        // declaring or defining the variable named datapin as a character type which has a value A2

    char resetpin = A3;       // declaring or defining the variable named resetpin as a character type which has a value A3

    char ioupdatepin = A4;    // declaring or defining the variable named ioupdatepin as a character type which has a value A4

     

    // DECLARING / DEFINING DDS REGISTERS

    // INTEGERS ARE THE PRIMARY DATA TYPE FOR NUMBER STORAGE

    // THE DDS HAS 11 REGISTERS WHICH NEED TO BE PROGRAMMED

    // REGISTERS HAVE VARIOUS LENGTHS FROM 2 BYTES TO 5 BYTES

    // I HAVE DECLARED EACH REGISTER HAS AN ARRAY OF INTEGER TYPE

    // THE NUMBERS IN THE ARRAYS ARE DECIMAL NUMBERS

     

    int CFR1 [] = {128, 174, 0, 112, 8};      // declaring or defining the variable named CFR1[] (control function register 1) as a 5 byte array and integer data type

    int CFR2 [] = {129, 0, 0, 164};           // declaring or defining the variable named CFR2[] (control function register 2) as a 4 byte array and integer data type

    int ASF [] = {130, 0, 0};                 // declaring or defining the variable named ASF[] (amplitude scale factor) as a 3 byte array and integer data type

    int ARR [] = {131, 0};                    // declaring or defining the variable named ARR[] (amplitude ramp rate) as a 2 byte array and integer data type

    int FTW0 [] = {132, 26, 57, 224, 0};      // declaring or defining the variable named FTW0[] (frequency tuning word 0) as a 5 byte array and integer data type

    int POW0 [] = {133, 0, 0};                // declaring or defining the variable named POW0[] (phase offset word) as a 3 byte array and integer data type

    int FTW1 [] = {134, 26, 57, 224, 0};      // declaring or defining the variable named FTW0[] (frequency tuning word 1) as a 5 byte array and integer data type

    int RSCW0 [] = {135, 0, 0, 0, 0};         // declaring or defining the variable named RSCW0[] (ram segment control word 0) as a 5 byte array and integer data type

    int RSCW1 [] = {136, 0, 0, 0, 0};         // declaring or defining the variable named RSCW1[] (ram segment control word 1) as a 5 byte array and integer data type

    int RSCW2 [] = {137, 0, 0, 0, 0};         // declaring or defining the variable named RSCW2[] (ram segment control word 2) as a 5 byte array and integer data type

    int RSCW3 [] = {138, 0, 0, 0, 0};         // declaring or defining the variable named RSCW3[] (ram segment control word 3) as a 5 byte array and integer data type

     

    void setup() {

     

      pinMode(clockpin, OUTPUT);    // sets pin A1 as OUTPUT

      pinMode(datapin, OUTPUT);    // sets pin A2 as OUTPUT

      pinMode(resetpin, OUTPUT);    // sets pin A3 as OUTPUT

      pinMode(ioupdatepin, OUTPUT);    // sets pin A4 as OUTPUT

     

      //reset the AD9954

     

      digitalWrite(resetpin, LOW);

      delay(1);

      digitalWrite(resetpin,HIGH);

      delay(1);

      digitalWrite(resetpin, LOW);

    }

     

    void loop() {

     

      for (int i=0; i<5; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, CFR1[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

      for (int i=0; i<4; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, CFR2[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

      for (int i=0; i<3; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, ASF[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

      for (int i=0; i<2; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, ARR[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

    for (int i=0; i<5; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, FTW0[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

    }

     

      for (int i=0; i<4; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, POW0[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

      for (int i=0; i<5; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, FTW1[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

      for (int i=0; i<5; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, RSCW0[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

      for (int i=0; i<5; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, RSCW1[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

      for (int i=0; i<5; i=i+1) {

     

    shiftOut(datapin, clockpin, MSBFIRST, RSCW2[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

      for (int i=0; i<5; i=i+1) {

     

      shiftOut(datapin, clockpin, MSBFIRST, RSCW3[i]);  // each bit is approximately 5us. The clock signal also has a period of 5us, 1.5 us ON and 3.5 us OFF.

      }

     

    }

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • michaelkellett
    michaelkellett over 5 years ago in reply to derick007

    Hello Derek,

    Shabaz is correct (almost) re the /CS pin, actually it is essential that you control it - see the diagram on page 24 of the AD9954 data sheet - CS is used to frame the serial control messages.

     

    From page 21 you can see that the IO Update pin must be toggled after a write to registers since the data is transferred from buffers to working registers in the AD9954 only on the rising edge

    of UPDATE, synched with the SYNC_CLK (which is an AD5594 output). There is (probably)  no way that the Arduino can sync correctly with the SYNC_CLK so just toggle UPDATE after setting the

    registers and hope for the best.

     

    Your scope only has two channels so you will need to be creative about checking the signals:

    Use ch1 on SCLK and ch2 on SDIO and trigger from the falling edge of /CS - you should see bursts of clock cycles on SCLK and data patterns on SDIO.

     

    I haven't checked the code in the loop{}, and I've no idea exactly what shiftOut() does - but with your scope you should be able to see if its doing what page 24 requires, if not then let us know !

     

    MK

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 5 years ago in reply to derick007

    Hi Derek,

     

    I've now got a MKRZERO board, so I'll try that out, and see if I can get some code to work. I'm going to write the code so it can instruct the DDS to generate a signal, then if you like you can modify that to suit your needs.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 5 years ago in reply to derick007

    Hi Derek,

     

    The code below is confirmed working with the MKR ZERO board. I wired it to my home-built AD9954 board.

    The code below puts the value 89 MHz into an array variable called data, and it puts 89.1 MHz into an array called data2,

    and then it rapidly writes data into the AD9954, followed by data2.

    So, to test, if an FM radio is tuned to around 89MHz, then a loud tone is heard (provided you've got a short length of wire sticking out of the SMA connector).

    It is a violation to broadcast at that frequency, so if you have some other legal frequency you can use, then the code can be modified for that, but probably no-one will notice if you only do it for a second or two to confirm operation.

     

    This code should work on your board, if it has a 100 MHz crystal oscillator. My board has that, but your board may have a different value crystal oscillator populated so that needs to be verified by you (it may be printed on the crystal oscillator module, if it is not mentioned in your board documentation).

    If the crystal oscillator is of a different value, then the set_sysfreq function needs to be modified (table 12 in the AD9954 documentation PDF specifies what register contents are needed). The AD9954 multiplies up the crystal frequency. I multiplied to the maximum supported in the code for a 100 MHz crystal, i.e. it multiplies to 400 MHz, called the system frequency. If you choose to use a different system frequency, then the SYSFREQ definition in the code will also need to be modified.

     

    //************************************************
    //* dds test code for MKRZERO
    //* rev 0.2 Nov 2019
    //************************************************
    
    
    
    
    // ************** definitions ******************** 
    #define IO_UPD_LOW digitalWrite(A4, LOW)
    #define IO_UPD_HIGH digitalWrite(A4, HIGH)
    #define SCLK_LOW digitalWrite(A1, LOW)
    #define SCLK_HIGH digitalWrite(A1, HIGH)
    #define SDIO_LOW digitalWrite(A2, LOW)
    #define SDIO_HIGH digitalWrite(A2, HIGH)
    #define CS_BAR_LOW digitalWrite(A5, LOW)
    #define CS_BAR_HIGH digitalWrite(A5, HIGH)
    #define RESET_LOW digitalWrite(A3, LOW)
    #define RESET_HIGH digitalWrite(A3, HIGH)
    #define LED_ON digitalWrite(LED_BUILTIN, HIGH)
    #define LED_OFF digitalWrite(LED_BUILTIN, LOW)
    // DDS registers
    #define CFR1 0
    #define CFR2 1
    #define ASF 2
    #define ARR 3
    #define FTW0 4
    #define FTW1 6
    #define RSCW0 7
    #define RAM 0x0b
    
    
    
    
    #define SYSFREQ 400E6
    #define TWLEN 4294967296
    #define FOREVER 1
    
    
    
    
    // This disables the comparator. Set this to zero if
    // square wave output is required!
    #define COMPDIS 1
    
    
    
    
    
    
    // ********** function prototypes *******************
    void set_sysfreq(void); // set SYSFREQ
    void set_amplitude(unsigned int amp); // set amplitude register
    void freq2ftw(double freq, unsigned char* data); // convert freq into 4 bytes that represent it
    void write_bytes(unsigned char instr, unsigned char* data, int len); // write instruction and data to AD9954
    
    
    
    
    
    
    // ************** initialization code ***************
    void setup() {
      int i;
      pinMode(A1, OUTPUT);
      pinMode(A2, OUTPUT);
      pinMode(A3, OUTPUT);
      pinMode(A4, OUTPUT);
      pinMode(A5, OUTPUT);
      pinMode(LED_BUILTIN, OUTPUT);
    
    
      // wait a short while for power to stabilise in the system.
      // blink an LED while we wait
      for (i=0; i<5; i++) {
        LED_ON;
        delay(50);
        LED_OFF;
        delay(50);
      }
      
      // Reset the AD9954 and get the pins into a known state
      RESET_HIGH;
      CS_BAR_HIGH; // initialize *CS to be high
      IO_UPD_LOW; // initialise IO_UPD to be low
      SCLK_LOW; // initialise SCLK to be low (data is toggled in on the rising edge)
      RESET_LOW; // bring the DDS board out of reset
    
    
      delay(50);
    
    
    }
    
    
    
    
    
    
    
    
    // *************** main loop ************************
    void loop() {
      unsigned int asf;
      double freq; // desired frequency
      unsigned char data[12];
      
      double freq2;
      unsigned char data2[12];
      
      asf=16383; // max amplitude is (2^14)-1 i.e. 16383
      freq=89000000; // default to a 89 MHz signal
      freq2=89100000; // this is 89.1 MHz
    
    
    
    
    
    
    
    
      data[0]=0x06; // OSK Enable; we need this to have amplitude control
      data[1]=0; data[2]=0x02; data[3]=0x00; // We always stick to MSB first mode. 0x42
      if (COMPDIS) data[3]|=0x40; // disable comparator
      write_bytes(CFR1, data, 4);
        
      data[0]=255;
      write_bytes(ARR, data, 1); // set amplitude ramp rate register
        
      set_sysfreq();
      set_amplitude(asf); // 0x3fff is max amplitude
        
      freq2ftw(freq, data); // convert frequency into 4 data bytes (frequency tuning word)
      freq2ftw(freq2, data2);
      write_bytes(FTW0, data, 4);
      
      // done! loop forever.
      while(FOREVER) // play a tone by rapidly switching the output frequency
      {
        LED_ON;
        write_bytes(FTW0, data, 4);
        delay(1); // 1 millisecond delay
        LED_OFF;
        write_bytes(FTW0, data2, 4);
        delay(1);
      }
      
    }
    
    
    
    
    
    
    
    
    // ************** set SYSFREQ *************************
    // Sets SYSFREQ to 400MHz
    void
    set_sysfreq(void)
    {
      unsigned char data[3];
      data[0]=0x18; data[1]=0; data[2]=0x24; // 100MHz external oscillator, multiplied by 4 for 400MHz SYSFREQ
      write_bytes(CFR2, data, 3);
    }
    
    
    
    
    
    
    
    
    // *********Set the amplitude register *****************
    // Range is 0 (lowest) to 0x3fff (highest)
    void
    set_amplitude(unsigned int amp)
    {
      unsigned char data[2];
      unsigned char* aptr;
      aptr=(unsigned char*)&
      if (amp>0x3fff)
      {
        // error
        exit(1);
      }
      data[1]=*aptr; // adjust these lines for endian'ness
      data[0]=*(aptr+1);
      write_bytes(ASF, data, 2);
    }
    
    
    
    
    
    
    
    
    // ******* convert freq into 4 data bytes that represent it *****
    void
    freq2ftw(double freq, unsigned char* data)
    {
      unsigned char* tptr;
      unsigned int tword; // 32-bit tuning word
      
      tptr=(unsigned char*)&tword;
      tword=(unsigned int)((freq)*TWLEN/(SYSFREQ));
      // endian'ness: MKR ZERO: *tptr+0 is the least significant byte
      data[3]=*tptr; // adjust these lines for endian'ness
      data[2]=*(tptr+1);
      data[1]=*(tptr+2);
      data[0]=*(tptr+3);
    
    }
    
    
    
    
    
    
    
    
    // **** Write instruction and data to the AD9954 *****************
    void
    write_bytes(unsigned char instr, unsigned char* data, int len)
    {
      int i, j;
      unsigned char dbyte, ibyte;
      
      ibyte=instr;
      CS_BAR_LOW;
      // transmit instruction byte
      for (i=0; i<8; i++)
      {
        if (ibyte & 0x80)
        {
          SDIO_HIGH;
        }
        else
        {
          SDIO_LOW;
        }
        ibyte=ibyte<<1;
        SCLK_HIGH;    
        SCLK_LOW;
      }
      
      // transmit data byte(s)
      for (j=0; j<len; j++)
      {
        dbyte=data[j];
        for (i=0; i<8; i++)
        {
          if (dbyte & 0x80)
          {
            SDIO_HIGH;
          }
          else
          {
            SDIO_LOW;
          }
          dbyte=dbyte<<1;
          SCLK_HIGH;
          SCLK_LOW;
        }
      }
    
    
    
    
    
    
    
    
      IO_UPD_HIGH; // toggle IO_UPD to transfer the data into the registers
      delay(1); // delay 1 millisec
      IO_UPD_LOW;
    }

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • michaelkellett
    michaelkellett over 5 years ago in reply to shabaz

    Interesting - you set /CS low in the write_bytes() function but you never set it high again.

    I'm quite surprised that this works.

    It has the downside that all your commands are effectively sent in one continuous splurge (until the next power cycle) so that a single error will knock it out.

     

    MK

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 5 years ago in reply to michaelkellett

    Hi Michael,

     

    Ohhh.. that was unintentional, I copied my older code and made some tweaks but forgot about the CS line. That's not very robust  : (

    I'll tidy up the code today, put the CS in there and retest. I'm not sure why that worked, in any case it makes sense to get it done correctly, and make the code look a bit nicer too.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to shabaz

    Some ICs allow to work in CS_HOLD mode, where the CS is held low as long as the communication happens.

    They do not need the CS->high to apply the traffic. As Michael indicates, it does not allow the IC to recover from a corrupt communication though. CS-> high allows it to reset its communication state machine.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to shabaz

    Some ICs allow to work in CS_HOLD mode, where the CS is held low as long as the communication happens.

    They do not need the CS->high to apply the traffic. As Michael indicates, it does not allow the IC to recover from a corrupt communication though. CS-> high allows it to reset its communication state machine.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
No Data
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube