Nicholas Korzik / SparkFun_MiniGen_AD9837
Committer:
nkorz
Date:
Wed Dec 12 02:35:13 2018 +0000
Revision:
1:f7f4f5acb9a2
Parent:
0:3782bc13e4f5
Fixed bug that kept chip-select high all the time

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