Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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