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.

Committer:
Taisuke Yamada
Date:
Mon Jan 13 23:20:58 2020 +0900
Branch:
switch-to-cmsis-bitops
Revision:
6:8c15ffb18f88
Parent:
4:c60c1caa2593
Minor macro update

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Taisuke Yamada 4:c60c1caa2593 1 /**
Taisuke Yamada 4:c60c1caa2593 2 * AD9850 driver using hardware SPI.
Taisuke Yamada 4:c60c1caa2593 3 *
Taisuke Yamada 4:c60c1caa2593 4 * - http://www.analog.com/static/imported-files/data_sheets/AD9850.pdf
Taisuke Yamada 4:c60c1caa2593 5 * - http://developer.mbed.org/users/liamg/code/AD9850-function-generator-SPI-driver/
Taisuke Yamada 4:c60c1caa2593 6 */
Taisuke Yamada 4:c60c1caa2593 7
Taisuke Yamada 4:c60c1caa2593 8 #include "AD9850SPI.h"
Taisuke Yamada 4:c60c1caa2593 9
Taisuke Yamada 4:c60c1caa2593 10 #ifndef AD9850_CLOCK_REF
Taisuke Yamada 4:c60c1caa2593 11 #define AD9850_CLOCK_REF 125000000U
Taisuke Yamada 4:c60c1caa2593 12 #endif
Taisuke Yamada 4:c60c1caa2593 13
Taisuke Yamada 4:c60c1caa2593 14 #ifndef UINT32_MAX
Taisuke Yamada 6:8c15ffb18f88 15 #define UINT32_MAX ((uint32_t)-1)
Taisuke Yamada 4:c60c1caa2593 16 #endif
Taisuke Yamada 4:c60c1caa2593 17
Taisuke Yamada 4:c60c1caa2593 18 #define UINT5_MAX 31
Taisuke Yamada 4:c60c1caa2593 19 #define AD9850_PHASE_MAX 360
Taisuke Yamada 4:c60c1caa2593 20
Taisuke Yamada 4:c60c1caa2593 21 /**
Taisuke Yamada 4:c60c1caa2593 22 * Create AD9850SPI instance.
Taisuke Yamada 4:c60c1caa2593 23 *
Taisuke Yamada 4:c60c1caa2593 24 * SPI instance should be externally created and passed in,
Taisuke Yamada 4:c60c1caa2593 25 * with DATA as MOSI and W_CLK as SCLK. Note that NC can be
Taisuke Yamada 4:c60c1caa2593 26 * given for MISO, as AD9850 does not have any output.
Taisuke Yamada 4:c60c1caa2593 27 *
Taisuke Yamada 4:c60c1caa2593 28 * @param spi SPI interface to use
Taisuke Yamada 4:c60c1caa2593 29 * @param fu_ud Chip select pin
Taisuke Yamada 4:c60c1caa2593 30 * @param reset Reset pin
Taisuke Yamada 4:c60c1caa2593 31 */
Taisuke Yamada 4:c60c1caa2593 32 AD9850SPI::AD9850SPI(SPI &spi, PinName fq_ud, PinName reset_pin)
Taisuke Yamada 4:c60c1caa2593 33 : _spi(spi), _fq_ud(fq_ud), _reset(reset_pin) {
Taisuke Yamada 4:c60c1caa2593 34 reset();
Taisuke Yamada 4:c60c1caa2593 35 }
Taisuke Yamada 4:c60c1caa2593 36
Taisuke Yamada 4:c60c1caa2593 37 AD9850SPI::~AD9850SPI() {}
Taisuke Yamada 4:c60c1caa2593 38
Taisuke Yamada 4:c60c1caa2593 39 /**
Taisuke Yamada 4:c60c1caa2593 40 * Reset SPI parameter (only) to match with AD9850 requirement.
Taisuke Yamada 4:c60c1caa2593 41 *
Taisuke Yamada 4:c60c1caa2593 42 * This is useful when sharing SPI bus line with multiple chips
Taisuke Yamada 4:c60c1caa2593 43 * with different SPI parameter.
Taisuke Yamada 4:c60c1caa2593 44 */
Taisuke Yamada 4:c60c1caa2593 45 void
Taisuke Yamada 4:c60c1caa2593 46 AD9850SPI::reset_spi(void) {
Taisuke Yamada 4:c60c1caa2593 47 //
Taisuke Yamada 4:c60c1caa2593 48 // Configure SPI mode. AD9850 isn't actually an SPI, but can be
Taisuke Yamada 4:c60c1caa2593 49 // handled as SPI in following manner:
Taisuke Yamada 4:c60c1caa2593 50 //
Taisuke Yamada 4:c60c1caa2593 51 // - SCLK is low on inactive (CPOL=0)
Taisuke Yamada 4:c60c1caa2593 52 // - DATA is sampled on asserting edge (CPHA=0)
Taisuke Yamada 4:c60c1caa2593 53 // - SSEL needs to be kept low during whole 40-bit transfer
Taisuke Yamada 4:c60c1caa2593 54 // - Hardware controlled SSEL tends to only support up to 16-bit transfer
Taisuke Yamada 4:c60c1caa2593 55 //
Taisuke Yamada 4:c60c1caa2593 56 // So AD9850 can be handled as SPI with CPOL=0, CPHA=0, with some
Taisuke Yamada 4:c60c1caa2593 57 // nonstandard pins that needs to be controlled separately.
Taisuke Yamada 4:c60c1caa2593 58 //
Taisuke Yamada 4:c60c1caa2593 59 // For SSEL, FQ_UD pin can be specified as GPIO-based SSEL in SPI module.
Taisuke Yamada 4:c60c1caa2593 60 // However, it is separately controlled in this code as semantic of FQ_UD
Taisuke Yamada 4:c60c1caa2593 61 // pin is quite different from usual SSEL. FQ_UD is expected to be usually
Taisuke Yamada 4:c60c1caa2593 62 // low, and expected to go high-low to finalize loaded value.
Taisuke Yamada 4:c60c1caa2593 63 //
Taisuke Yamada 4:c60c1caa2593 64
Taisuke Yamada 4:c60c1caa2593 65 _spi.format(8 /* bits */, 0 /* mode */);
Taisuke Yamada 4:c60c1caa2593 66 }
Taisuke Yamada 4:c60c1caa2593 67
Taisuke Yamada 4:c60c1caa2593 68 /**
Taisuke Yamada 4:c60c1caa2593 69 * Reset device and enable serial mode.
Taisuke Yamada 4:c60c1caa2593 70 * Also reconfigures SPI bus parameter for transfer.
Taisuke Yamada 4:c60c1caa2593 71 */
Taisuke Yamada 4:c60c1caa2593 72 void
Taisuke Yamada 4:c60c1caa2593 73 AD9850SPI::reset(void) {
Taisuke Yamada 4:c60c1caa2593 74 // initialize bus parameter first
Taisuke Yamada 4:c60c1caa2593 75 reset_spi();
Taisuke Yamada 4:c60c1caa2593 76
Taisuke Yamada 4:c60c1caa2593 77 //
Taisuke Yamada 4:c60c1caa2593 78 // DS Figure 7. Master Reset Timing Sequence
Taisuke Yamada 4:c60c1caa2593 79 //
Taisuke Yamada 4:c60c1caa2593 80 // - Minimum reset width is 5 CLKIN cycles
Taisuke Yamada 4:c60c1caa2593 81 // - Minimum reset latency is 13 CLKIN cycles
Taisuke Yamada 4:c60c1caa2593 82 // - Minimum reset delay from clkin edge is 3.5ns (= ~142MHz)
Taisuke Yamada 4:c60c1caa2593 83 //
Taisuke Yamada 4:c60c1caa2593 84 // Slowest reference clock supported by AD9850 is 1MHz (1us cycle time).
Taisuke Yamada 4:c60c1caa2593 85 // Even in that case, ~20us would be enough for resetting.
Taisuke Yamada 4:c60c1caa2593 86 //
Taisuke Yamada 4:c60c1caa2593 87
Taisuke Yamada 4:c60c1caa2593 88 _reset = 0; wait_us(20); // just in case
Taisuke Yamada 4:c60c1caa2593 89 _reset = 1; wait_us(6);
Taisuke Yamada 4:c60c1caa2593 90 _reset = 0; wait_us(14);
Taisuke Yamada 4:c60c1caa2593 91
Taisuke Yamada 4:c60c1caa2593 92 //
Taisuke Yamada 4:c60c1caa2593 93 // DS Figure 10. Serial Load Enable Sequence
Taisuke Yamada 4:c60c1caa2593 94 //
Taisuke Yamada 4:c60c1caa2593 95 // - Set hardware pins to D0=1, D1=1, D2=0
Taisuke Yamada 4:c60c1caa2593 96 // - Send a single pulse in W_CLK, then to FU_UD
Taisuke Yamada 4:c60c1caa2593 97 // - This is actually just a parallel load of D[012] values
Taisuke Yamada 4:c60c1caa2593 98 //
Taisuke Yamada 4:c60c1caa2593 99 // A dummy write to SPI followed by FU_UD update should do the trick.
Taisuke Yamada 4:c60c1caa2593 100 //
Taisuke Yamada 4:c60c1caa2593 101
Taisuke Yamada 4:c60c1caa2593 102 _fq_ud = 0; wait_us(1);
Taisuke Yamada 4:c60c1caa2593 103 _spi.write(0);
Taisuke Yamada 4:c60c1caa2593 104 _fq_ud = 1; wait_us(1);
Taisuke Yamada 4:c60c1caa2593 105 _fq_ud = 0; wait_us(1);
Taisuke Yamada 4:c60c1caa2593 106 }
Taisuke Yamada 4:c60c1caa2593 107
Taisuke Yamada 4:c60c1caa2593 108 /**
Taisuke Yamada 4:c60c1caa2593 109 * Set frequency.
Taisuke Yamada 4:c60c1caa2593 110 * Note frequency should be <33% of reference clock when used as a clock generator.
Taisuke Yamada 4:c60c1caa2593 111 *
Taisuke Yamada 4:c60c1caa2593 112 * @param freq Frequency in Hz (maps to W0-W31)
Taisuke Yamada 4:c60c1caa2593 113 * @param powerdown Power-down bit (maps to W34)
Taisuke Yamada 4:c60c1caa2593 114 * @param phase Phase in degrees (maps to W35-W39)
Taisuke Yamada 4:c60c1caa2593 115 */
Taisuke Yamada 4:c60c1caa2593 116 void
Taisuke Yamada 4:c60c1caa2593 117 AD9850SPI::setFrequency(int freq, int powerdown, int phase) {
Taisuke Yamada 4:c60c1caa2593 118 //
Taisuke Yamada 4:c60c1caa2593 119 // DS Table IV. 40-Bit Serial Load Word Function Assignment
Taisuke Yamada 4:c60c1caa2593 120 //
Taisuke Yamada 4:c60c1caa2593 121 // - 40bit data in total
Taisuke Yamada 4:c60c1caa2593 122 // - W0-W31: Frequency normalized to 32-bit value (LSB-first)
Taisuke Yamada 4:c60c1caa2593 123 // - W32-W33: MUST be 0 (DS Table II. Factory Reserved Internal Test Codes)
Taisuke Yamada 4:c60c1caa2593 124 // - W34: Power-down bit
Taisuke Yamada 4:c60c1caa2593 125 // - W35-W39: Phase normalized to 5-bit scaled value (LSB-first)
Taisuke Yamada 4:c60c1caa2593 126 //
Taisuke Yamada 4:c60c1caa2593 127 // AD9850 requires LSBit-first transfer while SPI is MSBit-first.
Taisuke Yamada 4:c60c1caa2593 128 // So bit order needs to be reversed before passing the data to SPI.
Taisuke Yamada 4:c60c1caa2593 129 //
Taisuke Yamada 4:c60c1caa2593 130
Taisuke Yamada 4:c60c1caa2593 131 unsigned int scaled_freq = __RBIT(freq * (UINT32_MAX / AD9850_CLOCK_REF));
Taisuke Yamada 4:c60c1caa2593 132 unsigned int scaled_phase = __RBIT(phase * (UINT5_MAX / AD9850_PHASE_MAX));
Taisuke Yamada 4:c60c1caa2593 133
Taisuke Yamada 4:c60c1caa2593 134 char buffer[5];
Taisuke Yamada 4:c60c1caa2593 135 *(unsigned int *)buffer = __REV(scaled_freq);
Taisuke Yamada 4:c60c1caa2593 136 buffer[4] = 0b00111111 & (
Taisuke Yamada 4:c60c1caa2593 137 ((!!powerdown) << 5) | (scaled_phase >> 27)
Taisuke Yamada 4:c60c1caa2593 138 );
Taisuke Yamada 4:c60c1caa2593 139
Taisuke Yamada 4:c60c1caa2593 140 _spi.write(buffer, sizeof(buffer), NULL, 0);
Taisuke Yamada 4:c60c1caa2593 141 _fq_ud = 1; wait_us(1);
Taisuke Yamada 4:c60c1caa2593 142 _fq_ud = 0; wait_us(1);
Taisuke Yamada 6:8c15ffb18f88 143 }