GY-63_MS5611/libraries/AD9833/README.md
2024-07-04 13:04:09 +02:00

12 KiB

Arduino CI Arduino-lint JSON check GitHub issues

License: MIT GitHub release PlatformIO Registry

AD9833

Arduino library for AD9833 function generator.

Description

Experimental library for the AD9833 function (waveform) generator (12.5 MHz) The library supports both hardware SPI and software SPI.

The AD9833 is a signal generator that has two channels for frequency and two channels for the phase. These channels can be set separately to have maximum flexibility.

The AD9833 can generate three waveforms: sine, square (2x) and triangle. The frequency of the waves cover a range from 0 to 12.5 MHz. The step size for frequency is ~0.1 Hz (using 25 MHz reference clock).

The library also can set the phase of each wave from 0° to 360°. The step size for phase is ~0.1°.

type freq max freq step phase phase step Notes
AD9833 12.5 MHz 0.1 Hz 0..360 0.1°

Note: With an external 1 MHz clock smaller frequency steps 0.004 Hz. can be made. This is not tested yet.

Compatibles ?

List of (partially) compatibles in the series, that might work (partially) with this library.

type freq max freq step wave forms Notes
AD9832 12.5 MHz SINE
AD9833 12.5 MHz 0.1 Hz SINE TRANGLE SQUARE for reference
AD9834 37.5 MHz 0.28 Hz SINE TRANGLE has extra HW lines.
AD9835 50.0 MHz 0.01 Hz ?? looks not compatible
AD9837 16.0 MHz 0.06 Hz SINE TRANGLE SQUARE
AD9837 8.0 MHz 0.06 Hz SINE TRANGLE

TODO: Investigations needed, verify table below (hardware needed).

If you have experience with one of the above "compatibles" and this library, please let me know by opening an issue. Probably they need a dedicated library based on this one.

0.4.0 Breaking change

Version 0.4.0 introduced a breaking change to improve handling the SPI dependency. The user has to call SPI.begin() or equivalent before calling AD.begin(). Optionally the user can provide parameters to the SPI.begin(...)

0.2.0 breaking change

The version 0.2.0 has breaking changes in the interface. The essence is removal of ESP32 specific code from the library. This makes it possible to support the ESP32-S3 and other processors in the future. Also it makes the library a bit simpler to maintain.

Note: the parameters for pins have moved to the constructor.
Note: the order of the parameters of the software SPI constructor has changed in 0.2.0.

Connection

Schema AD9833 chip, breakout will have different pins.

                  TOP VIEW
                +-----------+
   COMP         | 1      10 |  VOUT
   VDD          | 2       9 |  A-GND
   CAP / 2.5 V  | 3       8 |  FSYNC (select)
   D-GND        | 4       7 |  SCLK
   MCLK         | 5       6 |  SDATA
                +-----------+

PIN Description
COMP DAC Bias Pin
VDD Power supply
CAP / 2.5 V (datasheet)
D-GND Digital Ground
MCLK Digital Clock Input
SDATA Serial Data In
SCLK Serial Clock In
FSYNC Select
A-GND Analog Ground
VOUT Analog Out

Read datasheet for detailed description of the pins.

Interface

#include "AD9833.h"

Constructor

If the selectPin is set to 255, external FSYNC is used. See section below.

  • AD9833(uint8_t selectPin, SPIClassRP2040 * mySPI = &SPI) Constructor HW SPI.
  • AD9833(uint8_t selectPin,SPIClass * mySPI = &SPI ) Constructor HW SPI
  • AD9833(uint8_t selectPin, uint8_t dataPin, uint8_t clockPin) Constructor SW SPI.
  • void begin() initializes SPI + internals.
  • void reset() does a hardwareReset(), and sets the control register to B28 for the setFrequency()
  • void hardwareReset() resets all registers to 0.
  • bool setPowerMode(uint8_t mode = 0) set the powerMode. Default is 0, wake up. So use setPowerMode(0) to wake up the device. Returns false if mode is out of range. Details see datasheet.
  • uint8_t getPowerMode() returns current powerMode bits.
powerMode meaning
0 no power saving
1 powers down the on-chip DAC
2 disable internal MCLK clock
3 combination of mode 1 & 2

Waveform

  • void setWave(uint8_t waveform)
  • uint8_t getWave()
waveform define name value notes
No output AD9833_OFF 0
Sine AD9833_SINE 1
Square AD9833_SQUARE1 2
Square2 AD9833_SQUARE2 3 half frequency
Triangle AD9833_TRIANGLE 4

Frequency

Default channel is 0, which makes the function calls simpler when only using one channel.

  • float setFrequency(float freq, uint8_t channel = 0) SetFrequency sets the frequency and is limited by the MaxFrequency of 12.5 MHz. Returns the frequency set.
  • float getFrequency(uint8_t channel = 0) returns the frequency set.
  • float getMaxFrequency() returns the maximum frequency to set (convenience).
  • void setFrequencyChannel(uint8_t channel) select the active frequency of channel (0 or 1).

Note: the frequency depends on the internal reference clock which is default 25 MHz. The library does not support other reference clocks yet.

Phase

