├── .gitignore ├── LICENSE ├── README.md ├── ad983x.cpp └── ad983x.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Nick Johnson 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ad983x 2 | ====== 3 | 4 | Arduino library for interfacing with AD9833, AD9834 and AD9838 5 | -------------------------------------------------------------------------------- /ad983x.cpp: -------------------------------------------------------------------------------- 1 | #include "ad983x.h" 2 | 3 | #define REG_FREQ1 0x8000 4 | #define REG_FREQ0 0x4000 5 | #define REG_PHASE0 0xC000 6 | #define REG_PHASE1 0xE000 7 | 8 | #define REG_B28 0x2000 9 | #define REG_HLB 0x1000 10 | #define REG_FSEL 0x0800 11 | #define REG_PSEL 0x0400 12 | #define REG_PINSW 0x0200 13 | #define REG_RESET 0x0100 14 | #define REG_SLEEP1 0x0080 15 | #define REG_SLEEP12 0x0040 16 | #define REG_OPBITEN 0x0020 17 | #define REG_SIGNPIB 0x0010 18 | #define REG_DIV2 0x0008 19 | #define REG_MODE 0x0002 20 | 21 | #define SIGN_OUTPUT_MASK (REG_OPBITEN | REG_SIGNPIB | REG_DIV2 | REG_MODE) 22 | #define SLEEP_MASK (REG_SLEEP1 | REG_SLEEP12) 23 | 24 | static SPISettings settingsAD(40000000, MSBFIRST, SPI_MODE2); 25 | 26 | 27 | void AD983X::writeReg(uint16_t value) { 28 | SPI.beginTransaction(settingsAD); 29 | *m_select_out &= ~m_select_bit; //Set m_select_pin LOW 30 | 31 | SPI.transfer16(value); 32 | 33 | *m_select_out |= m_select_bit; //Set m_select_pin HIGH 34 | SPI.endTransaction(); 35 | } 36 | 37 | 38 | void AD983X::writeReg2(uint16_t value1, uint16_t value2) { 39 | SPI.beginTransaction(settingsAD); 40 | *m_select_out &= ~m_select_bit; //Set m_select_pin LOW 41 | 42 | SPI.transfer16(value1); 43 | SPI.transfer16(value2); 44 | 45 | *m_select_out |= m_select_bit; //Set m_select_pin HIGH 46 | SPI.endTransaction(); 47 | } 48 | 49 | 50 | void AD983X::setFrequencyWord(byte reg, uint32_t frequency) { 51 | uint16_t reg_freq = reg?REG_FREQ1:REG_FREQ0; 52 | writeReg2(reg_freq | (frequency & 0x3FFF), reg_freq | ((frequency >> 14) & 0x3FFF)); 53 | } 54 | 55 | void AD983X::setPhaseWord(byte reg, uint32_t phase) { 56 | writeReg((reg?REG_PHASE1:REG_PHASE0) | (phase & 0x0FFF)); 57 | } 58 | 59 | void AD983X::setSignOutput(SignOutput out) { 60 | m_reg = (m_reg & ~SIGN_OUTPUT_MASK) | out; 61 | writeReg(m_reg); 62 | } 63 | 64 | void AD983X::setOutputMode(OutputMode out) { 65 | if(out == OUTPUT_MODE_TRIANGLE) { 66 | m_reg = (m_reg & ~SIGN_OUTPUT_MASK) | out; 67 | } else { 68 | m_reg &= ~REG_MODE; 69 | } 70 | writeReg(m_reg); 71 | } 72 | 73 | void AD983X::setSleep(SleepMode out) { 74 | m_reg = (m_reg & ~SLEEP_MASK) | out; 75 | writeReg(m_reg & ~REG_PINSW); // Always per SW 76 | } 77 | 78 | void AD983X::init() { 79 | SPI.begin(); 80 | writeReg(m_reg); 81 | 82 | // Initialize frequency and phase registers to 0 83 | this->setFrequencyWord(0, 0); 84 | this->setFrequencyWord(1, 0); 85 | this->setPhaseWord(0, 0); 86 | this->setPhaseWord(1, 0); 87 | } 88 | 89 | AD983X::AD983X(byte select_pin, int frequency_mhz) : 90 | m_select_pin(select_pin), m_frequency_mhz(frequency_mhz), m_reg(REG_B28) { 91 | uint8_t port = digitalPinToPort(select_pin); 92 | m_select_out = portOutputRegister(port); 93 | m_select_bit = digitalPinToBitMask(select_pin); 94 | digitalWrite(select_pin, HIGH); // Set it first to HIGH, to avoid small period LOW state 95 | pinMode(select_pin, OUTPUT); 96 | } 97 | 98 | void AD983X_SW::reset(boolean in_reset) { 99 | if(in_reset) { 100 | m_reg |= REG_RESET; 101 | } else { 102 | m_reg &= ~REG_RESET; 103 | } 104 | this->writeReg(m_reg); 105 | } 106 | 107 | void AD983X_SW::begin() { 108 | m_reg |= REG_RESET; 109 | init(); 110 | reset(false); 111 | } 112 | 113 | AD983X_SW::AD983X_SW(byte select_pin, int frequency_mhz) : 114 | AD983X(select_pin, frequency_mhz) { 115 | } 116 | 117 | void AD983X_SW::selectFrequency(byte reg) { 118 | if(reg) { 119 | m_reg |= REG_FSEL; 120 | } else { 121 | m_reg &= ~REG_FSEL; 122 | } 123 | writeReg(m_reg); 124 | } 125 | 126 | void AD983X_SW::selectPhase(byte reg) { 127 | if(reg) { 128 | m_reg |= REG_PSEL; 129 | } else { 130 | m_reg &= ~REG_PSEL; 131 | } 132 | writeReg(m_reg); 133 | } 134 | 135 | void AD983X_PIN::reset(boolean in_reset) { 136 | digitalWrite(m_reset_pin, in_reset); 137 | } 138 | 139 | void AD983X_PIN::begin() { 140 | reset(true); 141 | init(); 142 | reset(false); 143 | } 144 | 145 | AD983X_PIN::AD983X_PIN(byte select_pin, int frequency_mhz, byte reset_pin) : 146 | AD983X(select_pin, frequency_mhz), m_reset_pin(reset_pin) { 147 | pinMode(m_reset_pin, OUTPUT); 148 | m_reg |= REG_PINSW | REG_RESET; 149 | } 150 | -------------------------------------------------------------------------------- /ad983x.h: -------------------------------------------------------------------------------- 1 | #ifndef __ARDUINO_AD9838X 2 | #define __ARDUINO_AD9838X 3 | 4 | #include 5 | #include 6 | 7 | enum SignOutput { 8 | SIGN_OUTPUT_NONE = 0x0000, 9 | SIGN_OUTPUT_MSB = 0x0028, 10 | SIGN_OUTPUT_MSB_2 = 0x0020, 11 | SIGN_OUTPUT_COMPARATOR = 0x0038, 12 | }; 13 | 14 | enum OutputMode { 15 | OUTPUT_MODE_SINE = 0x0000, 16 | OUTPUT_MODE_TRIANGLE = 0x0002, 17 | }; 18 | 19 | enum SleepMode { 20 | SLEEP_MODE_NONE = 0x0000, 21 | SLEEP_MODE_MCLK = 0x0080, 22 | SLEEP_MODE_DAC = 0x0040, 23 | SLEEP_MODE_ALL = 0x00C0, 24 | }; 25 | 26 | class AD983X { 27 | public: 28 | AD983X(byte select_pin, int frequency_mhz); 29 | void setFrequencyWord(byte reg, uint32_t frequency); 30 | void setPhaseWord(byte reg, uint32_t phase); 31 | void setSignOutput(SignOutput out); 32 | void setOutputMode(OutputMode out); 33 | void setSleep(SleepMode out); 34 | 35 | inline uint32_t computeFrequencyWord(uint32_t frequency) { 36 | // This is a manual expansion of (frequency * 2^28) / m_frequency_mhz 37 | // Since it doesn't require 64 bit multiplies or divides, it results in 38 | // substantially smaller code sizes. 39 | uint32_t lval = ((frequency & 0xFF) << 22) / (15625l * m_frequency_mhz); 40 | uint32_t mval = ((frequency & 0xFF00) << 14) / (15625l * m_frequency_mhz); 41 | uint32_t hval = ((frequency & 0xFF0000) << 6) / (15625l * m_frequency_mhz); 42 | return (hval << 16) + (mval << 8) + lval; 43 | } 44 | 45 | inline void setFrequency(byte reg, long int frequency) { 46 | frequency = this->computeFrequencyWord(frequency); 47 | this->setFrequencyWord(reg, frequency); 48 | } 49 | 50 | inline void setFrequency(byte reg, float frequency) { 51 | this->setFrequencyWord(reg, (frequency * (1l << 28)) / (m_frequency_mhz * 1000000)); 52 | } 53 | 54 | protected: 55 | void init(); 56 | void writeReg(uint16_t value); 57 | void writeReg2(uint16_t value1, uint16_t value2); 58 | 59 | byte m_select_pin; 60 | int m_frequency_mhz; 61 | uint16_t m_reg; 62 | volatile uint8_t *m_select_out; 63 | uint8_t m_select_bit; 64 | }; 65 | 66 | class AD983X_PIN : public AD983X { 67 | private: 68 | byte m_reset_pin; 69 | public: 70 | AD983X_PIN(byte select_pin, int frequency_mhz, byte reset_pin); 71 | void reset(boolean in_reset); 72 | void begin(); 73 | }; 74 | 75 | class AD983X_SW : public AD983X { 76 | public: 77 | AD983X_SW(byte select_pin, int frequency_mhz); 78 | void reset(boolean in_reset); 79 | void begin(); 80 | void selectFrequency(byte reg); 81 | void selectPhase(byte reg); 82 | }; 83 | 84 | #endif 85 | --------------------------------------------------------------------------------