├── AD5933.cpp ├── AD5933.h ├── LICENSE ├── README.md ├── examples └── ad5933-test │ └── ad5933-test.ino └── keywords.txt /AD5933.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AD5933.cpp 3 | * @brief Library code for AD5933 4 | * 5 | * Library code for AD5933. Referenced the datasheet and code found at 6 | * https://github.com/WuMRC/drive 7 | * 8 | * @author Michael Meli 9 | */ 10 | 11 | #include "AD5933.h" 12 | #include 13 | 14 | /** 15 | * Request to read a byte from the AD5933. 16 | * 17 | * @param address Address of register requesting data from 18 | * @param value Pointer to a byte where the return value should be stored, or 19 | * where the error code will be stored if fail. 20 | * @return Success or failure 21 | */ 22 | int AD5933::getByte(byte address, byte *value) { 23 | // Request to read a byte using the address pointer register 24 | Wire.beginTransmission(AD5933_ADDR); 25 | Wire.write(ADDR_PTR); 26 | Wire.write(address); 27 | 28 | // Ensure transmission worked 29 | if (byte res = Wire.endTransmission() != I2C_RESULT_SUCCESS) { 30 | *value = res; 31 | return false; 32 | } 33 | 34 | // Read the byte from the written address 35 | Wire.requestFrom(AD5933_ADDR, 1); 36 | if (Wire.available()) { 37 | *value = Wire.read(); 38 | return true; 39 | } else { 40 | *value = 0; 41 | return false; 42 | } 43 | } 44 | 45 | /** 46 | * Write a byte to a register on the AD5933. 47 | * 48 | * @param address The register address to write to 49 | * @param value The byte to write to the address 50 | * @return Success or failure of transmission 51 | */ 52 | bool AD5933::sendByte(byte address, byte value) { 53 | // Send byte to address 54 | Wire.beginTransmission(AD5933_ADDR); 55 | Wire.write(address); 56 | Wire.write(value); 57 | 58 | // Check that transmission completed successfully 59 | if (byte res = Wire.endTransmission() != I2C_RESULT_SUCCESS) { 60 | return false; 61 | } else { 62 | return true; 63 | } 64 | } 65 | 66 | /** 67 | * Set the control mode register, CTRL_REG1. This is the register where the 68 | * current command needs to be written to so this is used a lot. 69 | * 70 | * @param mode The control mode to set 71 | * @return Success or failure 72 | */ 73 | bool AD5933::setControlMode(byte mode) { 74 | // Get the current value of the control register 75 | byte val; 76 | if (!getByte(CTRL_REG1, &val)) 77 | return false; 78 | 79 | // Wipe out the top 4 bits...mode bits are bits 5 through 8. 80 | val &= 0x0F; 81 | 82 | // Set the top 4 bits appropriately 83 | val |= mode; 84 | 85 | // Write back to the register 86 | return sendByte(CTRL_REG1, val); 87 | } 88 | 89 | /** 90 | * Reset the AD5933. This interrupts a sweep if one is running, but the start 91 | * frequency, number of increments, and frequency increment register contents 92 | * are not overwritten, but an initialize start frequency command is required 93 | * to restart a frequency sweep. 94 | * 95 | * @return Success or failure 96 | */ 97 | bool AD5933::reset() { 98 | // Get the current value of the control register 99 | byte val; 100 | if (!getByte(CTRL_REG2, &val)) 101 | return false; 102 | 103 | // Set bit D4 for restart 104 | val |= CTRL_RESET; 105 | 106 | // Send byte back 107 | return sendByte(CTRL_REG2, val); 108 | } 109 | 110 | /** 111 | * Set enable temperature measurement. This interferes with frequency sweep 112 | * operation, of course. 113 | * 114 | * @param enable Option to enable to disable temperature measurement. 115 | * @return Success or failure 116 | */ 117 | bool AD5933::enableTemperature(byte enable) { 118 | // If enable, set temp measure bits. If disable, reset to no operation. 119 | if (enable == TEMP_MEASURE) { 120 | return setControlMode(CTRL_TEMP_MEASURE); 121 | } else { 122 | return setControlMode(CTRL_NO_OPERATION); 123 | } 124 | } 125 | 126 | /** 127 | * Get the temperature reading from the AD5933. Waits until a temperature is 128 | * ready. Also ensures temperature measurement mode is active. 129 | * 130 | * @return The temperature in celcius, or -1 if fail. 131 | */ 132 | double AD5933::getTemperature() { 133 | // Set temperature mode 134 | if (enableTemperature(TEMP_MEASURE)) { 135 | // Wait for a valid temperature to be ready 136 | while((readStatusRegister() & STATUS_TEMP_VALID) != STATUS_TEMP_VALID) ; 137 | 138 | // Read raw temperature from temperature registers 139 | byte rawTemp[2]; 140 | if (getByte(TEMP_DATA_1, &rawTemp[0]) && 141 | getByte(TEMP_DATA_2, &rawTemp[1])) 142 | { 143 | // Combine raw temperature bytes into an interger. The ADC 144 | // returns a 14-bit 2's C value where the 14th bit is a sign 145 | // bit. As such, we only need to keep the bottom 13 bits. 146 | int rawTempVal = (rawTemp[0] << 8 | rawTemp[1]) & 0x1FFF; 147 | 148 | // Convert into celcius using the formula given in the 149 | // datasheet. There is a different formula depending on the sign 150 | // bit, which is the 5th bit of the byte in TEMP_DATA_1. 151 | if ((rawTemp[0] & (1<<5)) == 0) { 152 | return rawTempVal / 32.0; 153 | } else { 154 | return (rawTempVal - 16384) / 32.0; 155 | } 156 | } 157 | } 158 | return -1; 159 | } 160 | 161 | 162 | /** 163 | * Set the color source. Choices are between internal and external. 164 | * 165 | * @param source Internal or External clock 166 | * @return Success or failure 167 | */ 168 | bool AD5933::setClockSource(byte source) { 169 | // Determine what source was selected and set it appropriately 170 | switch (source) { 171 | case CLOCK_EXTERNAL: 172 | return sendByte(CTRL_REG2, CTRL_CLOCK_EXTERNAL); 173 | case CLOCK_INTERNAL: 174 | return sendByte(CTRL_REG2, CTRL_CLOCK_INTERNAL); 175 | default: 176 | return false; 177 | } 178 | } 179 | 180 | /** 181 | * Set the clock source to internal or not. 182 | * 183 | * @param internal Whether or not to set the clock source as internal. 184 | * @return Success or failure 185 | */ 186 | bool AD5933::setInternalClock(bool internal) { 187 | // This function is mainly a wrapper for setClockSource() 188 | if (internal) 189 | return setClockSource(CLOCK_INTERNAL); 190 | else 191 | return setClockSource(CLOCK_EXTERNAL); 192 | } 193 | 194 | /** 195 | * Set the settling time cycles use for frequency sweep. 196 | * 197 | * @param time The settling time cycles to set. 198 | * @return Success or failure 199 | */ 200 | bool AD5933::setSettlingCycles(int time) 201 | { 202 | int cycles; 203 | byte settleTime[2], rsTime[2], val; 204 | 205 | settleTime[0] = time & 0xFF; // LSB - 8B 206 | settleTime[1] = (time >> 8) & 0xFF; // MSB - 8A 207 | 208 | cycles = (settleTime[0] | (settleTime[1] & 0x1)); 209 | val = (byte)((settleTime[1] & 0x7) >> 1); 210 | 211 | if ((cycles > 0x1FF) || !(val == 0 || val == 1 || val == 3)) 212 | { 213 | return false; 214 | } 215 | 216 | if (sendByte(NUM_SCYCLES_1, settleTime[1]) && (sendByte(NUM_SCYCLES_2, settleTime[0]))) 217 | { 218 | // Reading values which wrote above 219 | if (getByte(NUM_SCYCLES_1, &rsTime[1]) && getByte(NUM_SCYCLES_2, &rsTime[0])) 220 | { 221 | //checking settling time which send and then read both are same or not 222 | if ((settleTime[0] == rsTime[0]) && (settleTime[1] == rsTime[1])) 223 | { 224 | return true; 225 | } 226 | } 227 | } 228 | return false; 229 | } 230 | 231 | /** 232 | * Set the start frequency for a frequency sweep. 233 | * 234 | * @param start The initial frequency. 235 | * @return Success or failure 236 | */ 237 | bool AD5933::setStartFrequency(unsigned long start) { 238 | // Page 24 of the Datasheet gives the following formula to represent the 239 | // start frequency. 240 | // TODO: Precompute for better performance if we want to keep this constant. 241 | long freqHex = (start / (clockSpeed / 4.0))*pow(2, 27); 242 | if (freqHex > 0xFFFFFF) { 243 | return false; // overflow 244 | } 245 | 246 | // freqHex should be a 24-bit value. We need to break it up into 3 bytes. 247 | byte highByte = (freqHex >> 16) & 0xFF; 248 | byte midByte = (freqHex >> 8) & 0xFF; 249 | byte lowByte = freqHex & 0xFF; 250 | 251 | // Attempt sending all three bytes 252 | return sendByte(START_FREQ_1, highByte) && 253 | sendByte(START_FREQ_2, midByte) && 254 | sendByte(START_FREQ_3, lowByte); 255 | } 256 | 257 | /** 258 | * Set the increment frequency for a frequency sweep. 259 | * 260 | * @param start The frequency to increment by. Max of 0xFFFFFF. 261 | * @return Success or failure 262 | */ 263 | bool AD5933::setIncrementFrequency(unsigned long increment) { 264 | // Page 25 of the Datasheet gives the following formula to represent the 265 | // increment frequency. 266 | // TODO: Precompute for better performance if we want to keep this constant. 267 | long freqHex = (increment / (clockSpeed / 4.0))*pow(2, 27); 268 | if (freqHex > 0xFFFFFF) { 269 | return false; // overflow 270 | } 271 | 272 | // freqHex should be a 24-bit value. We need to break it up into 3 bytes. 273 | byte highByte = (freqHex >> 16) & 0xFF; 274 | byte midByte = (freqHex >> 8) & 0xFF; 275 | byte lowByte = freqHex & 0xFF; 276 | 277 | // Attempt sending all three bytes 278 | return sendByte(INC_FREQ_1, highByte) && 279 | sendByte(INC_FREQ_2, midByte) && 280 | sendByte(INC_FREQ_3, lowByte); 281 | } 282 | 283 | /** 284 | * Set the number of frequency increments for a frequency sweep. 285 | * 286 | * @param start The number of increments to use. Max 511. 287 | * @return Success or failure 288 | */ 289 | bool AD5933::setNumberIncrements(unsigned int num) { 290 | // Check that the number sent in is valid. 291 | if (num > 511) { 292 | return false; 293 | } 294 | 295 | // Divide the 9-bit integer into 2 bytes. 296 | byte highByte = (num >> 8) & 0xFF; 297 | byte lowByte = num & 0xFF; 298 | 299 | // Write to register. 300 | return sendByte(NUM_INC_1, highByte) && 301 | sendByte(NUM_INC_2, lowByte); 302 | } 303 | 304 | /** 305 | * Set the PGA gain factor. 306 | * 307 | * @param gain The gain factor to select. Use constants or 1/5. 308 | * @return Success or failure 309 | */ 310 | bool AD5933::setPGAGain(byte gain) { 311 | // Get the current value of the control register 312 | byte val; 313 | if (!getByte(CTRL_REG1, &val)) 314 | return false; 315 | 316 | // Clear out the bottom bit, D8, which is the PGA gain set bit 317 | val &= 0xFE; 318 | 319 | // Determine what gain factor was selected 320 | if (gain == PGA_GAIN_X1 || gain == 1) { 321 | // Set PGA gain to x1 in CTRL_REG1 322 | val |= PGA_GAIN_X1; 323 | return sendByte(CTRL_REG1, val); 324 | } else if (gain == PGA_GAIN_X5 || gain == 5) { 325 | // Set PGA gain to x5 in CTRL_REG1 326 | val |= PGA_GAIN_X5; 327 | return sendByte(CTRL_REG1, val); 328 | } else { 329 | return false; 330 | } 331 | } 332 | 333 | /** 334 | * Read the value of a register. 335 | * 336 | * @param reg The address of the register to read. 337 | * @return The value of the register. Returns 0xFF if can't read it. 338 | */ 339 | byte AD5933::readRegister(byte reg) { 340 | // Read status register and return it's value. If fail, return 0xFF. 341 | byte val; 342 | if (getByte(reg, &val)) { 343 | return val; 344 | } else { 345 | return STATUS_ERROR; 346 | } 347 | } 348 | 349 | /** 350 | * Set the output voltage range. 351 | * Default value to select in this function is CTRL_OUTPUT_RANGE_1 352 | * 353 | * @param range The output voltage range to select. 354 | * @return Success or failure 355 | */ 356 | bool AD5933::setRange(byte range) 357 | { 358 | byte val; 359 | 360 | // Get the current value of the control register 361 | if(!getByte(CTRL_REG1, &val)) 362 | { 363 | return false; 364 | } 365 | 366 | // Clear out the bottom bit, D9 and D10, which is the output voltage range set bit 367 | val &= 0xF9; 368 | 369 | // Determine what output voltage range was selected 370 | switch (range) 371 | { 372 | case CTRL_OUTPUT_RANGE_2: 373 | // Set output voltage range to 1.0 V p-p typical in CTRL_REG1 374 | val |= CTRL_OUTPUT_RANGE_2; 375 | break; 376 | 377 | case CTRL_OUTPUT_RANGE_3: 378 | // Set output voltage range to 400 mV p-p typical in CTRL_REG1 379 | val |= CTRL_OUTPUT_RANGE_3; 380 | break; 381 | 382 | case CTRL_OUTPUT_RANGE_4: 383 | // Set output voltage range to 200 mV p-p typical in CTRL_REG1 384 | val |= CTRL_OUTPUT_RANGE_4; 385 | break; 386 | 387 | default: 388 | // Set output voltage range to 200 mV p-p typical in CTRL_REG1 389 | val |= CTRL_OUTPUT_RANGE_1; 390 | break; 391 | } 392 | 393 | //Write to register 394 | return sendByte(CTRL_REG1, val); 395 | } 396 | 397 | /** 398 | * Read the value of the status register. 399 | * 400 | * @return The value of the status register. Returns 0xFF if can't read it. 401 | */ 402 | byte AD5933::readStatusRegister() { 403 | return readRegister(STATUS_REG); 404 | } 405 | 406 | /** 407 | * Read the value of the control register. 408 | * 409 | * @return The value of the control register. Returns 0xFFFF if can't read it. 410 | */ 411 | int AD5933::readControlRegister() { 412 | return ((readRegister(CTRL_REG1) << 8) | readRegister(CTRL_REG2)) & 0xFFFF; 413 | } 414 | 415 | /** 416 | * Get a raw complex number for a specific frequency measurement. 417 | * 418 | * @param real Pointer to an int that will contain the real component. 419 | * @param imag Pointer to an int that will contain the imaginary component. 420 | * @return Success or failure 421 | */ 422 | bool AD5933::getComplexData(int *real, int *imag) { 423 | // Wait for a measurement to be available 424 | while ((readStatusRegister() & STATUS_DATA_VALID) != STATUS_DATA_VALID); 425 | 426 | // Read the four data registers. 427 | // TODO: Do this faster with a block read 428 | byte realComp[2]; 429 | byte imagComp[2]; 430 | if (getByte(REAL_DATA_1, &realComp[0]) && 431 | getByte(REAL_DATA_2, &realComp[1]) && 432 | getByte(IMAG_DATA_1, &imagComp[0]) && 433 | getByte(IMAG_DATA_2, &imagComp[1])) 434 | { 435 | // Combine the two separate bytes into a single 16-bit value and store 436 | // them at the locations specified. 437 | *real = (int16_t)(((realComp[0] << 8) | realComp[1]) & 0xFFFF); 438 | *imag = (int16_t)(((imagComp[0] << 8) | imagComp[1]) & 0xFFFF); 439 | 440 | return true; 441 | } else { 442 | *real = -1; 443 | *imag = -1; 444 | return false; 445 | } 446 | } 447 | 448 | /** 449 | * Set the power level of the AD5933. 450 | * 451 | * @param level The power level to choose. Can be on, standby, or down. 452 | * @return Success or failure 453 | */ 454 | bool AD5933::setPowerMode(byte level) { 455 | // Make the appropriate switch. TODO: Does no operation even do anything? 456 | switch (level) { 457 | case POWER_ON: 458 | return setControlMode(CTRL_NO_OPERATION); 459 | case POWER_STANDBY: 460 | return setControlMode(CTRL_STANDBY_MODE); 461 | case POWER_DOWN: 462 | return setControlMode(CTRL_POWER_DOWN_MODE); 463 | default: 464 | return false; 465 | } 466 | } 467 | 468 | /** 469 | * Perform a complete frequency sweep. 470 | * 471 | * @param real An array of appropriate size to hold the real data. 472 | * @param imag An array of appropriate size to hold the imaginary data. 473 | * @param n Length of the array (or the number of discrete measurements) 474 | * @return Success or failure 475 | */ 476 | bool AD5933::frequencySweep(int real[], int imag[], int n) { 477 | // Begin by issuing a sequence of commands 478 | // If the commands aren't taking hold, add a brief delay 479 | if (!(setPowerMode(POWER_STANDBY) && // place in standby 480 | setControlMode(CTRL_INIT_START_FREQ) && // init start freq 481 | setControlMode(CTRL_START_FREQ_SWEEP))) // begin frequency sweep 482 | { 483 | return false; 484 | } 485 | 486 | // Perform the sweep. Make sure we don't exceed n. 487 | int i = 0; 488 | while ((readStatusRegister() & STATUS_SWEEP_DONE) != STATUS_SWEEP_DONE) { 489 | // Make sure we aren't exceeding the bounds of our buffer 490 | if (i >= n) { 491 | return false; 492 | } 493 | 494 | // Get the data for this frequency point and store it in the array 495 | if (!getComplexData(&real[i], &imag[i])) { 496 | return false; 497 | } 498 | 499 | // Increment the frequency and our index. 500 | i++; 501 | setControlMode(CTRL_INCREMENT_FREQ); 502 | } 503 | 504 | // Put into standby 505 | return setPowerMode(POWER_STANDBY); 506 | } 507 | 508 | /** 509 | * Computes the gain factor and phase for each point in a frequency sweep. 510 | * 511 | * @param gain An array of appropriate size to hold the gain factors 512 | * @param phase An array of appropriate size to hold phase data. 513 | * @param ref The known reference resistance. 514 | * @param n Length of the array (or the number of discrete measurements) 515 | * @return Success or failure 516 | */ 517 | bool AD5933::calibrate(double gain[], int phase[], int ref, int n) { 518 | // We need arrays to hold the real and imaginary values temporarily 519 | int *real = new int[n]; 520 | int *imag = new int[n]; 521 | 522 | // Perform the frequency sweep 523 | if (!frequencySweep(real, imag, n)) { 524 | delete [] real; 525 | delete [] imag; 526 | return false; 527 | } 528 | 529 | // For each point in the sweep, calculate the gain factor and phase 530 | for (int i = 0; i < n; i++) { 531 | gain[i] = (double)(1.0/ref)/sqrt(pow(real[i], 2) + pow(imag[i], 2)); 532 | // TODO: phase 533 | } 534 | 535 | delete [] real; 536 | delete [] imag; 537 | return true; 538 | } 539 | 540 | /** 541 | * Computes the gain factor and phase for each point in a frequency sweep. 542 | * Also provides the caller with the real and imaginary data. 543 | * 544 | * @param gain An array of appropriate size to hold the gain factors 545 | * @param phase An array of appropriate size to hold the phase data 546 | * @param real An array of appropriate size to hold the real data 547 | * @param imag An array of appropriate size to hold the imaginary data. 548 | * @param ref The known reference resistance. 549 | * @param n Length of the array (or the number of discrete measurements) 550 | * @return Success or failure 551 | */ 552 | bool AD5933::calibrate(double gain[], int phase[], int real[], int imag[], 553 | int ref, int n) { 554 | // Perform the frequency sweep 555 | if (!frequencySweep(real, imag, n)) { 556 | return false; 557 | } 558 | 559 | // For each point in the sweep, calculate the gain factor and phase 560 | for (int i = 0; i < n; i++) { 561 | gain[i] = (double)(1.0/ref)/sqrt(pow(real[i], 2) + pow(imag[i], 2)); 562 | // TODO: phase 563 | } 564 | 565 | return true; 566 | } 567 | -------------------------------------------------------------------------------- /AD5933.h: -------------------------------------------------------------------------------- 1 | #ifndef AD5933_h 2 | #define AD5933_h 3 | 4 | /** 5 | * Includes 6 | */ 7 | #include 8 | #include 9 | 10 | /** 11 | * AD5933 Register Map 12 | * Datasheet p23 13 | */ 14 | // Device address and address pointer 15 | #define AD5933_ADDR (0x0D) 16 | #define ADDR_PTR (0xB0) 17 | // Control Register 18 | #define CTRL_REG1 (0x80) 19 | #define CTRL_REG2 (0x81) 20 | // Start Frequency Register 21 | #define START_FREQ_1 (0x82) 22 | #define START_FREQ_2 (0x83) 23 | #define START_FREQ_3 (0x84) 24 | // Frequency increment register 25 | #define INC_FREQ_1 (0x85) 26 | #define INC_FREQ_2 (0x86) 27 | #define INC_FREQ_3 (0x87) 28 | // Number of increments register 29 | #define NUM_INC_1 (0x88) 30 | #define NUM_INC_2 (0x89) 31 | // Number of settling time cycles register 32 | #define NUM_SCYCLES_1 (0x8A) 33 | #define NUM_SCYCLES_2 (0x8B) 34 | // Status register 35 | #define STATUS_REG (0x8F) 36 | // Temperature data register 37 | #define TEMP_DATA_1 (0x92) 38 | #define TEMP_DATA_2 (0x93) 39 | // Real data register 40 | #define REAL_DATA_1 (0x94) 41 | #define REAL_DATA_2 (0x95) 42 | // Imaginary data register 43 | #define IMAG_DATA_1 (0x96) 44 | #define IMAG_DATA_2 (0x97) 45 | 46 | /** 47 | * Constants 48 | * Constants for use with the AD5933 library class. 49 | */ 50 | // Temperature measuring 51 | #define TEMP_MEASURE (CTRL_TEMP_MEASURE) 52 | #define TEMP_NO_MEASURE (CTRL_NO_OPERATION) 53 | // Clock sources 54 | #define CLOCK_INTERNAL (CTRL_CLOCK_INTERNAL) 55 | #define CLOCK_EXTERNAL (CTRL_CLOCK_EXTERNAL) 56 | // PGA gain options 57 | #define PGA_GAIN_X1 (CTRL_PGA_GAIN_X1) 58 | #define PGA_GAIN_X5 (CTRL_PGA_GAIN_X5) 59 | // Power modes 60 | #define POWER_STANDBY (CTRL_STANDBY_MODE) 61 | #define POWER_DOWN (CTRL_POWER_DOWN_MODE) 62 | #define POWER_ON (CTRL_NO_OPERATION) 63 | // I2C result success/fail 64 | #define I2C_RESULT_SUCCESS (0) 65 | #define I2C_RESULT_DATA_TOO_LONG (1) 66 | #define I2C_RESULT_ADDR_NAK (2) 67 | #define I2C_RESULT_DATA_NAK (3) 68 | #define I2C_RESULT_OTHER_FAIL (4) 69 | // Control output voltage range options 70 | #define CTRL_OUTPUT_RANGE_1 (0b00000000) 71 | #define CTRL_OUTPUT_RANGE_2 (0b00000110) 72 | #define CTRL_OUTPUT_RANGE_3 (0b00000100) 73 | #define CTRL_OUTPUT_RANGE_4 (0b00000010) 74 | // Control register options 75 | #define CTRL_NO_OPERATION (0b00000000) 76 | #define CTRL_INIT_START_FREQ (0b00010000) 77 | #define CTRL_START_FREQ_SWEEP (0b00100000) 78 | #define CTRL_INCREMENT_FREQ (0b00110000) 79 | #define CTRL_REPEAT_FREQ (0b01000000) 80 | #define CTRL_TEMP_MEASURE (0b10010000) 81 | #define CTRL_POWER_DOWN_MODE (0b10100000) 82 | #define CTRL_STANDBY_MODE (0b10110000) 83 | #define CTRL_RESET (0b00010000) 84 | #define CTRL_CLOCK_EXTERNAL (0b00001000) 85 | #define CTRL_CLOCK_INTERNAL (0b00000000) 86 | #define CTRL_PGA_GAIN_X1 (0b00000001) 87 | #define CTRL_PGA_GAIN_X5 (0b00000000) 88 | // Status register options 89 | #define STATUS_TEMP_VALID (0x01) 90 | #define STATUS_DATA_VALID (0x02) 91 | #define STATUS_SWEEP_DONE (0x04) 92 | #define STATUS_ERROR (0xFF) 93 | // Frequency sweep parameters 94 | #define SWEEP_DELAY (1) 95 | 96 | /** 97 | * AD5933 Library class 98 | * Contains mainly functions for interfacing with the AD5933. 99 | */ 100 | class AD5933 { 101 | public: 102 | // Reset the board 103 | static bool reset(void); 104 | 105 | // Temperature measuring 106 | static bool enableTemperature(byte); 107 | static double getTemperature(void); 108 | 109 | // Clock 110 | static bool setClockSource(byte); 111 | static bool setInternalClock(bool); 112 | bool setSettlingCycles(int); 113 | 114 | // Frequency sweep configuration 115 | static bool setStartFrequency(unsigned long); 116 | static bool setIncrementFrequency(unsigned long); 117 | static bool setNumberIncrements(unsigned int); 118 | 119 | // Gain configuration 120 | static bool setPGAGain(byte); 121 | 122 | // Excitation range configuration 123 | bool setRange(byte); 124 | 125 | // Read registers 126 | static byte readRegister(byte); 127 | static byte readStatusRegister(void); 128 | static int readControlRegister(void); 129 | 130 | // Impedance data 131 | static bool getComplexData(int*, int*); 132 | 133 | // Set control mode register (CTRL_REG1) 134 | static bool setControlMode(byte); 135 | 136 | // Power mode 137 | static bool setPowerMode(byte); 138 | 139 | // Perform frequency sweeps 140 | static bool frequencySweep(int real[], int imag[], int); 141 | static bool calibrate(double gain[], int phase[], int ref, int n); 142 | static bool calibrate(double gain[], int phase[], int real[], 143 | int imag[], int ref, int n); 144 | private: 145 | // Private data 146 | static const unsigned long clockSpeed = 16776000; 147 | 148 | // Sending/Receiving byte method, for easy re-use 149 | static int getByte(byte, byte*); 150 | static bool sendByte(byte, byte); 151 | }; 152 | 153 | #endif 154 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Michael Meli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AD5933 Arduino Library 2 | 3 | This is a simple library for using the AD5933 impedance convert system with an Arduino compatible device. 4 | 5 | ## AD5933 6 | The AD5933 is developed by Analog Devices. [From the AD5933 page](http://www.analog.com/en/products/rf-microwave/direct-digital-synthesis-modulators/ad5933.html#product-overview): 7 | 8 | >The AD5933 is a high precision impedance converter system solution that combines an on-board frequency generator with a 12-bit, 1 MSPS, analog-to-digital converter (ADC). The frequency generator allows an external complex impedance to be excited with a known frequency. The response signal from the impedance is sampled by the on-board ADC and a discrete Fourier transform (DFT) is processed by an on-board DSP engine. The DFT algorithm returns a real (R) and imaginary (I) data-word at each output frequency. 9 | 10 | >Once calibrated, the magnitude of the impedance and relative phase of the impedance at each frequency point along the sweep is easily calculated. This is done off chip using the real and imaginary register contents, which can be read from the serial I2C interface. 11 | 12 | In other words, the AD5933 lets you measure the complex impedance of something. 13 | 14 | ## Library 15 | 16 | ### Compatibility 17 | This library *should* be compatible with any Arduino compatible device, albeit perhaps with some changes. It was developed and tested with an RFduino, but I do not see a reason why it would not work with a regular Arduino. 18 | 19 | ### Missing Features 20 | While the library is enough to get impedance readings, I must admit that it is somewhat incomplete. The following features are yet to be implemented (and likely will not be implemented by me): 21 | 22 | * Configure the AD5933 excitation range 23 | * When performing calibration, calibrate the phase such that the phase of impedance readings can be analyzed 24 | 25 | ### Installation 26 | Simply move the entire folder `arduino-ad5933` to your `Arduino/libraries` folder, usually in your home directory or documents folder. 27 | 28 | ### Usage 29 | 30 | #### Example 31 | Perhaps the easiest way to see how to use the library is to look at the example code in the `examples` directory. Once you install the library, you can easily open this code in the Arduino editor by going to `File > Examples > arduino-ad5933 > ad5933-test`. 32 | 33 | This example will show you how to initially setup the AD5933 and run frequency sweeps. There are two methods for doing a frequency sweep. The first is by using the `frequencySweep()` function. This function is very easy to use, but you have to wait until the entire sweep is complete before handling data in bulk and this requires more memory, especially for large sweeps. The other method is by manipulating the AD5933 at the I2C level directly, which is slightly more complex, but allows you to handle data immediately after a reading and has significantly lower memory overhead. 34 | 35 | #### Brief Overview 36 | There are an assortment of functions in `AD5933.h` that can be used with numerous constants. Each one of the functions are static, so be sure to include `AD5933::` in front. Here I cover a few of the main ones. 37 | 38 | NOTE: Many of these functions return booleans indicating their success. This may be useful for debugging. 39 | 40 | To reset the board: `AD5933::reset();` 41 | 42 | To enable on-board temperature measurement: `AD5933::enableTemperature()` 43 | 44 | To get a temperature reading: `double temp = AD5933::getTemperature()` 45 | 46 | To set the clock source to internal: `AD5933::setInternalClock(true)` OR `AD5933::setClockSource(CTRL_CLOCK_INTERNAL)` 47 | 48 | To set the clock source to external: `AD5933::setClockSource(CTRL_CLOCK_EXTERNAL)` 49 | 50 | To set the frequency sweep start frequency: `AD5933::setStartFrequency(#)` 51 | 52 | To set the frequency sweep increment frequency: `AD5933::setIncrementFrequency(#)` 53 | 54 | To set the frequency sweep number of increments: `AD5933::setNumberIncrements(#)` 55 | 56 | To set the PGA gain: `AD5933::setPGAGain(PGA_GAIN_X1/PGA_GAIN_X5)` 57 | 58 | To set the power mode: `AD5933:setPowerMode(POWER_ON/POWER_DOWN/POWER_STANDBY)` 59 | 60 | To perform a calibration sweep, which computes gain factor for each frequency step based on a known reference resistance (the results are stored in the gainFactor array, so make sure this is large enough): `AD5933::calibration(double[] gainFactor, double[] phase, int referenceResistor, int numIncrements)` 61 | 62 | To perform an entire frequency sweep (the results are stored in the real and imaginary arrays): `AD5933::frequencySweep(int[] real, int[] imag, int numIncrements)` 63 | 64 | To read a register: `byte res = AD5933::readRegister(/* register address */)` 65 | 66 | That is sufficient for most things. Check out the header file for more functions and constants. Also check out the example code for better illustrations of usage. 67 | 68 | ## Reference 69 | A lot of code was reference from [this GitHub repo](https://github.com/WuMRC/drive). I made a fair amount of modifications, but the repo can also probably be used for additional sample code if needed. 70 | -------------------------------------------------------------------------------- /examples/ad5933-test/ad5933-test.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ad5933-test 3 | Reads impedance values from the AD5933 over I2C and prints them serially. 4 | */ 5 | 6 | #include 7 | #include "AD5933.h" 8 | 9 | #define START_FREQ (80000) 10 | #define FREQ_INCR (1000) 11 | #define NUM_INCR (40) 12 | #define REF_RESIST (10000) 13 | 14 | double gain[NUM_INCR+1]; 15 | int phase[NUM_INCR+1]; 16 | 17 | void setup(void) 18 | { 19 | // Begin I2C 20 | Wire.begin(); 21 | 22 | // Begin serial at 9600 baud for output 23 | Serial.begin(9600); 24 | Serial.println("AD5933 Test Started!"); 25 | 26 | // Perform initial configuration. Fail if any one of these fail. 27 | if (!(AD5933::reset() && 28 | AD5933::setInternalClock(true) && 29 | AD5933::setStartFrequency(START_FREQ) && 30 | AD5933::setIncrementFrequency(FREQ_INCR) && 31 | AD5933::setNumberIncrements(NUM_INCR) && 32 | AD5933::setPGAGain(PGA_GAIN_X1))) 33 | { 34 | Serial.println("FAILED in initialization!"); 35 | while (true) ; 36 | } 37 | 38 | // Perform calibration sweep 39 | if (AD5933::calibrate(gain, phase, REF_RESIST, NUM_INCR+1)) 40 | Serial.println("Calibrated!"); 41 | else 42 | Serial.println("Calibration failed..."); 43 | } 44 | 45 | void loop(void) 46 | { 47 | // Easy to use method for frequency sweep 48 | frequencySweepEasy(); 49 | 50 | // Delay 51 | delay(5000); 52 | 53 | // Complex but more robust method for frequency sweep 54 | frequencySweepRaw(); 55 | 56 | // Delay 57 | delay(5000); 58 | } 59 | 60 | // Easy way to do a frequency sweep. Does an entire frequency sweep at once and 61 | // stores the data into arrays for processing afterwards. This is easy-to-use, 62 | // but doesn't allow you to process data in real time. 63 | void frequencySweepEasy() { 64 | // Create arrays to hold the data 65 | int real[NUM_INCR+1], imag[NUM_INCR+1]; 66 | 67 | // Perform the frequency sweep 68 | if (AD5933::frequencySweep(real, imag, NUM_INCR+1)) { 69 | // Print the frequency data 70 | int cfreq = START_FREQ/1000; 71 | for (int i = 0; i < NUM_INCR+1; i++, cfreq += FREQ_INCR/1000) { 72 | // Print raw frequency data 73 | Serial.print(cfreq); 74 | Serial.print(": R="); 75 | Serial.print(real[i]); 76 | Serial.print("/I="); 77 | Serial.print(imag[i]); 78 | 79 | // Compute impedance 80 | double magnitude = sqrt(pow(real[i], 2) + pow(imag[i], 2)); 81 | double impedance = 1/(magnitude*gain[i]); 82 | Serial.print(" |Z|="); 83 | Serial.println(impedance); 84 | } 85 | Serial.println("Frequency sweep complete!"); 86 | } else { 87 | Serial.println("Frequency sweep failed..."); 88 | } 89 | } 90 | 91 | // Removes the frequencySweep abstraction from above. This saves memory and 92 | // allows for data to be processed in real time. However, it's more complex. 93 | void frequencySweepRaw() { 94 | // Create variables to hold the impedance data and track frequency 95 | int real, imag, i = 0, cfreq = START_FREQ/1000; 96 | 97 | // Initialize the frequency sweep 98 | if (!(AD5933::setPowerMode(POWER_STANDBY) && // place in standby 99 | AD5933::setControlMode(CTRL_INIT_START_FREQ) && // init start freq 100 | AD5933::setControlMode(CTRL_START_FREQ_SWEEP))) // begin frequency sweep 101 | { 102 | Serial.println("Could not initialize frequency sweep..."); 103 | } 104 | 105 | // Perform the actual sweep 106 | while ((AD5933::readStatusRegister() & STATUS_SWEEP_DONE) != STATUS_SWEEP_DONE) { 107 | // Get the frequency data for this frequency point 108 | if (!AD5933::getComplexData(&real, &imag)) { 109 | Serial.println("Could not get raw frequency data..."); 110 | } 111 | 112 | // Print out the frequency data 113 | Serial.print(cfreq); 114 | Serial.print(": R="); 115 | Serial.print(real); 116 | Serial.print("/I="); 117 | Serial.print(imag); 118 | 119 | // Compute impedance 120 | double magnitude = sqrt(pow(real, 2) + pow(imag, 2)); 121 | double impedance = 1/(magnitude*gain[i]); 122 | Serial.print(" |Z|="); 123 | Serial.println(impedance); 124 | 125 | // Increment the frequency 126 | i++; 127 | cfreq += FREQ_INCR/1000; 128 | AD5933::setControlMode(CTRL_INCREMENT_FREQ); 129 | } 130 | 131 | Serial.println("Frequency sweep complete!"); 132 | 133 | // Set AD5933 power mode to standby when finished 134 | if (!AD5933::setPowerMode(POWER_STANDBY)) 135 | Serial.println("Could not set to standby..."); 136 | } 137 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | AD5933 KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | reset KEYWORD2 16 | setTemperature KEYWORD2 17 | getTemperature KEYWORD2 18 | setClockSource KEYWORD2 19 | setInternalClock KEYWORD2 20 | setStartFrequency KEYWORD2 21 | read_bytes KEYWORD2 22 | setIncrementFrequency KEYWORD2 23 | setNumberIncrements KEYWORD2 24 | setPGAGain KEYWORD2 25 | readRegister KEYWORD2 26 | readStatusRegister KEYWORD2 27 | readControlRegister KEYWORD2 28 | getComplexData KEYWORD2 29 | setControlMode KEYWORD2 30 | setRange KEYWORD2 31 | frequencySweep KEYWORD2 32 | setPowerMode KEYWORD2 33 | calibrate KEYWORD2 34 | 35 | ####################################### 36 | # Instances (KEYWORD2) 37 | ####################################### 38 | 39 | 40 | ####################################### 41 | # Constants (LITERAL1) 42 | ####################################### 43 | AD5933_ADDR LITERAL1 44 | ADDR_PTR LITERAL1 45 | CTRL_REG1 LITERAL1 46 | CTRL_REG2 LITERAL1 47 | START_FREQ_1 LITERAL1 48 | START_FREQ_2 LITERAL1 49 | START_FREQ_3 LITERAL1 50 | INC_FREQ_1 LITERAL1 51 | INC_FREQ_2 LITERAL1 52 | INC_FREQ_3 LITERAL1 53 | NUM_INC_1 LITERAL1 54 | NUM_INC_2 LITERAL1 55 | NUM_SCYCLES_1 LITERAL1 56 | NUM_SCYCLES_2 LITERAL1 57 | STATUS_REG LITERAL1 58 | TEMP_DATA_1 LITERAL1 59 | TEMP_DATA_2 LITERAL1 60 | REAL_DATA_1 LITERAL1 61 | REAL_DATA_2 LITERAL1 62 | IMAG_DATA_1 LITERAL1 63 | IMAG_DATA_2 LITERAL1 64 | TEMP_MEASURE LITERAL1 65 | TEMP_NO_MEASURE LITERAL1 66 | CLOCK_INTERNAL LITERAL1 67 | CLOCK_EXTERNAL LITERAL1 68 | PGA_GAIN_X1 LITERAL1 69 | PGA_GAIN_X5 LITERAL1 70 | POWER_STANDBY LITERAL1 71 | POWER_DOWN LITERAL1 72 | POWER_ON LITERAL1 73 | I2C_RESULT_SUCCESS LITERAL1 74 | I2C_RESULT_DATA_TOO_LONG LITERAL1 75 | I2C_RESULT_ADDR_NAK LITERAL1 76 | I2C_RESULT_DATA_NAK LITERAL1 77 | I2C_RESULT_OTHER_FAIL LITERAL1 78 | CTRL_NO_OPERATION LITERAL1 79 | CTRL_INIT_START_FREQ LITERAL1 80 | CTRL_START_FREQ_SWEEP LITERAL1 81 | CTRL_INCREMENT_FREQ LITERAL1 82 | CTRL_REPEAT_FREQ LITERAL1 83 | CTRL_TEMP_MEASURE LITERAL1 84 | CTRL_POWER_DOWN_MODE LITERAL1 85 | CTRL_STANDBY_MODE LITERAL1 86 | CTRL_RESET LITERAL1 87 | CTRL_CLOCK_EXTERNAL LITERAL1 88 | CTRL_CLOCK_INTERNAL LITERAL1 89 | CTRL_PGA_GAIN_X1 LITERAL1 90 | CTRL_PGA_GAIN_X5 LITERAL1 91 | STATUS_TEMP_VALID LITERAL1 92 | STATUS_DATA_VALID LITERAL1 93 | STATUS_SWEEP_DONE LITERAL1 94 | STATUS_ERROR LITERAL1 95 | SWEEP_DELAY LITERAL1 96 | --------------------------------------------------------------------------------