Fast SPI-based serial interface to AD9850 clock generator. Has same interface as (bit-banging based) AD9850 library, but more than x100 faster in frequency update speed. Can sweep 1KHz to 30MHz in 1KHz step in a few seconds.
AD9850SPI.cpp
00001 /** 00002 * AD9850 driver using hardware SPI. 00003 * 00004 * - http://www.analog.com/static/imported-files/data_sheets/AD9850.pdf 00005 * - http://developer.mbed.org/users/liamg/code/AD9850-function-generator-SPI-driver/ 00006 */ 00007 00008 #include "AD9850SPI.h" 00009 00010 #ifndef AD9850_CLOCK_REF 00011 #define AD9850_CLOCK_REF 125000000U 00012 #endif 00013 00014 #ifndef UINT32_MAX 00015 #define UINT32_MAX ((uint32_t)-1) 00016 #endif 00017 00018 #define UINT5_MAX 31 00019 #define AD9850_PHASE_MAX 360 00020 00021 /** 00022 * Create AD9850SPI instance. 00023 * 00024 * SPI instance should be externally created and passed in, 00025 * with DATA as MOSI and W_CLK as SCLK. Note that NC can be 00026 * given for MISO, as AD9850 does not have any output. 00027 * 00028 * @param spi SPI interface to use 00029 * @param fu_ud Chip select pin 00030 * @param reset Reset pin 00031 */ 00032 AD9850SPI::AD9850SPI(SPI &spi, PinName fq_ud, PinName reset_pin) 00033 : _spi(spi), _fq_ud(fq_ud), _reset(reset_pin) { 00034 reset(); 00035 } 00036 00037 AD9850SPI::~AD9850SPI() {} 00038 00039 /** 00040 * Reset SPI parameter (only) to match with AD9850 requirement. 00041 * 00042 * This is useful when sharing SPI bus line with multiple chips 00043 * with different SPI parameter. 00044 */ 00045 void 00046 AD9850SPI::reset_spi(void) { 00047 // 00048 // Configure SPI mode. AD9850 isn't actually an SPI, but can be 00049 // handled as SPI in following manner: 00050 // 00051 // - SCLK is low on inactive (CPOL=0) 00052 // - DATA is sampled on asserting edge (CPHA=0) 00053 // - SSEL needs to be kept low during whole 40-bit transfer 00054 // - Hardware controlled SSEL tends to only support up to 16-bit transfer 00055 // 00056 // So AD9850 can be handled as SPI with CPOL=0, CPHA=0, with some 00057 // nonstandard pins that needs to be controlled separately. 00058 // 00059 // For SSEL, FQ_UD pin can be specified as GPIO-based SSEL in SPI module. 00060 // However, it is separately controlled in this code as semantic of FQ_UD 00061 // pin is quite different from usual SSEL. FQ_UD is expected to be usually 00062 // low, and expected to go high-low to finalize loaded value. 00063 // 00064 00065 _spi.format(8 /* bits */, 0 /* mode */); 00066 } 00067 00068 /** 00069 * Reset device and enable serial mode. 00070 * Also reconfigures SPI bus parameter for transfer. 00071 */ 00072 void 00073 AD9850SPI::reset(void) { 00074 // initialize bus parameter first 00075 reset_spi(); 00076 00077 // 00078 // DS Figure 7. Master Reset Timing Sequence 00079 // 00080 // - Minimum reset width is 5 CLKIN cycles 00081 // - Minimum reset latency is 13 CLKIN cycles 00082 // - Minimum reset delay from clkin edge is 3.5ns (= ~142MHz) 00083 // 00084 // Slowest reference clock supported by AD9850 is 1MHz (1us cycle time). 00085 // Even in that case, ~20us would be enough for resetting. 00086 // 00087 00088 _reset = 0; wait_us(20); // just in case 00089 _reset = 1; wait_us(6); 00090 _reset = 0; wait_us(14); 00091 00092 // 00093 // DS Figure 10. Serial Load Enable Sequence 00094 // 00095 // - Set hardware pins to D0=1, D1=1, D2=0 00096 // - Send a single pulse in W_CLK, then to FU_UD 00097 // - This is actually just a parallel load of D[012] values 00098 // 00099 // A dummy write to SPI followed by FU_UD update should do the trick. 00100 // 00101 00102 _fq_ud = 0; wait_us(1); 00103 _spi.write(0); 00104 _fq_ud = 1; wait_us(1); 00105 _fq_ud = 0; wait_us(1); 00106 } 00107 00108 /** 00109 * Set frequency. 00110 * Note frequency should be <33% of reference clock when used as a clock generator. 00111 * 00112 * @param freq Frequency in Hz (maps to W0-W31) 00113 * @param powerdown Power-down bit (maps to W34) 00114 * @param phase Phase in degrees (maps to W35-W39) 00115 */ 00116 void 00117 AD9850SPI::setFrequency(int freq, int powerdown, int phase) { 00118 // 00119 // DS Table IV. 40-Bit Serial Load Word Function Assignment 00120 // 00121 // - 40bit data in total 00122 // - W0-W31: Frequency normalized to 32-bit value (LSB-first) 00123 // - W32-W33: MUST be 0 (DS Table II. Factory Reserved Internal Test Codes) 00124 // - W34: Power-down bit 00125 // - W35-W39: Phase normalized to 5-bit scaled value (LSB-first) 00126 // 00127 // AD9850 requires LSBit-first transfer while SPI is MSBit-first. 00128 // So bit order needs to be reversed before passing the data to SPI. 00129 // 00130 00131 unsigned int scaled_freq = __RBIT(freq * (UINT32_MAX / AD9850_CLOCK_REF)); 00132 unsigned int scaled_phase = __RBIT(phase * (UINT5_MAX / AD9850_PHASE_MAX)); 00133 00134 char buffer[5]; 00135 *(unsigned int *)buffer = __REV(scaled_freq); 00136 buffer[4] = 0b00111111 & ( 00137 ((!!powerdown) << 5) | (scaled_phase >> 27) 00138 ); 00139 00140 _spi.write(buffer, sizeof(buffer), NULL, 0); 00141 _fq_ud = 1; wait_us(1); 00142 _fq_ud = 0; wait_us(1); 00143 }
Generated on Mon Jul 25 2022 22:39:35 by
1.7.2