Default channel is 0, which makes the function calls simpler when only using one channel.

  • float setPhase(float phase, uint8_t channel = 0) setPhase sets the phase and is limited to 0° - 360°. Returns the phase set in degrees.
  • float getPhase(uint8_t channel = 0) returns the phase set in degrees.
  • float getMaxPhase() returns the maximum phase to set (convenience).
  • void setPhaseChannel(uint8_t channel) select the active phase channel (0 or 1).

Since 0.4.2 the library supports get and set the phase in radians.

  • float setPhaseRadians(float phase, uint8_t channel = 0) setPhase sets the phase and is limited to 0 - 2PI. Returns the phase set in radians.
  • float getPhaseRadians(uint8_t channel = 0) returns the phase set in radians.

Hardware SPI

To be used only if one needs a specific speed. Has no effect when using SW SPI.

  • void setSPIspeed(uint32_t speed) set SPI transfer rate.
  • uint32_t getSPIspeed() returns SPI transfer rate.
  • bool usesHWSPI() returns true if HW SPI is used.

Low level API

Use at your own risk, please read the datasheet carefully.

Since version 0.1.1 writing to the registers is made public. By using the low level API to access the registers directly, one has maximum control over the AD9833 device. Especially frequency setting is improved as the float parameter of setFrequency() does not have the 28 bits precision of the register.

  • void writeControlRegister(uint16_t value) see datasheet
  • void writeFrequencyRegister(uint8_t channel, uint32_t freq) channel = 0 or 1, freq = 0 .. 268435455 (2^28 - 1 )
  • void writePhaseRegister(uint8_t channel, uint16_t value) channel = 0 or 1, value = 0 .. 4095

HLB mode

To support the HLB mode the library supports (experimental 0.3.0) two new calls. These functions allow one to set the frequency in coarse steps (MSB) of around 3050 Hz and fine steps (LSB) of around 0.093 Hz.

  • void writeFrequencyRegisterLSB(uint8_t channel, uint16_t LSB) channel = 0 or 1, LSB = 0 .. 32767
  • void writeFrequencyRegisterMSB(uint8_t channel, uint16_t MSB) channel = 0 or 1, MSB = 0 .. 32767 (in theory)

Note the HLB calls take only 2 SPI calls to adjust the frequency. Therefore they are slightly faster than the setFrequency() (uses 3 SPI calls) if you only need to modify one of the two frequency registers.

Only using the LSB register allows one to go from 0 .. 3050 Hz. In piano scales this covers C0 (16.35 Hz) to F#7 (2959.96 Hz).

https://pages.mtu.edu/~suits/notefreqs.html

SetCrystalFrequency()

(experimental, might change in the future)

Since version 0.3.1 the user can adjust the frequency of the crystal oscillator used. This can be done when the default crystal of 25 MHz is replaced e.g. by one of 10 MHz. It also allows adjustments of the clock e.g. for temperature drift or otherwise. This drift is not investigated further. (Feedback welcome).

Two functions are defined for this:

  • void setCrystalFrequency(float crystalFrequency = 25000000) set the frequency of the new crystal. There is no range check.
  • float getCrystalFrequency() returns the current (set) crystal frequency.

An effect of using a variable for crystal frequency instead of a const float is that the function setFrequency() has become slower from 44 us to 76 us on UNO. So instead of these functions the crystalFreqFactor might become a const float in the library.

External FSYNC

Experimental => use with care!

If in the begin() function the selectPin is set to 255 external FSYNC is used. This allows to control e.g many AD9833 devices in parallel, with multi-IO chips. Think of the SPI based MCP23S08/17, or the I2C based PCF8574/75.

The advantage is that one can control many (e.g. 16 devices) with a minimum of IO lines.

The disadvantage is that you need to add extra code lines to set / clear the FSYNC line(s). Furthermore one should know that using this "external FSYNC" is slower than direct control with MCU pins from within the library.

Pin count wise this concept is only interesting for 3 or more AD9833 devices.

Code wise you need to "manual" control the FSYNC.

  setFsyncLow(5);        //  select device 5
  AD.setFrequency(440);  //  set a new frequency
  setFsyncHigh(5);       //  update the setting.

As this implementation is experimental, the interface might change in the future.

Future

Must

  • update documentation

Should

  • investigate external clock
  • test on ESP32 (3V3)

Could

  • setFrequency() if cache value equals new frequency?
    • if (_freq[channel] == frequency) return frequency;
  • setPhase() if cache value equals new phase?
    • if (_phase[channel] == phase) return phase;
    • make phase array 16 bit "register value"?
  • getFrequency() calculate freq back from set register value?
    • slower
    • more accurate.
    • separate function getFrequencyFromRegister() !
  • getPhase() calculate phase back from set register value?
    • idem.
  • extend unit tests
  • add examples
    • for ESP32 HWSPI interface
  • solve MAGIC numbers (defaults)
  • extend performance measurements
  • add defines AD9833_POWERMODE_xxxx ?
  • investigate compatibility AD9834 a.o.
    • need time / HW for this.

Wont

Support

If you appreciate my libraries, you can support the development and maintenance. Improve the quality of the libraries by providing issues and Pull Requests, or donate through PayPal or GitHub sponsors.

Thank you,