Nicholas Korzik / SparkFun_MiniGen_AD9837
Committer:
nkorz
Date:
Fri Dec 07 20:38:44 2018 +0000
Revision:
0:3782bc13e4f5
Child:
1:f7f4f5acb9a2
Final code used for Sparkfun MiniGen Chip.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
nkorz 0:3782bc13e4f5 1 /****************************************************************
nkorz 0:3782bc13e4f5 2 Core class file for MiniGen board.
nkorz 0:3782bc13e4f5 3
nkorz 0:3782bc13e4f5 4 This code is beerware; if you use it, please buy me (or any other
nkorz 0:3782bc13e4f5 5 SparkFun employee) a cold beverage next time you run into one of
nkorz 0:3782bc13e4f5 6 us at the local.
nkorz 0:3782bc13e4f5 7
nkorz 0:3782bc13e4f5 8 2 Jan 2014- Mike Hord, SparkFun Electronics
nkorz 0:3782bc13e4f5 9
nkorz 0:3782bc13e4f5 10 Code developed in Arduino 1.0.5, on an Arduino Pro Mini 5V.
nkorz 0:3782bc13e4f5 11
nkorz 0:3782bc13e4f5 12 **Updated to Arduino 1.6.4 5/2015**
nkorz 0:3782bc13e4f5 13
nkorz 0:3782bc13e4f5 14 Edited by Aniruddh Marellapudi to be compatible
nkorz 0:3782bc13e4f5 15 with mbed LPC1768
nkorz 0:3782bc13e4f5 16 ****************************************************************/
nkorz 0:3782bc13e4f5 17 #include "SparkFun_MiniGen.h"
nkorz 0:3782bc13e4f5 18 #include "mbed.h"
nkorz 0:3782bc13e4f5 19
nkorz 0:3782bc13e4f5 20 // Overloaded constructor, for cases where the chip select pin is not
nkorz 0:3782bc13e4f5 21 // connected to the regular pin. Still assumes standard SPI connections.
nkorz 0:3782bc13e4f5 22 MiniGen::MiniGen(PinName mosi, PinName miso, PinName sclk, PinName cs,
nkorz 0:3782bc13e4f5 23 PinName RX, PinName TX) : _spi(mosi, miso, sclk), _cs(cs), _pc(RX, TX)
nkorz 0:3782bc13e4f5 24 {
nkorz 0:3782bc13e4f5 25 configReg=0;
nkorz 0:3782bc13e4f5 26 configSPIPeripheral();
nkorz 0:3782bc13e4f5 27 }
nkorz 0:3782bc13e4f5 28
nkorz 0:3782bc13e4f5 29 void MiniGen::SPIWrite(uint16_t data)
nkorz 0:3782bc13e4f5 30 {
nkorz 0:3782bc13e4f5 31 //_pc.printf("data: %#X\n\r", data);
nkorz 0:3782bc13e4f5 32 _cs=0;
nkorz 0:3782bc13e4f5 33 wait_us(5);
nkorz 0:3782bc13e4f5 34 _spi.write(data >> 8);
nkorz 0:3782bc13e4f5 35 _spi.write(data & 0xFF);
nkorz 0:3782bc13e4f5 36 wait_us(5);
nkorz 0:3782bc13e4f5 37 _cs=1;
nkorz 0:3782bc13e4f5 38 }
nkorz 0:3782bc13e4f5 39
nkorz 0:3782bc13e4f5 40 void MiniGen::configSPIPeripheral()
nkorz 0:3782bc13e4f5 41 {
nkorz 0:3782bc13e4f5 42 _spi.format(8, 2);
nkorz 0:3782bc13e4f5 43 _spi.frequency(1000000);
nkorz 0:3782bc13e4f5 44 _cs = 1;
nkorz 0:3782bc13e4f5 45 }
nkorz 0:3782bc13e4f5 46
nkorz 0:3782bc13e4f5 47 // reset the AD part. This will disable all function generation and set the
nkorz 0:3782bc13e4f5 48 // output to approximately mid-level, constant voltage. Since we're resetting,
nkorz 0:3782bc13e4f5 49 // we can also forego worrying about maintaining the state of the other bits
nkorz 0:3782bc13e4f5 50 // in the config register.
nkorz 0:3782bc13e4f5 51 void MiniGen::reset()
nkorz 0:3782bc13e4f5 52 {
nkorz 0:3782bc13e4f5 53 uint32_t defaultFreq = freqCalc(100);
nkorz 0:3782bc13e4f5 54 //_pc.printf("default freq: %d, %#X \r\n", defaultFreq, defaultFreq);
nkorz 0:3782bc13e4f5 55 adjustFreq(FREQ0, FULL, defaultFreq);
nkorz 0:3782bc13e4f5 56 adjustFreq(FREQ1, FULL, defaultFreq);
nkorz 0:3782bc13e4f5 57 adjustPhaseShift(PHASE0, 0x0000);
nkorz 0:3782bc13e4f5 58 adjustPhaseShift(PHASE1, 0x0000);
nkorz 0:3782bc13e4f5 59 SPIWrite(0x0100);
nkorz 0:3782bc13e4f5 60 SPIWrite(0x0000);
nkorz 0:3782bc13e4f5 61 }
nkorz 0:3782bc13e4f5 62
nkorz 0:3782bc13e4f5 63 // Set the mode of the part. The mode (trinagle, sine, or square) is set by
nkorz 0:3782bc13e4f5 64 // three bits in the status register: D5 (OPBITEN), D3 (DIV2), and D1 (MODE).
nkorz 0:3782bc13e4f5 65 // Here's a nice truth table for those settings:
nkorz 0:3782bc13e4f5 66 // D5 D1 D3
nkorz 0:3782bc13e4f5 67 // 0 0 x Sine wave output
nkorz 0:3782bc13e4f5 68 // 0 1 x Triangle wave output
nkorz 0:3782bc13e4f5 69 // 1 0 0 Square wave @ 1/2 frequency
nkorz 0:3782bc13e4f5 70 // 1 0 1 Square wave @ frequency
nkorz 0:3782bc13e4f5 71 // 1 1 x Not allowed
nkorz 0:3782bc13e4f5 72 void MiniGen::setMode(MODE newMode)
nkorz 0:3782bc13e4f5 73 {
nkorz 0:3782bc13e4f5 74 // We want to adjust the three bits in the config register that we're
nkorz 0:3782bc13e4f5 75 // interested in without screwing up anything else. Unfortunately, this
nkorz 0:3782bc13e4f5 76 // part is write-only, so we need to maintain a local shadow, adjust that,
nkorz 0:3782bc13e4f5 77 // then write it.
nkorz 0:3782bc13e4f5 78 configReg &= ~0x002A; // Clear D5, D3, and D1.
nkorz 0:3782bc13e4f5 79 // This switch statement sets the appropriate bit in the config register.
nkorz 0:3782bc13e4f5 80 switch(newMode)
nkorz 0:3782bc13e4f5 81 {
nkorz 0:3782bc13e4f5 82 case TRI:
nkorz 0:3782bc13e4f5 83 configReg |= 0x0002;
nkorz 0:3782bc13e4f5 84 break;
nkorz 0:3782bc13e4f5 85 case SQUARE_2:
nkorz 0:3782bc13e4f5 86 configReg |= 0x0020;
nkorz 0:3782bc13e4f5 87 break;
nkorz 0:3782bc13e4f5 88 case SQUARE:
nkorz 0:3782bc13e4f5 89 configReg |= 0x0028;
nkorz 0:3782bc13e4f5 90 break;
nkorz 0:3782bc13e4f5 91 case SINE:
nkorz 0:3782bc13e4f5 92 configReg |= 0x0000;
nkorz 0:3782bc13e4f5 93 break;
nkorz 0:3782bc13e4f5 94 }
nkorz 0:3782bc13e4f5 95
nkorz 0:3782bc13e4f5 96 // Make sure to clear the top two bit to make sure we're writing the config register:
nkorz 0:3782bc13e4f5 97 configReg &= ~0xC000;
nkorz 0:3782bc13e4f5 98
nkorz 0:3782bc13e4f5 99 SPIWrite(configReg); // Now write our shadow copy to the part.
nkorz 0:3782bc13e4f5 100 }
nkorz 0:3782bc13e4f5 101
nkorz 0:3782bc13e4f5 102 // The AD9837 has two frequency registers that can be independently adjusted.
nkorz 0:3782bc13e4f5 103 // This allows us to fiddle with the value in one without affecting the output
nkorz 0:3782bc13e4f5 104 // of the device. The register used for calculating the output is selected by
nkorz 0:3782bc13e4f5 105 // toggling bit 11 of the config register.
nkorz 0:3782bc13e4f5 106 void MiniGen::selectFreqReg(FREQREG reg)
nkorz 0:3782bc13e4f5 107 {
nkorz 0:3782bc13e4f5 108 // For register FREQ0, we want to clear bit 11.
nkorz 0:3782bc13e4f5 109 if (reg == FREQ0) configReg &= ~0x0800;
nkorz 0:3782bc13e4f5 110 // Otherwise, set bit 11.
nkorz 0:3782bc13e4f5 111 else configReg |= 0x0800;
nkorz 0:3782bc13e4f5 112
nkorz 0:3782bc13e4f5 113 // Make sure to clear the top two bit to make sure we're writing the config register:
nkorz 0:3782bc13e4f5 114 configReg &= ~0xC000;
nkorz 0:3782bc13e4f5 115
nkorz 0:3782bc13e4f5 116 SPIWrite(configReg);
nkorz 0:3782bc13e4f5 117 }
nkorz 0:3782bc13e4f5 118
nkorz 0:3782bc13e4f5 119 // Similarly, there are two phase registers, selected by bit 10 of the config
nkorz 0:3782bc13e4f5 120 // register.
nkorz 0:3782bc13e4f5 121 void MiniGen::selectPhaseReg(PHASEREG reg)
nkorz 0:3782bc13e4f5 122 {
nkorz 0:3782bc13e4f5 123 if (reg == PHASE0) configReg &= ~0x0400;
nkorz 0:3782bc13e4f5 124 else configReg |= 0x0400;
nkorz 0:3782bc13e4f5 125
nkorz 0:3782bc13e4f5 126 // Make sure to clear the top two bit to make sure we're writing the config register:
nkorz 0:3782bc13e4f5 127 configReg &= ~0xC000;
nkorz 0:3782bc13e4f5 128
nkorz 0:3782bc13e4f5 129 SPIWrite(configReg);
nkorz 0:3782bc13e4f5 130 }
nkorz 0:3782bc13e4f5 131
nkorz 0:3782bc13e4f5 132 // The frequency registers are 28 bits in size (combining the lower 14 bits of
nkorz 0:3782bc13e4f5 133 // two 16 bit writes; the upper 2 bits are the register address to write).
nkorz 0:3782bc13e4f5 134 // Bits 13 and 12 of the config register select how these writes are handled:
nkorz 0:3782bc13e4f5 135 // 13 12
nkorz 0:3782bc13e4f5 136 // 0 0 Any write to a frequency register is treated as a write to the lower
nkorz 0:3782bc13e4f5 137 // 14 bits; this allows for fast fine adjustment.
nkorz 0:3782bc13e4f5 138 // 0 1 Writes are send to upper 14 bits, allowing for fast coarse adjust.
nkorz 0:3782bc13e4f5 139 // 1 x First write of a pair goes to LSBs, second to MSBs. Note that the
nkorz 0:3782bc13e4f5 140 // user must, in this case, be certain to write in pairs, to avoid
nkorz 0:3782bc13e4f5 141 // unexpected results!
nkorz 0:3782bc13e4f5 142 void MiniGen::setFreqAdjustMode(FREQADJUSTMODE newMode)
nkorz 0:3782bc13e4f5 143 {
nkorz 0:3782bc13e4f5 144 // Start by clearing the bits in question.
nkorz 0:3782bc13e4f5 145 configReg &= ~0x3000;
nkorz 0:3782bc13e4f5 146 // Now, adjust the bits to match the truth table above.
nkorz 0:3782bc13e4f5 147 switch(newMode)
nkorz 0:3782bc13e4f5 148 {
nkorz 0:3782bc13e4f5 149 case COARSE: // D13:12 = 01
nkorz 0:3782bc13e4f5 150 configReg |= 0x1000;
nkorz 0:3782bc13e4f5 151 break;
nkorz 0:3782bc13e4f5 152 case FINE: // D13:12 = 00
nkorz 0:3782bc13e4f5 153 break;
nkorz 0:3782bc13e4f5 154 case FULL: // D13:12 = 1x (we use 10)
nkorz 0:3782bc13e4f5 155 configReg |= 0x2000;
nkorz 0:3782bc13e4f5 156 break;
nkorz 0:3782bc13e4f5 157 }
nkorz 0:3782bc13e4f5 158
nkorz 0:3782bc13e4f5 159 // Make sure to clear the top two bit to make sure we're writing the config register:
nkorz 0:3782bc13e4f5 160 configReg &= ~0xC000;
nkorz 0:3782bc13e4f5 161
nkorz 0:3782bc13e4f5 162 SPIWrite(configReg);
nkorz 0:3782bc13e4f5 163 }
nkorz 0:3782bc13e4f5 164
nkorz 0:3782bc13e4f5 165 // The phase shift value is 12 bits long; it gets routed to the proper phase
nkorz 0:3782bc13e4f5 166 // register based on the value of the 3 MSBs (4th MSB is ignored).
nkorz 0:3782bc13e4f5 167 void MiniGen::adjustPhaseShift(PHASEREG reg, uint16_t newPhase)
nkorz 0:3782bc13e4f5 168 {
nkorz 0:3782bc13e4f5 169 // First, let's blank the top four bits. Just because it's the right thing
nkorz 0:3782bc13e4f5 170 // to do, you know?
nkorz 0:3782bc13e4f5 171 newPhase &= ~0xF000;
nkorz 0:3782bc13e4f5 172 // Now, we need to set the top three bits to properly route the data.
nkorz 0:3782bc13e4f5 173 // D15:D13 = 110 for PHASE0...
nkorz 0:3782bc13e4f5 174 if (reg == PHASE0) newPhase |= 0xC000;
nkorz 0:3782bc13e4f5 175 // ... and D15:D13 = 111 for PHASE1.
nkorz 0:3782bc13e4f5 176 else newPhase |= 0xE000;
nkorz 0:3782bc13e4f5 177 SPIWrite(newPhase);
nkorz 0:3782bc13e4f5 178 }
nkorz 0:3782bc13e4f5 179
nkorz 0:3782bc13e4f5 180 // Okay, now we're going to handle frequency adjustments. This is a little
nkorz 0:3782bc13e4f5 181 // trickier than a phase adjust, because in addition to properly routing the
nkorz 0:3782bc13e4f5 182 // data, we need to know whether we're writing all 32 bits or just 16. I've
nkorz 0:3782bc13e4f5 183 // overloaded this function call for three cases: write with a mode change (if
nkorz 0:3782bc13e4f5 184 // one is needed), and write with the existing mode.
nkorz 0:3782bc13e4f5 185
nkorz 0:3782bc13e4f5 186 // Adjust the contents of the given register, and, if necessary, switch mode
nkorz 0:3782bc13e4f5 187 // to do so. This is probably the slowest method of updating a register.
nkorz 0:3782bc13e4f5 188 void MiniGen::adjustFreq(FREQREG reg, FREQADJUSTMODE mode, uint32_t newFreq)
nkorz 0:3782bc13e4f5 189 {
nkorz 0:3782bc13e4f5 190 setFreqAdjustMode(mode);
nkorz 0:3782bc13e4f5 191 // Now, we can just call the normal 32-bit write.
nkorz 0:3782bc13e4f5 192 adjustFreq(reg, newFreq);
nkorz 0:3782bc13e4f5 193 }
nkorz 0:3782bc13e4f5 194
nkorz 0:3782bc13e4f5 195 // Fine or coarse update of the given register; change modes if necessary to
nkorz 0:3782bc13e4f5 196 // do this.
nkorz 0:3782bc13e4f5 197 void MiniGen::adjustFreq(FREQREG reg, FREQADJUSTMODE mode, uint16_t newFreq)
nkorz 0:3782bc13e4f5 198 {
nkorz 0:3782bc13e4f5 199 setFreqAdjustMode(mode); // Set the mode
nkorz 0:3782bc13e4f5 200 adjustFreq(reg, newFreq); // Call the known-mode write.
nkorz 0:3782bc13e4f5 201 }
nkorz 0:3782bc13e4f5 202
nkorz 0:3782bc13e4f5 203 // Adjust the contents of the register, but assume that the write mode is
nkorz 0:3782bc13e4f5 204 // already set to full. Note that if it is NOT set to full, bad things will
nkorz 0:3782bc13e4f5 205 // happen- the coarse or fine register will be updated with the contents of
nkorz 0:3782bc13e4f5 206 // the upper 14 bits of the 28 bits you *meant* to send.
nkorz 0:3782bc13e4f5 207 void MiniGen::adjustFreq(FREQREG reg, uint32_t newFreq)
nkorz 0:3782bc13e4f5 208 {
nkorz 0:3782bc13e4f5 209 // We need to split the 32-bit input into two 16-bit values, blank the top
nkorz 0:3782bc13e4f5 210 // two bits of those values, and set the top two bits according to the
nkorz 0:3782bc13e4f5 211 // value of reg.
nkorz 0:3782bc13e4f5 212 // Start by acquiring the low 16-bits...
nkorz 0:3782bc13e4f5 213 uint16_t temp = (uint16_t) (newFreq & 0xFFFF);
nkorz 0:3782bc13e4f5 214 // ...and blanking the first two bits.
nkorz 0:3782bc13e4f5 215 temp &= ~0xC000;
nkorz 0:3782bc13e4f5 216 // Now, set the top two bits according to the reg parameter.
nkorz 0:3782bc13e4f5 217 if (reg==FREQ0) temp |= 0x4000;
nkorz 0:3782bc13e4f5 218 else temp |= 0x8000;
nkorz 0:3782bc13e4f5 219 // Now, we can write temp out to the device.
nkorz 0:3782bc13e4f5 220 SPIWrite(temp);
nkorz 0:3782bc13e4f5 221 // Okay, that's the lower 14 bits. Now let's grab the upper 14.
nkorz 0:3782bc13e4f5 222 temp = (uint16_t)(newFreq>>14);
nkorz 0:3782bc13e4f5 223 // ...and now, we can just repeat the process.
nkorz 0:3782bc13e4f5 224 temp &= ~0xC000;
nkorz 0:3782bc13e4f5 225 // Now, set the top two bits according to the reg parameter.
nkorz 0:3782bc13e4f5 226 if (reg==FREQ0) temp |= 0x4000;
nkorz 0:3782bc13e4f5 227 else temp |= 0x8000;
nkorz 0:3782bc13e4f5 228 // Now, we can write temp out to the device.
nkorz 0:3782bc13e4f5 229 SPIWrite(temp);
nkorz 0:3782bc13e4f5 230 }
nkorz 0:3782bc13e4f5 231
nkorz 0:3782bc13e4f5 232 // Adjust the coarse or fine register, depending on the current mode. Note that
nkorz 0:3782bc13e4f5 233 // if the current adjust mode is FULL, this is going to cause undefined
nkorz 0:3782bc13e4f5 234 // behavior, as it will leave one transfer hanging. Maybe that means only
nkorz 0:3782bc13e4f5 235 // half the register gets loaded? Maybe nothing happens until another write
nkorz 0:3782bc13e4f5 236 // to that register? Either way, it's not going to be good.
nkorz 0:3782bc13e4f5 237 void MiniGen::adjustFreq(FREQREG reg, uint16_t newFreq)
nkorz 0:3782bc13e4f5 238 {
nkorz 0:3782bc13e4f5 239 // We need to blank the first two bits...
nkorz 0:3782bc13e4f5 240 newFreq &= ~0xC000;
nkorz 0:3782bc13e4f5 241 // Now, set the top two bits according to the reg parameter.
nkorz 0:3782bc13e4f5 242 if (reg==FREQ0) newFreq |= 0x4000;
nkorz 0:3782bc13e4f5 243 else newFreq |= 0x8000;
nkorz 0:3782bc13e4f5 244 // Now, we can write newFreq out to the device.
nkorz 0:3782bc13e4f5 245 SPIWrite(newFreq);
nkorz 0:3782bc13e4f5 246 }
nkorz 0:3782bc13e4f5 247
nkorz 0:3782bc13e4f5 248 // Helper function, used to calculate the integer value to be written to a
nkorz 0:3782bc13e4f5 249 // freq register for a desired output frequency.
nkorz 0:3782bc13e4f5 250 // The output frequency is fclk/2^28 * FREQREG. For us, fclk is 16MHz. We can
nkorz 0:3782bc13e4f5 251 // save processor time by specifying a constant for fclk/2^28- .0596. That is,
nkorz 0:3782bc13e4f5 252 // in Hz, the smallest step size for adjusting the output frequency.
nkorz 0:3782bc13e4f5 253 uint32_t MiniGen::freqCalc(float desiredFrequency)
nkorz 0:3782bc13e4f5 254 {
nkorz 0:3782bc13e4f5 255 return (uint32_t) (desiredFrequency/.0596);
nkorz 0:3782bc13e4f5 256 }